pax_global_header00006660000000000000000000000064142556256360014530gustar00rootroot0000000000000052 comment=27dc3425a6f1e00a3621ed7442a34513d5ad4ead GlassCoder-2.0.1/000077500000000000000000000000001425562563600135565ustar00rootroot00000000000000GlassCoder-2.0.1/.gitignore000066400000000000000000000134461425562563600155560ustar00rootroot00000000000000*~ *.1 *.7 *.buildinfo *.changes *.ddeb *.deb *.dsc *.fo *.html *.la *.lo *.o *.rpm *.spec *.tar.gz *.tar.xz .deps aclocal.m4 autom4te.cache build_debs.sh compile config.guess config.log config.status config.sub configure debian/changelog debian/control depcomp docs/glasscoder.xml install-sh Makefile Makefile.in missing moc_* plugins/install_pypad.sh src/common/paths.h src/glasscoder/asihpi.cpp src/glasscoder/asihpi.h src/glasscoder/audiodevice.cpp src/glasscoder/audiodevice.h src/glasscoder/cmdswitch.cpp src/glasscoder/cmdswitch.h src/glasscoder/codec.cpp src/glasscoder/codec.h src/glasscoder/connector.cpp src/glasscoder/connector.h src/glasscoder/glasscoder src/glasscoder/glassconv src/glasscoder/glasslimits.h src/glasscoder/logging.cpp src/glasscoder/logging.h src/glasscoder/metaevent.cpp src/glasscoder/metaevent.h src/glasscoder/paths.h src/glasscoder/ringbuffer.cpp src/glasscoder/ringbuffer.h src/glasscoder/codecdialog.cpp src/glasscoder/codecdialog.h src/glasscoder/codeviewer.cpp src/glasscoder/codeviewer.h src/glasscoder/combobox.cpp src/glasscoder/combobox.h src/glasscoder/guiapplication.cpp src/glasscoder/guiapplication.h src/glasscoder/hpiinputlistview.cpp src/glasscoder/hpiinputlistview.h src/glasscoder/hpiwidget.cpp src/glasscoder/hpiwidget.h src/glasscoder/messagewidget.cpp src/glasscoder/messagewidget.h src/glasscoder/profile.cpp src/glasscoder/profile.h src/glasscoder/segmeter.cpp src/glasscoder/segmeter.h src/glasscoder/serverdialog.cpp src/glasscoder/serverdialog.h src/glasscoder/sourcedialog.cpp src/glasscoder/sourcedialog.h src/glasscoder/spinbox.cpp src/glasscoder/spinbox.h src/glasscoder/statuswidget.cpp src/glasscoder/statuswidget.h src/glasscoder/stereometer.cpp src/glasscoder/stereometer.h src/glasscoder/streamdialog.cpp src/glasscoder/streamdialog.h src/glasscommander/asihpi.cpp src/glasscommander/asihpi.h src/glasscommander/audiodevice.cpp src/glasscommander/audiodevice.h src/glasscommander/cmdswitch.cpp src/glasscommander/cmdswitch.h src/glasscommander/codec.cpp src/glasscommander/codec.h src/glasscommander/codecdialog.cpp src/glasscommander/codecdialog.h src/glasscommander/codeviewer.cpp src/glasscommander/codeviewer.h src/glasscommander/combobox.cpp src/glasscommander/combobox.h src/glasscommander/connector.cpp src/glasscommander/connector.h src/glasscommander/glasscommander src/glasscommander/glasslimits.h src/glasscommander/guiapplication.cpp src/glasscommander/guiapplication.h src/glasscommander/hpiinputlistview.cpp src/glasscommander/hpiinputlistview.h src/glasscommander/hpiwidget.cpp src/glasscommander/hpiwidget.h src/glasscommander/logging.cpp src/glasscommander/logging.h src/glasscommander/messagewidget.cpp src/glasscommander/messagewidget.h src/glasscommander/metaevent.cpp src/glasscommander/metaevent.h src/glasscommander/paths.h src/glasscommander/profile.cpp src/glasscommander/profile.h src/glasscommander/ringbuffer.cpp src/glasscommander/ringbuffer.h src/glasscommander/segmeter.cpp src/glasscommander/segmeter.h src/glasscommander/serverdialog.cpp src/glasscommander/serverdialog.h src/glasscommander/sourcedialog.cpp src/glasscommander/sourcedialog.h src/glasscommander/spinbox.cpp src/glasscommander/spinbox.h src/glasscommander/statuswidget.cpp src/glasscommander/statuswidget.h src/glasscommander/stereometer.cpp src/glasscommander/stereometer.h src/glasscommander/streamdialog.cpp src/glasscommander/streamdialog.h src/glassgui/asihpi.cpp src/glassgui/asihpi.h src/glassgui/audiodevice.cpp src/glassgui/audiodevice.h src/glassgui/cmdswitch.cpp src/glassgui/cmdswitch.h src/glassgui/codec.cpp src/glassgui/codec.h src/glassgui/connector.cpp src/glassgui/connector.h src/glassgui/glassgui src/glassgui/glasslimits.h src/glassgui/logging.cpp src/glassgui/logging.h src/glassgui/metaevent.cpp src/glassgui/metaevent.h src/glassgui/paths.h src/glassgui/ringbuffer.cpp src/glassgui/ringbuffer.h src/glassgui/codecdialog.cpp src/glassgui/codecdialog.h src/glassgui/codeviewer.cpp src/glassgui/codeviewer.h src/glassgui/combobox.cpp src/glassgui/combobox.h src/glassgui/guiapplication.cpp src/glassgui/guiapplication.h src/glassgui/hpiinputlistview.cpp src/glassgui/hpiinputlistview.h src/glassgui/hpiwidget.cpp src/glassgui/hpiwidget.h src/glassgui/messagewidget.cpp src/glassgui/messagewidget.h src/glassgui/profile.cpp src/glassgui/profile.h src/glassgui/segmeter.cpp src/glassgui/segmeter.h src/glassgui/serverdialog.cpp src/glassgui/serverdialog.h src/glassgui/sourcedialog.cpp src/glassgui/sourcedialog.h src/glassgui/spinbox.cpp src/glassgui/spinbox.h src/glassgui/statuswidget.cpp src/glassgui/statuswidget.h src/glassgui/stereometer.cpp src/glassgui/stereometer.h src/glassgui/streamdialog.cpp src/glassgui/streamdialog.h src/tests/asihpi.cpp src/tests/asihpi.h src/tests/audiodevice.cpp src/tests/audiodevice.h src/tests/cmdswitch.cpp src/tests/cmdswitch.h src/tests/codec.cpp src/tests/codec.h src/tests/connector.cpp src/tests/connector.h src/tests/glasslimits.h src/tests/logging.cpp src/tests/logging.h src/tests/metaevent.cpp src/tests/metaevent.h src/tests/paths.h src/tests/pipe_connect src/tests/ringbuffer.cpp src/tests/ringbuffer.h src/tests/codecdialog.cpp src/tests/codecdialog.h src/tests/codeviewer.cpp src/tests/codeviewer.h src/tests/combobox.cpp src/tests/combobox.h src/tests/guiapplication.cpp src/tests/guiapplication.h src/tests/hpiinputlistview.cpp src/tests/hpiinputlistview.h src/tests/hpiwidget.cpp src/tests/hpiwidget.h src/tests/messagewidget.cpp src/tests/messagewidget.h src/tests/profile.cpp src/tests/profile.h src/tests/segmeter.cpp src/tests/segmeter.h src/tests/serverdialog.cpp src/tests/serverdialog.h src/tests/sourcedialog.cpp src/tests/sourcedialog.h src/tests/spinbox.cpp src/tests/spinbox.h src/tests/statuswidget.cpp src/tests/statuswidget.h src/tests/stereometer.cpp src/tests/stereometer.h src/tests/streamdialog.cpp src/tests/streamdialog.h src/tests/urldecode src/tests/urlencode GlassCoder-2.0.1/AUTHORS000066400000000000000000000001601425562563600146230ustar00rootroot00000000000000The following have contributed to GlassCoder: Fred Gleason Application Architect GlassCoder-2.0.1/CODINGSTYLE000066400000000000000000000176071425562563600153000ustar00rootroot00000000000000This is the CODINGSTYLE file for the GlassCoder package. OVERVIEW: This file, CODINGSTYLE, describes the coding style guidelines for writing new code, how to submit patches to be incorporated into the official GlassCoder Git repository, and other code related information. General info on the GlassCoder project can be found at https://github.com/ElvishArtisan/GlassCoder as well as in the 'README' and 'INSTALL' files. The code style used for GlassCoder is a somewhat idiosyncratic mixture of the style generally used for Qt C++ programs combined with the classic UNIX C style. Some of the specifics include: LINE LENGTH: Should not be longer than 78 characters unless doing so would severely compromise the readability of the code. Where it is necessary to break a line, it should be done at a point that preserves maximum clarity and ease of reading. Good: *report+=QString(" ")+ logLine(i)->startTime(RDLogLine::Logged).toString("hh:mm:ss")+ QString().sprintf(" - cart %06d [",logLine(i)->cartNumber())+ q->value(1).toString()+"] "+QObject::tr("is not playable")+"\n"; Bad: *report+=QString(" ")+logLine(i)->startTime(RDLogLine::Logged).toString("hh:mm:ss")+QString().sprintf(" - cart %06d [",logLine(i)->cartNumber())+q->value(1).toString()+"] "+QObject::tr("is not playable")+"\n"; INDENTATION: Should be two spaces per level. This helps to keep the line length down. Good: if(to_line<0) { to_line=size(); for(int i=from_line;itimeType()==RDLogLine::Hard) { to_line=i; i=size(); if(sched_time!=NULL) { *sched_time=logLine(i)->startTime(RDLogLine::Logged); } } } } Bad: if(to_line<0) { to_line=size(); for(int i=from_line;itimeType()==RDLogLine::Hard) { to_line=i; i=size(); if(sched_time!=NULL) { *sched_time=logLine(i)->startTime(RDLogLine::Logged); } } } } CURLY BRACES: Conditional statements (such as 'if' and 'for') should *always* use curly braces, even where the affected block is but one line long. The opening brace should be on the same line as the conditional and the closing one on a line by itself. This style greatly facilitates debugging, allowing a single line to be provisionally commented out or additional lines to be provisionally added without making the enclosing conditional syntactically invalid. Good: if(i==12) { printf("i is equal to twelve!\n"); } Bad: if(i==12) printf("i is equal to twelve!\n"); PADDING WHITESPACE: Wherever possible, there should be no whitespace between constants/variables and operators. This helps to keep the line length down. Good: for(int i=from_line;itimeType()==RDLogLine::Hard) { to_line=i; } } Bad: for(int i = from_line; i < size(); i++) { if(logLine(i)->timeType() == RDLogLine::Hard) { to_line = i; } } CLASS NAMES: Should have the initial letter of each word capitalized, e.g. 'ThisIsMyClass'. METHOD NAMES: Public method names as well as signal and slot method names should follow the general style used by Qt. A special convention for GlassCoder is to reserve names beginning with an uppercase letter for private methods only. Good: class LogPlayer { Q_OBJECT public: LogPlayer(int id,RDEventPlayer *player,QObject *parent=0); QString serviceName() const; void setServiceName(const QString &svcname); private slots: void transTimerData(); void graceTimerData(); signals: void renamed(); void reloaded(); void transportChanged(); private: bool StartEvent(int line,RDLogLine::TransType trans_type,int trans_length, RDLogLine::StartSource src,int mport=-1,int duck_length=0); bool StartAudioEvent(int line); }; Bad: class LogPlayer { Q_OBJECT public: LogPlayer(int id,RDEventPlayer *player,QObject *parent=0); QString servicename() const; void set_service_name(const QString &svcname); private slots: void TransTimerData(); void grace_timer_data(); signals: void RENAMED(); void Reloaded(); void transport_changed(); private: bool startEvent(int line,RDLogLine::TransType trans_type,int trans_length, RDLogLine::StartSource src,int mport=-1,int duck_length=0); bool startAudioEvent(int line); }; VARIABLE NAMES: *All* variables should be lowercase only, with uppercase being reserved for class and method names. Words should be separated by underscores. Good: int log_position_number=1; Bad: int logPositionNumnber=1; Class variables should be prefaced with a short base name that is common to all, followed by an underscore. For example, the class 'MyClass' might use 'myclass_', as in 'myclass_foo1', 'myclass_foo2', etc. Local variables (including function parameter names) should be kept short, preferably a single word. WRITING TO THE SYSLOG: GlassCoder makes extensive use of the syslog(3) system found on all POSIX-compliant systems. Sending messages to the syslog should always be done by means of the following function: #include "logging.h" void Log(int prio,const QString &msg); For a discussion of the parameters of these methods, see the syslog(3) man page. The 'prio' parameter should be one of the following values: LOG_ERR - Indicates that a fatal error has occurred; 'fatal' meaning that the program is unable to continue and will now exit. LOG_WARNING - Indicates that a non-fatal error has occured; meaning that the program will continue to operate, but with possibly significant operational degradation. This would be appropriate for things like failure to connect to an external switcher or other device. LOG_INFO - Information useful for tracking operational state --e.g. the player has switched to a different sub-stream utilizing a different bitrate, etc. LOG_DEBUG - Information useful for tracking or verifying GlassCoder software internal state. These messages will not normally be seen by users, but can be made visible to allow for program debugging. CONTRIBUTING CHANGES: The master code repository for GlassCoder resides at GitHub, and can be found at: https://github.com/ElvishArtisan/GlassCoder Changes should be submitted in the form of a pull request [PR] against the 'master' branch. Information about drafting and submitting PRs can be found at: https://help.github.com/en/articles/about-pull-requests PULL REQUEST CHECKLIST: Before submitting a pull request, the following guidelines should be completed: 1) The code should compile without errors or warnings [the '-Werrors' switch for gcc(1) is your friend here!]. 2) Add an entry to the 'ChangeLog' file at the base of the GlassCoder source code tree, describing your changes. The format of the ChangeLog file has the most recent changes at the bottom of the file. Entries start with a date stamp and have a format like: YYYY-MM-DD NAME * Description of change For example: 2007-02-23 John Coder * Modified the code in 'lib/rdimport_audio.cpp' to use the 'RDCart::setMetadata()' and 'RDCut::setMetadata()' methods. 3) If your change alters any user-visible aspect (UI or behavior), update the user documentation appropriately. The documentation is written in DocBook 5 markup, and can be found at the following locations in the source tree: Manual pages - 'docs/' 4) If you wish your work to be mentioned in the 'AUTHORS' file, add or modify the appropriate entry there. Entries should be sorted by surname, then christian name of the author. QUESTIONS: Questions about coding style, or indeed any aspect of GlassCoder development, are welcomed on the Rivendell-prog mailing list. Subscription information and list archives are available at: http://caspian.paravelsystems.com/mailman/listinfo/rivendell-prog GlassCoder-2.0.1/COPYING000066400000000000000000000431101425562563600146100ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. GlassCoder-2.0.1/ChangeLog000066400000000000000000001476111425562563600153420ustar00rootroot000000000000002014-02-17 Fred Gleason * Initial package creation. 2014-02-18 Fred Gleason * Implemented basic MPEG-1 Layer 3 streaming. 2014-02-18 Fred Gleason * Refactored sources into separate components for Jack, Lame and Shout. 2014-02-18 Fred Gleason * Implemented sample rate conversion. 2014-02-18 Fred Gleason * Implemented the 'make rpm' target. 2014-02-18 Fred Gleason * Changed the names of the input ports from 'sirion_' to 'input_'. 2014-02-24 Fred Gleason * Added '--stream-description', '--stream-genre', '--stream-name' and '--stream-url' options. 2014-02-24 Fred Gleason * Incremented the package version to 0.5.0. 2014-03-25 Fred Gleason * Changed the project name to 'glasscoder'. 2014-03-25 Fred Gleason * Updated 'README'. 2014-03-25 Fred Gleason * Added manpage for glasscoder in 'docs/glasscoder.1'. 2014-03-25 Fred Gleason * Added 'IcyConnection' class in 'src/icyconnection.[cpp,h]. 2014-03-26 Fred Gleason * Removed 'libshout' dependency. 2014-03-29 Fred Gleason * Added a 'Connector' base class in 'src/connector.cpp' and 'src/connector.h'. * Added an 'IcyConnector' class in 'src/icyconnector.cpp' and 'src/icyconnector.h'. 2014-03-29 Fred Gleason * Removed 'src/shout.cpp'. 2014-03-29 Fred Gleason * Removed 'src/layer3.cpp'. * Refactored code to employ abstract Codec and Connector classes. 2014-03-29 Fred Gleason * Added 'Connector::urlEncode()' and 'Connector::urlDecode()' methods in 'src/connector.cpp' and 'src/connector.h'. 2014-03-29 Fred Gleason * Renamed 'src/icyconnector.cpp' to 'src/iceconnector.cpp' * Renamed 'src/icyconnector.h' to 'src/iceconnector.h' * Added 'Connector::base64Encode()' and 'Connector::base64Decode()' methods in 'src/connector.cpp' and 'src/connector.h'. * Implemented proper password processing in 'src/iceconnector.cpp'. 2014-03-30 Fred Gleason * Implemented automatic servier reconnection in 'src/connector.cpp', 'src/connector.h', 'src/iceconnector.cpp' and 'src/iceconnector.h'. 2014-03-30 Fred Gleason * Added a connector for the ICY [Shoutcast 1] protocol in 'src/icyconnector.cpp' and 'src/icyconnector.h'. 2014-03-30 Fred Gleason * Refactored signaling in server connectors to provide reliable notification of authentication failures. 2014-03-30 Fred Gleason * Removed 'Connector::Shoutcast2Server' and 'Connector::Icecast1Server' enumeration values from 'src/connector.h'. * Updated the glasscoder(1) man page. 2014-03-30 Fred Gleason * Added accessor methods for 'streamIrc', 'streamIcq' and 'streamAim' in 'src/connector.cpp' and 'src/connector.h'. * Implemented fields for 'streamIrc', 'streamIcq' and 'streamAim' in 'src/icyconnector.cpp' and 'src/icyconnector.h'. * Updated the glasscoder(1) man page. 2014-03-31 Fred Gleason * Added '-Wno-strict-aliasing' to CPP_FLAGS in 'src/Makefile.am'. 2014-03-31 Fred Gleason * Updated 'AUTHORS'. * Updated 'INSTALL'. * Updated 'README'. 2014-03-31 Fred Gleason * Documented the '-d' switch in the glasscoder(1) man page. 2014-03-31 Fred Gleason * Incremented the package version to 0.6.0. 2014-03-31 Fred Gleason * Updated 'INSTALL'. 2014-06-11 Fred Gleason * Implemented AAC single channel format. 2014-06-14 Fred Gleason * Implemented AAC stereo format. * Refactored the 'Codec::encode()' method in 'src/codec.cpp' to support exact PCM buffer sizes. 2014-06-14 Fred Gleason * Documented AAC format in 'docs/glasscoder.1' 2014-06-14 Fred Gleason * Documented the FAAC dependency in 'INSTALL'. 2014-06-15 Fred Gleason * Implemented MPEG-1 Layer 2 format in 'src/mpegl2codec.cpp' and 'src/mpegl2codec.h'. 2014-06-15 Fred Gleason * Added '--audio-bitmode=' and '--audio-quality=' switches in 'src/glasscoder.cpp', 'src/glasscoder.h', 'src/codec.cpp' and 'src/codec.h'. 2014-06-16 Fred Gleason * Removed the '--audio-bitmode=' switch in 'src/glasscoder.cpp', 'src/glasscoder.h', 'src/codec.cpp' and 'src/codec.h'. * Removed the '-d' switch in 'src/glasscoder.cpp'. * Added support for VBR encoding in 'src/mpeg3codec.cpp' and 'src/mpeg3codec.h'. * Added support for VBR encoding in 'src/mpeg2codec.cpp' and 'src/mpeg2codec.h'. * Added support for VBR encoding in 'src/aaccodec.cpp' and 'src/aaccodec.h'. 2014-06-16 Fred Gleason * Implemented OggVorbis format in 'src/vorbiscodec.cpp' and 'src/vorbiscodec.h'. 2014-06-17 Fred Gleason * Added optional dependencies for OggVorbis support in 'INSTALL'. 2014-06-17 Fred Gleason * Updated 'README'. 2014-06-17 Fred Gleason * Implemented MPEG-4 HE-AAC+ format in 'src/heaaccodec.cpp' and 'src/heaaccodec.h'. 2014-06-17 Fred Gleason * Fixed build errors when building with no codec libraries. 2014-06-17 Fred Gleason * Implemented Opus [RFC 6717] format in 'src/opuscodec.cpp' and 'src/opuscodec.h'. 2014-06-17 Fred Gleason * Changed the mime-type for Ogg Vorbis to 'audio/ogg' in 'src/vorbiscodec.cpp'. 2014-06-17 Fred Gleason * Added error reporting in 'src'opuscodec.cpp'. 2014-06-18 Fred Gleason * Refactored rules for detecting Ogg components in 'configure.ac'. 2014-06-18 Fred Gleason * Removed LibTool components from 'configure.ac' and 'autogen.sh'. 2014-06-18 Fred Gleason * Fixed a bug in 'src/heaaccodec.h' that broke the build when the AAACPlus library was not present. 2014-06-19 Fred Gleason * Added note for building on Ubuntu in 'INSTALL'. 2014-06-19 Fred Gleason * Changed the default audio format to 'VORBIS' in 'src/glasscoder.cpp'. 2014-06-19 Fred Gleason * Incremented the package version to 0.8.0. 2014-06-19 Fred Gleason * Updated 'glasscoder.spec.in'. 2014-07-10 Fred Gleason * Added libdl to the libraries list in 'configure.ac'. 2014-08-22 Fred Gleason * Modernized the AM_INIT() and AC_INIT() invocations in 'configure.ac'. 2015-02-24 Fred Gleason * Updated the 'make rpm' target to use in-home build directories. 2015-03-20 Fred Gleason * Added '-Wno-portability' flag to the automake(1) invocation in 'autogen.sh'. 2015-03-30 Fred Gleason * Added support for Shoutcast v2. 2015-04-02 Fred Gleason * Added a sanity check for specifying no mountpoint when using an IceCast2 server in 'src/glasscoder.cpp'. 2015-04-02 Fred Gleason * Fixed a bug in 'src/jack.cpp' that threw a segfault when jack_port_get_buffer() returned a NULL port value. 2015-04-20 Fred Gleason * Incremented the package version to 0.8.2. 2015-07-14 Fred Gleason * Added a 'bad password' diagnostic message to the Shoutcast connector in 'src/icyconnector.cpp' and 'src/icyconnector.h'. 2015-08-07 Fred Gleason * Added a connector for HLS/HTTP streaming in 'src/hlsconnector.cpp' and 'src/hlsconnector.h'. 2015-08-10 Fred Gleason * Added code for stream distribution via http PUT. 2015-08-10 Fred Gleason * Added stop mechanism for connectors. 2015-08-10 Fred Gleason * Refactored build system to allow co-existence with Qt5. 2015-08-10 Fred Gleason * Implemented MPEG-2 elemental stream timestamps for HLS streams in 'src/hlsconnector.cpp'. 2015-08-10 Fred Gleason * Enabled live streaming mode in 'src/hlsconnector.cpp'. 2015-08-10 Fred Gleason * Optimized temporary directory space usage in 'src/hlsconnector.cpp'. 2015-08-10 Fred Gleason * Added a 'EXT-X-START' tag to the playlist for HLS streaming in 'src/hlsconnector.cpp'. * Incremented 'HLS_VERSION' to 6 in 'src/hlsconnector.h'. 2015-08-11 Fred Gleason * Implemented garbage collection for stale media segments in HLS streaming in 'src/hlsconnector.cpp' and 'src/hlsconnector.h' 2015-08-11 Fred Gleason * Added a curl(1) dependency in 'glasscoder.spec.in'. 2015-08-11 Fred Gleason * Implemented support for HTTP Basic authentication in 'src/hlsconnector.cpp' and 'src/hlsconnector.h'. 2015-08-11 Fred Gleason * Added a sample HLS publishing configuration for Apache in 'conf/httpd/'. 2015-08-12 Fred Gleason * Removed useless distro detection code in 'configure.ac'. 2015-08-12 Fred Gleason * Added 'Codec::completeFrames()' and 'Codec::setCompleteFrames()' methods in 'src/codec.cpp' and 'src/codec.h'. 2015-08-12 Fred Gleason * Added support for multi-rate HLS streams. 2015-08-12 Fred Gleason * Added support for Master Playlists for multi-rate HLS streams. 2015-08-12 Fred Gleason * Fixed a bug in 'src/heaccodec.cpp' that threw a segfault when specifying a non-supported stream bitrate. 2015-08-13 Fred Gleason * Refactored the JACK audio source to use an abstract class-based design. * Swallowed the Jack_ringbuffer_* routines from Jack2. 2015-08-13 Fred Gleason * Added a 'FileDevice' audio source in 'src/filedevice.cpp' and 'src/filedevice.h'. 2015-08-13 Fred Gleason * Added an 'AudioDevice::remixChannels()' method in 'src/audiodevice.cpp' and 'src/audiodevice.h'. 2015-08-13 Fred Gleason * Added support for reading the filename from standard input for the FILE source in 'src/filedevice.cpp'. 2015-08-13 Fred Gleason * Added an 'AlsaDevice' audio source in 'src/alsadevice.cpp' and 'src/alsadevice.h'. 2015-08-13 Fred Gleason * Tweaked the values for buffer size in 'src/alsadevice.cpp' and 'src/alsadevice.h'. 2015-08-16 Fred Gleason * Added an 'AsiHpiDevice' audio source in 'src/asihpidevice.cpp' and 'src/asihpidevice.h'. 2015-08-17 Fred Gleason * Fixed a bug in 'src/asihpidevice.cpp' that broke processing of the '--asihpi-adapter-index' and '--asihpi-input-index' switches. 2015-08-17 Fred Gleason * Implemented the '--list-codecs' switch in 'src/glasscoder.cpp' and 'src/glasscoder.h'. * Implemented the '--list-devices' switch in 'src/glasscoder.cpp' and 'src/glasscoder.h'. 2015-08-17 Fred Gleason * Moved source for glasscoder(1) into 'src/glasscoder/'. * Moved 'src/cmdswitch.cpp' and 'src'/cmdswitch.h' into 'src/common/'. 2015-08-18 Fred Gleason * Stubbed out glassgui(1) in 'src/glassgui/'. 2015-08-19 Fred Gleason * Implemented Start/Stop button functionality in glassgui(1). * Added a 'Show Code' button in glassgui(1). 2015-08-19 Fred Gleason * Implemented save/restore of current settings for glassgui(1). 2015-08-19 Fred Gleason * Implemented logic to cleanly shutdown the underlying glasscoder(1) process if glassgui(1) is closed with an active encoder process. 2015-08-19 Fred Gleason * Added a 'Stream Metadata Settings' section to glassgui(1). 2015-08-19 Fred Gleason * Added an 'ALSA Device' control to GlassGui(1). 2015-08-19 Fred Gleason * Added an HpiInputListView widget in 'src/glassgui/hpiinputlistview.cpp' and 'src/glassgui/hpiinputlistview.h'. 2015-08-20 Fred Gleason * Implemented persistent data for '--asihpi-adapter-index' and '--asihpi-input-index' values in 'src/glassgui/glassgui.cpp'. 2015-08-20 Fred Gleason * Implemented persistent data for '--jack-server-name' and '--jack-client-name' values in 'src/glassgui/glassgui.cpp'. 2015-08-20 Fred Gleason * Implemented persistent data for '--alsa-device' value in 'src/glassgui/glassgui.cpp'. 2015-08-20 Fred Gleason * Added an 'AudioDevice::isAvailable()' method in 'src/common/audiodevice.cpp' and 'src/common/audiodevice.h'. * Fixed a bug in 'src/glassgui/hpiinputlistview.cpp' that threw a segfault when attempting to read parameters when no ASI card was present. 2015-08-20 Fred Gleason * Implemented a '--meter-data' switch in for glasscoder(1). 2015-08-24 Fred Gleason * Implemented meter level display in glassgui(1). 2015-08-24 Fred Gleason * Implemented meter level display for the FILE source in glasscoder(1). 2015-08-24 Fred Gleason * Calibrated meter for 0 dBFS in 'src/glassgui/stereometer.cpp'. 2015-08-24 Fred Gleason * Implemented meter level display for the ALSA source in glasscoder(1). 2015-08-24 Fred Gleason * Fixed a bug in 'src/glassgui/glassgui.cpp' that caused the codec channels parameter to always be '2'. 2015-08-24 Fred Gleason * Fixed a bug in 'src/glasscoder/glasscoder.cpp' that broke audio metering for single channel modes. 2015-08-24 Fred Gleason * Fixed a bug in 'src/glassgui/glassgui.cpp' that broke preservation of codec parameter settings between sessions. 2015-08-24 Fred Gleason * Implemented meter level display for the JACK source in glasscoder(1). 2015-08-24 Fred Gleason * Implemented control locks in glassgui(1). 2015-08-25 Fred Gleason * Implemented a status bar in glassgui(1). 2015-08-25 Fred Gleason * Fixed a bug in 'src/glasscoder/asihpidevice.cpp' that broke the build when configured with no HPI support. 2015-08-25 Fred Gleason * Added code in 'src/glassgui/glassgui.cpp' to escape arguments containing spaces where generating a code dump. 2015-08-25 Fred Gleason * Changed the default Server Port from '8000' to '80'. * Added support for multi-rate streaming to glassgui(1). 2015-08-25 Fred Gleason Added 'Connector::curlStrError()' and 'Connector::httpStrError()' metehods in 'src/common/connector.cpp' and 'src/common/connector.h'. * Cleaned up error logging for the HLS server connector in 'src/glasscoder/hlsconnector.cpp'. 2015-08-25 Fred Gleason * Added icons in 'icons/'. * Added 'xdg/glassgui.desktop'. 2015-08-25 Fred Gleason * Fixed a bug in 'common/audiodevice.cpp' that broke the build on RHEL 6. 2015-08-26 Fred Gleason * Added a 'gui' subpackage in 'glasscoder.spec.in'. 2015-08-26 Fred Gleason * Updated 'INSTALL'. * Updated 'README'. * Updated glasscoder(1) man page. 2015-08-26 Fred Gleason * Removed a 'jack2' build dependency from 'glasscoder.spec.in'. 2015-08-26 Fred Gleason * Tweaked the required version for 'libsndfile' in 'INSTALL'. 2015-08-26 Fred Gleason * Added a '--verbose' switch to glasscoder(1). 2015-08-26 Fred Gleason * Updated 'NEWS'. * Incremented the package version to 0.9.0. 2015-08-26 Fred Gleason * Fixed a bug in 'src/glasscoder/hlsconnector.cpp' that caused the HLS stream connector to intermittently generate "CURL overrun" errors. 2015-08-26 Fred Gleason * Incremented the package version to 0.9.1. 2015-08-27 Fred Gleason * Fixed a bug in 'src/glasscoder/hlsconnector.cpp' that caused the HLS stream connector to fail to purge stale media files from active publish points. 2015-08-27 Fred Gleason * Cleaned up a moc(1) warning in 'src/glassgui/hpiinputlistview.h'. 2015-08-27 Fred Gleason * Fixed a bug in 'src/glasscoder/hlsconnector.cpp' that caused error logging for the DELETE process to fail. 2015-08-27 Fred Gleason * Added a 'Codec Settings' dialog in 'src/glassgui/codecdialog.cpp' and 'src/glassgui/codecdialog.h'. * Added a 'Stream Settings' dialog in 'src/glassgui/streamdialog.cpp' and 'src/glassgui/streamdialog.h'. 2015-08-27 Fred Gleason * Added a 'Source Settings' dialog in 'src/glassgui/sourcedialog.cpp' and 'src/glassgui/sourcedialog.h'. 2015-08-28 Fred Gleason * Added an '--asihpi-input-gain' switch to the ASIHPI source in 'src/glasscoder/asihpidevice.cpp' and 'src/glasscoder/asihpidevice.h'. * Added an '--asihpi-channel-mode' switch to the ASIHPI source in 'src/glasscoder/asihpidevice.cpp' and 'src/glasscoder/asihpidevice.h'. 2015-08-28 Fred Gleason * Added an '--asihpi-input-source' switch to the ASIHPI source in 'src/glasscoder/asihpidevice.cpp' and 'src/glasscoder/asihpidevice.h'. * Added an '--asihpi-input-type' switch to the ASIHPI source in 'src/glasscoder/asihpidevice.cpp' and 'src/glasscoder/asihpidevice.h'. 2015-08-28 Fred Gleason * Fixed bugs in 'src/glassgui/hpiwidget.cpp' and 'src/glassgui/hpiwidget.h' that broke the build when configured with no ASIHPI support. 2015-08-29 Fred Gleason * Commented out the 'HPI_SOURCENODE_BLULINK' enumeration point in 'src/glassgui/hpiwidget.cpp' to fix a build problem on RHEL6. 2015-08-31 Fred Gleason * Fixed a bug in 'src/glassgui/sourcedialog.cpp' that caused connections to fail when using a ASI card with no input type multiplexer control. 2015-08-31 Fred Gleason * Added code in 'src/glassgui/hpiwidget.cpp' to suppress spurious HPI error messages. 2015-09-09 Fred Gleason * Incremented the package version to 0.9.2. 2015-09-10 Fred Gleason * Implemented --server-script-up and --server-script-down switches in 'src/glasscoder/glasscoder.cpp', 'src/glasscoder/glasscoder.h', 'src/common/connector.cpp' and 'src/common/connector.h'. 2015-09-10 Fred Gleason * Added a Server Settings dialog in 'src/glassgui/serverdialog.cpp' and 'src/glassgui/serverdialog.h'. 2015-09-10 Fred Gleason * Added 'CONNECTED Script' and 'DISCONNECTED Script' controls to the Server Settings dialog in 'src/glassgui/serverdialog.cpp' and 'src/glassgui/serverdialog.h'. 2015-09-10 Fred Gleason * Increased the size of the View Code dialog in 'src/glassgui/codeviewer.cpp'. 2015-09-10 Fred Gleason * Implemented an '--instance-name' switch in 'src/glassgui/glassgui.cpp' and 'src/glassgui/glassgui.h'. 2015-09-11 Fred Gleason * Fixed a bug in 'src/glasscoder/alsadevice.cpp' that broke S32_LE format support for ALSA. 2015-09-11 Fred Gleason * Added a 'MessageWidget' widget in 'src/glassgui/messagewidget.cpp' and 'src/glassgui/messagewidget.h'. * Added logging statements for the ALSA sample format code in 'src/glasscoder/alsadevice.cpp'. 2015-09-14 Fred Gleason * Fixed a bug in 'src/glassgui/glasgui.cpp' that would throw a segfault when processing an error message. 2015-09-14 Fred Gleason * Incremented the package version to 0.9.3. 2015-09-21 Fred Gleason * Changed the global default --server-username value to the empty string. * Set an Icecast-specific default value for --server-username to 'source'. 2015-09-21 Fred Gleason * Fixed a bug in 'src/glasscoder/hlsconnector.cpp' that caused the master index file to be mis-formatted when configured to use multi-rate streams. 2015-09-21 Fred Gleason * Incremented the package version to 0.9.4. 2015-09-21 Fred Gleason * Added a 'FileConveyor' class in 'src/glasscoder/fileconveyor.cpp' and 'src/glasscoder/fileconveyor.h'. * Refactored the HLS streaming connector to serialize HTTP operations. 2015-09-22 Fred Gleason * Fixed a bug in 'src/glasscoder/hlsconnector.cpp' that caused a lockup upon exit. 2015-09-22 Fred Gleason * Refactored the HLS streaming connector to use a single FileConveyor. 2015-09-24 Fred Gleason * Incremented the package version to 0.9.5. 2015-09-24 Fred Gleason * Replaced '--server-hostname', '--server-mountpoint' and '--server-port' options with the '--server-url' option. * Replaced '--server-username' and '--server=password' options with the '--server-auth' option. 2015-09-24 Fred Gleason * Added an 'Enable verbose logging' checkbox to the Server Settings dialog in 'src/glassgui/serverdialog.cpp' and 'src/glassgui/serverdialog.h'. 2015-09-24 Fred Gleason * Modified the 'Connector::writeData()' method in 'src/common/connector.cpp' so as to send media data to the send queue regardless of the connection state. * Added a 'ConveyorEvent::origination()' method in 'src/glasscoder/fileconveyor.cpp' and 'src/glasscoder/fileconveyor.h'. 2015-09-24 Fred Gleason * Added a glassgui(1) man page. * Added a '--list-instances' option to glassgui(1). * Added a '--delete-instance' options to glassgui(1). 2015-09-25 Fred Gleason * Optimized 'FileConveyor::push()' to unlink stale target files before attempting to link a new one. 2015-10-08 Fred Gleason * Rewrote glasscoder(1) and glassgui(1) man pages in XML-DocBook 5. 2015-10-09 Fred Gleason * Tweaked markup glasscoder(1) and glassgui(1) man pages. 2015-10-09 Fred Gleason * Incremented the package version to 0.9.6. 2015-10-13 Fred Gleason * Implemented support for HE-AAC+ using fdk-aac in 'src/glasscoder/fdkcodec.cpp' and 'src/glasscoder/fdkcodec.h'. 2015-11-17 Fred Gleason * Added an '--autostart' switch to glassgui(1) in 'src/glassgui/glassgui.cpp' and 'src/glassgui/glassgui.h'. 2015-11-17 Fred Gleason * Added a psuedo-random name element to the media segment filenames in 'src/glasscoder/hlsconnector.cpp' and 'src/glasscoder/hlsconnector.h'. 2015-11-17 Fred Gleason * Removed 'src/glasscoder/heaaccodec.cpp' and 'src/glasscoder/heaaccodec.h'. 2015-11-17 Fred Gleason * Updated 'NEWS'. * Incremented the package version to 0.9.7. 2015-11-17 Fred Gleason * Fixed a regression in 'src/glasscoder.fdkcodec.cpp' that caused HeAAC encoding to be listed unavailable unless the AACPlus library was installed. 2015-11-17 Fred Gleason * Incremented the package version to 0.9.8. 2015-11-19 Fred Gleason * Fixed a positioning error in the status widget in 'src/glassgui/glassgui.cpp'. 2015-11-21 Fred Gleason * Fixed a bug in 'src/glasscoder/fdkcodec.cpp' and 'src/glasscoder/fdkcodec.h' that broke compilation when the FDK-AAC library was not present. 2015-11-21 Fred Gleason * Implemented a FILE server type. 2015-11-21 Fred Gleason * Refactored option-processing to utilize the 'Connector::optionKeyword()' method in 'src/glasscoder/glasscoder.cpp'. 2015-11-21 Fred Gleason * Refactored option-processing to utilize the 'Codec::optionKeyword()' method in 'src/glasscoder/glasscoder.cpp'. 2015-11-21 Fred Gleason * Implemented a FILEARCHIVE server type. 2015-11-21 Fred Gleason * Fixed a bug in 'src/glasscoder/fileconnector.cpp' that broke compilation under RHEL 7. 2015-11-21 Fred Gleason * Incremented the package version to 0.9.9. 2015-11-23 Fred Gleason * Implemented a PCM16 (little endian) codec in 'src/glasscoder/pcm16codec.cpp' and 'src/glasscoder/pcm16codec.h'. * Modified the FILE server driver in 'src/glasscoder/fileconnector.cpp' and 'src/glasscoder/fileconnector.h' to use a WAV file container when streaming PCM16 data. * Modified the FILEARCHIVE server driver in 'src/glasscoder/filearchiveconnector.cpp' and 'src/glasscoder/filearchiveconnector.h' to use a WAV file container when streaming PCM16 data. 2015-11-23 Fred Gleason * Fixed a bug in 'src/glassgui/codecdialog.cpp' that enabled bitrate settings for the PCM16 codec. 2015-11-23 Fred Gleason * Modified PCM16 codec to use big-endian encoding. 2015-11-23 Fred Gleason * Update error reporting in 'src/glasscoder/asihpisource.cpp'. 2015-11-23 Fred Gleason * Updated the package version to 0.9.10. 2015-11-24 Fred Gleason * Removed FLOAT support from the ALSA driver in 'src/glasscoder/alsadevice.cpp'. 2015-11-24 Fred Gleason * Updated the package version to 0.9.11. 2015-12-08 Fred Gleason * Changed the HLS version value to '3' in 'src/glasscoder/hlsconnector.h'. 2015-12-08 Fred Gleason * Updated the package version to 0.9.12. 2015-12-09 Fred Gleason * Added a HLS_OMIT_ID3_TIMESTAMPS define in 'src/glasscoder/hlsconnector.cpp' to allow supression of ID3 PRIV timestamp in media segments. 2015-12-09 Fred Gleason * Fixed a bug in 'src/glasscoder/aaccodec.cpp' where an incomplete ObjectTypeIndication (OTI) was being emitted by the 'AacCodec::formatIdentifier()' method. * Fixed a bug in 'src/glasscoder/fdkcodec.cpp' where an incomplete ObjectTypeIndication (OTI) was being emitted by the 'FdkCodec::formatIdentifier()' method. * Fixed a bug in 'src/glasscoder/mpegl2codec.cpp' where an incomplete ObjectTypeIndication (OTI) was being emitted by the 'MpegL2Codec::formatIdentifier()' method. * Fixed a bug in 'src/glasscoder/mpegl3codec.cpp' where an incomplete ObjectTypeIndication (OTI) was being emitted by the 'MpegL3Codec::formatIdentifier()' method. * Removed generation of the '#EXT-X-VERSION' tag for master index files in 'src/glasscoder/hlsconnector.cpp'. 2015-12-09 Fred Gleason * Updated the package version to 0.9.13. 2015-12-17 Fred Gleason * Commented out the HLS_OMIT_ID3_TIMESTAMPS define in 'src/glasscoder/hlsconnector.h'. 2015-12-23 Fred Gleason * Incremented the package version to 0.9.14. 2016-01-04 Fred Gleason * Implemented the #EXT-X-PROGRAM-DATE-TIME tag for HLS streams in 'src/glasscoder/hlsconnector.cpp' and 'src/glasscoder/hlsconnector.h'. * Added a '--stream-timestamp-offset=' switch to glasscoder(1). 2016-01-13 Fred Gleason * Added an '--audio-atomic-frames' option to glasscoder(1). 2016-02-16 Fred Gleason * Merged changes from Sascha Ludwig [saschaludwig] to fix bug in glassgui(1) when compiled without HPI support [GitHub pull request #000002]. 2016-02-16 Fred Gleason * Fixed a bug in 'src/common/connector.cpp' that caused the script specified by --server-script-down to fail to be executed during a normal shutdown. 2016-02-16 Fred Gleason * Updated 'NEWS'. * Incremented the package version to 0.9.15. 2016-04-06 Fred Gleason * Added rules to build an HTML version of the docs. 2016-09-13 Fred Gleason * Added an '--errors-string' switch to glasscoder(1). 2016-09-13 Fred Gleason * Modified glassgui(1) to use the --instance-name value for logging. * Fixed a bug in 'src/glasscoder/asihpidevice.cpp' that returned an error when passed negative values for '--asihpi-input-gain'. 2016-09-15 Fred Gleason * Added a 'Config' class in 'src/glasscoder/config.cpp' and 'src/glasscoder/config.h'. * Added a '--metadata-port' option to glasscoder(1). * Added a WebHost library dependency. * Added an overloaded 'FileConveyor::push()' method in 'glasscoder/fileconveyor.cpp' and 'glasscoder/fileconveyor.h'. * Implemented metadata updates for Icecast2. 2016-09-15 Fred Gleason * Added a 'FileConveyor::setAddedHeaders()' method in 'glasscoder/fileconveyor.cpp' and 'glasscoder/fileconveyor.h'. * Implemented metadata updates for Shoutcast1. 2016-09-15 Fred Gleason * Added a 'Local Metadata Port' control to the Server Settings dialog in 'src/glassgui/serversettings.cpp' and 'src/glassgui/serversettings.h'. 2016-09-15 Fred Gleason * Added a 'Stream Metadata' control to the glassgui(1) main window in 'src/glassgui/glassgui.cpp' and 'src/glassgui/glassgui.h'. * Implemented a send metadata command via standard input for glasscoder(1) in 'src/glasscoder/glasscoder.cpp' and 'src/glasscoder/glasscoder.h'. 2016-09-16 Fred Gleason * Added a glasscoder-ipc(7) man page. 2016-09-16 Fred Gleason * Updated 'INSTALL'. * Updated 'NEWS'. * Incremented the package version to 0.9.16. 2016-11-04 Fred Gleason * Fixed a bug in 'src/glasscoder/glasscoder.cpp' that caused a spurious 'metadataReceived' error to be generated when starting a connection with no metadata port. 2016-11-04 Fred Gleason * Added an 'IcecastStreamerServer' connector in 'src/glasscoder/icestreamconnector.cpp' and 'src/glasscoder/icestreamconnector.h'. 2016-11-08 Fred Gleason * Documented the 'IcecastStreamer' server type in the glasscoder(1) man page. 2016-11-08 Fred Gleason * Added code to the 'Server Settings' dialog to disable irrelevant fields when using the IceStreamer server. 2016-11-08 Fred Gleason * Implemented stream metadata updates for the 'IceStreamer' server type in 'src/glasscoder/icestreamconnector.cpp' and 'src/glasscoder/icestreamconnector.h'. 2016-11-08 Fred Gleason * Implemented an idle connection timeout for the 'IceStreamer' server type in 'src/glasscoder/icestreamconnector.cpp' and 'src/glasscoder/icestreamconnector.h'. 2016-11-09 Fred Gleason * Added a 'Connector::serverBasicAuthString()' method in 'src/common/connector.cpp' and 'src/common/connector.h'. * Implemented the 'mount' administrative interface in the IceStreamer server type in 'src/glasscoder/icestreamconnector.cpp' and 'src/glasscoder/icestreamconnector.h'. 2016-11-09 Fred Gleason * Updated the glasscoder(1) man page. * Enabled the 'Username' and 'Password fields in the 'Server Settings' dialog for the 'IceStreamer' server type. 2016-11-09 Fred Gleason * Fixed a bug in 'src/glasscoder/icestreamconnector.cpp' and 'src/glasscoder/icestreamconnector.h' that threw a segfault when stopping with active client connections. 2016-11-10 Fred Gleason * Added a pipe_connect(1) test harness in 'src/tests/'. * Added a '--server-pipe=' parameter to glasscoder(1). 2016-11-10 Fred Gleason * Added a '--server-exit-on-last' parameter to glasscoder(1). 2016-11-10 Fred Gleason * Added an 'iceout' server connector in 'src/glasscoder/iceoutconnector.cpp' and 'src/glasscoder/iceoutconnector.h'. 2016-11-11 Fred Gleason * Removed HTTP negotiation parameters from the Icecast headers of the 'IceOut' server type in 'src/glasscoder/iceoutconnector.cpp'. 2016-11-11 Fred Gleason * Added a '--server-max-connections' parameter to glasscoder(1). * Added a 'Max Player Connections' control to the Server Settings dialog in 'src/glasscoder/serverdialog.cpp' and 'src/glasscoder/serverdialog.h'. 2016-11-11 Fred Gleason * Relaxed the requirement that a --server-url parameter always be provided for the IceStreamer server. 2016-11-14 Fred Gleason * Incremented the package version to 0.9.17. 2016-11-15 Fred Gleason * Added a '--server-start-connections=' parameter to glasscoder(1). 2016-11-16 Fred Gleason * Moved dialog elementsfrom 'src/glassgui/' into 'src/common/'. 2016-11-17 Fred Gleason * Added a glasscommander(1) application in 'src/glasscommander/'. 2016-11-17 Fred Gleason * Added 'Add', 'Remove' and 'Abandon' buttons for managing instances in glasscommander(1). 2016-11-17 Fred Gleason * Added 'Start All' and 'Stop All' buttons to glasscommander(1). 2016-11-18 Fred Gleason * Added a check for proper stream shutdown when exiting glasscommander(1). 2016-11-18 Fred Gleason * Added a 'Pick Instance' dialog for glasscommander(1) in 'src/glasscommander/instancedialog.cpp' and 'src/glasscommander/instancedialog.h'. 2016-11-18 Fred Gleason * Added a 'FilenameValidator' class in 'src/glasscommander/filenamevalidator.cpp' and 'src/glasscommander/filenamevalidator.h'. * Added code to check for duplicate/invalid instance names in 'src/glasscommander/instancedialog.cpp' and 'src/glasscommander/instancedialog.h'. 2016-11-18 Fred Gleason * Added code to select an item in the 'Pick Instance' dialog when its name is keyed into the 'Instance' control. 2016-11-18 Fred Gleason * Added a 'Remove Instance' dialog in 'src/glasscommander/deletedialog.cpp' and 'src/glasscommander/deletedialog.h'. 2016-11-18 Fred Gleason * Added a 'Start this instance at application launch' control to the Settings dialog in 'src/glasscommander/configdialog.cpp' and 'src/glasscommander/configdialog.h'. 2016-11-18 Fred Gleason * Added a glasscommander(1) man page. 2016-11-18 Fred Gleason * Added glasscommander(1) to 'glasscoder.spec.in'. 2016-11-18 Fred Gleason * Incremented the package version to 0.9.18. 2016-11-22 Fred Gleason * Implemented staggered startup in glasscommander(1). 2016-11-22 Fred Gleason * Fixed a bug in 'src/glasscoder/icestreamconnector.cpp' that caused incorrect an date-time header to be emitted for timezones other than UTC+0400. 2016-11-23 Fred Gleason * Added a '--server-user-agent' option to glasscoder(1). 2016-11-23 Fred Gleason * Fixed a typo in 'docs/glasscoder.xml.in'. 2016-11-23 Fred Gleason * Fixed a bug in 'src/common/serverdialog.cpp' that caused the 'Max Player Connections' spin box to default to '0' instead of 'unlimited'. 2016-11-23 Fred Gleason * Incremented the player version to 0.9.19. 2016-11-30 Fred Gleason * Added a codec for OggOpus in 'src/glasscoder/opuscodec.cpp' and 'src/glasscoder/opuscodec.h'. 2016-11-30 Fred Gleason * Added a compile-time define for encoder complexity in 'src/glasscoder/opuscodec.cpp' and 'src/glasscoder/opuscodec.h'. 2016-11-30 Fred Gleason * Added configuration values for the Opus codec in 'src/glassgui/codecdialog.cpp'. 2016-12-01 Fred Gleason * Fixed typo in vendor string in 'src/glasscoder/opuscodec.cpp'. 2016-12-01 Fred Gleason * Updated 'INSTALL'. 2016-12-02 Fred Gleason * Enabled 'Opus' and 'Vorbis' codec types to work with IceStreamer server. 2016-12-02 Fred Gleason * Fixed a memory leak in 'src/glasscommander/playmeter.cpp'. 2016-12-02 Fred Gleason * Fixed a bug in 'src/glasscoder/opuscodec.cpp' that broke compilation when building without Opus support. 2016-12-02 Fred Gleason * Altered dynamic library targets to include major version numbers. 2016-12-02 Fred Gleason * Removed MPEG-2/AAC support. 2016-12-02 Fred Gleason * Incremented the package version to 0.9.20. 2016-12-05 Fred Gleason * Added a termination timeout to glasscommander(1) in 'src/glasscommander/glasswidget.cpp' and 'src/glasscommander/glasswidget.h'. 2016-12-05 Fred Gleason * Incremented the package version to 0.9.21. 2018-03-20 Fred Gleason * Added support for DMA bus-mastering to the ASIHPI driver. 2018-03-20 Fred Gleason * Updated 'NEWS'. * Incremented the package version to 0.9.22. 2019-01-08 Fred Gleason * Fixed a bug that caused double-escaped metadata strings to be sent to IceCast and Shoutcast servers. 2019-08-01 Fred Gleason * Added the ability to the Shoutcast connector to utilize a username in addition to a password to authenticate connections. 2019-08-05 Fred Gleason * Added a '--jack-gain=' directive to glasscoder(1). * Added a 'Gain' control for JACK to the 'Audio Sources' dialog in glassgui(1). 2019-08-05 Fred Gleason * Removed the example environment for RHEL 5 from 'INSTALL'. 2019-08-05 Fred Gleason * Incremented the package version to 0.9.23. 2019-09-10 Fred Gleason * Updated Qt4 to Qt5. 2019-09-11 Fred Gleason * Swallowed the 'WHHttpServer' class and dependent classes from 'webhost'. * Removed the 'webhost' dependency. * Added an 'OpenSSL' dependency. 2019-09-12 Fred Gleason * Updated 'INSTALL'. 2019-09-12 Fred Gleason * Fixed a regression in glassgui(1) that caused meters to be rendered incorrectly. 2019-09-16 Fred Gleason * Refactored the 'MetaEvent' class to operate with arbitrary field keys. * Added a mandatory dependency for TagLib. * Added a 'json-pad' web service. 2019-09-17 Fred Gleason * Added a 'pypad_glasscoder.py' PyPAD script. * Added a 'AX_COUNT_CPUS()' autoconf macro. * Modified the 'make rpm' target to use one less than the total available CPU cores. 2019-09-17 Fred Gleason * Updated 'INSTALL'. 2019-09-17 Fred Gleason * Fixed a bug in glassgui(1) that caused instances names to be truncated when using the '--list-instances' option. 2019-09-19 Fred Gleason * Added support for HLS timed metadata. 2019-10-14 Fred Gleason * Fixed a bug in glasscoder(1) that could cause extremely long shutdown times when generating HLS streams. 2019-10-16 Fred Gleason * Added support to glasscoder(1) for streaming HLS via the 'FILE' scheme. 2019-10-16 Fred Gleason * Added support to glasscoder(1) for streaming HLS via the 'SFTP' scheme using passwords. 2019-10-16 Fred Gleason * Fixed a bug in glasscoder(1) that caused a 'Resource not found' CURL error to be generated when streaming HLS via SFTP. 2019-10-16 Fred Gleason * Fixed a regression in glasscoder(1) that caused XCast metadata updates to be improperly escaped. 2019-10-17 Fred Gleason * Modified segment tracking in the HLS connector so as to update the list of current PUTted files only after receiving confirmation of success from curl(1). 2019-10-17 Fred Gleason * Added a 'Supported URL Schemes' section to the glasscoder(1) man page. 2019-10-18 Fred Gleason * Added support for the 'StreamUrl' metadata field on ShoutCast servers. 2019-10-18 Fred Gleason * Added a 'METADATA' section to the glasscoder(1) man page. 2019-10-31 Fred Gleason * Incremented the package version to 0.10.0. 2020-01-09 Fred Gleason * Updated glasscoder(1) to work properly with FDK-AACv2. 2020-01-20 Fred Gleason * Fixed a bug in the 'Connector::urlDecode()' method that caused incorrect results when processing multi-byte UTF-8 characters. * Added a 'urldecode' test harness. * Fixed a bug in the 'Connector::urlEncode()' method that caused incorrect results when processing multi-byte UTF-8 characters. * Added a 'urlencode' test harness. 2020-01-20 Fred Gleason * Fixed a double-decode bug when processing IceCast-style metadata updates in glasscoder(1). 2020-01-20 Fred Gleason * Fixed a double-decode bug when processing ShoutCast-style metadata updates in glasscoder(1). 2020-01-21 Fred Gleason * Added a '--dump-headers' switch to glasscoder(1). * Added a '--dump-headers' switch to glasscommander(1). * Implemented the '--dump-headers' swich for the IceCast2 server type. 2020-01-22 Fred Gleason * Fixed a bug in glasscoder(1) where the script specified in the '--server-script-down' switch would fail to be executed during shutdown. 2020-01-23 Gabriele Fergola * Corrected the exception on utf8 characters movin pypad_glasscoder.py from pycurl to requests. * Rewrote the json loop in pypad_glasscoder.py * Increased debug in pypad_glasscoder.py * Added [PyPAD][Glasscoder] Tags to better recognize in syslog/messages 2020-01-23 Fred Gleason * Removed 'python36' dependency from the 'glasscoder' RPM package. * Added 'python36' and 'python36-requests' dependencies to the 'glasscoder-pypad' RPM package. * Updated 'INSTALL'. 2020-01-24 Fred Gleason * Added a 'CODINGSTYLE' file. 2020-01-24 Fred Gleason * Switched from MM/DD/YYYY to DD MMM YYYY date formats in 'NEWS'. 2020-01-24 Fred Gleason * Incremented the package version to 1.0.0. 2020-01-27 Fred Gleason * Added OpenSSL use notice to 'README. * Added GPLv2 exception for linking to OpenSSL to 'README'. 2020-01-27 Fred Gleason * Updated the description of glassgui(1) in 'README'. * Added a description of glasscommander(1) in 'README'. 2020-06-01 Fred Gleason * Added a static 'Connector::socketErrorText()' method. * Fixed a regression in glasscoder(1) that caused automatic reconnection to IceCast servers to fail. 2020-06-01 Fred Gleason * Fixed a regression in glasscoder(1) that caused automatic reconnection to ShoutCast servers to fail. 2020-06-01 Fred Gleason * Added 'Connection Starting...' and 'Connection Stopping...' status messages to glassgui(1) and glasscommander(1). 2020-06-02 Fred Gleason * Added a check for LibCURL to 'configure.ac'. 2020-06-02 Fred Gleason * Cleaned up strncpy(3) warnings in glasscoder(1). 2020-06-02 Fred Gleason * Cleaned up strncpy(3) warnings in 'tests/pipeconnect.cpp'. 2020-06-02 Fred Gleason * Cleaned up QFontMetrics warnings in glassgui(1). * Cleaned up QFontMetrics warnings in glasscommander(1). 2020-06-02 Fred Gleason * Reverted the QFontMetrics warning cleanup in glassgui(1). * Reverted up QFontMetrics warning cleanups in glasscommander(1). 2020-06-02 Fred Gleason * Incremented the package version to 1.0.1. 2021-05-04 Fred Gleason * Fixed build warnings in glasscoder(1). 2021-05-04 Fred Gleason * Added a 'make deb' target. 2021-05-21 Fred Gleason * Updated dependency information for the DEB packages. 2021-05-21 Fred Gleason * Fixed a typo in 'debian/control'. 2021-08-16 Fred Gleason * Incremented the package version to 1.0.2. 2022-01-09 Fred Gleason * Updated the PyPAD plug-in to support Rivendell v4.x. 2022-01-09 Fred Gleason * Updated the 'make deb' target to generate Debian installation metadata automatically. 2022-01-09 Fred Gleason * Commented out the HLS Timed Metadata example in the PyPAD plug-in exemplar. 2022-01-09 Fred Gleason * Incremented the package version to 1.0.3. 2022-01-10 Fred Gleason * Fixed a bug in 'glasscoder.spec.in' that broke the 'make rpm' target on RHEL 8. 2022-01-10 Fred Gleason * Removed the byte compiled binary from the PyPAD plug-in from the 'glasscoder-pypad package under RHEL 8. 2022-01-10 Fred Gleason * Incremented the package version to 1.0.4. 2022-01-28 Fred Gleason * Added a glassconv(1) program in 'src/glasscoder/'. 2022-01-28 Fred Gleason * Added a 'NetConveyor' class in 'src/glasscoder/'. 2022-01-28 Fred Gleason * Refactored HLS support to use glassconv(1). 2022-01-31 Fred Gleason * Renamed the 'FileConveyor' class to 'GetConveyor' and removed vestigal file transfer functionality. 2022-01-31 Fred Gleason * Fixed a regression in glassconv(1) that caused publishing HLS to a FILE: scheme to fail. 2022-01-31 Fred Gleason * Fixed a regression in glassconv(1) that caused publishing HLS to a SFTP: scheme to fail. 2022-01-31 Fred Gleason * Added a '--credentials-file' option to glasscoder(1). * Added a '--delete-credentials' option to glasscoder(1). * Deprecated the --server-auth' option in glasscoder(1). 2022-02-01 Fred Gleason * Modified glassgui(1) to use the '--credentials-file' option when starting glasscoder(1). * Modified glasscommander(1) to use the '--credentials-file' option when starting glasscoder(1). 2022-02-01 Fred Gleason * Modified glassgui(1) to use the '--ssh-identity' option when starting glasscoder(1). * Modified glasscommander(1) to use the '--ssh-identity' option when starting glasscoder(1). 2022-02-01 Fred Gleason * Fixed a bug in glasscommander(1) that caused dialog titlebars to display their parent as 'GlassGui'. 2022-02-01 Fred Gleason * Fixed bugs in glasscommander(1) that caused the strip insertion icons on the toolbar to be enabled incorrectly. 2022-02-07 Fred Gleason * Fixed a regression in glasscoder(1) when generating HLS streams that caused posting errors to fail to be reported to the logging system. 2022-02-08 Fred Gleason * Updated the 'Supported URL Schemes' section of the glasscoder(1) man page. 2022-02-08 Fred Gleason * Added a '--server-no-deletes' option to glasscoder(1). 2022-02-08 Fred Gleason * Added a 'Show Code' button to the configuration dialog in glasscommander(1). 2022-02-08 Fred Gleason * Fixed a bug in glasscoder(1) that caused excess media segment files to be retained on an HLS publishing point. * Fixed a bug in glasscoder(1) that caused superfluous DELETE requested to be generated when shutting down an HLS stream. 2022-02-08 Fred Gleason * Added a 'Purge stale files on publishing point' checkbox to the 'Server Settings' dialog in glassgui(1) and glasscommander(1). 2022-02-08 Fred Gleason * Modified glassgui(1) and glasscommander(1) to obfuscate server credentials within instance data files. * Modified glassgui(1) and glasscommander(1) to use mode 0700 for the '~/.glassgui' directory. * Modified glassgui(1) and glasscommander to use mode 0600 for instance data files. 2022-03-10 Fred Gleason * Fixed a regression in glasscoder(1) that caused stale media segments to fail to be removed from the index file when using HLS with the '--server-no-deletes' switch. 2022-03-10 Fred Gleason * Removed support for multi-rate HLS streams from glasscoder(1). 2022-03-10 Fred Gleason * Removed support for multi-rate HLS streams from glassgui(1) and glasscommander(1). 2022-03-11 Fred Gleason * Corrected a typo in the entry for '--server-no-deletes' in the glasscoder(1) man page. 2022-03-11 Fred Gleason * Fixed a 'no such slot' error generated by glasscommander(1) at startup. 2022-03-12 Fred Gleason * Modified glassconv(1) to log all messages to syslog(3). 2022-03-13 Fred Gleason * Fixed a bug in glassconv(1) that caused lockups due to exhaustion of available file handles. 2022-03-17 Fred Gleason * Fixed a bug that broke the 'make rpm' target. 2022-03-17 Fred Gleason * Tightened the 'glasscoder' dependency for the 'glasscoder-gui' RPM sub-package to require matching versions. 2022-03-17 Fred Gleason * Added glassconv(1) to the 'glasscoder' Deb package. 2022-03-17 Fred Gleason * Removed the 'glasscoder' dependency from the 'glasscoder-pypad' Deb package. 2022-03-17 Fred Gleason * Updated the package version to 2.0.0rc0. 2022-05-03 Fred Gleason * Fixed a bug in glasscommander(1) that could cause the startup of encoder instances to fail with a 'Unable to create credential file' error message. 2022-05-03 Fred Gleason * Updated the package version to 2.0.0rc1. 2022-05-04 Fred Gleason * Fixed a regression in glasscoder(1) that caused the value of the '#EXTINF' tags to always be '0.00000' in HLS streams. 2022-05-04 Fred Gleason * Updated the package version to 2.0.0rc2. 2022-05-24 Fred Gleason * Fixed a regression in glasscoder(1) when configured to emit HLS that caused the '#EXT-X-PROGRAM-DATE-TIME' tag to get an invalid value when the '--server-no-deletes' flag was given. 2022-05-24 Fred Gleason * Updated the package version to 2.0.0rc3. 2022-06-25 Fred Gleason * Updated the package version to 2.0.1. GlassCoder-2.0.1/INSTALL000066400000000000000000000100011425562563600145770ustar00rootroot00000000000000This is the installation file for the GlassCoder package. MANDATORY PREREQUISITES --------------------------------------------------------------------------- You will need the following installed and configured properly on your system before building GlassCoder: Qt5 Toolkit, v5.9 or better (http://www.qt.io/). LibCURL A client-side URL transfer library. Available at https://curl.haxx.se/libcurl/. Secret Rabbit Code A sample-rate converter library, written by Erik de Castro Lopo. Included with most distros, or you can find it at http://www.mega-nerd.com/SRC/. OpenSSL Library, a robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols. Available at: https://www.openssl.org/. TagLib Audio Meta-Data Library, v1.8.0 or better (https://taglib.org/) A library for reading and writing the meta-data of several popular audio formats. OPTIONAL PREREQUISITES --------------------------------------------------------------------------- GlassCoder depends upon various third-party libraries to provide support for both encoding formats ('codecs') and some audio sources. You will need at least one of each in order to build a functioning binary. CODECS ------ FDK-AAC - Fraunhofer FDK AAC codec for Android. A codec for generating MPEG-4 HE-AAC+ streams. Available at https://github.com/mstorsjo/fdk-aac. LAME - MPEG-1 Layer III Encoder Library, v3.99.3 or later. Needed for generating MPEG-1 Layer III ("MP3") streams. Available at http://lame.sourceforge.net/. OggOpus - A free, patent-clear audio codec, formally defined in RFC-6716. Both 'libogg' and 'libopus' libraries are required. Available at http://www.xiph.org. OggVorbis - A free, patent-clear audio codec. Both 'libogg' and 'libvorbis' libraries are required. Available at http://www.xiph.org. TwoLAME - MPEG-1 Layer II Encoder Library, v0.2.12 or later. Needed for generating MPEG-1 Layer II ("MP2") streams. Available at http://www.twolame.org/. AUDIO SOURCES ------------- Advanced Linux Sound Architecture (ALSA), v1.0 or later. Included in virtually all Linux distros. Further information is available at http://www.alsa-project.org/. AudioScience HPI (ASIHPI), v4.00 or later. Advanced audio API for using the line of professional, high-end audio cards from AudioScience Corporation. Available at http://www.audioscience.com/. LibSndFile (FILE), v1.0.20 or later. Library for reading audio files. Available at http://mega-nerd.com/libsndfile/. JACK Audio Connection Kit (JACK). System for interconnecting audio devices and applications. Available at http://jackaudio.org/. PLUG-INS -------- GlassCoder include a PyPAD script for the Rivendell Radio Automation System that allows Rivendell to send articulated PAD to GlassCoder instance. Use of this requires Python 3.4 or later, as well as the Python 'Requests' module. DOCUMENTATION ------------- The GlassCoder man pages are written in XML-DocBook5. Pre-generated troff versions are included in the source tarball, so special tools will not normally be required to install them. However, if you need to rebuild them (either because you've modified the DocBook sources or are installing from the primary GitHub repository), then you will need the following: XML-DocBook5 Stylesheets. Available at http://sourceforge.net/projects/docbook/. You will also need to create a $DOCBOOK_STYLESHEETS variable in you environment that points to the top of the stylesheet tree. More information can be found at http://www.docbook.org/tdg5/en/html/appa.html#s.stylesheetinstall. xsltproc. Command line XSLT processor. Available at http://xmlsoft.org/XSLT/xsltproc2.html INSTALLATION Once the prerequisites are set up, building and installation of the code is done by cd'ing to the top of the source tree and typing './configure [options]', 'make', followed (as root) by 'make install'. Those who obtained the source via CVS will need to do './autogen.sh' first. There are a number of options for the 'configure' script; do './configure --help' for a detailed list. GlassCoder-2.0.1/Makefile.am000066400000000000000000000047601425562563600156210ustar00rootroot00000000000000## automake.am ## ## Top level automake.am ## ## Use automake to process this into a Makefile.in ## ## (C) Copyright 2014-2022 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## if DOCBOOK_AM DOCBOOK_AM_OPT = docs endif if PYTHON_AM PYTHON_AM_OPT = plugins endif SUBDIRS = debian $(DOCBOOK_AM_OPT) conf\ icons $(PYTHON_AM_OPT) \ src\ xdg deb: dist ./build_debs.sh rpm: dist cp glasscoder-$(VERSION).tar.gz $(HOME)/rpmbuild/SOURCES/ $(RPMBUILD) -ba --target $(ARCH)-$(VENDOR)-linux glasscoder.spec mv $(HOME)/rpmbuild/RPMS/$(ARCH)/glasscoder-*.rpm $(top_srcdir)/ mv $(HOME)/rpmbuild/SRPMS/glasscoder-*.src.rpm $(top_srcdir)/ rm $(HOME)/rpmbuild/SOURCES/glasscoder-$(VERSION).tar.gz rm -rf $(HOME)/rpmbuild/BUILD/glasscoder-$(VERSION) EXTRA_DIST = autogen.sh\ CODINGSTYLE\ get_distro.pl\ get_target.sh\ glasscoder.spec.in\ link_common.sh\ PACKAGE_VERSION CLEANFILES = *~\ *.buildinfo\ *.changes\ *.ddeb\ *.deb\ *.dsc\ *.mo\ *.rpm\ *.tar.gz\ *.tar.xz\ *.tgz\ *.zip DISTCLEANFILES = build_debs.sh\ glasscoder.spec\ config.guess\ config.sub\ ltmain.sh MAINTAINERCLEANFILES = *~\ *.tar.gz\ aclocal.m4\ compile\ config.guess\ config.status\ config.sub\ configure\ depcomp\ install-sh\ libtool\ ltmain.sh\ Makefile.in\ missing\ mkinstalldirs GlassCoder-2.0.1/NEWS000066400000000000000000000146341425562563600142650ustar00rootroot00000000000000The NEWS file for the GlassCoder package. ------------------------------------------------------------------------------- v2.0.1 -- 25 June 2022 Changes: HLS Streaming. Major rework of the HLS encoder to increase multi-threading and hence performance on multi-core processors. Removed support for generating multi-rate streams via HLS. Added a '--server-no-deletes' switch to glasscoder(1) to suppress DELETE transactions (useful when publishing to services such as Akamai MSL-4 which do not require client-generated publish-point cleanup). Credentials Handling. Added '--credentials-file' and '--delete-credentials' switches to glasscoder(1) while deprecating use of the '--server-auth' switch. Also added to glasscoder(1) is the ability to use an ssh(1) identity file (private key) for authentication when publishing HLS streams. See the glasscoder(1) man page for details. Bugfixes. See the ChangeLog for details. ------------------------------------------------------------------------------- v1.0.4 -- 10 January 2022 Changes: Fixed a packaging error on RHEL-8. ------------------------------------------------------------------------------- v1.0.3 -- 9 January 2022 Changes: Updated the PyPAD plug-in to be compatible with Rivendell v4.x. ------------------------------------------------------------------------------- v1.0.2 -- 16 August 2021 Changes: Added integrated Debian package generation. ------------------------------------------------------------------------------- v1.0.1 -- 2 June 2020 Changes: Fixed bugs in glasscoder(1) that affected automatic reconnection to Icecast and Shoutcast servers. Various minor bugfixes and cleanups. See the 'ChangeLog' for details. ------------------------------------------------------------------------------- v1.0.0 -- 24 January 2020 Changes: Updated codec support. Glasscoder(1) has been updated to work properly with FDK-AACv2. UTF-8 Enhancements. Updates and bug-fixes to support multi-byte UTF-8 characters fully in ICY and HLS metadata. PyPAD Plug-in Fixes. Bug-fixes for 'pypad-glasscoder' plug-in for Rivendell [thanks to Gabriele Fergola ]. ------------------------------------------------------------------------------- v0.10.0 -- 31 October 2019 Changes: Dependency Changes. GlassCoder now requires Qt5, OpenSSL and TagLib libraries. See the 'INSTALL' file for more information. Metadata Support. Added a JSON-based IPC mechanism for updating timed metadata. See the 'METADATA' section of the glasscoder(1) man page for details. HLS Streaming. Added support for timed metadata on HLS streams, as well as SFTP and FILE transport options. See the 'SUPPORTED URL SCHEMES' section of the glasscoder(1) man page for details. Rivendell Support. Added a 'pypad_glasscoder.py' PyPAD script for integration with Rivendell 3.x. ------------------------------------------------------------------------------- v0.9.23 -- 5 August 2019 Changes: JACK Support. Added a '--jack-gain=' switch to glasscoder(1) to allow insertion of a fixed gain before the encoding stage. Shoutcast Support. Fixed a bug in glasscoder(1) that made it impossible to connect to a Shoutcast server using credentials that included a user name. ------------------------------------------------------------------------------- v0.9.22 -- 20 March 2018 Changes: AudioScience HPI. Added support for DMA bus-mastering to the AudioScience ASIHPI driver. ------------------------------------------------------------------------------- v0.9.16 -- 16 September 2016 Changes: Webhost Library. Beginning with this release, GlassCoder requires the Webhost library. See the 'INSTALL' file for details. Metadata Update Support. It is now possible to update stream metadata for Icecast and Shoutcast stream by means of HTTP messages as well as commands sent to standard input of glasscoder(1) instances. See the glasscoder-ipc(7) man page for details. Also added is a live metadata update control to glassgui(1). Enhanced Logging. Added the ability to add text tags to log entries to allow disambiguation of messages from multiple glasscoder(1) instances. See the --errors-string option in the glasscoder(1) man page for details. ------------------------------------------------------------------------------- v0.9.15 -- 16 February 2016 Changes: HLS Changes. Added support for the #EXT-X-PROGRAM-DATE-TIME tag in HLS playlists. New Codec. Added a PCM16 codec (--audio-format=PCM16). New Command Switches. Added --audio-atomic-frames and --stream-timestamp-offset switches. New Server Type. Added a File Archive server type (--server-type=FILEARCHIVE). Various bugfixes. See the ChangeLog for details. ------------------------------------------------------------------------------- v0.9.7 -- 17 November 2015 Changes: Dependency Change. HeAAC support is now accomplished using the Fraunhofer Android FDK library. See the 'INSTALL' file for details. GlassGui Layout. The widget layout in GlassGuui has been extensively refactored to reduce required desktop real estate. HLS Rewrite. Refactored HLS support to be compatible with Akamai's HLS streaming service. Command Switch Changes. Replaced the '--server-hostname', '--server-mountpoint' and '--server-port' options with a single '--server-url' option and the '--server-username' and '--server=password' options with the '--server-auth' option in glasscoder(1). See the glasscoder(1) man page for details. AudioScience HPI Support. Added support for HPI mixers in glasscoder(1) and glassgui(1). ------------------------------------------------------------------------------- v0.9.0 -- 26 August 2015 Changes: New Streaming Server. Support has been added for generating Apple HTTP Live Streams. See https://developer.apple.com/streaming/. New Audio Sources. Support has been added for the following audio sources: Advance Linux Sound Architecture (ALSA) AudioScience HPI (ASIHPI) File Streaming (FILE) Point and Click Interface. A 'GlassGui' applet has been added that allow glasscoder(1) to be driven from a point and click interface while also providing the ability to generate command-line invocations for glasscoder(1) that can be copy/pasted into other environments. ------------------------------------------------------------------------------- GlassCoder-2.0.1/PACKAGE_VERSION000066400000000000000000000000051425562563600157340ustar00rootroot000000000000002.0.1GlassCoder-2.0.1/README000066400000000000000000000041521425562563600144400ustar00rootroot00000000000000This is the README file for the GlassCoder package. This software comes with ABSOLUTELY NO WARRANTY. See the file 'COPYING' for details. See the file 'INSTALL' for instructions on building GlassCoder from source. GlassCoder is a minimalist audio encoder for IceCast and Shoutcast streaming servers as well as HTTP Live Streaming (HLS). It supports the following audio sources: Advanced Linux Sound Architeture (ALSA) AudioScience HPI (ASIHPI) File Streaming (FILE) JACK Audio Connection Kit (JACK) The base GlassCoder streamer component utilizes no configuration files or GUI interface whatsoever; with the 'user interface' consisting entirely of the command-line invocation. As such, it is well suited for use cases where the encoder is completely driven by an external system or script. The following audio codecs are supported: MPEG-1/1.5 Layer 2 ('MP2') MPEG-1/1.5 Layer 3 ('MP3') MPEG-4 Advanced Audio Coding, High Efficiency Profile ('HE-AAC+') Ogg Opus Ogg Vorbis The following additional utilities are included: GlassGui, a GUI front end for a single instance of the glasscoder(1) audio encoder. It provides a convenient and discoverable means for creating and saving glasscoder(1) invocations as well as an instance management system for saving multiple sets of configurations on the local system. GlassCommander, a high-density GUI front end for the glasscoder(1) audio encoder. It provides a convenient and discoverable means for creating and saving glasscoder(1) invocations and an instance management system (fully compatible with that of glassgui(1)) for saving multiple sets of configurations on the local system in a manner that conserves screen real-estate. Glasscommander(1) is capable of hosting dozens of simultaneous glasscoder(1) instances while still allowing easy access to the control and status of each one. This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/). This program is released under the GNU General Public License version 2 with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. GlassCoder-2.0.1/acinclude.m4000066400000000000000000000204361425562563600157540ustar00rootroot00000000000000dnl acinclude.m4 dnl dnl Local Autoconf macro definitions dnl dnl (C) Copyright 2012-2021 Fred Gleason dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License version 2 as dnl published by the Free Software Foundation. dnl dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public dnl License along with this program; if not, write to the Free Software dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. dnl dnl AR_GCC_TARGET() dnl dnl Get gcc(1)'s idea of the target architecture, distribution and os. dnl The following variables are set: dnl $ar_gcc_arch = Target Architecture (i586, XF86_64, etc) dnl $ar_gcc_distro = Target Distribution (suse, slackware, etc) dnl $ar_gcc_os = Target Operating System (linux, solaris, etc) dnl AC_DEFUN([AR_GCC_TARGET],[AC_REQUIRE([AC_PROG_CC])] [ AC_MSG_CHECKING(target architecture) ar_gcc_arch=$(./get_target.sh $CC $AWK arch) ar_gcc_distro=$(./get_target.sh $CC $AWK distro) ar_gcc_os=$(./get_target.sh $CC $AWK os) AC_MSG_RESULT([$ar_gcc_arch-$ar_gcc_distro-$ar_gcc_os]) ] ) # AR_GET_DISTRO() # # Try to determine the name and version of the distribution running # on the host machine, based on entries in '/etc/'. # The following variables are set: # $ar_distro_name = Distribution Name (SuSE, Debian, etc) # $ar_distro_version = Distribution Version (10.3, 3.1, etc) # $ar_distro_major = Distribution Version Major Number (10, 3, etc) # $ar_distro_minor = Distribution Version Minor Number (3, 1, etc) # $ar_distro_pretty_name = Full Distribution Name (Ubuntu 20.04.2 LTS, etc) # $ar_distro_id = All lowercase identifier (ubuntu, debian, centos, etc) # $ar_distro_id_like = Identifier(s) of similar distros (rhel fedora, etc) # AC_DEFUN([AR_GET_DISTRO],[] [ AC_MSG_CHECKING(distribution) ar_distro_name=$(./get_distro.pl NAME) ar_distro_version=$(./get_distro.pl VERSION) ar_distro_major=$(./get_distro.pl MAJOR) ar_distro_minor=$(./get_distro.pl MINOR) ar_distro_pretty_name=$(./get_distro.pl PRETTY_NAME) ar_distro_id=$(./get_distro.pl ID) ar_distro_id_like=$(./get_distro.pl ID_LIKE) AC_MSG_RESULT([$ar_distro_pretty_name $ar_distro_version]) ] ) # =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_count_cpus.html # =========================================================================== # # SYNOPSIS # # AX_COUNT_CPUS([ACTION-IF-DETECTED],[ACTION-IF-NOT-DETECTED]) # # DESCRIPTION # # Attempt to count the number of logical processor cores (including # virtual and HT cores) currently available to use on the machine and # place detected value in CPU_COUNT variable. # # On successful detection, ACTION-IF-DETECTED is executed if present. If # the detection fails, then ACTION-IF-NOT-DETECTED is triggered. The # default ACTION-IF-NOT-DETECTED is to set CPU_COUNT to 1. # # LICENSE # # Copyright (c) 2014,2016 Karlson2k (Evgeny Grin) # Copyright (c) 2012 Brian Aker # Copyright (c) 2008 Michael Paul Bailey # Copyright (c) 2008 Christophe Tournayre # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. ##serial 22 AC_DEFUN([AX_COUNT_CPUS],[dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_EGREP])dnl AC_MSG_CHECKING([the number of available CPUs]) CPU_COUNT="0" # Try generic methods # 'getconf' is POSIX utility, but '_NPROCESSORS_ONLN' and # 'NPROCESSORS_ONLN' are platform-specific command -v getconf >/dev/null 2>&1 && \ CPU_COUNT=`getconf _NPROCESSORS_ONLN 2>/dev/null || getconf NPROCESSORS_ONLN 2>/dev/null` || CPU_COUNT="0" AS_IF([[test "$CPU_COUNT" -gt "0" 2>/dev/null || ! command -v nproc >/dev/null 2>&1]],[[: # empty]],[dnl # 'nproc' is part of GNU Coreutils and is widely available CPU_COUNT=`OMP_NUM_THREADS='' nproc 2>/dev/null` || CPU_COUNT=`nproc 2>/dev/null` || CPU_COUNT="0" ])dnl AS_IF([[test "$CPU_COUNT" -gt "0" 2>/dev/null]],[[: # empty]],[dnl # Try platform-specific preferred methods AS_CASE([[$host_os]],dnl [[*linux*]],[[CPU_COUNT=`lscpu -p 2>/dev/null | $EGREP -e '^@<:@0-9@:>@+,' -c` || CPU_COUNT="0"]],dnl [[*darwin*]],[[CPU_COUNT=`sysctl -n hw.logicalcpu 2>/dev/null` || CPU_COUNT="0"]],dnl [[freebsd*]],[[command -v sysctl >/dev/null 2>&1 && CPU_COUNT=`sysctl -n kern.smp.cpus 2>/dev/null` || CPU_COUNT="0"]],dnl [[netbsd*]], [[command -v sysctl >/dev/null 2>&1 && CPU_COUNT=`sysctl -n hw.ncpuonline 2>/dev/null` || CPU_COUNT="0"]],dnl [[solaris*]],[[command -v psrinfo >/dev/null 2>&1 && CPU_COUNT=`psrinfo 2>/dev/null | $EGREP -e '^@<:@0-9@:>@.*on-line' -c 2>/dev/null` || CPU_COUNT="0"]],dnl [[mingw*]],[[CPU_COUNT=`ls -qpU1 /proc/registry/HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/System/CentralProcessor/ 2>/dev/null | $EGREP -e '^@<:@0-9@:>@+/' -c` || CPU_COUNT="0"]],dnl [[msys*]],[[CPU_COUNT=`ls -qpU1 /proc/registry/HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/System/CentralProcessor/ 2>/dev/null | $EGREP -e '^@<:@0-9@:>@+/' -c` || CPU_COUNT="0"]],dnl [[cygwin*]],[[CPU_COUNT=`ls -qpU1 /proc/registry/HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/System/CentralProcessor/ 2>/dev/null | $EGREP -e '^@<:@0-9@:>@+/' -c` || CPU_COUNT="0"]]dnl )dnl ])dnl AS_IF([[test "$CPU_COUNT" -gt "0" 2>/dev/null || ! command -v sysctl >/dev/null 2>&1]],[[: # empty]],[dnl # Try less preferred generic method # 'hw.ncpu' exist on many platforms, but not on GNU/Linux CPU_COUNT=`sysctl -n hw.ncpu 2>/dev/null` || CPU_COUNT="0" ])dnl AS_IF([[test "$CPU_COUNT" -gt "0" 2>/dev/null]],[[: # empty]],[dnl # Try platform-specific fallback methods # They can be less accurate and slower then preferred methods AS_CASE([[$host_os]],dnl [[*linux*]],[[CPU_COUNT=`$EGREP -e '^processor' -c /proc/cpuinfo 2>/dev/null` || CPU_COUNT="0"]],dnl [[*darwin*]],[[CPU_COUNT=`system_profiler SPHardwareDataType 2>/dev/null | $EGREP -i -e 'number of cores:'|cut -d : -f 2 -s|tr -d ' '` || CPU_COUNT="0"]],dnl [[freebsd*]],[[CPU_COUNT=`dmesg 2>/dev/null| $EGREP -e '^cpu@<:@0-9@:>@+: '|sort -u|$EGREP -e '^' -c` || CPU_COUNT="0"]],dnl [[netbsd*]], [[CPU_COUNT=`command -v cpuctl >/dev/null 2>&1 && cpuctl list 2>/dev/null| $EGREP -e '^@<:@0-9@:>@+ .* online ' -c` || \ CPU_COUNT=`dmesg 2>/dev/null| $EGREP -e '^cpu@<:@0-9@:>@+ at'|sort -u|$EGREP -e '^' -c` || CPU_COUNT="0"]],dnl [[solaris*]],[[command -v kstat >/dev/null 2>&1 && CPU_COUNT=`kstat -m cpu_info -s state -p 2>/dev/null | $EGREP -c -e 'on-line'` || \ CPU_COUNT=`kstat -m cpu_info 2>/dev/null | $EGREP -c -e 'module: cpu_info'` || CPU_COUNT="0"]],dnl [[mingw*]],[AS_IF([[CPU_COUNT=`reg query 'HKLM\\Hardware\\Description\\System\\CentralProcessor' 2>/dev/null | $EGREP -e '\\\\@<:@0-9@:>@+$' -c`]],dnl [[: # empty]],[[test "$NUMBER_OF_PROCESSORS" -gt "0" 2>/dev/null && CPU_COUNT="$NUMBER_OF_PROCESSORS"]])],dnl [[msys*]],[[test "$NUMBER_OF_PROCESSORS" -gt "0" 2>/dev/null && CPU_COUNT="$NUMBER_OF_PROCESSORS"]],dnl [[cygwin*]],[[test "$NUMBER_OF_PROCESSORS" -gt "0" 2>/dev/null && CPU_COUNT="$NUMBER_OF_PROCESSORS"]]dnl )dnl ])dnl AS_IF([[test "x$CPU_COUNT" != "x0" && test "$CPU_COUNT" -gt 0 2>/dev/null]],[dnl AC_MSG_RESULT([[$CPU_COUNT]]) m4_ifvaln([$1],[$1],)dnl ],[dnl m4_ifval([$2],[dnl AS_UNSET([[CPU_COUNT]]) AC_MSG_RESULT([[unable to detect]]) $2 ], [dnl CPU_COUNT="1" AC_MSG_RESULT([[unable to detect (assuming 1)]]) ])dnl ])dnl ])dnl GlassCoder-2.0.1/autogen.sh000077500000000000000000000026771425562563600155730ustar00rootroot00000000000000#!/bin/sh ## ## (C) Copyright 2012-2022 Fred Gleason ## ## Adapted from './autogen.sh' in the Jack Audio Connection Kit. ## Copyright (C) 2001-2003 Paul Davis, et al. ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of version 2 of the GNU General Public License as ## published by the Free Software Foundation; ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, ## Boston, MA 02111-1307 USA ## # # Generate Debian packaging metadata # DATESTAMP=`date +%a,\ %d\ %b\ %Y\ %T\ %z` sed s/@VERSION@/`cat PACKAGE_VERSION`/ < debian/control.src > debian/control sed s/@VERSION@/`cat PACKAGE_VERSION`/ < debian/changelog.src | sed "s/@DATESTAMP@/$DATESTAMP/" > debian/changelog aclocal $ACLOCAL_FLAGS || { echo "aclocal \$ACLOCAL_FLAGS where \$ACLOCAL_FLAGS= failed, exiting..." exit 1 } automake --add-missing -Wno-portability || { echo "automake --add-missing failed, exiting..." exit 1 } autoconf || { echo "autoconf failed, exiting..." exit 1 } GlassCoder-2.0.1/build_debs.sh.in000077500000000000000000000016711425562563600166230ustar00rootroot00000000000000#!/bin/sh # build_debs.sh # # Build Debian packages. # # (C) Copyright 2021 Fred Gleason # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # tar -zvxf glasscoder-@VERSION@.tar.gz mv glasscoder-@VERSION@.tar.gz glasscoder_@VERSION@.orig.tar.gz cd glasscoder-@VERSION@ dpkg-buildpackage -us -uc cd .. rm -rf glasscoder-@VERSION@ GlassCoder-2.0.1/conf/000077500000000000000000000000001425562563600145035ustar00rootroot00000000000000GlassCoder-2.0.1/conf/Makefile.am000066400000000000000000000017501425562563600165420ustar00rootroot00000000000000## automake.am ## ## conf/Automake.am for GlassCoder ## ## (C) Copyright 2015 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## by Fred Gleason ## ## Use automake to process this into a Makefile.in SUBDIRS = httpd CLEANFILES = *~ DISTCLEANFILES = moc_* MAINTAINERCLEANFILES = *~\ Makefile.in GlassCoder-2.0.1/conf/httpd/000077500000000000000000000000001425562563600156265ustar00rootroot00000000000000GlassCoder-2.0.1/conf/httpd/Makefile.am000066400000000000000000000021051425562563600176600ustar00rootroot00000000000000## automake.am ## ## conf/httpd/Automake.am for GlassCoder ## ## (C) Copyright 2015 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## by Fred Gleason ## ## Use automake to process this into a Makefile.in EXTRA_DIST = hls.conf\ hls_methods.pl\ hls.passwd\ README.httpd CLEANFILES = *~ DISTCLEANFILES = moc_* MAINTAINERCLEANFILES = *~\ Makefile.in GlassCoder-2.0.1/conf/httpd/README.httpd000066400000000000000000000007361425562563600176360ustar00rootroot00000000000000 README for Apache HLS Configuration The files in this directory are for a basic HTTP Live Streaming (HLS) setup on Apache. They should be installed as follows: Apache configuration directory: hls.conf hls.passwd The 'hls.passwd' file will need at least one password entry added to it to allow GlassCoder's HLS streaming connector to upload files. See the htpasswd(1) man page for details. Apache cgi-bin directory: hls_methods.pl GlassCoder-2.0.1/conf/httpd/hls.conf000066400000000000000000000004621425562563600172650ustar00rootroot00000000000000 AuthType Basic AuthName "HLS Publish Point" AuthBasicProvider file AuthUserFile /etc/httpd/conf.d/hls.passwd Require valid-user Script DELETE /cgi-bin/hls_methods.pl Script PUT /cgi-bin/hls_methods.pl GlassCoder-2.0.1/conf/httpd/hls.passwd000066400000000000000000000000001425562563600176250ustar00rootroot00000000000000GlassCoder-2.0.1/conf/httpd/hls_methods.pl000077500000000000000000000036041425562563600205020ustar00rootroot00000000000000#!/usr/bin/perl # hls_methods.pl # # HTTP methods script for HLS streaming # # (C) 2015 Fred Gleason # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # sub Put { my $response=201; my $file_name=&DestinationPath(); my $file_len = $ENV{'CONTENT_LENGTH'}; if(stat($file_name)) { $response=204; } # # Copy Data # my $toread = $file_len; $content = ""; while ($toread > 0) { $nread = read(STDIN, $data, $file_len); $toread -= $nread; $content = $data; } if(!open(OUT, "> $file_name")) { &Reply(500,"PUT failed!\n"); } print OUT $content; close(OUT); &Reply($response,"OK"); } sub Delete { my $file_name=&DestinationPath(); if(stat($file_name)) { if(!unlink($file_name)) { &Reply(500,"Unable to delete\n"); } } &Reply(204,"OK"); } sub DestinationPath() { my $file_name=$ENV{'PATH_TRANSLATED'}; if(!$file_name) { &Reply(500,"No destination path provided."); } return $file_name; } sub Reply { print "Status: ".$_[0]."\n"; print "Content-type: text/html\n"; print "\n"; print $_[1]; exit 0; } # # Verify Method # if($ENV{'REQUEST_METHOD'} eq "PUT") { &Put(); } if($ENV{'REQUEST_METHOD'} eq "DELETE") { &Delete(); } &Reply(500,"Unsupported method.\n"); GlassCoder-2.0.1/configure.ac000066400000000000000000000257651425562563600160630ustar00rootroot00000000000000dnl configure.ac dnl dnl Autoconf configuration dnl dnl Use autoconf to process this into a configure script dnl dnl (C) Copyright 2014-2022 Fred Gleason dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License version 2 as dnl published by the Free Software Foundation. dnl dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public dnl License along with this program; if not, write to the Free Software dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. dnl AC_INIT(glasscoder,m4_esyscmd(cat PACKAGE_VERSION)) AM_INIT_AUTOMAKE AC_SUBST(RPM_RELEASE,1) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) # # Some Fully Resolved Install Paths # (for the packaging system and friends) # if test ${prefix} = "NONE" ; then AC_SUBST(LOCAL_PREFIX,/usr/local) else AC_SUBST(LOCAL_PREFIX,${prefix}) fi # # Basic Compiler Checks # AC_PROG_CXX AC_LANG(C++) # to avoid rpath usage : # http://wiki.debian.net/index.cgi?RpathIssue case ${host} in *-pc-linux-gnu) AC_MSG_RESULT([Fixing libtool for -rpath problems.]) sed < libtool > libtool-2 \ 's/^hardcode_libdir_flag_spec.*$/hardcode_libdir_flag_spec=" -D__LIBTOOL_IS_A_FOOL__ "/' mv libtool-2 libtool chmod 755 libtool ;; esac # # Option Switches # AC_ARG_ENABLE(twolame,[ --disable-twolame disable MPEG-1 Layer 2 support], [TWOLAME_DISABLED=yes],[]) AC_ARG_ENABLE(lame,[ --disable-lame disable MPEG-1 Layer 3 support], [LAME_DISABLED=yes],[]) AC_ARG_ENABLE(fdkaac,[ --disable-fdkaac disable MPEG-4 HE-AAC+ support], [FDKAAC_DISABLED=yes],[]) AC_ARG_ENABLE(vorbis,[ --disable-vorbis disable Ogg Vorbis support], [VORBIS_DISABLED=yes],[]) AC_ARG_ENABLE(alsa,[ --disable-alsa disable ALSA sound support], [ALSA_DISABLED=yes],[]) AC_ARG_ENABLE(asihpi,[ --disable-asihpi disable AudioScience HPI sound support], [ASIHPI_DISABLED=yes],[]) AC_ARG_ENABLE(docbook,[ --disable-docbook disable building of documentation],[DOCBOOK_DISABLED=yes],[]) AC_ARG_ENABLE(file,[ --disable-file disable FILE sound support], [SNDFILE_DISABLED=yes],[]) AC_ARG_ENABLE(jack,[ --disable-jack disable JACK sound support], [JACK_DISABLED=yes],[]) AC_ARG_ENABLE(opus,[ --disable-opus disable Opus support], [OPUS_DISABLED=yes],[]) # # Check for Qt (Mandatory) # PKG_CHECK_MODULES(QT5_CLI,Qt5Core Qt5Network,[],[AC_MSG_ERROR([*** Qt5 not found ***])]) PKG_CHECK_MODULES(QT5_GUI,Qt5Widgets Qt5Gui Qt5Core Qt5Network,[],[AC_MSG_ERROR([*** Qt5 not found ***])]) AC_CHECK_PROG(MOC_NAME,moc-qt5,[moc-qt5],[moc]) AC_SUBST(MOC,$MOC_NAME) # # Check for OpenSSL # PKG_CHECK_MODULES(OPENSSL,libcrypto,[],[AC_MSG_ERROR([*** OpenSSL not found ***])]) # # Check for OpenSSL # PKG_CHECK_MODULES(LIBCURL,libcurl,[],[AC_MSG_ERROR([*** LibCURL not found ***])]) # # Check for Jack Audio Connection Kit # AC_CHECK_HEADER(jack/jack.h,[JACK_FOUND=y],[]) if test $JACK_FOUND ; then if test -z $JACK_DISABLED ; then AC_DEFINE(JACK,yes) AC_SUBST(LIBJACK,-ljack) USING_JACK=yes else AC_SUBST(LIBJACK,"") fi else AC_SUBST(LIBJACK,"") fi # # Check for libsndfile (needed for FILE support) # PKG_CHECK_MODULES(SNDFILE,sndfile,[SNDFILE_FOUND=y],[]) if test $SNDFILE_FOUND ; then if test -z $SNDFILE_DISABLED ; then AC_DEFINE(SNDFILE,yes) USING_SNDFILE=yes fi fi # # Check for AudioScience HPI # AC_CHECK_HEADER(asihpi/hpi.h,[ASIHPI_FOUND=yes],[]) if test $ASIHPI_FOUND ; then if test -z $ASIHPI_DISABLED ; then AC_DEFINE(ASIHPI,yes) AC_SUBST(ASIHPI_LIBS,-lhpi) USING_ASIHPI=yes else AC_SUBST(ASIHPI_LIBS,"") fi fi # # Check for ALSA # PKG_CHECK_MODULES(ALSA,alsa,[ALSA_FOUND=y],[PKGCONFIG_FAILED=y]) if test $ALSA_FOUND ; then if test -z $ALSA_DISABLED ; then AC_DEFINE(ALSA,yes) USING_ALSA=yes fi fi # # Check for Secret Rabbit Code (Mandatory) # AC_CHECK_HEADER(samplerate.h,[],[AC_MSG_ERROR([*** libsamplerate not found ***])]) # # Check for TagLib (Mandatory) # PKG_CHECK_MODULES(TAGLIB,taglib,[TAGLIB_FOUND=y],[]) # # Check for Ogg and friends (Optional, required for Vorbis and Opus support) # AC_CHECK_HEADER(ogg/ogg.h,[OGG_FOUND=yes],[]) if test $OGG_FOUND ; then AC_CHECK_HEADER(vorbis/vorbisfile.h,[VORBIS_FOUND=yes],[]) if test $VORBIS_FOUND ; then if test -z $VORBIS_DISABLED ; then AC_DEFINE(HAVE_VORBIS) USING_VORBIS=yes fi fi AC_CHECK_HEADER(opus/opus.h,[OPUS_FOUND=yes],[]) if test $OPUS_FOUND ; then if test -z $OPUS_DISABLED ; then AC_DEFINE(HAVE_OPUS) USING_OPUS=yes fi fi fi # # Check for TwoLame (Optional, required for MPEG Layer 2 support) # AC_CHECK_HEADER(twolame.h,[TWOLAME_FOUND=yes],[]) if test $TWOLAME_FOUND ; then if test -z $TWOLAME_DISABLED ; then AC_DEFINE(HAVE_TWOLAME) USING_TWOLAME=yes fi fi # # Check for Lame (Optional, required for MPEG Layer 3 support) # AC_CHECK_HEADER(lame/lame.h,[LAME_FOUND=yes],[]) if test $LAME_FOUND ; then if test -z $LAME_DISABLED ; then AC_DEFINE(HAVE_LAME) USING_LAME=yes fi fi # # Check for Fraunhofer FDK (Optional, required for MPEG-4 AAC-HE support) # AC_CHECK_HEADER(fdk-aac/aacenc_lib.h,[FDKAAC_FOUND=yes],[]) if test $FDKAAC_FOUND ; then if test -z $FDKAAC_DISABLED ; then AC_DEFINE(HAVE_FDKAAC) USING_FDKAAC=yes fi fi # # Check for Python 3 (Optional, required for PyPAD scripts) # AM_PATH_PYTHON([3],PYTHON_FOUND=yes],[]) if test $PYTHON_FOUND ; then AC_SUBST(PYTHON_BASE_DEP,"python"`echo $PYTHON_VERSION | sed -e s/3./3/`) USING_PYTHON=yes fi AM_CONDITIONAL([PYTHON_AM], [test "$USING_PYTHON" = yes]) # # Build DocBook Items? # if test -z $DOCBOOK_DISABLED ; then USING_DOCBOOK=yes fi AM_CONDITIONAL([DOCBOOK_AM], [test "$USING_DOCBOOK" = yes]) # # Determine the target architecture # AR_GCC_TARGET() AC_SUBST(VENDOR,$ar_gcc_distro) AC_SUBST(ARCH,$ar_gcc_arch) # # Determine number of available CPU cores for building packages # AX_COUNT_CPUS([],[]) if test $CPU_COUNT -lt 2 ; then AC_SUBST(CPUS_AVAIL,1) else if test $CPU_COUNT -gt 16 ; then AC_SUBST(CPUS_AVAIL,16) else AC_SUBST(CPUS_AVAIL,$(($CPU_COUNT-1))) fi fi # # Determine Distro # AR_GET_DISTRO() AC_SUBST(DISTRO,$ar_gcc_distro) case "$ar_distro_id" in centos|fedora|rhel|rocky) if test $ar_distro_major -eq 8 ; then AC_SUBST(PYPAD_BINARY,"") else AC_SUBST(PYPAD_BINARY,"%{_libdir}/rivendell/pypad/__pycache__/*.pyc") fi ;; *) AC_SUBST(PYPAD_BINARY,"") ;; esac # # Linux vs. OS X Setup # if test $ar_gcc_os = "darwin10" ; then AC_SUBST(SIRLIBS,"-lsamplerate") AC_SUBST(SIRFLAGS,"-framework QtCore -framework QtNetwork") AC_SUBST(GUILIBS,"-lsamplerate") AC_SUBST(GUIFLAGS,"-framework QtCore -framework QtNetwork -framework QtGui") AC_DEFINE(OSX) else AC_SUBST(SIRLIBS,"-lsamplerate -ldl") AC_SUBST(SIRFLAGS,"") AC_SUBST(GUILIBS,"-lsamplerate") AC_SUBST(GUIFLAGS,"") AC_DEFINE(LINUX) fi # # Configure RPM Build # AC_CHECK_PROG(RPMBUILD_FOUND,rpmbuild,[yes],[]) if test -z $RPMBUILD_FOUND ; then AC_SUBST(RPMBUILD,rpm) else AC_SUBST(RPMBUILD,rpmbuild) fi if test -d /usr/src/redhat ; then AC_SUBST(RPM_ROOT,/usr/src/redhat) AC_SUBST(VENDOR,redhat) else AC_SUBST(RPM_ROOT,/usr/src/packages) AC_SUBST(VENDOR,suse) fi AC_CONFIG_FILES([ \ debian/Makefile \ debian/source/Makefile \ conf/Makefile \ conf/httpd/Makefile \ docs/Makefile \ docs/glasscoder.xml \ icons/Makefile \ plugins/Makefile \ plugins/install_pypad.sh \ src/common/paths.h \ src/common/Makefile \ src/glasscoder/Makefile \ src/glasscommander/Makefile \ src/glassgui/Makefile \ src/tests/Makefile \ src/Makefile \ xdg/Makefile \ glasscoder.spec \ build_debs.sh \ Makefile ]) AC_OUTPUT() chmod 755 build_debs.sh # # PyPAD Installer # chmod 755 plugins/install_pypad.sh # # Link Common Elements # ./link_common.sh glasscoder ./link_common.sh glasscommander ./link_common.sh glassgui ./link_common.sh tests AC_MSG_NOTICE() AC_MSG_NOTICE("|-----------------------------------------------------|") AC_MSG_NOTICE("| *** GLASSCODER CONFIGURATION SUMMARY *** |") AC_MSG_NOTICE("|-----------------------------------------------------|") AC_MSG_NOTICE("| Encoding Formats: |") if test -z $USING_TWOLAME ; then AC_MSG_NOTICE("| MPEG-1 Layer 2 (via TwoLAME) ... No |") else AC_MSG_NOTICE("| MPEG-1 Layer 2 (via TwoLAME) ... Yes |") fi if test -z $USING_LAME ; then AC_MSG_NOTICE("| MPEG-1 Layer 3 (via LAME) ... No |") else AC_MSG_NOTICE("| MPEG-1 Layer 3 (via LAME) ... Yes |") fi if test -z $USING_FDKAAC ; then AC_MSG_NOTICE("| MPEG-4 HE-AAC+ (via FDKAAC) ... No |") else AC_MSG_NOTICE("| MPEG-4 HE-AAC+ (via FDKAAC) ... Yes |") fi if test -z $USING_OPUS ; then AC_MSG_NOTICE("| Ogg Opus (via libogg and libopus) ... No |") else AC_MSG_NOTICE("| Ogg Opus (via libogg and libopus) ... Yes |") fi if test -z $USING_VORBIS ; then AC_MSG_NOTICE("| Ogg Vorbis (via libogg and libvorbis) ... No |") else AC_MSG_NOTICE("| Ogg Vorbis (via libogg and libvorbis) ... Yes |") fi if test -z $USING_SNDFILE ; then AC_MSG_NOTICE("| PCM16 (via libsndfile) ... No |") else AC_MSG_NOTICE("| PCM16 (via libsndfile) ... Yes |") fi AC_MSG_NOTICE("| |") AC_MSG_NOTICE("| Audio Sources: |") if test -z $USING_ALSA ; then AC_MSG_NOTICE("| Advanced Linux Sound Architecture (ALSA) ... No |") else AC_MSG_NOTICE("| Advanced Linux Sound Architecture (ALSA) ... Yes |") fi if test -z $USING_ASIHPI ; then AC_MSG_NOTICE("| AudioScience HPI (ASIHPI) ... No |") else AC_MSG_NOTICE("| AudioScience HPI (ASIHPI) ... Yes |") fi if test -z $USING_SNDFILE ; then AC_MSG_NOTICE("| File Streaming (FILE) ... No |") else AC_MSG_NOTICE("| File Streaming (FILE) ... Yes |") fi if test -z $USING_JACK ; then AC_MSG_NOTICE("| Jack Audio Connection Kit (JACK) ... No |") else AC_MSG_NOTICE("| Jack Audio Connection Kit (JACK) ... Yes |") fi AC_MSG_NOTICE("| |") AC_MSG_NOTICE("| Plug-Ins: |") if test -z $USING_PYTHON ; then AC_MSG_NOTICE("| Install PyPAD scripts ... No |") else AC_MSG_NOTICE("| Install PyPAD scripts ... Yes |") fi AC_MSG_NOTICE("|-----------------------------------------------------|") AC_MSG_NOTICE() AC_MSG_NOTICE() AC_MSG_NOTICE(Now enter 'make' to build the software.) AC_MSG_NOTICE() GlassCoder-2.0.1/debian/000077500000000000000000000000001425562563600150005ustar00rootroot00000000000000GlassCoder-2.0.1/debian/Makefile.am000066400000000000000000000021541425562563600170360ustar00rootroot00000000000000## automake.am ## ## Automake.am for GlassCoder debian/ ## ## (C) Copyright 2021-2022 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of ## the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## Use automake to process this into a Makefile.in SUBDIRS = source EXTRA_DIST = changelog\ changelog.src\ control\ control.src\ copyright\ rules MAINTAINERCLEANFILES = changelog\ control GlassCoder-2.0.1/debian/changelog.src000066400000000000000000000002401425562563600174340ustar00rootroot00000000000000glasscoder (@VERSION@-1) unstable; urgency=medium * Production release (see ChangeLog for details) -- Fred Gleason @DATESTAMP@ GlassCoder-2.0.1/debian/control.src000066400000000000000000000036721425562563600172010ustar00rootroot00000000000000Source: glasscoder Section: sound Priority: optional Maintainer: Fred Gleason Build-Depends: debhelper-compat (= 12), autotools-dev, libfdk-aac-dev, libtwolame-dev, libmp3lame-dev, libopus-dev, libvorbis-dev Standards-Version: 4.4.1 Homepage: https://github.com/ElvishArtisan/GlassCoder Package: glasscoder Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Recommends: libfdk-aac1, libtwolame0, libmp3lame0 Description: Minimalist Audio Streaming Encoder Glasscoder is a live audio encoder intended for use with Shoutcast and Icecast streaming servers as well as HTTP Live Streams [HLS]. It can source audio from ALSA, JACK and AudioScience HPI devices and is capable of generating live audio streams in a variety of formats. It is controlled completely from the command-line, with no configuration files or GUI required. Package: glasscoder-gui Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, glasscoder (= @VERSION@-1) Description: GlassGui and GlassCommander control applets for GlassCoder GlassGui is a control applet for driving a single instance of the GlassCoder audio streaming encoder. It provides a complete point-and-click interface for setting all of the GlassCoder parameters and can be used directly to instantiate an actual instance of GlassCoder or to generate a command-line invocation that can be copy/pasted into another environment. GlassCommander is a control applet for driving multiple simultaneous instances (potentially dozens) of the GlassCoder audio streaming encoder. It provides full visibility and control of the operating state of each GlassCoder instance while conserving screen real estate. Package: glasscoder-pypad Architecture: all Depends: ${misc:Depends}, python3 Description: PyPAD script for Rivendell This package provides a PyPAD script for use with the Rivendell Radio Automation System. It can be used to send articulated PAD updates to GlassCoder instances. GlassCoder-2.0.1/debian/copyright000066400000000000000000000026301425562563600167340ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: glasscoder Upstream-Contact: fredg@paravelsystems.com Source: https://github.com/ElvishArtisan/GlassCoder Files: * Copyright: 2012-2021 Fred Gleason License: GPL-2 This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This package 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 On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". Files: docs/* Copyright: 2015-2021 Fred Gleason License: GFDL-1.3 Permission is granted to copy, distribute and/or modify this documentation under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in "/usr/share/common-licenses/GFDL-1.3". GlassCoder-2.0.1/debian/rules000077500000000000000000000034371425562563600160670ustar00rootroot00000000000000#!/usr/bin/make -f build: dh_update_autotools_config dh_autoreconf ./configure --prefix=/usr make $(DEBUILD_MAKE_ARGS) binary: dh_testroot dh_prep dh_auto_install dh_installchangelogs ChangeLog mkdir -p debian/glasscoder/usr/bin mv debian/tmp/usr/bin/glasscoder debian/glasscoder/usr/bin/ mv debian/tmp/usr/bin/glassconv debian/glasscoder/usr/bin/ mkdir -p debian/glasscoder/usr/share/man/man1 mv debian/tmp/usr/share/man/man1/glasscoder.1 debian/glasscoder/usr/share/man/man1/ mkdir -p debian/glasscoder/usr/share/man/man7 mv debian/tmp/usr/share/man/man7/glasscoder-ipc.7 debian/glasscoder/usr/share/man/man7/ mkdir -p debian/glasscoder/usr/share/doc/glasscoder cp NEWS debian/glasscoder/usr/share/doc/glasscoder/ mkdir -p debian/glasscoder-gui/usr/bin mv debian/tmp/usr/bin/glasscommander debian/glasscoder-gui/usr/bin/ mv debian/tmp/usr/bin/glassgui debian/glasscoder-gui/usr/bin/ mkdir -p debian/glasscoder-gui/usr/share/man/man1 mv debian/tmp/usr/share/man/man1/glasscommander.1 debian/glasscoder-gui/usr/share/man/man1 mv debian/tmp/usr/share/man/man1/glassgui.1 debian/glasscoder-gui/usr/share/man/man1 mkdir -p debian/glasscoder-gui/usr/share/icons/hicolor mv debian/tmp/usr/share/icons/hicolor/16x16 debian/glasscoder-gui/usr/share/icons/hicolor/ mv debian/tmp/usr/share/icons/hicolor/22x22 debian/glasscoder-gui/usr/share/icons/hicolor/ mv debian/tmp/usr/share/icons/hicolor/48x48 debian/glasscoder-gui/usr/share/icons/hicolor/ mv debian/tmp/usr/share/applications debian/glasscoder-gui/usr/share/ mkdir -p debian/glasscoder-pypad/usr/lib mv debian/tmp/usr/lib/rivendell debian/glasscoder-pypad/usr/lib/ dh_strip_nondeterminism dh_compress dh_fixperms dh_missing dh_dwz dh_strip dh_makeshlibs dh_shlibdeps dh_installdeb dh_gencontrol dh_md5sums dh_builddeb %: dh $@ GlassCoder-2.0.1/debian/source/000077500000000000000000000000001425562563600163005ustar00rootroot00000000000000GlassCoder-2.0.1/debian/source/Makefile.am000066400000000000000000000016371425562563600203430ustar00rootroot00000000000000## automake.am ## ## Automake.am for GlassPlayer debian/source ## ## (C) Copyright 2021 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of ## the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## Use automake to process this into a Makefile.in EXTRA_DIST = format GlassCoder-2.0.1/debian/source/format000066400000000000000000000000141425562563600175060ustar00rootroot000000000000003.0 (quilt) GlassCoder-2.0.1/docs/000077500000000000000000000000001425562563600145065ustar00rootroot00000000000000GlassCoder-2.0.1/docs/Makefile.am000066400000000000000000000040041425562563600165400ustar00rootroot00000000000000## automake.am ## ## docs/Automake.am for GlassCoder ## ## (C) Copyright 2014-2015 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## by Fred Gleason ## ## Use automake to process this into a Makefile.in # The dependency for man pages %.1: %.xml xsltproc $(DOCBOOK_STYLESHEETS)/manpages/docbook.xsl $< %.7: %.xml xsltproc $(DOCBOOK_STYLESHEETS)/manpages/docbook.xsl $< %.html: %.xml xsltproc -o $@ $(DOCBOOK_STYLESHEETS)/xhtml/docbook.xsl $< all-local: glasscoder.1 glasscoder.html\ glasscoder-ipc.7 glasscoder-ipc.xml\ glasscommander.1 glasscommander.html\ glassgui.1 glassgui.html man_MANS = glasscoder.1\ glasscoder-ipc.7\ glasscommander.1\ glassgui.1 EXTRA_DIST = glasscoder.1\ glasscoder.html\ glasscoder.xml.in\ glasscoder-ipc.7\ glasscoder-ipc.html\ glasscoder-ipc.xml\ glasscommander.1\ glasscommander.html\ glasscommander.xml\ glassgui.1\ glassgui.html\ glassgui.xml CLEANFILES = *~ DISTCLEANFILES = *.1\ *.7\ *.html\ glasscoder.xml\ moc_* MAINTAINERCLEANFILES = *~\ *.fo\ *.pdf\ Makefile.in GlassCoder-2.0.1/docs/glasscoder-ipc.xml000066400000000000000000000134061425562563600201330ustar00rootroot00000000000000 glasscoder-ipc 7 October 2019 Linux Audio Manual glasscoder-ipc IPC methods for the GlassCoder stream encoder Fred Gleason fredg@paravelsystems.com Application Author glasscoder OPTIONS Description glasscoder1 is an audio encoder that is capable of generating live streams using a variety of formats and sending them to an Icecast or Shoutcast audio streaming server or posting them as HTTP Live Streams [HLS]. For more information regarding its overall capabilities, see the glasscoder1 man page. A running glasscoder1 instance can be monitored and controlled by means of two different IPC methods: its standard input/output streams and HTTP calls. Monitoring via Standard Output glasscoder1 can output realtime information about current operating state via its standard output and/or error streams. The location and format in which this information is generated is controlled by means of the and options. When --errors-to=STDOUT is specified, glasscoder1 will output error messages on standard output in the following machine-readable format: ER priority msg where priority is an integer the indicates the priority level of the error as defined by the syslog3 interface and msg is a text string describing the error. Each message is terminated by a newline character. When --meter-data is specified, glasscoder1 will output realtime audio level information suitable for being rendered as an audio meter display in the following format: ME left-lvlright-lvl where each argument is the current audio level of the corresponding channel in hexidecimal, referenced to 0 dBFS. Each message is terminated by a newline character. Control via Standard Input glasscoder1 supports the following command, sent via standard input: MD text Send the metadata text to the stream. The command should be terminated by newline. Control via HTTP If the option is given a non-zero value, metadata updates can be sent to the stream by means of HTTP calls. For details, see the METADATA section of the glasscoder1 man page. Proxy Connections It is possible to implement proxy connectors for glasscoder1's IceStreamer server through use of the command switch. This switch will create a UNIX socket which can be used to pipe in open descriptors for player connections by means of ancilliary socket messages with the SCM_RIGHTS message type. See the unix7 man page for more information. A fully worked-out example is also available in the GlassCoder source package in the 'src/tests/' subdirectory. Author Fred Gleason <fredg@paravelsystems.com> See Also glasscoder1 , cmsg3 , sendmsg2 , syslog3 , unix7 GlassCoder-2.0.1/docs/glasscoder.xml.in000066400000000000000000001314211425562563600177650ustar00rootroot00000000000000 glasscoder 1 February 2022 Linux Audio Manual glasscoder Minimalist audio encoder for live streaming Fred Gleason fredg@paravelsystems.com Application Author glasscoder OPTIONS Description glasscoder1 is an audio encoder that is capable of generating live streams using a variety of formats and sending them to an Icecast or Shoutcast audio streaming server or posting them as HTTP Live Streams [HLS]. It is also capable of acting as an Icecast-compatible server in its own right, serving streams directly to client players and thus eliminating the need for an intervening Icecast server instance. glasscoder1 has no GUI or configuration file components at all; its sole 'user interface' being its command-line invocation. As such, it is particularly well suited for being driven by an external process or controller such as glassgui1 or glasscommander1. Options Emit a stream consisting of self-contained frames --e.g. by disabling the MPEG-1 bit reservoir. Useful mostly for debugging. kbps The constant stream data rate in kilobits per second. Default value is 128. Use of this option is mutually exclusive with that of the option (see below). chans The number of audio channels to use. Valid values are 1 or 2. Default value is 2. type The type of audio device to use. The default value is JACK. See the DEVICE OPTIONS section (below) for the options appropriate for each audio device type. Valid values are:
ALSA The Advanced Linux Sound Architecture.
ASIHPI AudioScience HPI.
FILE Stream directly from a file.
JACK The Jack Audio Connection Kit.
fmt The audio encoding format to use. The default value is VORBIS. Valid fmt values are:
AACP MPEG-4 Advanced Audio Coding "Plus" (High Efficiency Profile)
MP2 MPEG-1/1.5 Layer 2
MP3 MPEG-1/1.5 Layer 3
OPUS Ogg Opus (RFC-6716)
PCM16 PCM16 Uncompressed
VORBIS Ogg Vorbis
qual Use variable bitrate streaming at the given audio quality. qual can be in the range 0.0 (lowest quality) to 1.0 (highest). Use of this option is mutually exclusive with that of the option (see above). rate The audio sample rate to use for streaming. If the underlying audio layer is operating at a different sample rate, the input will be automatically resampled to this rate. Default value is 44100. filename Get the credentials for connecting to the server in the filename file. The contents of the file should be formatted as follows: [Credentials] Username=<username> Password=<password> When using the option, the passphrase for the specified identity file should be used as the password value. Delete the file specified by after being read. string Prepend string to messages sent to the syslog service (see the option, below). Useful for disambiguating messages from multiple glasscoder1 instances. dest Send error messages to dest (default standard error). Valid destinations are:
STDERR Standard error.
SYSLOG The system syslog service.
STDOUT Standard output,in machine readable format (useful for communication with another 'controller' program). See also the option below.
Print a short usage message and exit. Print a list of available codecs and then exit. Print a list of available source devices and then exit. port Accept metadata updates via HTTP at port port. Default value is 0, which disables metadata updates. See the METADATA section (below) for information regarding the supported update formats. Output meter level updates on standard output. Useful for driving an external metering display. [username][password] (DEPRECATED) The authentication parameters to use. This parameter has no default. NEVER USE THIS OPTION! It allows credentials to be seen by other users on the system. Use the option to specify server credentials instead. Exit the program upon closure of the last player connection. This setting is used only by the IceStreamer server. conns Allow a maximum of conns simultaneous player connections. Players beyond this maximum attempting to connect will receive an immediate TCP disconnect before the HTTP handshake. This setting is used only by the IceStreamer server. Do not clean up or delete content previously posted to the publishing point. This setting is used only by the HLS server type. pathname Location to create a UNIX socket for piping connection socket descriptors. Useful for implementing proxy connectors for the IceStreamer server. For further details about this feature, see the Proxy Connections section of the glasscoder-ipc7 man page. The default is to create no UNIX socket. This setting is used only by the IceStreamer server. cmd Run the command cmd when the connection enters the disconnected state. cmd Run the command cmd when the connection enters the connected state. conns Do not start the audio transport until at least conns connections have been established. Used only by the IceStreamer server in conjunction with the FILE audio device. Default value is 0 --i.e. start the transport immediately. type The type of streaming server to use (default is Icecast2). Valid values for type are:
FILE Local file FILEARCHIVE Local file archive. Stream to a set of files on the local system, starting a new file at the beginning of each hour. HLS HLS/HTTP Live Streaming IceCast2 IceCast v2 IceOut Output an Icecast-compatible stream on standard output. IceStreamer Stream directly to players using the internal Icecast-compatible server. Shout1 Shoutcast v1 Shout2 Shoutcast v2
url The URL describing the server resource to stream to. See the SUPPORTED URL SCHEMES section (below) for a list of what URL schemes are appropriate for which server types. When used with a of IceStreamer, the host part of the URL is used to specify the address of the network interface to use for streaming (use 0.0.0.0 to indicate ALL interfaces). This parameter has no default. agent-string The User-Agent header value to use when connecting to external servers. Default value is GlassCoder/@VERSION@. This setting is used only by IceCast2 and HLS servers. filename The path to the file containing an ssh(1) identity to use when connecting via SFTP. When using this option, the passphrase for the identity file should be provided as the server password. See the option for details. aim The AOL Instant Messenger ID to associate with the stream. There is no default value. This setting is used only by Shoutcast servers. string The string to show as the stream description. There is no default value. This setting is used only by Icecast servers. string The string to show as the stream genre. There is no default value. This setting is used only by Icecast and Shoutcast servers. icq The ICQ ID to associate with the stream. There is no default value. This setting is used only by Shoutcast servers. irc The Internet Relay Chat ID to associate with the stream. There is no default value. This setting is used only by Shoutcast servers. string The string to show as the stream name. There is no default value. This setting is used only by Icecast and Shoutcast servers. offset The offset to add to the value of stream timestamps, in seconds. Default value is 0. This setting is used only for HLS streams. url The URL to show for a page giving more information about the stream. There is no default value. This setting is used only by Icecast and Shoutcast servers, but is ignored by Shoutcast v2 servers. Increase verbosity level of information printed to standard error. WARNING: this may cause cleartext passwords to printed! Output version information and exit.
Device Options Advanced Linux Sound Architecture (ALSA) dev The name of the ALSA device to use. If no option is given, then the hw:0 device will be used. AudioScience HPI (=ASIHPI) index The index number of the audio adapter to use. index The number of the input audio stream to use on the specified adapter. gain The gain to apply to the input audio on the specified adapter in dB. Valid values are -100 to +20 inclusive. Default value is 0. mode The channel mode for the input audio on the specified adapter. The default value is NORMAL. Valid values are:
NORMAL Left signal goes to left channel, right signal goes to right channel. SWAP Left signal goes to right channel, right signal goes to left channel. LEFT Left signal goes to both left and right channels. RIGHT Right signal goes to both left and right channels.
src-node The input source to use on the specified adapter. See the HPI Source Nodes section below for the list of valid src-node values. The default value is LINEIN. src-node The input type to use on the specified adapter. See the HPI Source Nodes section below for the list of valid src-node values. The default value is LINEIN. HPI Source Nodes The following values can used for the and options above: NONE OSTREAM Internal output stream LINEIN Generic input signal AESEBU AES3 digital input TUNER Tuner RF RF input CLOCK Clock source BITSTREAM Raw bitstream MIC Microphone COBRANET CobraNet ANALOG Analog input ADAPTER Adapter RTP INTERNAL Device internal AVB AVB input
Direct File Streaming (FILE) name The name of the file to stream. If no option is given, then the name of the file will be read from standard input. The Jack Audio Connection Kit (JACK) name The name of the JACK client to use. Default is glasscoder. gain Apply a fixed gain of gain dB before encoding. Default is 0 dB. name The name of the JACK server instance to use.
Supported URL Schemes Not all URL schemes are supported by all server types. The following chart breaks down the options. Supported URL Schemes by Server Type Server Type FILE:// HTTP:// SFTP:// FILE Yes No No FILEARCHIVE Yes No No HLS Yes Yes [1] Yes IceCast2 No Yes [2] No IceOut No Yes No IceStreamer No Yes No Shout1 No Yes [2] No Shout2 No Yes [2] No [1] Utilizes the HTTP PUT and DELETE methods [2] Utilizes the HTTP GET method
Metadata GlassCoder supports the notion of two types of metadata: "channel-based" metadata, which applies to the stream as a whole and does not change for the duration of an encoding session; and "timed" metadata, which can be changed in synchronization with the content of the audio stream. Channel-based metadata can be specified by means of options given to glasscoder1 and will be covered in detail in the sections devoted to specific server types (below). The primary mechanism for supplying timed metadata in GlassCoder is by means of a JSON document containing the desired metadata, sent to the target glasscoder1 instance at the port specified by the option by means of an HTTP POST operation. The basic format of the JSON document is as follows: { "Metadata": { "Field1": "Some value", "Field2": "Some other value" } } Not all server types support metadata, and those that do utilize wildly different schemas. Following is a breakdown of the available metadata options by server type: IceCast2 Channel Metadata IceCast2 supports the following channel metadata fields: Name Specified by the option. Description Specified by the option. URL Should be a link to content related to the stream. Specified by the option. Genre Should be a single word describing the nature of the stream content. Specified by the option. Timed Metadata IceCast2 provides one field of text, called StreamTitle, which can be dynamically updated to reflect the content currently playing on the stream. By convention, this is usually formatted as 'Artist - Title' on streams containing musical content. For example, to set the StreamTitle field to The Beatles - Hey Jude, the following JSON could be used: { "Metadata": { "StreamTitle": "The Beatles - Hey Jude" } } Legacy Interface In addition to the primary JSON interface, the StreamTitle can be set by sending an HTTP GET request to a running glasscoder1 instance, using the TCP port specified in the port option. The request must be in the following format: http://hostname:tcp-port/admin/metadata?mount=mount-point&mode=updinfo&song=string Where: hostname - The hostname or IP address of the system running glasscoder1 tcp-port - The TCP port number specified in the option to glasscoder1 mount-point - The mountpoint of the stream string - The string to set, encoded as specified in Section 2 of RFC3986 For example, to set a string of "The Beatles - Hey Jude" via a glasscoder1 instance running at encoder.example.com with a value of 1234 for a mountpoint of MyStream, the URL would be: http://encoder.example.com:1234/admin/metadata?mount=MyStream&mode=updinfo&song=The%20Beatles%20-%20Hey%20Jude ShoutCast Channel Metadata ShoutCast supports the following channel metadata fields: Name Specified by the option. URL Should be a link to content related to the stream. Specified by the option. Genre Should be a single word describing the nature of the stream content. Specified by the option. ICQ ID Should be User Identification Number for an ICQ user associated with the stream content. Specified by the option. AOL Instant Messenger ID Should be an ID for an AOL Instant Messenger user associated with the stream content. Specified by the option. IRC ID Should be an ID for an Internet Relay Chat channel associated with the stream content. Specified by the option. Timed Metadata ShoutCast provides two fields of text which can be dynamically updated to reflect the content currently playing on the stream, called StreamTitle and StreamUrl. By convention, the StreamTitle is usually formatted as 'Artist - Title' on streams containing musical content, while StreamUrl is used to provide a URL whence stream specific content --e.g. album cover art -- can be retrieved. For example, to set the StreamTitle field to The Beatles - Hey Jude and the StreamUrl field to http://images.example.com/1234.png, the following JSON could be used: { "Metadata": { "StreamTitle": "The Beatles - Hey Jude", "StreamUrl": "http://images.example.com/1234.png" } } The use of either of these fields is optional in any given metadata update. If only one field is given, the other will remain unchanged. Legacy Interface In addition to the primary JSON interface, the StreamTitle and StreamUrl fields can be set by sending an HTTP GET request to a running glasscoder1 instance, using the TCP port specified in the port option. The request must be in the following format: http://hostname:tcp-port/admin.cgi?pass=password&mode=updinfo&song=stream-title&url=stream-url Where: hostname - The hostname or IP address of the system running glasscoder1 tcp-port - The TCP port number specified in the option to glasscoder1 password - The ShoutCast password, encoded as specified in Section 2 of RFC3986 stream-title - The string to set for StreamTitle, encoded as specified in Section 2 of RFC3986 stream-url - The string to set for StreamUrl, encoded as specified in Section 2 of RFC3986 For example, to set a StreamTitle of "The Beatles - Hey Jude" and a StreamUrl of "http://image.example.com/1234.png" with a password of "MyPassword" via a glasscoder1 instance running at encoder.example.com with a value of 1234, the URL would be: http://encoder.example.com:1234/admin.cgi?pass=MyPassword&mode=updinfo&song=The%20Beatles%20-%20Hey%20Jude&url=http://image.example.com/1234.png HTTP Live Streams (HLS) Timed Metadata HLS supports timed metadata in the form of embedded ID3v2.4 tags. Available fields thus include the entire set of text tags defined in the ID3v2.4 frame specification (available at http://id3.org/id3v2.4.0-frames). For example, a typical metadata update could use the following JSON: { "Metadata": { "TIT2": "Hey Jude", "TPE1": "The Beatles", "TALB": "The White Album", "TRSO": "WXYZ Radio" } } A user defined text information frame (TXXX) can be sent by using the following special notation for the field identifier: TXXXdesc Where: desc - The TXXX Description string (see Section 4.2.6 of the ID3v2.4 Frame Specification) For example: { "Metadata": { "TIT2": "Hey Jude", "TPE1": "The Beatles", "TALB": "The White Album", "TRSO": "WXYZ Radio", "TXXXxyz": "TXXX frame with a description string of \"xyz\"" } } Bugs Never use the option; it allows credentials to be seen by other users on the system. Use the option to specify server credentials instead. Ogg metadata support is still missing. Author Fred Gleason <fredg@paravelsystems.com> See Also glasscoder-ipc7 , glasscommander1 , glassgui1 , jackd1 RFC3986 - Uniform Resource Identifier (URI): Generic Syntax ID3v2.4 Native Frame Specification (http://id3.org/id3v2.4.0-frames)
GlassCoder-2.0.1/docs/glasscommander.xml000066400000000000000000000054001425562563600202260ustar00rootroot00000000000000 glasscommander 1 January 2020 Linux Audio Manual glasscommander High-density GUI front-end for the glasscoder1 audio encoder Fred Gleason fredg@paravelsystems.com Application Author glasscommander OPTIONS Description glasscommander1 is a high-density GUI front end for the glasscoder1 audio encoder. It provides a convenient and discoverable means for creating and saving glasscoder1 invocations and an instance management system (fully compatible with that of glassgui1) for saving multiple sets of configurations on the local system in a manner that conserves screen real-estate. glasscommander1 is capable of hosting dozens of simultaneous glasscoder1 instances while still allowing easy access to the control and status of each one. Options dir Use dir as the location for storing instance data. Default value is $HOME/.glassgui. Author Fred Gleason <fredg@paravelsystems.com> See Also glasscoder1 , glassgui1 GlassCoder-2.0.1/docs/glassgui.xml000066400000000000000000000070361425562563600170540ustar00rootroot00000000000000 glassgui 1 January 2020 Linux Audio Manual glassgui Single-stream GUI front-end for the glasscoder1 audio encoder Fred Gleason fredg@paravelsystems.com Application Author glassgui OPTIONS Description glassgui1 is a GUI front end for a single instance of the glasscoder1 audio encoder. It provides a convenient and discoverable means for creating and saving glasscoder1 invocations as well as an instance management system for saving multiple sets of configurations on the local system. For a GUI front-end that is capable of managing dozens of glasscoder1 instances simultaneously, see glasscommander1. Options Start encoding immediately upon startup. name Delete the name instance from storage and then exit. dir Use dir as the location for storing instance data. Default value is $HOME/.glassgui. name Load and save settings from/to the name instance. Print the list of saved instances to standard output and then exit. Author Fred Gleason <fredg@paravelsystems.com> See Also glasscoder1 , glasscommander1 GlassCoder-2.0.1/get_distro.pl000077500000000000000000000055441425562563600162710ustar00rootroot00000000000000#!/usr/bin/perl -W # get_distro.pl # # Read various fields from 'os-release'. # Used as part of the AR_GET_DISTRO() macro. # # See https://www.freedesktop.org/software/systemd/man/os-release.html # for a description of the various fields. # # (C) Copyright 2012-2021 Fred Gleason # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # my $usage="USAGE: get_distro.pl NAME|PRETTY_NAME|ID|ID_LIKE|VERSION|MAJOR|MINOR|POINT"; if($ARGV[0] eq "NAME") { print &Extract("NAME"); exit 0; } if($ARGV[0] eq "PRETTY_NAME") { print &Extract("PRETTY_NAME"); exit 0; } if($ARGV[0] eq "ID") { print &Extract("ID"); exit 0; } if($ARGV[0] eq "ID_LIKE") { print &Extract("ID_LIKE"); exit 0; } if($ARGV[0] eq "VERSION") { print &Extract("VERSION_ID"); exit 0; } if($ARGV[0] eq "MAJOR") { my $ver=&Extract("VERSION_ID"); my @f0=split '\.',$ver; print $f0[0]; exit 0; } if($ARGV[0] eq "MINOR") { my $ver=&Extract("VERSION_ID"); my @f0=split '\.',$ver; if(scalar(@f0)>=2) { print $f0[1]; exit 0; } print "0"; exit 0; } if($ARGV[0] eq "POINT") { my $ver=&Extract("VERSION_ID"); my @f0=split '\.',$ver; if(scalar(@f0)>=3) { print $f0[2]; exit 0; } print "0"; exit 0; } print $usage; exit 256; sub Extract { if((open RELEASE,"<","/etc/os-release") || (open RELEASE,"<","/usr/lib/os-release")) { while() { my @f0=split "\n",$_; for(my $i=0;$i<@f0;$i++) { my @f1=split "=",$f0[$i]; if($f1[0] eq $_[0]) { $f1[1]=~s/^"(.*)"$/$1/; return $f1[1]; # return substr($f1[1],1,length($f1[1])-2); } } } } return ""; } sub GetVersion { if(open VERSION,"<",$_[0]) { my $version=; my @f0=split " ",$version; for(my $i=0;$i<@f0;$i++) { my @f1=split "[.]",$f0[$i]; if(@f1>1) { return $f0[$i]; } } } return ""; } sub GetMajor { my @f0=split "[.]",&GetVersion($_[0]); return $f0[0]; } sub GetMinor { my @f0=split "[.]",&GetVersion($_[0]); if(@f0 ge 2) { return $f0[1]; } return ""; } sub GetPoint { my @f0=split "[.]",&GetVersion($_[0]); if(@f0 ge 3) { return $f0[2]; } return ""; } GlassCoder-2.0.1/get_target.sh000077500000000000000000000023011425562563600162360ustar00rootroot00000000000000#!/bin/bash # get_target.sh # # Return machine target information from gcc(1) # Used as part of the AR_GCC_TARGET() macro. # # (C) Copyright 2012 Fred Gleason # # $Id: get_target.sh,v 1.1.1.1 2014/02/17 13:26:17 cvs Exp $ # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # $1 -dumpmachine > ./gcc_arch case "$3" in arch) $2 -F- "// {print \$1}" ./gcc_arch ;; distro) $2 -F- "// {print \$2}" ./gcc_arch ;; os) $2 -F- "// {print \$3}" ./gcc_arch ;; esac rm -f ./gcc_arch # End of get_target.sh GlassCoder-2.0.1/glasscoder.spec.in000066400000000000000000000117221425562563600171700ustar00rootroot00000000000000## glasscoder.spec.in ## ## Minimalist Audio Streaming Encoder ## ## Copyright (C) 2014-2019 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of version 2 of the GNU General Public License as ## published by the Free Software Foundation; ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, ## Boston, MA 02111-1307 USA ## # So python bytecompilation works correctly %define __python /usr/bin/python3.6 Summary: Minimalist Audio Streaming Encoder Name: glasscoder Version: @VERSION@ Release: @RPM_RELEASE@%{?dist} License: GPLv2 Packager: Fred Gleason Group: Productivity/Multimedia/Other Source: %{name}-@VERSION@.tar.gz BuildRequires: qt5-qtbase-devel libsamplerate-devel libsndfile-devel alsa-lib-devel hpklinux-devel BuildRoot: /var/tmp/%{name}-@VERSION@ Requires: curl %description Glasscoder is a live audio encoder intended for use with Shoutcast and Icecast streaming servers as well as HTTP Live Streams [HLS]. It can source audio from ALSA, JACK and AudioScience HPI devices and is capable of generating live audio streams in a variety of formats. It is controlled completely from the command-line, with no configuration files or GUI required. %package gui Summary: GlassGui and GlassCommander control applets for GlassCoder Group: Productivity/Multimedia/Other Requires: glasscoder = @VERSION@ %description gui GlassGui is a control applet for driving a single instance of the GlassCoder audio streaming encoder. It provides a complete point-and-click interface for setting all of the GlassCoder parameters and can be used directly to instantiate an actual instance of GlassCoder or to generate a command-line invocation that can be copy/pasted into another environment. GlassCommander is a control applet for driving multiple simultaneous instances (potentially dozens) of the GlassCoder audio streaming encoder. It provides full visibility and control of the operating state of each GlassCoder instance while conserving screen real estate. %package pypad Summary: PyPAD script for Rivendell Requires: rivendell-pypad python36 python36-requests %description pypad This package provides a PyPAD script for use with the Rivendell Radio Automation System. It can be used to send articulated PAD updates to GlassCoder instances. %prep %setup %build %configure make -j @CPUS_AVAIL@ %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT %post if [ -x %{_bindir}/gtk-update-icon-cache ] ; then %{_bindir}/gtk-update-icon-cache -f --quiet %{_datadir}/icons/hicolor || : fi %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %{_bindir}/glasscoder %{_bindir}/glassconv %{_mandir}/man1/glasscoder.1.gz %{_mandir}/man1/glasscommander.1.gz %{_mandir}/man1/glassgui.1.gz %{_mandir}/man7/glasscoder-ipc.7.gz %doc AUTHORS %doc ChangeLog %doc COPYING %doc NEWS %doc README %doc conf/httpd/hls.conf %doc conf/httpd/hls_methods.pl %doc conf/httpd/hls.passwd %doc conf/httpd/README.httpd %files gui %{_bindir}/glassgui %{_bindir}/glasscommander %{_datadir}/applications/glassgui.desktop %{_datadir}/applications/glasscommander.desktop %{_datadir}/icons/hicolor/16x16/apps/glasscoder.png %{_datadir}/icons/hicolor/22x22/apps/glasscoder.png %{_datadir}/icons/hicolor/48x48/apps/glasscoder.png %files pypad %{_libdir}/rivendell/pypad/pypad_glasscoder.py %{_libdir}/rivendell/pypad/pypad_glasscoder.exemplar @PYPAD_BINARY@ %changelog * Thu Mar 17 2022 Fred Gleason -- Added glassconv(1). -- Tightened the 'glasscoder' dependency for the 'glasscoder-gui' sub-package to require matching versions. * Mon Jan 10 2022 Fred Gleason -- Replaced the 'qt-devel' build dependency with 'qt5-qtbase-devel'. * Thu Jan 23 2020 Fred Gleason -- Removed 'python36' dependency from the 'glasscoder' package. -- Added 'python36' and 'python36-requests' depedencies to the 'glasscoder-pypad' package. * Tue Sep 17 2019 Fred Gleason -- Add the 'pypad' subpackage. * Fri Nov 18 2016 Fred Gleason -- Added glasscommander(1) to the 'gui' subpackage. * Wed Aug 26 2015 Fred Gleason -- Added a 'gui' subpackage. * Tue Aug 11 2015 Fred Gleason -- Added a curl(1) dependency. -- Added HLS httpd example configuration. * Tue Jun 10 2014 Fred Gleason -- Removed the 'libshout-devel' and 'lame-devel' build dependencies. * Tue Feb 18 2014 Fred Gleason -- Initial RPM creation. GlassCoder-2.0.1/icons/000077500000000000000000000000001425562563600146715ustar00rootroot00000000000000GlassCoder-2.0.1/icons/Makefile.am000066400000000000000000000035571425562563600167370ustar00rootroot00000000000000## automake.am ## ## icons/automake.am for GlassCoder ## ## (C) Copyright 2015 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Use automake to process this into a Makefile.in install-exec-am: mkdir -p $(DESTDIR)@prefix@/share/icons/hicolor/16x16/apps cp glasscoder-16x16.png $(DESTDIR)@prefix@/share/icons/hicolor/16x16/apps/glasscoder.png mkdir -p $(DESTDIR)@prefix@/share/icons/hicolor/22x22/apps cp glasscoder-22x22.png $(DESTDIR)@prefix@/share/icons/hicolor/22x22/apps/glasscoder.png mkdir -p $(DESTDIR)@prefix@/share/icons/hicolor/48x48/apps cp glasscoder-48x48.png $(DESTDIR)@prefix@/share/icons/hicolor/48x48/apps/glasscoder.png uninstall-local: rm -f $(DESTDIR)@prefix@/share/icons/hicolor/16x16/apps/glasscoder.png rm -f $(DESTDIR)@prefix@/share/icons/hicolor/22x22/apps/glasscoder.png rm -f $(DESTDIR)@prefix@/share/icons/hicolor/48x48/apps/glasscoder.png EXTRA_DIST = back.xpm\ glasscoder-16x16.png\ glasscoder-16x16.xpm\ glasscoder-22x22.png\ glasscoder-48x48.png\ minussign.xpm\ plussign.xpm CLEANFILES = *~ MAINTAINERCLEANFILES = *~\ aclocal.m4\ configure\ Makefile.in GlassCoder-2.0.1/icons/back.xpm000066400000000000000000000056321425562563600163250ustar00rootroot00000000000000/* XPM */ static const char * back_xpm[] = { "22 22 116 2", " c None", ". c #84989C", "+ c #9DA696", "@ c #9EA795", "# c #9FA896", "$ c #9FA897", "% c #A0A997", "& c #A0A998", "* c #9EA999", "= c #82959F", "- c #C7CCC2", "; c #F9F9F8", "> c #F9F9F9", ", c #FAFAF9", "' c #FAFAFA", ") c #C8CFC9", "! c #8AA4BC", "~ c #CFD5D0", "{ c #D0D6D0", "] c #CED4D0", "^ c #819CBA", "/ c #3667A5", "( c #74A0D0", "_ c #729FCF", ": c #719CCB", "< c #709DCE", "[ c #3666A5", "} c #79A4D1", "| c #719CCA", "1 c #5A646A", "2 c #75A1D0", "3 c #7FA8D3", "4 c #719BC9", "5 c #616B71", "6 c #555753", "7 c #7BA5D2", "8 c #84ABD5", "9 c #709AC7", "0 c #61696F", "a c #A0A2A2", "b c #80A8D3", "c c #89AFD7", "d c #709AC6", "e c #60676C", "f c #B1B3B2", "g c #F5F5F5", "h c #676965", "i c #636561", "j c #606563", "k c #6A7782", "l c #38659F", "m c #8FB3D9", "n c #7099C3", "o c #5F666A", "p c #B3B4B4", "q c #F3F3F3", "r c #EDEDED", "s c #F2F2F2", "t c #F6F6F6", "u c #F8F8F8", "v c #FCFCFC", "w c #FEFEFE", "x c #FFFFFF", "y c #8B8D8C", "z c #4D5F71", "A c #95B7DB", "B c #5D6A72", "C c #EBEBEB", "D c #E4E4E4", "E c #E6E6E6", "F c #EAEAEA", "G c #EEEEEE", "H c #F1F1F1", "I c #C3C3C3", "J c #555B5B", "K c #9ABADD", "L c #6E97C1", "M c #5A6062", "N c #A6A7A6", "O c #F4F4F4", "P c #C6C6C6", "Q c #565B5A", "R c #A0BEDF", "S c #668CB4", "T c #5B6163", "U c #A3A5A4", "V c #565854", "W c #5C5F5C", "X c #818281", "Y c #A5C2E1", "Z c #678DB5", "` c #5A6163", " . c #929493", ".. c #5A7EA4", "+. c #567595", "@. c #5B5F5C", "#. c #ABC6E2", "$. c #678EB7", "%. c #5A6265", "&. c #8191A2", "*. c #B0CAE4", "=. c #688FB8", "-. c #575F64", ";. c #ACC5DF", ">. c #3C6293", ",. c #3666A4", "'. c #B6CDE6", "). c #B4CCE5", "!. c #3566A5", "~. c #B0C9E3", "{. c #BCD1E8", "]. c #ADC6E2", "^. c #305A8F", "/. c #3565A3", "(. c #3465A4", "_. c #2E5789", " ", " . + @ @ @ @ @ @ @ @ @ @ @ @ # $ % & * = ", " - ; > > > > > , , , , , , , , ' ' ' , ) ", " ! ~ { { { { { { { { { { { { { { { { ] ^ ", " / ( _ _ _ _ _ : _ _ _ _ _ _ _ _ _ _ < [ ", " / } _ _ _ _ | 1 _ _ _ _ _ _ _ _ _ _ 2 [ ", " / 3 _ _ _ 4 5 6 _ _ _ _ _ _ _ _ _ _ 7 [ ", " / 8 _ _ 9 0 a 6 _ _ _ _ _ _ _ _ _ _ b [ ", " / c _ d e f g h i i i i i i i i i j k l ", " / m n o p q r s q q g t u ' v w x t y z ", " / A B h g C D E E E F G H g > v w w I J ", " / K L M N q O O q q g t u ' v w x w P Q ", " / R _ S T U O V 6 6 6 6 6 6 6 6 6 W X Q ", " / Y _ _ Z ` .6 ..................+.@.6 ", " / #._ _ _ $.%.6 _ _ _ _ _ _ _ _ _ _ &.6 ", " / *._ _ _ _ =.-._ _ _ _ _ _ _ _ _ _ ;.>. ", " ,.'._ _ _ _ _ | _ _ _ _ _ _ _ _ _ _ ).!. ", " ,.~.{.{.{.{.{.{.{.{.{.{.{.{.{.{.{.{.].,. ", " ^./.(.(.(.(.(.(.(.(.(.(.(.(.(.(.(.(./._. ", " ", " ", " "}; GlassCoder-2.0.1/icons/glasscoder-16x16.png000066400000000000000000000012161425562563600203100ustar00rootroot00000000000000‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEß%P xôIDAT8Ë¥’OkQÅ“¼¶´ Ô…è&ÖÖ¤nl­ß"%èF]YP¨à¶ßD° ¢RÝ‹ÍG\$mk*Mé¿$3mʼ{¯‹™I(¸ðÀcÞ\xg~ç¼ ~4¾­Lß~4::F&3ÃÌÃŒ ûì] £ÐZ;¿¾9U©ŽŽqtt@«ÕbíóKÏ–(•J—Î'Æ'‚XâZÈg_^¯×i4›4šMÖëuþ5WÕ¼SÕ677( ƒ}¦Ëæf†©áÎóB@ÖE ÎãFnÄ u¤*¨)C¢ÂËåW¨*oV_‹E.Ê{O.¤zNÐíôø¹½‰ŠÒíôÈÌ )~&‘ïT§vNpsfŠï?¡ª,,Ì#âÓ¬B{o€ë׮⽼÷"â©”+TÊ•!T3ew·Í‹ç˨*oß­¦s#ŠÂ¤Á{Ïä•ÉÔœsƒn÷B´nªFa!3˜-Ïò»µ ÀÒ"1fÆâb•µ_PUž>y<0£^AÄã}L¹xX/fÊÞ^UˆÏçð^è÷OÙÚj •ÛÜjšs®î:Qo¥»qHÿì¬ÇqÞû˜8N—ñÙ3-7ùKqÎÕûq%¨V«üþy£µ‘f;!IEND®B`‚GlassCoder-2.0.1/icons/glasscoder-16x16.xpm000066400000000000000000000054261425562563600203370ustar00rootroot00000000000000/* XPM */ static const char * glasscoder_16x16_xpm[] = { "16 16 137 2", " c None", ". c #D5D9D2", "+ c #F9F9F8", "@ c #FFFFFF", "# c #FEFEFE", "$ c #FDFDFD", "% c #FDFDFC", "& c #FCFCFC", "* c #FBFBFB", "= c #FBFBFA", "- c #FAFAF9", "; c #F9F9F9", "> c #F8F8F7", ", c #ECEDEB", "' c #D0D2CC", ") c #D3D7CF", "! c #EEEEEC", "~ c #D2D2D0", "{ c #737471", "] c #F6F6F5", "^ c #CBCFC7", "/ c #838482", "( c #4C4E4B", "_ c #C9CDC5", ": c #D1D5CD", "< c #5F605D", "[ c #585A56", "} c #F5F5F4", "| c #C8CBC4", "1 c #D0D4CC", "2 c #60615D", "3 c #5E605D", "4 c #636561", "5 c #EDEDEB", "6 c #F3F3F1", "7 c #C6CAC2", "8 c #CED2CA", "9 c #5D5F5A", "0 c #C4C5C4", "a c #C2C3C2", "b c #575954", "c c #6F716D", "d c #E9E9E7", "e c #EFEFEC", "f c #C5C8C1", "g c #CDD0C9", "h c #4F504D", "i c #A3A7A0", "j c #A1A49E", "k c #494A47", "l c #ECECEA", "m c #EBEBE9", "n c #7A7D78", "o c #E6E6E2", "p c #EBEBE8", "q c #C3C6BF", "r c #FAFAFA", "s c #6E716B", "t c #40423F", "u c #3E403D", "v c #6D6F6A", "w c #E9E9E6", "x c #E8E8E5", "y c #797B75", "z c #626561", "A c #E2E2DE", "B c #E6E6E3", "C c #BFC2BB", "D c #CACDC6", "E c #92948F", "F c #E5E5E2", "G c #E4E4E0", "H c #61635E", "I c #5B5D58", "J c #DEDEDA", "K c #E1E1DD", "L c #B4B7AF", "M c #E7E7E4", "N c #9DA09A", "O c #E1E1DE", "P c #E0E0DC", "Q c #535551", "R c #4D4F4B", "S c #DBDBD6", "T c #DCDCD7", "U c #A8ACA3", "V c #F3F3F2", "W c #E3E3E0", "X c #C6C7C3", "Y c #A9ABA5", "Z c #DEDED9", "` c #DDDDD8", " . c #9B9D98", ".. c #444543", "+. c #424341", "@. c #9A9A97", "#. c #D7D7D2", "$. c #D6D6D0", "%. c #9CA196", "&. c #F0F0EE", "*. c #DFDFDB", "=. c #E9EAE8", "-. c #C7C9C4", ";. c #E8E9E6", ">. c #DADAD5", ",. c #D9D9D4", "'. c #E7E8E6", "). c #E6E7E5", "!. c #D3D3CE", "~. c #D2D2CC", "{. c #91958A", "]. c #F6F6F4", "^. c #D6D6D1", "/. c #D5D5D0", "(. c #F4F4F2", "_. c #D1D1CB", ":. c #CDCDC7", "<. c #878C80", "[. c #C1C4BF", "}. c #D9DBD6", "|. c #E4E4E1", "1. c #E3E3DF", "2. c #E0E0DD", "3. c #D9D9D3", "4. c #D3D3CD", "5. c #A7AAA1", "6. c #8B9084", "7. c #B3B6AD", "8. c #ABAFA6", "9. c #A6AAA1", "0. c #A2A69C", "a. c #9DA197", "b. c #989C92", "c. c #93988D", "d. c #8F9388", "e. c #8A8F83", "f. c #868B7F", ". + @ @ @ # $ % & * = - ; > , ' ", ") @ ! ~ { { ~ ! ! ~ { { ~ ! ] ^ ", ") @ ! / ( ( / ! ! / ( ( / ! ] _ ", ": # ! < [ [ < ! ! < [ [ < ! } | ", "1 $ ! 2 3 3 2 ! ! 4 4 4 4 5 6 7 ", "8 & ! 9 0 a b ! ! c c c c d e f ", "g * ! h i j k l m n n n n o p q ", "^ r ! s t u v w x y z z y A B C ", "D + p E E E E F G H 0 a I J K L ", "| ] M j N N j O P Q i j R S T U ", "7 V W X Y Y X Z ` ...+.@.#.$.%.", "f &.*.=.-.-.;.>.,.'.-.-.).!.~.{.", "q m T K ].} J ^./.S (.(.,._.:.<.", "[.}.|.1.2.Z S 3./.4._._._.:.5.6.", " 7.8.9.0.a.b.c.d.e.f.f.f.<.6. ", " "}; GlassCoder-2.0.1/icons/glasscoder-22x22.png000066400000000000000000000021161425562563600203020ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEß9]úÛIDAT8Ë•IoTG…¿ª~¦_<€m6HÜâ„)˜…ÔR$¦,ˆ”¿AÅ:‹DþYÅa#¦° )ÆA1Äf ¸ín¿¶»_OUu³èçH$W:zª£[§Î»÷ªJ?ü}qwמ ÓÓS\¼t‘;wîÐ××Ë©“§Ø¼y3¹ÜÂ;óÛ¶mçáä÷ 5à\¾r™±ŸÆ˜æùùÖ-Î_¸ÀÿáµÒ¶RÁóˆâÞo÷ð}iÉøø}ªÕ*wïÝ¥¥¥¥@+]ç×Ê/—Ë XðD" üÄëDcªˆXö}ºÓ§¿dlì W¯_ØêšùÖÄ9 Å "‚“ðýeE>ŸgàÈq¿CŸdnvkí[òÁ9!<'Úéj™”‚XÌcdäN~q‚ëWo¯Ãó¼5óE, BMX?î£Vf¢Øºe+“&9{fˆööv>ؾhˆ5¯æ)„Þ[¿D"޵qrà‰D¥Hø(­V8V4ol¦·¯—Þ¾Þ:ïœ#,èîÚU¯ýí_nc­ÃÕ ¿®qLkrAŽ'ÓOÈÍçÐJáœ[³–ÎYffÒËjÿìÙs*• ÖÙåÂÙl–dG'ß};L?éÙ¹èO« âhjjbtt„ñ‰ûœ?ÿ#ÍÍÍh­¨T*@€®•ÂñâÅ &î7pðЦþž¢Z-ãÇ×­r,"ìîéa6fèësL<˜`ïÞ=Xk(†!AM…sŽÖÖVFGG8qê87®Ý¤­µ¥4qß'æ™݇Mm›<:ÈàÑÁ7æÞP, (Ôšçœcÿýܼq“¡³çH&;8r¡±q¥R©¾YkýVÞZK),Bž8Á9KÿÇŸÐØØÈ±ãÇØºå}”‚ÎdÿU¨ñ²&BX*°@¹æØZKòÃNb±¯f^ÒÖÚN²c3¯žÓÙщç-ç_þó”ÎŽä*þÉô$aX„ÒâkáB!O÷Îzº?BÄ‘ÍfBa‘î»ê|&3‡1†\. kGwÎ1;û c ¥r‘à!T­5þÂB@d”R(¥!²ÌÏgq8W› !“I“ɤk°Ô+k­gžZ¯T,^Êd3_556é¨çˆ€sç\‹s²b2¤>! kx<õ­õ¯ÃÃ߇ÞN|,æš•âskm̃µc-ÖŒ5Xc£o´¶¶.üF8…º5“ž=S»e ˆë#$¢WE³êþZn:B( J€UÑæX$´$¨y÷pl—J¥œN¥RÕè´Èÿ,¹¬6•J9€Y‰~@.PÑpIEND®B`‚GlassCoder-2.0.1/icons/glasscoder-48x48.png000066400000000000000000000061411425562563600203240ustar00rootroot00000000000000‰PNG  IHDR00Wù‡bKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEß9Wƒ= îIDAThÞÍ™kl×uÇw;ûàŠ")¾,Š/™zQ’+R¶d[‰ƒ)U·HˆQKm#iì"È—Övƒ~ Û€ ÷C ©FQ;FÓJv9b V$ʰåÔ®èH¶,‘zK¤v—Ï}ÍÎÜÛ3;ûI (ìÜ{çðÞó?çwFŒŒŒP¼þù‡ÏmÒ•ù’Tr/ ñY. õÿó[@êç¶PÏ|ÿÙï_/nk”ë +óÇ]ÝÄ㱄®ªz¬jД¦Tÿ©}^’©?–Rš‰äíÇnݾ™¾UàÅ_Œ !ŠÇ™›› 6Èf³œÿô<[6oÁ4Í;š%r+[«¾œ¦i47µè·¦n“þØKÇÒuV–…V¢x}kÖ*½R€žòåe©Fø{9U1hÜ­òõ,Z«ôÊ꺤ïzà‹¯»´–¦ÐòÖTw¸´¬×ã+?MVÓ§´†ëJœ%¨¥è¢Èåò¼ûÞ{\º4Áää%’©$íímôt÷Ò××ÇÐŽ!4M¬àøGqôÍ£,.¦hiYßýë~£'«Œš¶ ‰ªûö@)Åùóçyùß^fóæÍì|à~ýÓG ‡#dÒ¦¦¦85vŠ·Þz‹ýû÷ÑÙѹ µ®+ù¯Ã‡Y½º‘Õ«W0??Ï/åÉ¿ýV¥)]Ü‚»œ*­ôÓƒ?åܹsh†ÎÐݽŽT*E2™À¶m,ËbuS#ûÿj?çÎ~ÌòöŽìe÷®j,ŸœI0yu‚¼àKø`Õѣѻ¾ãzºïé!‰y)Tê5…´ À,ŠxýJã8ŒsàoYÌÐÞÞÁÄÄE§äÆB¡Àâ¢×·oØ0À7àÐÁƒôõvÓÚÚVQÈΜýžµ=456[(%*»Ñ2ÃÙ¶ÍíÔ4Ÿ^ü˜ûwk¸²’BZ=xÁ$½×$–Źߟexx˜ë×®T(_~Ùvž«W/³¾¿Ÿ‡|ˆW~ö®ëX(ØdÒiÚÖ´ÓØØ„aX†aèºwkšŽ¦ihšFCCík:HÎ$ü· %޳d•Aóöñ·YÓÚÊ®]»™š¾U@ÕWÞΓJ%æÌøÎŒ³}Û6”’Hå%Ó´X½º™p8ŒšÒÂï6‹M›$“YÀq¦ÈgíŠdâ._ÈT.\¼ÈªU«XÛÕÅää… Rjš†@•¶¥”d²š›ZhlläÊ•KlÛº¥À …°m›L&Í™3ÿK6›]Òá°EÈ2B/«Q˵½¹àê•«l¿o;ÑH”|>i˜hºïf!‚sªT 麸Ò%ŸÏ¯"‹qùÊ5„ðÎÄ Ü´•7~y88Y9ÒñŒPUA‹EM7Lví¼?ÈdRÉ•x@úÖTÌ/Ì£ivÁ&kÀ)|¾j¡ù'%ßúš†p5bÑétÓ4I&Sèºã8(åu£6aÛ²Ù,?üÑèêê*k ’É$ßù»§ü÷¢¢"3.  Ô8yÕ1377G"‘ Þ°Š……9Œ àJ\é⺟ãñULOO1??OssS)}j ¡ãÁ)¬šåGÒrï(©jâP«çâf]k×277LJ~@kk¦B7 ÃÄ4Cþmb&†n‡ijjf|ü óóóôtw—eÝàpSïdµTÃX>¾#€ÙŠð6íï[ÏÂÂ'¸81A[[‡§¬¡c&!3„a˜èº2éèèäw¿{Ÿ[·¦H§Ótw¯ èX¾îJÔSÞ£™\Î¥¬²gÏl;Ïìì ÿý›_sùÊeÚÚ;ˆ54 yÖ·,‹†xœŽŽ{8sfœ±±“$·±B÷m߆ëzÖ/*]ì6—në YU4€ÑÑQ‘^L‹òt(¥Ä4 öýÅã$Ir¹<Ç~ý&¯¿q„d"‰i…hlZnèÜšºÅ«?{…'ŽcÛ6©Ô û÷=ŽãÈ J–Þë,GŸ;µ)ê;::* ÿ‡¸~õºÞ¶±©ªq`÷î9õÎ)ZZš¹49É¥ÉÉà•a¹«Óé4©Ô òè^Z[×`ÛvYWY©œiš!°m;Ú\.GC¬aIåË ¯FGG1ü<¥%¦ºÚ ªª±×üñÞ6 làÕ×^aqqÓ aY¦i`Ûòù<…B+â©'¿M{[+…BáŽÊß'}å‘/óöñßF0Mƒ?ûÚ×–ïïA\@>ãú•ë!¥Å>¨ØNH©pÝ<==ëxæžæôû§™¼t™k×®qóæ,ÍÍMôt¯£¯¯Ÿûwã8.ù¼½"ZìÞõ÷ïƶ €Â²,¿è±T‹òôøà>°ö}ã1­¼”·Â®ë i:=ø0{~Ø_Gøñâ"ý*\ÚX-ûå±è‰pX[æ«§w9ÀÂ@Eæ;o¿žº=±¦¥uC<¯ùê5[”}dPþ‡7üÊì)SïVœ“„¯/«ir'åí‚Íù‹ŸÈd2uˆyÀ5Ê8~é¥9òäSßþFoOo¯¦kBJ‡‚í •ge·˜¡\×ïÍý¿®çוe‘%™²ù` Þõ¯àCºœšº}îÍ_Œ¾^N!X>¢N`XëÏO>¿«è(øõö,ð 0`! ´m@´,>øA9&}ÊÌS><àŸ>†ÄòÄôç¿(p|¥ó€í]á[¹xëþ]žÊ×!}:¹Å±(³rñ.Wü‹ DH¾ÄÈÈž⠨x½€V### àÿiJ=6v9MIEND®B`‚GlassCoder-2.0.1/icons/minussign.xpm000066400000000000000000000032051425562563600174330ustar00rootroot00000000000000/* XPM */ static const char * minussign_xpm[] = { "22 22 69 1", " c None", ". c #3565A4", "+ c #3363A2", "@ c #3161A0", "# c #30609E", "$ c #2F5F9D", "% c #2E5D9C", "& c #2D5C9B", "* c #2D5B99", "= c #2C5A98", "- c #2B5997", "; c #2A5795", "> c #295694", ", c #285593", "' c #285491", ") c #2A5592", "! c #3162A0", "~ c #7F9EC6", "{ c #88A5CA", "] c #87A4CA", "^ c #86A3C9", "/ c #84A2C9", "( c #83A2C8", "_ c #82A0C7", ": c #80A0C7", "< c #7F9DC5", "[ c #7D9DC5", "} c #7B9BC4", "| c #6F92BE", "1 c #25508D", "2 c #2F5E9D", "3 c #7798C2", "4 c #3B6AA7", "5 c #3869A6", "6 c #3768A5", "7 c #3667A4", "8 c #3566A4", "9 c #3667A5", "0 c #3768A6", "a c #396AA7", "b c #396AA8", "c c #406FAB", "d c #234D8B", "e c #2D5C9A", "f c #4271AC", "g c #4573AD", "h c #4674AE", "i c #4574AE", "j c #4674AF", "k c #4575AF", "l c #4574B0", "m c #4675B0", "n c #4776B0", "o c #4675B1", "p c #4272AD", "q c #224B88", "r c #2B5B98", "s c #2B5896", "t c #285592", "u c #275391", "v c #265290", "w c #25518E", "x c #24508D", "y c #234E8C", "z c #224D8B", "A c #224C89", "B c #214B88", "C c #214A87", "D c #244D8A", " ", " ", " ", " ", " ", " ", " ", " ", " .+@#$%&*=-;>,') ", " !~{]^/(_:~<[}|1 ", " 23456789605abcd ", " efghijjklmnmopq ", " rs;>tuvwxyzABCD ", " ", " ", " ", " ", " ", " ", " ", " ", " "}; GlassCoder-2.0.1/icons/plussign.xpm000066400000000000000000000053561425562563600172740ustar00rootroot00000000000000/* XPM */ static const char * plussign_xpm[] = { "22 22 105 2", " c None", ". c #3B6BA8", "+ c #3666A5", "@ c #3465A4", "# c #3566A5", "$ c #AAC1DC", "% c #B0C5DE", "& c #8AA9CE", "* c #3364A2", "= c #ABC2DC", "- c #7B9EC7", "; c #86A6CC", "> c #30609F", ", c #3262A1", "' c #A2BAD8", ") c #6D92C0", "! c #799BC5", "~ c #2F5E9C", "{ c #9AB3D3", "] c #5E87BA", "^ c #6C91BF", "/ c #2D5B99", "( c #3565A4", "_ c #3363A2", ": c #3161A0", "< c #30609E", "[ c #2F5F9D", "} c #3B67A3", "| c #8FABCE", "1 c #507BB1", "2 c #5F86B9", "3 c #315D9B", "4 c #2A5795", "5 c #295694", "6 c #285593", "7 c #285491", "8 c #2A5592", "9 c #3162A0", "0 c #82A1C8", "a c #8AA7CB", "b c #89A6CA", "c c #86A4CA", "d c #658ABA", "e c #416FAA", "f c #4975AE", "g c #7D9CC5", "h c #84A2C8", "i c #83A2C8", "j c #7597C0", "k c #25508D", "l c #2F5E9D", "m c #7A9BC4", "n c #3B6AA7", "o c #3969A7", "p c #3868A6", "q c #3667A4", "r c #3667A5", "s c #3768A6", "t c #3A6AA8", "u c #3A6AA9", "v c #4C79B1", "w c #234D8B", "x c #2D5C9A", "y c #4775AE", "z c #4D7AB1", "A c #4E7AB2", "B c #4674AF", "C c #3C6EAB", "D c #4775B0", "E c #517DB4", "F c #527EB5", "G c #537FB6", "H c #4D7BB2", "I c #224B88", "J c #2B5B98", "K c #2B5896", "L c #285592", "M c #2B5796", "N c #80A2CB", "O c #4576B1", "P c #5783BA", "Q c #285391", "R c #224D8B", "S c #224C89", "T c #214B88", "U c #214A87", "V c #244D8A", "W c #25518E", "X c #8DACD1", "Y c #4D7EB9", "Z c #5E8BBF", "` c #214C89", " . c #234E8C", ".. c #92B1D5", "+. c #5486BF", "@. c #6592C4", "#. c #204A87", "$. c #96B6DA", "%. c #5C8EC4", "&. c #6C98CB", "*. c #204B88", "=. c #8FB0D7", "-. c #719ECF", ";. c #6996C9", ">. c #1F4172", " ", " ", " ", " . + @ + . ", " # $ % & * ", " @ = - ; > ", " , ' ) ! ~ ", " > { ] ^ / ", " ( _ : < [ } | 1 2 3 4 5 6 7 8 ", " 9 0 a a b c d e f g h i 0 j k ", " l m n o p q @ # r s o t u v w ", " x y v z z A B C D E F F G H I ", " J K 4 5 L M N O P Q R S T U V ", " W X Y Z ` ", " ...+.@.#. ", " ` $.%.&.#. ", " *.=.-.;.*. ", " >.*.#.*.>. ", " ", " ", " ", " "}; GlassCoder-2.0.1/link_common.sh000077500000000000000000000125071425562563600164270ustar00rootroot00000000000000#!/bin/sh # link_common.sh # # Link common sources for rivendell-browser # # (C) Copyright 2016 Fred Gleason # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # DESTDIR=$1 rm -f src/$DESTDIR/asihpi.cpp ln -s ../../src/common/asihpi.cpp src/$DESTDIR/asihpi.cpp rm -f src/$DESTDIR/asihpi.h ln -s ../../src/common/asihpi.h src/$DESTDIR/asihpi.h rm -f src/$DESTDIR/audiodevice.cpp ln -s ../../src/common/audiodevice.cpp src/$DESTDIR/audiodevice.cpp rm -f src/$DESTDIR/audiodevice.h ln -s ../../src/common/audiodevice.h src/$DESTDIR/audiodevice.h rm -f src/$DESTDIR/cmdswitch.cpp ln -s ../../src/common/cmdswitch.cpp src/$DESTDIR/cmdswitch.cpp rm -f src/$DESTDIR/cmdswitch.h ln -s ../../src/common/cmdswitch.h src/$DESTDIR/cmdswitch.h rm -f src/$DESTDIR/codec.cpp ln -s ../../src/common/codec.cpp src/$DESTDIR/codec.cpp rm -f src/$DESTDIR/codec.h ln -s ../../src/common/codec.h src/$DESTDIR/codec.h rm -f src/$DESTDIR/codecdialog.cpp ln -s ../../src/common/codecdialog.cpp src/$DESTDIR/codecdialog.cpp rm -f src/$DESTDIR/codecdialog.h ln -s ../../src/common/codecdialog.h src/$DESTDIR/codecdialog.h rm -f src/$DESTDIR/codeviewer.cpp ln -s ../../src/common/codeviewer.cpp src/$DESTDIR/codeviewer.cpp rm -f src/$DESTDIR/codeviewer.h ln -s ../../src/common/codeviewer.h src/$DESTDIR/codeviewer.h rm -f src/$DESTDIR/combobox.cpp ln -s ../../src/common/combobox.cpp src/$DESTDIR/combobox.cpp rm -f src/$DESTDIR/combobox.h ln -s ../../src/common/combobox.h src/$DESTDIR/combobox.h rm -f src/$DESTDIR/connector.cpp ln -s ../../src/common/connector.cpp src/$DESTDIR/connector.cpp rm -f src/$DESTDIR/connector.h ln -s ../../src/common/connector.h src/$DESTDIR/connector.h rm -f src/$DESTDIR/guiapplication.cpp ln -s ../../src/common/guiapplication.cpp src/$DESTDIR/guiapplication.cpp rm -f src/$DESTDIR/guiapplication.h ln -s ../../src/common/guiapplication.h src/$DESTDIR/guiapplication.h rm -f src/$DESTDIR/hpiinputlistview.cpp ln -s ../../src/common/hpiinputlistview.cpp src/$DESTDIR/hpiinputlistview.cpp rm -f src/$DESTDIR/hpiinputlistview.h ln -s ../../src/common/hpiinputlistview.h src/$DESTDIR/hpiinputlistview.h rm -f src/$DESTDIR/hpiwidget.cpp ln -s ../../src/common/hpiwidget.cpp src/$DESTDIR/hpiwidget.cpp rm -f src/$DESTDIR/hpiwidget.h ln -s ../../src/common/hpiwidget.h src/$DESTDIR/hpiwidget.h rm -f src/$DESTDIR/logging.cpp ln -s ../../src/common/logging.cpp src/$DESTDIR/logging.cpp rm -f src/$DESTDIR/logging.h ln -s ../../src/common/logging.h src/$DESTDIR/logging.h rm -f src/$DESTDIR/messagewidget.cpp ln -s ../../src/common/messagewidget.cpp src/$DESTDIR/messagewidget.cpp rm -f src/$DESTDIR/messagewidget.h ln -s ../../src/common/messagewidget.h src/$DESTDIR/messagewidget.h rm -f src/$DESTDIR/metaevent.cpp ln -s ../../src/common/metaevent.cpp src/$DESTDIR/metaevent.cpp rm -f src/$DESTDIR/metaevent.h ln -s ../../src/common/metaevent.h src/$DESTDIR/metaevent.h rm -f src/$DESTDIR/profile.cpp ln -s ../../src/common/profile.cpp src/$DESTDIR/profile.cpp rm -f src/$DESTDIR/profile.h ln -s ../../src/common/profile.h src/$DESTDIR/profile.h rm -f src/$DESTDIR/ringbuffer.cpp ln -s ../../src/common/ringbuffer.cpp src/$DESTDIR/ringbuffer.cpp rm -f src/$DESTDIR/ringbuffer.h ln -s ../../src/common/ringbuffer.h src/$DESTDIR/ringbuffer.h rm -f src/$DESTDIR/segmeter.cpp ln -s ../../src/common/segmeter.cpp src/$DESTDIR/segmeter.cpp rm -f src/$DESTDIR/segmeter.h ln -s ../../src/common/segmeter.h src/$DESTDIR/segmeter.h rm -f src/$DESTDIR/serverdialog.cpp ln -s ../../src/common/serverdialog.cpp src/$DESTDIR/serverdialog.cpp rm -f src/$DESTDIR/serverdialog.h ln -s ../../src/common/serverdialog.h src/$DESTDIR/serverdialog.h rm -f src/$DESTDIR/sourcedialog.cpp ln -s ../../src/common/sourcedialog.cpp src/$DESTDIR/sourcedialog.cpp rm -f src/$DESTDIR/sourcedialog.h ln -s ../../src/common/sourcedialog.h src/$DESTDIR/sourcedialog.h rm -f src/$DESTDIR/spinbox.cpp ln -s ../../src/common/spinbox.cpp src/$DESTDIR/spinbox.cpp rm -f src/$DESTDIR/spinbox.h ln -s ../../src/common/spinbox.h src/$DESTDIR/spinbox.h rm -f src/$DESTDIR/statuswidget.cpp ln -s ../../src/common/statuswidget.cpp src/$DESTDIR/statuswidget.cpp rm -f src/$DESTDIR/statuswidget.h ln -s ../../src/common/statuswidget.h src/$DESTDIR/statuswidget.h rm -f src/$DESTDIR/stereometer.cpp ln -s ../../src/common/stereometer.cpp src/$DESTDIR/stereometer.cpp rm -f src/$DESTDIR/stereometer.h ln -s ../../src/common/stereometer.h src/$DESTDIR/stereometer.h rm -f src/$DESTDIR/streamdialog.cpp ln -s ../../src/common/streamdialog.cpp src/$DESTDIR/streamdialog.cpp rm -f src/$DESTDIR/streamdialog.h ln -s ../../src/common/streamdialog.h src/$DESTDIR/streamdialog.h rm -f src/$DESTDIR/glasslimits.h ln -s ../../src/common/glasslimits.h src/$DESTDIR/glasslimits.h rm -f src/$DESTDIR/paths.h ln -s ../../src/common/paths.h src/$DESTDIR/paths.h GlassCoder-2.0.1/plugins/000077500000000000000000000000001425562563600152375ustar00rootroot00000000000000GlassCoder-2.0.1/plugins/Makefile.am000066400000000000000000000033301425562563600172720ustar00rootroot00000000000000## Makefile.am ## ## Makefile.am for GlassCoder 'plugins/' ## ## (C) Copyright 2019 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of ## the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## Use automake to process this into a Makefile.in install-exec-am: mkdir -p $(DESTDIR)@libdir@/rivendell/pypad cp pypad_glasscoder.exemplar $(DESTDIR)@libdir@/rivendell/pypad/ ./install_pypad.sh pypad_glasscoder.py $(DESTDIR)@libdir@/rivendell/pypad/pypad_glasscoder.py uninstall-local: rm -f $(DESTDIR)@libdir@/rivendell/pypad/pypad_glasscoder.exemplar rm -f $(DESTDIR)@libdir@/rivendell/pypad/pypad_glasscoder.py EXTRA_DIST = install_pypad.sh.in\ pypad_glasscoder.exemplar\ pypad_glasscoder.py CLEANFILES = *~\ *.idb\ *ilk\ *.obj\ *.pdb\ *.qm\ moc_* DISTCLEANFILES = install_pypad.sh MAINTAINERCLEANFILES = *~\ *.tar.gz\ aclocal.m4\ configure\ Makefile.in\ moc_* GlassCoder-2.0.1/plugins/install_pypad.sh.in000066400000000000000000000014441425562563600210460ustar00rootroot00000000000000#!/bin/sh # install_pypad.sh # # (C) Copyright 2019 Fred Gleason # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # cat $1 | sed -e s^%PYTHON_BANGPATH%^@PYTHON@^ > $2 chmod 755 $2 GlassCoder-2.0.1/plugins/pypad_glasscoder.exemplar000066400000000000000000000041711425562563600223240ustar00rootroot00000000000000; This is the sample configuration for the 'pypad_glasscoder.py' PyPAD script ; for Rivendell, which can be used to send articulated PAD updates to an ; instance of glasscoder(1). ; [Glasscoder] ; UpdateUrl=http://: ; ; The update URL of the GlassCoder instance, where: ; - The hostname or IP address of the system running ; glasscoder(1). ; - The number passed to the '--metadata-port=' switch ; when invoking glasscoder(1). ; ; N.B. - The above values refer to the GlassCoder instance, *not* the ; streaming server! UpdateUrl=http://192.168.21.21:1234 ; Log Selection ; ; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether ; state changes on that log should be output. If set to 'Onair', then ; output will be generated only if RDAirPlays OnAir flag is active. MasterLog=Yes Aux1Log=No Aux2Log=No VLog101=No VLog102=No VLog103=No VLog104=No VLog105=No VLog106=No VLog107=No VLog108=No VLog109=No VLog110=No VLog111=No VLog112=No VLog113=No VLog114=No VLog115=No VLog116=No VLog117=No VLog118=No VLog119=No VLog120=No ; ; This plug-in uses a concept of 'lines', where each [Line] section refers ; to a specific dynamic presentation field in the target stream's metadata ; scheme. Some streaming schemes have but one available dynamic field ; --e.g. IceCast-- and hence use just a single line section ('[Line1]'), ; while others have multiple fields and thus can use multiple line sections. ; ; See below for specific examples. ; ; ; An example of 'ICY-style' metadata (IceCast and Shoutcast) ; [Line1] Key=StreamTitle Value=%a - %t ; ; An example of HLS Timed Metadata ; ; The 'Key=' directive takes the name of the ID3v2.4 Text Identification ; Frame to use for the given value. For the full list of defined frames, ; see Section 4.2 of the ID3v2.4 spec, available at ; http://id3.org/id3v2.4.0-frames. ; ;[Line1] ;Key=TIT2 ;Value=%t ; ;[Line2] ;Key=TPE1 ;Value=%a ; ;[Line3] ;Key=TALB ;Value=%l ; ;[Line4] ;Key=TRSN ;Value=Rivendell ; ;[Line5] ;Key=TRSO ;Value=A Radio Automation System ; ;[Line6] ;Key=TFLT ;Value=MPG/AAC GlassCoder-2.0.1/plugins/pypad_glasscoder.py000077500000000000000000000044261425562563600211450ustar00rootroot00000000000000#!%PYTHON_BANGPATH% # pypad_glasscoder.py # # Send articulated PAD updates to an instance of glasscoder(1). # # (C) Copyright 2019-2022 Fred Gleason # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # import sys import syslog import configparser try: from rivendellaudio import pypad except ModuleNotFoundError: import pypad # Rivendell v3.x style import json import requests def eprint(*args,**kwargs): print(*args,file=sys.stderr,**kwargs) def ProcessPad(update): if update.config().has_section('Glasscoder'): update_url=update.config().get('Glasscoder','UpdateUrl') n=1 lines=[] section='Line'+str(n) while(update.config().has_section(section)): lines.append('"%s": "%s"' % (update.config().get(section,'Key'), update.resolvePadFields(update.config().get(section,'Value'),pypad.ESCAPE_JSON))) n=n+1 section='Line'+str(n) if update.shouldBeProcessed('Glasscoder'): req_data='{ "Metadata": { %s } }' % ", ".join(lines) req_url = update_url+'/json_pad' try: r = requests.post(req_url, json=json.loads(req_data)) update.syslog(syslog.LOG_INFO,'[PyPAD][Glasscoder] Update exit code: ' + str(r.status_code)) except requests.exceptions.RequestException as e: update.syslog(syslog.LOG_WARNING,'[PyPAD][Glasscoder] Update failed: ' + str(e)) # # 'Main' function # rcvr=pypad.Receiver() try: rcvr.setConfigFile(sys.argv[3]) except IndexError: eprint('pypad_glasscoder.py: USAGE: cmd ') sys.exit(1) rcvr.setPadCallback(ProcessPad) rcvr.start(sys.argv[1],int(sys.argv[2])) GlassCoder-2.0.1/src/000077500000000000000000000000001425562563600143455ustar00rootroot00000000000000GlassCoder-2.0.1/src/Makefile.am000066400000000000000000000024611425562563600164040ustar00rootroot00000000000000## automake.am ## ## Automake.am for GlassCoder src/ ## ## (C) Copyright 2015 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of ## the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## Use automake to process this into a Makefile.in SUBDIRS = common\ glasscoder\ glasscommander\ glassgui\ tests CLEANFILES = *~\ *.idb\ *ilk\ *.obj\ *.pdb\ *.qm\ moc_* MAINTAINERCLEANFILES = *~\ *.tar.gz\ aclocal.m4\ configure\ Makefile.in\ moc_* GlassCoder-2.0.1/src/common/000077500000000000000000000000001425562563600156355ustar00rootroot00000000000000GlassCoder-2.0.1/src/common/Makefile.am000066400000000000000000000043421425562563600176740ustar00rootroot00000000000000## automake.am ## ## Automake.am for GlassCoder src/common ## ## (C) Copyright 2015-2016 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 2 of ## the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## Use automake to process this into a Makefile.in EXTRA_DIST = asihpi.cpp asihpi.h\ audiodevice.cpp audiodevice.h\ cmdswitch.cpp cmdswitch.h\ codec.cpp codec.h\ codecdialog.cpp codecdialog.h\ codeviewer.cpp codeviewer.h\ combobox.cpp combobox.h\ connector.cpp connector.h\ glasslimits.h\ guiapplication.cpp guiapplication.h\ hpiinputlistview.cpp hpiinputlistview.h\ hpiwidget.cpp hpiwidget.h\ logging.cpp logging.h\ messagewidget.cpp messagewidget.h\ metaevent.cpp metaevent.h\ paths.h.in\ profile.cpp profile.h\ ringbuffer.cpp ringbuffer.h\ segmeter.cpp segmeter.h\ serverdialog.cpp serverdialog.h\ sourcedialog.cpp sourcedialog.h\ spinbox.cpp spinbox.h\ statuswidget.cpp statuswidget.h\ stereometer.cpp stereometer.h\ streamdialog.cpp streamdialog.h CLEANFILES = *~\ *.idb\ *ilk\ *.obj\ *.pdb\ *.qm\ moc_*\ paths.h MAINTAINERCLEANFILES = *~\ *.tar.gz\ aclocal.m4\ configure\ Makefile.in\ moc_* GlassCoder-2.0.1/src/common/asihpi.cpp000066400000000000000000000033001425562563600176120ustar00rootroot00000000000000// asihpi.cpp // // Source Mappings for ASIHPI devices // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifdef ASIHPI #include #endif // ASIHPI #include "asihpi.h" // // Name Table // QString asihpi_common_source_names[]={"NONE", "OSTREAM", "LINEIN", "AESEBU", "TUNER", "RF", "CLOCK", "BITSTREAM", "MIC", "COBRANET", "ANALOG", "ADAPTER", "RTP", "INTERNAL", "AVB", "BLULINK"}; uint16_t AsihpiSourceNode(const QString &str) { uint16_t ret=0; #ifdef ASIHPI for(uint16_t i=HPI_SOURCENODE_NONE;i=HPI_SOURCENODE_NONE)&&(src // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef ASIHPI_H #define ASIHPI_H #include #include uint16_t AsihpiSourceNode(const QString &str); QString AsihpiSourceName(uint16_t src); #endif // ASIHPI_H GlassCoder-2.0.1/src/common/audiodevice.cpp000066400000000000000000000126731425562563600206330ustar00rootroot00000000000000// audiodevice.cpp // // Abstract base class for audio input sources. // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include "audiodevice.h" #include "logging.h" AudioDevice::AudioDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent) : QObject(parent) { audio_ring=ring; audio_channels=chans; audio_samplerate=samprate; } AudioDevice::~AudioDevice() { } bool AudioDevice::isAvailable() const { return true; } unsigned AudioDevice::deviceSamplerate() const { return audio_samplerate; } void AudioDevice::meterLevels(int *lvls) const { for(unsigned i=0;iaudio_meter_levels[i]) { audio_meter_levels[i]=lvls[i]; } } } Ringbuffer *AudioDevice::ringBuffer() { return audio_ring; } unsigned AudioDevice::channels() const { return audio_channels; } unsigned AudioDevice::samplerate() const { return audio_samplerate; } void AudioDevice::remixChannels(float *pcm_out,unsigned chans_out,float *pcm_in, unsigned chans_in,unsigned nframes) { if(chans_out==chans_in) { memcpy(pcm_out,pcm_in,nframes*chans_in*sizeof(float)); return; } if((chans_in==1)&&(chans_out==2)) { for(unsigned i=0;ilvls[j]) { lvls[j]=pcm[i+j]; } } } } void AudioDevice::peakLevels(int *lvls,const float *pcm,unsigned nframes, unsigned chans) { float levels[chans]; peakLevels(levels,pcm,nframes,chans); for(unsigned i=0;i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef AUDIODEVICE_H #define AUDIODEVICE_H #include #include #include #include "glasslimits.h" #include "ringbuffer.h" #define AUDIO_METER_INTERVAL 50 class AudioDevice : public QObject { Q_OBJECT; public: enum DeviceType {Alsa=0,AsiHpi=1,File=2,Jack=3,LastType=4}; enum Format {FLOAT=0,S16_LE=1,S32_LE=2,LastFormat=3}; AudioDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent=0); ~AudioDevice(); virtual bool isAvailable() const; virtual bool processOptions(QString *err,const QStringList &keys, const QStringList &values)=0; virtual bool start(QString *err)=0; virtual unsigned deviceSamplerate() const; void meterLevels(int *lvls) const; static QString deviceTypeText(AudioDevice::DeviceType type); static QString optionKeyword(AudioDevice::DeviceType type); static AudioDevice::DeviceType deviceType(const QString &key); static QString formatString(AudioDevice::Format fmt); signals: void hasStopped(); public slots: void unmute(); protected: void setMeterLevels(float *lvls); void setMeterLevels(int *lvls); void updateMeterLevels(int *lvls); Ringbuffer *ringBuffer(); unsigned channels() const; unsigned samplerate() const; void remixChannels(float *pcm_out,unsigned chans_out, float *pcm_in,unsigned chans_in,unsigned nframes); void convertToFloat(float *pcm_out,const void *pcm_in,Format fmt_in, unsigned nframes,unsigned chans); void peakLevels(float *lvls,const float *pcm,unsigned nframes,unsigned chans); void peakLevels(int *lvls,const float *pcm,unsigned nframes,unsigned chans); private: Ringbuffer *audio_ring; unsigned audio_channels; unsigned audio_samplerate; int audio_meter_levels[MAX_AUDIO_CHANNELS]; }; #endif // AUDIODEVICE_H GlassCoder-2.0.1/src/common/cmdswitch.cpp000066400000000000000000000060041425562563600203260ustar00rootroot00000000000000// cmdswitch.cpp // // Process Command-Line Switches // // (C) Copyright 2012-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include "cmdswitch.h" #include "logging.h" CmdSwitch::CmdSwitch(const char *modname,const char *usage) { int l=0; bool handled=false; QStringList args=qApp->arguments(); for(int i=1;i0)&&(f0[0].left(1)!="#")) { for(unsigned i=0;i=2) { f0.erase(f0.begin()); switch_values.insert(switch_values.begin(),f0.join("=").trimmed()); } else { switch_values.insert(switch_values.begin(),QString()); } } } } fclose(f); return true; } GlassCoder-2.0.1/src/common/cmdswitch.h000066400000000000000000000024711425562563600177770ustar00rootroot00000000000000// cmdswitch.h // // Process Command-Line Switches // // (C) Copyright 2012-2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef CMDSWITCH_H #define CMDSWITCH_H #include #include class CmdSwitch { public: CmdSwitch(const char *modname,const char *usage); unsigned keys() const; QString key(unsigned n) const; QString value(unsigned n) const; bool processed(unsigned n) const; void setProcessed(unsigned n,bool state); bool allProcessed() const; bool addOverlay(const QString &filename); private: std::vector switch_keys; std::vector switch_values; std::vector switch_processed; }; #endif // CMDSWITCH_H GlassCoder-2.0.1/src/common/codec.cpp000066400000000000000000000124151425562563600174210ustar00rootroot00000000000000// codec.cpp // // Abstract base class for audio codecs. // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "codec.h" #include "logging.h" Codec::Codec(Codec::Type type,Ringbuffer *ring,QObject *parent) { codec_ring1=ring; codec_bitrate=128; codec_channels=2; codec_quality=0.5; codec_source_samplerate=48000; codec_stream_samplerate=48000; codec_complete_frames=false; codec_ring2=NULL; } Codec::~Codec() { if(codec_src_state!=NULL) { src_delete(codec_src_state); } if(codec_src_data!=NULL) { delete codec_src_data; } if((codec_ring2!=codec_ring1)&&(codec_ring2!=NULL)) { delete codec_ring2; } } unsigned Codec::bitrate() const { return codec_bitrate; } void Codec::setBitrate(unsigned rate) { codec_bitrate=rate; } unsigned Codec::channels() const { return codec_channels; } void Codec::setChannels(unsigned chans) { codec_channels=chans; } double Codec::quality() const { return codec_quality; } void Codec::setQuality(double qual) { codec_quality=qual; } unsigned Codec::sourceSamplerate() const { return codec_source_samplerate; } void Codec::setSourceSamplerate(unsigned rate) { codec_source_samplerate=rate; } unsigned Codec::streamSamplerate() const { return codec_stream_samplerate; } void Codec::setStreamSamplerate(unsigned rate) { codec_stream_samplerate=rate; } bool Codec::completeFrames() const { return codec_complete_frames; } void Codec::setCompleteFrames(bool state) { codec_complete_frames=state; } QByteArray Codec::streamPrologue() const { return QByteArray(); } bool Codec::start() { int err; if(codec_source_samplerate==codec_stream_samplerate) { codec_pcm_buffer[0]=new float[MAX_AUDIO_CHANNELS*MAX_AUDIO_BUFFER]; codec_pcm_buffer[1]=NULL; codec_pcm_in=codec_pcm_buffer[0]; codec_pcm_out=codec_pcm_buffer[0]; codec_src_state=NULL; codec_src_data=NULL; codec_ring2=codec_ring1; } else { codec_pcm_buffer[0]=new float[MAX_AUDIO_CHANNELS*MAX_AUDIO_BUFFER]; codec_pcm_buffer[1]=new float[MAX_AUDIO_CHANNELS*MAX_AUDIO_BUFFER*6]; codec_pcm_in=codec_pcm_buffer[0]; codec_pcm_out=codec_pcm_buffer[1]; if((codec_src_state=src_new(SRC_SINC_FASTEST,codec_channels,&err))==NULL) { Log(LOG_ERR,"unable to create sample rate converter"); return false; } codec_src_data=new SRC_DATA; memset(codec_src_data,0,sizeof(SRC_DATA)); codec_src_data->data_in=codec_pcm_buffer[0]; codec_src_data->data_out=codec_pcm_buffer[1]; codec_src_data->output_frames=MAX_AUDIO_BUFFER*6; codec_src_data->src_ratio= (double)codec_stream_samplerate/(double)codec_source_samplerate; codec_ring2=new Ringbuffer(262144,codec_channels); } return startCodec(); } QString Codec::codecTypeText(Codec::Type type) { QString ret=tr("Unknown"); switch(type) { case Codec::TypeFdk: ret=tr("MPEG-4 AAC High Efficiency"); break; case Codec::TypeMpegL2: ret=tr("MPEG-1 Layer 2"); break; case Codec::TypeMpegL3: ret=tr("MPEG-1 Layer 3"); break; case Codec::TypeOpus: ret=tr("Ogg Opus"); break; case Codec::TypePcm16: ret=tr("PCM16 Uncompressed"); break; case Codec::TypeVorbis: ret=tr("Ogg Vorbis"); break; case Codec::TypeLast: break; } return ret; } QString Codec::optionKeyword(Codec::Type type) { QString ret; switch(type) { case Codec::TypeFdk: ret="aacp"; break; case Codec::TypeMpegL2: ret="mp2"; break; case Codec::TypeMpegL3: ret="mp3"; break; case Codec::TypeOpus: ret="opus"; break; case Codec::TypePcm16: ret="pcm16"; break; case Codec::TypeVorbis: ret="vorbis"; break; case Codec::TypeLast: break; } return ret; } Codec::Type Codec::codecType(const QString &key) { Codec::Type ret=Codec::TypeLast; for(int i=0;ireadSpace()>=pcmFrames()) { n=codec_ring1->read(codec_pcm_in,pcmFrames()); codec_src_data->input_frames=n; if((err=src_process(codec_src_state,codec_src_data))!=0) { Log(LOG_WARNING,QString().sprintf("SRC error [%s]",src_strerror(err))); continue; } n=codec_src_data->output_frames_gen; codec_ring2->write(codec_pcm_out,n); } } while(codec_ring2->readSpace()>=pcmFrames()) { n=codec_ring2->read(codec_pcm_in,pcmFrames()); encodeData(conn,codec_pcm_in,n); } } Ringbuffer *Codec::ring() { return codec_ring1; } GlassCoder-2.0.1/src/common/codec.h000066400000000000000000000052631425562563600170710ustar00rootroot00000000000000// codec.h // // Abstract base class for audio codecs. // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef CODEC_H #define CODEC_H #include #include #include #include #include #include #include #include "connector.h" #include "glasslimits.h" #include "ringbuffer.h" #define MAX_AUDIO_BUFFER 4096 class Codec : public QObject { Q_OBJECT; public: enum Type {TypeFdk=0,TypeMpegL2=1,TypeMpegL3=2, TypeVorbis=3,TypePcm16=4,TypeOpus=5,TypeLast=6}; Codec(Codec::Type type,Ringbuffer *ring,QObject *parent=0); ~Codec(); unsigned bitrate() const; void setBitrate(unsigned rate); unsigned channels() const; void setChannels(unsigned chans); double quality() const; void setQuality(double qual); unsigned sourceSamplerate() const; void setSourceSamplerate(unsigned rate); unsigned streamSamplerate() const; void setStreamSamplerate(unsigned rate); bool completeFrames() const; void setCompleteFrames(bool state); virtual QByteArray streamPrologue() const; virtual bool isAvailable() const=0; virtual QString contentType() const=0; virtual unsigned pcmFrames() const=0; virtual QString defaultExtension() const=0; virtual QString formatIdentifier() const=0; virtual bool start(); static QString codecTypeText(Codec::Type type); static QString optionKeyword(Codec::Type type); static Codec::Type codecType(const QString &key); public slots: virtual void encode(Connector *conn); protected: virtual void encodeData(Connector *conn,const float *pcm,int len)=0; virtual bool startCodec()=0; Ringbuffer *ring(); private: Ringbuffer *codec_ring1; Ringbuffer *codec_ring2; unsigned codec_bitrate; unsigned codec_channels; double codec_quality; unsigned codec_source_samplerate; unsigned codec_stream_samplerate; bool codec_complete_frames; SRC_STATE *codec_src_state; SRC_DATA *codec_src_data; float *codec_pcm_in; float *codec_pcm_out; float *codec_pcm_buffer[2]; }; #endif // CODEC_H GlassCoder-2.0.1/src/common/codecdialog.cpp000066400000000000000000000276771425562563600206210ustar00rootroot00000000000000// codecdialog.cpp // // Configuration dialog for codec settings // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "codecdialog.h" CodecDialog::CodecDialog(const QString &caption,QWidget *parent) : QDialog(parent,Qt::Dialog) { gui_caption=caption; // // Fonts // QFont label_font("helvetica",14,QFont::Bold); label_font.setPixelSize(14); setWindowTitle(caption+" - "+tr("Codec Settings")); // // Codec Type // gui_codec_type_label=new QLabel(tr("Type")+":",this); gui_codec_type_label->setFont(label_font); gui_codec_type_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_codec_type_box=new ComboBox(this); connect(gui_codec_type_box,SIGNAL(activated(int)), this,SLOT(codecTypeChanged(int))); // // Codec Samplerate // gui_codec_samplerate_label=new QLabel(tr("Sample Rate")+":",this); gui_codec_samplerate_label->setFont(label_font); gui_codec_samplerate_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_codec_samplerate_box=new ComboBox(this); connect(gui_codec_samplerate_box,SIGNAL(activated(int)), this,SLOT(codecSamplerateChanged(int))); // // Codec Channels // gui_codec_channels_label=new QLabel(tr("Channels")+":",this); gui_codec_channels_label->setFont(label_font); gui_codec_channels_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_codec_channels_box=new ComboBox(this); // // Codec Bitrate // gui_codec_bitrate_label=new QLabel(tr("Bit Rate")+":",this); gui_codec_bitrate_label->setFont(label_font); gui_codec_bitrate_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_codec_bitrate_box=new ComboBox(this); // // Close Button // gui_close_button=new QPushButton(tr("Close"),this); gui_close_button->setFont(label_font); connect(gui_close_button,SIGNAL(clicked()),this,SLOT(hide())); setMaximumSize(sizeHint()); setMinimumSize(sizeHint()); } QSize CodecDialog::sizeHint() const { return QSize(400,230-52); } void CodecDialog::setControlsLocked(bool state) { gui_codec_type_box->setReadOnly(state); gui_codec_samplerate_box->setReadOnly(state); gui_codec_channels_box->setReadOnly(state); gui_codec_bitrate_box->setReadOnly(state); } void CodecDialog::resizeEvent(QResizeEvent *e) { int ypos=10; gui_codec_type_label->setGeometry(10,ypos,110,24); gui_codec_type_box->setGeometry(125,ypos,250,24); ypos+=26; gui_codec_samplerate_label->setGeometry(10,ypos,145,24); gui_codec_samplerate_box->setGeometry(160,ypos,200,24); ypos+=26; gui_codec_channels_label->setGeometry(10,ypos,145,24); gui_codec_channels_box->setGeometry(160,ypos,50,24); ypos+=26; gui_codec_bitrate_label->setGeometry(10,ypos,145,24); gui_codec_bitrate_box->setGeometry(160,ypos,150,24); ypos+=26; ypos+=9; gui_close_button->setGeometry(size().width()-80,size().height()-50,70,40); } void CodecDialog::codecTypeChanged(int n) { Codec::Type type=(Codec::Type)gui_codec_type_box->itemData(n).toInt(); gui_codec_samplerate_box->clear(); gui_codec_channels_box->clear(); gui_codec_bitrate_box->clear(); switch(type) { case Codec::TypeFdk: case Codec::TypeMpegL2: case Codec::TypeMpegL3: gui_codec_samplerate_box->insertItem(-1,"16000 samples/sec",16000); gui_codec_samplerate_box->insertItem(-1,"22050 samples/sec",22050); gui_codec_samplerate_box->insertItem(-1,"24000 samples/sec",24000); gui_codec_samplerate_box->insertItem(-1,"32000 samples/sec",32000); gui_codec_samplerate_box->insertItem(-1,"44100 samples/sec",44100); gui_codec_samplerate_box->insertItem(-1,"48000 samples/sec",48000); gui_codec_channels_box->insertItem(-1,"1",1); gui_codec_channels_box->insertItem(-1,"2",2); gui_codec_bitrate_label->setText(tr("Bit Rate(s)")+":"); break; case Codec::TypePcm16: gui_codec_samplerate_box->insertItem(-1,"16000 samples/sec",16000); gui_codec_samplerate_box->insertItem(-1,"22050 samples/sec",22050); gui_codec_samplerate_box->insertItem(-1,"24000 samples/sec",24000); gui_codec_samplerate_box->insertItem(-1,"32000 samples/sec",32000); gui_codec_samplerate_box->insertItem(-1,"44100 samples/sec",44100); gui_codec_samplerate_box->insertItem(-1,"48000 samples/sec",48000); gui_codec_channels_box->insertItem(-1,"1",1); gui_codec_channels_box->insertItem(-1,"2",2); gui_codec_bitrate_label->setText(tr("Bit Rate(s)")+":"); break; case Codec::TypeVorbis: gui_codec_samplerate_box->insertItem(-1,"16000 samples/sec",16000); gui_codec_samplerate_box->insertItem(-1,"22050 samples/sec",22050); gui_codec_samplerate_box->insertItem(-1,"24000 samples/sec",24000); gui_codec_samplerate_box->insertItem(-1,"32000 samples/sec",32000); gui_codec_samplerate_box->insertItem(-1,"44100 samples/sec",44100); gui_codec_samplerate_box->insertItem(-1,"48000 samples/sec",48000); gui_codec_channels_box->insertItem(-1,"1",1); gui_codec_channels_box->insertItem(-1,"2",2); gui_codec_bitrate_label->setText(tr("Quality")+":"); break; case Codec::TypeOpus: gui_codec_samplerate_box->insertItem(-1,"48000 samples/sec",48000); gui_codec_channels_box->insertItem(-1,"1",1); gui_codec_channels_box->insertItem(-1,"2",2); gui_codec_bitrate_label->setText(tr("Quality")+":"); break; case Codec::TypeLast: break; } codecSamplerateChanged(0); } void CodecDialog::codecSamplerateChanged(int n) { Codec::Type type=(Codec::Type)gui_codec_type_box-> itemData(gui_codec_type_box->currentIndex()).toInt(); gui_codec_bitrate_box->clear(); switch(type) { case Codec::TypeFdk: gui_codec_bitrate_box->insertItem(-1,"16 kbits/sec",16); gui_codec_bitrate_box->insertItem(-1,"24 kbits/sec",24); gui_codec_bitrate_box->insertItem(-1,"32 kbits/sec",32); gui_codec_bitrate_box->insertItem(-1,"40 kbits/sec",40); gui_codec_bitrate_box->insertItem(-1,"48 kbits/sec",48); break; case Codec::TypeMpegL2: gui_codec_bitrate_box->insertItem(-1,"8 kbits/sec",8); gui_codec_bitrate_box->insertItem(-1,"16 kbits/sec",16); gui_codec_bitrate_box->insertItem(-1,"24 kbits/sec",24); gui_codec_bitrate_box->insertItem(-1,"32 kbits/sec",32); gui_codec_bitrate_box->insertItem(-1,"40 kbits/sec",40); gui_codec_bitrate_box->insertItem(-1,"48 kbits/sec",48); gui_codec_bitrate_box->insertItem(-1,"56 kbits/sec",56); gui_codec_bitrate_box->insertItem(-1,"64 kbits/sec",64); gui_codec_bitrate_box->insertItem(-1,"80 kbits/sec",80); gui_codec_bitrate_box->insertItem(-1,"96 kbits/sec",96); gui_codec_bitrate_box->insertItem(-1,"112 kbits/sec",112); gui_codec_bitrate_box->insertItem(-1,"128 kbits/sec",128); gui_codec_bitrate_box->insertItem(-1,"144 kbits/sec",144); gui_codec_bitrate_box->insertItem(-1,"160 kbits/sec",160); gui_codec_bitrate_box->insertItem(-1,"192 kbits/sec",192); gui_codec_bitrate_box->insertItem(-1,"224 kbits/sec",224); gui_codec_bitrate_box->insertItem(-1,"256 kbits/sec",256); gui_codec_bitrate_box->insertItem(-1,"320 kbits/sec",320); gui_codec_bitrate_box->insertItem(-1,"384 kbits/sec",384); break; case Codec::TypeMpegL3: gui_codec_bitrate_box->insertItem(-1,"8 kbits/sec",8); gui_codec_bitrate_box->insertItem(-1,"16 kbits/sec",16); gui_codec_bitrate_box->insertItem(-1,"24 kbits/sec",24); gui_codec_bitrate_box->insertItem(-1,"32 kbits/sec",32); gui_codec_bitrate_box->insertItem(-1,"40 kbits/sec",40); gui_codec_bitrate_box->insertItem(-1,"48 kbits/sec",48); gui_codec_bitrate_box->insertItem(-1,"56 kbits/sec",56); gui_codec_bitrate_box->insertItem(-1,"64 kbits/sec",64); gui_codec_bitrate_box->insertItem(-1,"80 kbits/sec",80); gui_codec_bitrate_box->insertItem(-1,"96 kbits/sec",96); gui_codec_bitrate_box->insertItem(-1,"112 kbits/sec",112); gui_codec_bitrate_box->insertItem(-1,"128 kbits/sec",128); gui_codec_bitrate_box->insertItem(-1,"144 kbits/sec",144); gui_codec_bitrate_box->insertItem(-1,"160 kbits/sec",160); gui_codec_bitrate_box->insertItem(-1,"192 kbits/sec",192); gui_codec_bitrate_box->insertItem(-1,"224 kbits/sec",224); gui_codec_bitrate_box->insertItem(-1,"256 kbits/sec",256); gui_codec_bitrate_box->insertItem(-1,"320 kbits/sec",320); break; case Codec::TypeOpus: case Codec::TypeVorbis: gui_codec_bitrate_box->insertItem(-1,"0",0); gui_codec_bitrate_box->insertItem(-1,"1",1); gui_codec_bitrate_box->insertItem(-1,"2",2); gui_codec_bitrate_box->insertItem(-1,"3",3); gui_codec_bitrate_box->insertItem(-1,"4",4); gui_codec_bitrate_box->insertItem(-1,"5",5); gui_codec_bitrate_box->insertItem(-1,"6",6); gui_codec_bitrate_box->insertItem(-1,"7",7); gui_codec_bitrate_box->insertItem(-1,"8",8); gui_codec_bitrate_box->insertItem(-1,"9",9); gui_codec_bitrate_box->insertItem(-1,"10",10); break; case Codec::TypePcm16: gui_codec_bitrate_box->insertItem(-1,tr("None",0)); break; case Codec::TypeLast: break; } } void CodecDialog::makeArgs(QStringList *args) { Codec::Type type=(Codec::Type) gui_codec_type_box->itemData(gui_codec_type_box->currentIndex()).toInt(); args->push_back("--audio-format="+Codec::optionKeyword(type)); args->push_back("--audio-samplerate="+QString().sprintf("%u", gui_codec_samplerate_box-> itemData(gui_codec_samplerate_box->currentIndex()).toUInt())); args->push_back("--audio-channels="+QString().sprintf("%u", gui_codec_channels_box-> itemData(gui_codec_channels_box->currentIndex()).toUInt())); switch(type) { case Codec::TypeMpegL2: case Codec::TypeMpegL3: case Codec::TypeFdk: case Codec::TypeOpus: case Codec::TypeLast: if(gui_codec_bitrate_box->isEnabled()&& (gui_codec_bitrate_box->currentItemData().toUInt()!=0)) { args->push_back("--audio-bitrate="+QString().sprintf("%u", gui_codec_bitrate_box->currentItemData().toUInt())); } break; case Codec::TypePcm16: break; case Codec::TypeVorbis: args->push_back("--audio-quality="+QString().sprintf("%u", gui_codec_bitrate_box->currentItemData().toUInt())); break; } } void CodecDialog::addCodecTypes(const QString &types) { QStringList f0; f0=types.split("\n"); for(int i=0;i insertItem(-1,Codec::codecTypeText((Codec::Type)j),j); } } } } void CodecDialog::load(Profile *p) { gui_codec_type_box-> setCurrentItemData(Codec::codecType(p->stringValue("GlassGui", "AudioFormat"))); codecTypeChanged(gui_codec_type_box->currentIndex()); gui_codec_samplerate_box-> setCurrentItemData(p->intValue("GlassGui","AudioSamplerate")); codecSamplerateChanged(gui_codec_samplerate_box->currentIndex()); gui_codec_channels_box-> setCurrentItemData(p->intValue("GlassGui","AudioChannels")); gui_codec_bitrate_box-> setCurrentItemData(p->intValue("GlassGui","AudioBitrate1")); } void CodecDialog::save(FILE *f) { fprintf(f,"AudioFormat=%s\n", (const char *)Codec::optionKeyword((Codec::Type) gui_codec_type_box->currentItemData().toInt()).toUtf8()); fprintf(f,"AudioSamplerate=%u\n", gui_codec_samplerate_box->currentItemData().toUInt()); fprintf(f,"AudioChannels=%u\n", gui_codec_channels_box->currentItemData().toUInt()); fprintf(f,"AudioBitrate1=%u\n", gui_codec_bitrate_box->currentItemData().toUInt()); } GlassCoder-2.0.1/src/common/codecdialog.h000066400000000000000000000035111425562563600202430ustar00rootroot00000000000000// codecdialog.h // // Configuration dialog for codec settings // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef CODECDIALOG_H #define CODECDIALOG_H #include #include #include #include #include #include #include #include "codec.h" #include "combobox.h" #include "profile.h" class CodecDialog : public QDialog { Q_OBJECT; public: CodecDialog(const QString &caption,QWidget *parent); QSize sizeHint() const; void setControlsLocked(bool state); void makeArgs(QStringList *args); void addCodecTypes(const QString &types); void load(Profile *p); void save(FILE *f); protected: void resizeEvent(QResizeEvent *e); private slots: void codecTypeChanged(int n); void codecSamplerateChanged(int n); private: QLabel *gui_codec_type_label; ComboBox *gui_codec_type_box; QLabel *gui_codec_samplerate_label; ComboBox *gui_codec_samplerate_box; QLabel *gui_codec_channels_label; ComboBox *gui_codec_channels_box; QLabel *gui_codec_bitrate_label; ComboBox *gui_codec_bitrate_box; QPushButton *gui_close_button; QString gui_caption; }; #endif // CODECDIALOG_H GlassCoder-2.0.1/src/common/codeviewer.cpp000066400000000000000000000034131425562563600204760ustar00rootroot00000000000000// codeviewer.cpp // // Text viewer dialog // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "codeviewer.h" CodeViewer::CodeViewer(const QString &caption,QWidget *parent) : QDialog(parent) { view_caption=caption; setWindowTitle(view_caption+" - "+tr("View Code")); QFont button_font(font().family(),font().pointSize(),QFont::Bold); view_text=new QTextEdit(this); view_text->setReadOnly(true); view_close_button=new QPushButton(tr("Close"),this); view_close_button->setFont(button_font); connect(view_close_button,SIGNAL(clicked()),this,SLOT(closeData())); } QSize CodeViewer::sizeHint() const { return QSize(600,400); } int CodeViewer::exec(const QString &str) { view_text->setText(str); return QDialog::exec(); } int CodeViewer::exec(const QStringList &strs) { view_text->setText(strs.join(" \\\n")); return QDialog::exec(); } void CodeViewer::resizeEvent(QResizeEvent *e) { view_text->setGeometry(10,10,size().width()-20,size().height()-55); view_close_button->setGeometry(size().width()-70,size().height()-35,60,25); } void CodeViewer::closeData() { done(0); } GlassCoder-2.0.1/src/common/codeviewer.h000066400000000000000000000025061425562563600201450ustar00rootroot00000000000000// codeviewer.h // // Text viewer dialog // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef CODEVIEWER_H #define CODEVIEWER_H #include #include #include #include #include class CodeViewer : public QDialog { Q_OBJECT; public: CodeViewer(const QString &caption,QWidget *parent); QSize sizeHint() const; public slots: int exec(const QString &str); int exec(const QStringList &strs); protected: void resizeEvent(QResizeEvent *e); private slots: void closeData(); private: QTextEdit *view_text; QPushButton *view_close_button; QString view_caption; }; #endif // CODEVIEWER_H GlassCoder-2.0.1/src/common/combobox.cpp000066400000000000000000000030021425562563600201440ustar00rootroot00000000000000// combobox.cpp // // ComboBox widget for GlassGui // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "combobox.h" ComboBox::ComboBox(QWidget *parent) : QComboBox(parent) { box_read_only=false; } void ComboBox::setReadOnly(bool state) { box_read_only=state; } QVariant ComboBox::currentItemData(int role) { return itemData(currentIndex(),role); } bool ComboBox::setCurrentItemData(unsigned val) { for(int i=0;iaccept(); } else { QComboBox::keyPressEvent(e); } } void ComboBox::mousePressEvent(QMouseEvent *e) { if(box_read_only) { e->accept(); } else { QComboBox::mousePressEvent(e); } } GlassCoder-2.0.1/src/common/combobox.h000066400000000000000000000023251425562563600176200ustar00rootroot00000000000000// combobox.h // // ComboBox widget for GlassGui // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef COMBOBOX_H #define COMBOBOX_H #include #include #include class ComboBox : public QComboBox { Q_OBJECT; public: ComboBox(QWidget *parent=0); void setReadOnly(bool state); QVariant currentItemData(int role=Qt::UserRole); bool setCurrentItemData(unsigned val); protected: void keyPressEvent(QKeyEvent *e); void mousePressEvent(QMouseEvent *e); private: bool box_read_only; }; #endif // COMBOBOX_H GlassCoder-2.0.1/src/common/connector.cpp000066400000000000000000000710111425562563600203330ustar00rootroot00000000000000// connector.cpp // // Abstract base class for streaming server source connections. // // (C) Copyright 2014-2020 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include "connector.h" #include "logging.h" Connector::Connector(QObject *parent) : QObject(parent) { conn_server_exit_on_last=false; conn_server_max_connections=-1; conn_server_username="source"; conn_server_password=""; conn_server_mountpoint=""; conn_server_start_connections=0; conn_content_type=""; conn_audio_channels=2; conn_audio_samplerate=44100; conn_audio_bitrates.push_back(128); conn_stream_name="no name"; conn_stream_description="unknown"; conn_stream_url=""; conn_stream_irc=""; conn_stream_icq=""; conn_stream_aim=""; conn_stream_genre="unknown"; conn_stream_public=true; conn_stream_timestamp_offset=0; conn_connected=false; conn_watchdog_active=false; conn_script_up_process=NULL; conn_script_down_process=NULL; conn_dump_headers=false; conn_is_stopping=false; conn_data_timer=new QTimer(this); connect(conn_data_timer,SIGNAL(timeout()),this,SLOT(dataTimeoutData())); conn_data_timer->start(RINGBUFFER_SERVICE_INTERVAL); conn_watchdog_timer=new QTimer(this); conn_watchdog_timer->setSingleShot(true); connect(conn_watchdog_timer,SIGNAL(timeout()), this,SLOT(watchdogTimeoutData())); conn_stop_timer=new QTimer(this); conn_stop_timer->setSingleShot(true); connect(conn_stop_timer,SIGNAL(timeout()),this,SLOT(stopTimeoutData())); conn_script_down_garbage_timer=new QTimer(this); conn_script_down_garbage_timer->setSingleShot(true); connect(conn_script_down_garbage_timer,SIGNAL(timeout()), this,SLOT(scriptDownCollectGarbageData())); conn_script_up_garbage_timer=new QTimer(this); conn_script_up_garbage_timer->setSingleShot(true); connect(conn_script_up_garbage_timer,SIGNAL(timeout()), this,SLOT(scriptUpCollectGarbageData())); } Connector::~Connector() { delete conn_stop_timer; delete conn_data_timer; } bool Connector::serverExitOnLast() const { return conn_server_exit_on_last; } bool Connector::isConnected() const { return conn_connected; } void Connector::setServerExitOnLast(bool state) { conn_server_exit_on_last=state; } int Connector::serverMaxConnections() const { return conn_server_max_connections; } void Connector::setServerMaxConnections(int max) { conn_server_max_connections=max; } QString Connector::serverUserAgent() const { return conn_server_user_agent; } void Connector::setServerUserAgent(const QString &str) { conn_server_user_agent=str; } QString Connector::serverUsername() const { return conn_server_username; } void Connector::setServerUsername(const QString &str) { conn_server_username=str; } QString Connector::serverPassword() const { return conn_server_password; } void Connector::setServerPassword(const QString &str) { conn_server_password=str; } QString Connector::serverBasicAuthString() const { // // As per RFC 2617 (2) // return Connector::base64Encode(serverUsername()+":"+serverPassword()); } QString Connector::serverMountpoint() const { return conn_server_mountpoint; } void Connector::setServerMountpoint(const QString &str) { conn_server_mountpoint=str; } QString Connector::serverPipe() const { return conn_server_pipe; } void Connector::setServerPipe(const QString &str) { conn_server_pipe=str; } int Connector::serverStartConnections() const { return conn_server_start_connections; } void Connector::setServerStartConnections(int conns) { conn_server_start_connections=conns; } QString Connector::contentType() const { return conn_content_type; } void Connector::setContentType(const QString &str) { conn_content_type=str; } unsigned Connector::audioChannels() const { return conn_audio_channels; } void Connector::setAudioChannels(unsigned chans) { conn_audio_channels=chans; } unsigned Connector::audioSamplerate() const { return conn_audio_samplerate; } void Connector::setAudioSamplerate(unsigned rate) { conn_audio_samplerate=rate; } unsigned Connector::audioBitrate() const { return conn_audio_bitrates[0]; } void Connector::setAudioBitrate(unsigned rate) { conn_audio_bitrates.clear(); conn_audio_bitrates.push_back(rate); } std::vector *Connector::audioBitrates() { return &conn_audio_bitrates; } void Connector::setAudioBitrates(std::vector *rates) { conn_audio_bitrates.clear(); for(unsigned i=0;isize();i++) { conn_audio_bitrates.push_back(rates->at(i)); } } QString Connector::streamName() const { return conn_stream_name; } void Connector::setStreamName(const QString &str) { conn_stream_name=str; } QString Connector::streamDescription() const { return conn_stream_description; } void Connector::setStreamDescription(const QString &str) { conn_stream_description=str; } QUrl Connector::streamUrl() const { return conn_stream_url; } void Connector::setStreamUrl(const QString &str) { conn_stream_url=QUrl(str); } void Connector::setStreamUrl(const QUrl &url) { conn_stream_url=url; } QString Connector::streamIrc() const { return conn_stream_irc; } void Connector::setStreamIrc(const QString &str) { conn_stream_irc=str; } QString Connector::streamIcq() const { return conn_stream_icq; } void Connector::setStreamIcq(const QString &str) { conn_stream_icq=str; } QString Connector::streamAim() const { return conn_stream_aim; } void Connector::setStreamAim(const QString &str) { conn_stream_aim=str; } QString Connector::streamGenre() const { return conn_stream_genre; } void Connector::setStreamGenre(const QString &str) { conn_stream_genre=str; } bool Connector::streamPublic() const { return conn_stream_public; } void Connector::setStreamPublic(bool state) { conn_stream_public=state; } int Connector::streamTimestampOffset() const { return conn_stream_timestamp_offset; } void Connector::setStreamTimestampOffset(int msec) { conn_stream_timestamp_offset=msec; } QString Connector::extension() const { return conn_extension; } void Connector::setExtension(const QString &str) { conn_extension=str; } QString Connector::formatIdentifier() const { return conn_format_identifier; } void Connector::setFormatIdentifier(const QString &str) { conn_format_identifier=str; } QUrl Connector::serverUrl() const { return conn_server_url; } void Connector::connectToServer(const QUrl &url) { conn_server_url=url; connectToHostConnector(url); } int64_t Connector::writeData(int frames,const unsigned char *data,int64_t len) { return writeDataConnector(frames,data,len); } void Connector::stop() { conn_is_stopping=true; startStopping(); setConnected(false); // // We have to run this synchronously since we're shutting down // if(conn_connected&&(!conn_script_down.isEmpty())) { QStringList args; args=conn_script_down.split(" "); QString cmd=args[0]; args.erase(args.begin()); conn_script_down_process=new QProcess(this); conn_script_down_process->start(cmd,args); conn_script_down_process->waitForFinished(3000); } } QString Connector::scriptUp() const { return conn_script_up; } void Connector::setScriptUp(const QString &cmd) { conn_script_up=cmd; } QString Connector::scriptDown() const { return conn_script_down; } void Connector::setScriptDown(const QString &cmd) { conn_script_down=cmd; } bool Connector::dumpHeaders() const { return conn_dump_headers; } void Connector::setDumpHeaders(bool state) { conn_dump_headers=state; } QString Connector::serverTypeText(Connector::ServerType type) { QString ret=tr("Unknown"); switch(type) { case Connector::HlsServer: ret=tr("HTTP Live Streaming (HLS)"); break; case Connector::Shoutcast1Server: ret=tr("Shoutcast v1"); break; case Connector::Shoutcast2Server: ret=tr("Shoutcast v2"); break; case Connector::Icecast2Server: ret=tr("Icecast v2"); break; case Connector::FileServer: ret=tr("Local File"); break; case Connector::FileArchiveServer: ret=tr("Local File Archive"); break; case Connector::IcecastStreamerServer: ret=tr("Integrated Icecast Server"); break; case Connector::IcecastOutServer: ret=tr("Icecast Direct Stream"); break; case Connector::LastServer: break; } return ret; } QString Connector::optionKeyword(Connector::ServerType type) { QString ret; switch(type) { case Connector::HlsServer: ret="hls"; break; case Connector::Shoutcast1Server: ret="shout1"; break; case Connector::Shoutcast2Server: ret="shout2"; break; case Connector::Icecast2Server: ret="icecast2"; break; case Connector::FileServer: ret="file"; break; case Connector::FileArchiveServer: ret="filearchive"; break; case Connector::IcecastStreamerServer: ret="icestreamer"; break; case Connector::IcecastOutServer: ret="iceout"; break; case Connector::LastServer: break; } return ret; } bool Connector::requiresServerUrl(Connector::ServerType type) { bool ret=true; switch(type) { case Connector::HlsServer: case Connector::Shoutcast1Server: case Connector::Shoutcast2Server: case Connector::Icecast2Server: case Connector::FileServer: case Connector::FileArchiveServer: case Connector::LastServer: ret=true; break; case Connector::IcecastStreamerServer: case Connector::IcecastOutServer: ret=false; break; } return ret; } Connector::ServerType Connector::serverType(const QString &key) { Connector::ServerType ret=Connector::LastServer; for(int i=0;istart(0); } void Connector::scriptUpCollectGarbageData() { delete conn_script_up_process; conn_script_up_process=NULL; } void Connector::scriptDownFinishedData(int exit_code, QProcess::ExitStatus exit_status) { conn_script_down_garbage_timer->start(0); } void Connector::scriptDownCollectGarbageData() { delete conn_script_down_process; conn_script_down_process=NULL; } void Connector::startStopping() { conn_stop_timer->start(0); } void Connector::setConnected(bool state) { QStringList args; QString cmd; if(conn_connected!=state) { if(!conn_is_stopping) { if(state) { if(conn_script_up_process==NULL) { if(!conn_script_up.isEmpty()) { args=conn_script_up.split(" "); cmd=args[0]; args.erase(args.begin()); conn_script_up_process=new QProcess(this); connect(conn_script_up_process, SIGNAL(error(QProcess::ProcessError)), this,SLOT(scriptErrorData(QProcess::ProcessError))); connect(conn_script_up_process, SIGNAL(finished(int,QProcess::ExitStatus)), this,SLOT(scriptUpFinishedData(int,QProcess::ExitStatus))); conn_script_up_process->start(cmd,args); } } else { if(global_log_verbose) { Log(LOG_WARNING,"curl(1) script-up command overrun, cmd: \""+ args.join(" ")+"\""); } else { Log(LOG_WARNING,"curl(1) command overrun"); } } } else { if(conn_script_down_process==NULL) { if(!conn_script_down.isEmpty()) { args=conn_script_down.split(" "); cmd=args[0]; args.erase(args.begin()); conn_script_down_process=new QProcess(this); connect(conn_script_down_process, SIGNAL(error(QProcess::ProcessError)), this,SLOT(scriptErrorData(QProcess::ProcessError))); connect(conn_script_down_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(scriptDownFinishedData(int,QProcess::ExitStatus))); conn_script_down_process->start(cmd,args); } } else { if(global_log_verbose) { Log(LOG_WARNING,"curl(1) script-down command overrun, cmd: \""+ args.join(" ")+"\""); } else { Log(LOG_WARNING,"curl(1) command overrun"); } } } } } if(state&&conn_watchdog_active) { if(conn_server_mountpoint.isEmpty()) { Log(LOG_WARNING, "connection to \""+conn_server_url.toString()+"\" restored"); } else { Log(LOG_WARNING, "connection to \""+conn_server_url.toString()+"/"+ conn_server_mountpoint+"\" restored"); } conn_watchdog_active=false; } conn_connected=state; emit connected(state); } void Connector::setError(QAbstractSocket::SocketError err) { syslog(LOG_DEBUG,"received socket error \"%s\"", Connector::socketErrorText(err).toUtf8().constData()); if(!conn_watchdog_active) { if(conn_server_mountpoint.isEmpty()) { Log(LOG_WARNING, "connection to \""+conn_server_url.toString()+"\" lost"); } else { Log(LOG_WARNING, "connection to \""+conn_server_url.toString()+"/"+ conn_server_mountpoint+"\" lost"); } conn_watchdog_active=true; } disconnectFromHostConnector(); conn_watchdog_timer->start(5000); } QString Connector::urlEncode(const QString &str) { QByteArray ret; QByteArray bytes=str.toUtf8(); for(int i=0;i9)) { istate=0; } code=str.mid(i,1); istate=2; break; case 2: n=str.mid(i,1).toUInt(&ok); if((!ok)||(n>9)) { istate=0; } code+=str.mid(i,1); ret+=code.toInt(&ok,16); istate=0; break; } } return QString::fromUtf8(ret); } char base64_dict[64]={'A','B','C','D','E','F','G','H','I','J','K','L','M','N', 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b', 'c','d','e','f','g','h','i','j','k','l','m','n','o','p', 'q','r','s','t','u','v','w','x','y','z','0','1','2','3', '4','5','6','7','8','9','+','/'}; QString Connector::base64Encode(const QString &str) { QString ret; uint32_t buf; // // Build Groups // for(int i=0;i=0;i--) { ret+=base64_dict[0x3f&(buf>>(6*i))]; } } // // Apply Padding // switch(str.length()%3) { case 1: ret=ret.left(ret.length()-2)+"=="; break; case 2: ret=ret.left(ret.length()-1)+"="; break; } return ret; } QString Connector::base64Decode(const QString &str,bool *ok) { QString ret; uint32_t buf=0; char c; int pad=0; bool found=false; // // Set Status // if(ok!=NULL) { *ok=true; } // // Dictionary Lookup // for(int i=0;i=0;i--) { ret+=(0xff&(buf>>(8*i))); } } // // Apply Padding // if(pad>2) { if(ok!=NULL) { *ok=false; } return QString(); } ret=ret.left(ret.length()-pad); return ret; } QString Connector::curlStrError(int exit_code) { // // From the curl(1) man page (v7.29.0) // QString ret=tr("Unknown CURL error"); switch(exit_code) { case 1: ret=tr("Unsupported protocol"); break; case 2: ret=tr("Failed to initialize"); break; case 3: ret=tr("Malformed URL"); break; case 4: ret=tr("Feature not supported"); break; case 5: ret=tr("Cannot resolve proxy"); break; case 6: ret=tr("Cannot resolve host"); break; case 7: ret=tr("Connection to host failed"); break; case 8: ret=tr("Unrecognized FTP server response"); break; case 9: ret=tr("FTP access denied"); break; case 11: ret=tr("Unrecognized FTP PASS reply"); break; case 13: ret=tr("Unrecognized FTP PASV reply"); break; case 14: ret=tr("Unrecognized FTP 227 format"); break; case 15: ret=tr("FTP can't get host"); break; case 17: ret=tr("FTP couldn't set binary mode"); break; case 18: ret=tr("Partial file transfer"); break; case 19: ret=tr("FTP download failed"); break; case 21: ret=tr("FTP quote error"); break; case 22: ret=tr("HTTP page not retrieved"); break; case 23: ret=tr("Write error"); break; case 25: ret=tr("FTP STOR error"); break; case 26: ret=tr("Read error"); break; case 27: ret=tr("Out of memory"); break; case 28: ret=tr("Write error"); break; case 30: ret=tr("FTP PORT failed"); break; case 31: ret=tr("FTP REST failed"); break; case 33: ret=tr("HTTP range error"); break; case 34: ret=tr("HTTP POST error"); break; case 35: ret=tr("SSL connect error"); break; case 36: ret=tr("FTP bad download resume"); break; case 37: ret=tr("FILE read failure"); break; case 38: ret=tr("LDAP bind error"); break; case 39: ret=tr("LDAP search failed"); break; case 41: ret=tr("LDAP function not found"); break; case 42: ret=tr("Aborted"); break; case 43: ret=tr("Internal error"); break; case 45: ret=tr("Interface error"); break; case 47: ret=tr("Too many redirects"); break; case 48: ret=tr("Unknown option"); break; case 49: ret=tr("Malformed telnet option"); break; case 51: ret=tr("Bad certificate"); break; case 52: ret=tr("No data returned"); break; case 53: ret=tr("No SSL crypto engine"); break; case 54: ret=tr("Cannot set SSL crypto engine as default"); break; case 55: ret=tr("Send failure"); break; case 56: ret=tr("Receive failure"); break; case 58: ret=tr("Local certificate problem"); break; case 59: ret=tr("Cannot use requested SSL cipher"); break; case 60: ret=tr("Peer certificate cannot be authenticated"); break; case 61: ret=tr("Unrecognized transfer encoding"); break; case 62: ret=tr("Invalid LDAP URL"); break; case 63: ret=tr("Maximum file size exceeded"); break; case 64: ret=tr("Requested FTP SSL level failed"); break; case 65: ret=tr("Rewind failed"); break; case 66: ret=tr("SSL engine initialization failed"); break; case 67: ret=tr("Authentication failure"); break; case 68: ret=tr("TFTP file not found"); break; case 69: ret=tr("TFTP permmission problem"); break; case 70: ret=tr("TFTP out of disc space"); break; case 71: ret=tr("TFTP illegal operation"); break; case 72: ret=tr("TFTP unkown transfer ID"); break; case 73: ret=tr("TFTP file already exists"); break; case 74: ret=tr("TFTP no such user"); break; case 75: ret=tr("Character conversion failed"); break; case 76: ret=tr("Character conversion functions required"); break; case 77: ret=tr("SSL CA cert read problems"); break; case 78: ret=tr("Reference resources does not exist"); break; case 79: ret=tr("SSH unspecified error"); break; case 80: ret=tr("SSL failed to shut down connection"); break; case 82: ret=tr("Cannot load CRL file"); break; case 83: ret=tr("Issuer check failed"); break; case 84: ret=tr("FTP PRET command failed"); break; case 85: ret=tr("RTSP CSeq mismatch"); break; case 86: ret=tr("RTSP SSID mismatch"); break; case 87: ret=tr("FTP unable to parse file list"); break; case 88: ret=tr("FTP chunk callback error"); break; default: ret=tr("Unknown CURL error")+QString().sprintf(" [%d]",exit_code); } return ret; } QString Connector::httpStrError(int status_code) { // // From RFC-2616 Section 6.1.1 // QString ret=tr("Unknown status code"); switch(status_code) { case 100: ret=tr("Continue"); break; case 101: ret=tr("Switching Protocols"); break; case 200: ret=tr("OK"); break; case 201: ret=tr("Created"); break; case 202: ret=tr("Accepted"); break; case 203: ret=tr("Non-Authoritative Information"); break; case 204: ret=tr("No Content"); break; case 205: ret=tr("Reset Content"); break; case 206: ret=tr("Partial Content"); break; case 300: ret=tr("Multiple Choices"); break; case 301: ret=tr("Moved Permanently"); break; case 302: ret=tr("Found"); break; case 303: ret=tr("See Other"); break; case 304: ret=tr("Not Modified"); break; case 305: ret=tr("Use Proxy"); break; case 307: ret=tr("Temporary Redirect"); break; case 400: ret=tr("Bad Request"); break; case 401: ret=tr("Unauthorized"); break; case 402: ret=tr("Payment Required"); break; case 403: ret=tr("Forbidden"); break; case 404: ret=tr("Not Found"); break; case 405: ret=tr("Method Not Allowed"); break; case 406: ret=tr("Not Acceptable"); break; case 407: ret=tr("Proxy Authentication Required"); break; case 408: ret=tr("Request Time-out"); break; case 409: ret=tr("Conflict"); break; case 410: ret=tr("Gone"); break; case 411: ret=tr("Length Required"); break; case 412: ret=tr("Precondition Failed"); break; case 413: ret=tr("Request Entity Too Large"); break; case 414: ret=tr("Request-URI Too Large"); break; case 415: ret=tr("Unsupported Media Type"); break; case 416: ret=tr("Requested range not satisfiable"); break; case 417: ret=tr("Expectation Failed"); break; case 500: ret=tr("Internal Server Error"); break; case 501: ret=tr("Not Implemented"); break; case 502: ret=tr("Bad Gateway"); break; case 503: ret=tr("Service Unavailable"); break; case 504: ret=tr("Gateway Time-out"); break; case 505: ret=tr("HTTP Version not supported"); break; } return QString().sprintf("%d - ",status_code)+ret; } QString Connector::timezoneOffset() { QString ret="Z"; time_t t=time(NULL); time_t gmt; time_t lt; gmt=mktime(gmtime(&t)); lt=mktime(localtime(&t)); if(gmtlt) { ret=QString().sprintf("+%02ld:%02ld",(gmt-lt)/3600,((gmt-lt)%3600)/60); } return ret; } int Connector::id3TagSize(const QByteArray &data) { // // See id3v2.4.0-structure, section 3 // return ((0xFF&data[6])*2048383)+ // Synchsafe integer ((0xFF&data[7])*16129)+ ((0xFF&data[8])*127)+ (0xFF&data[9])+ 10; } QString Connector::socketErrorText(QAbstractSocket::SocketError err) { // // This *really* ought to be part of Qt! // QString ret=QString().sprintf("unknown socket error [%d]",err); switch(err) { case QAbstractSocket::ConnectionRefusedError: ret="connection refused"; break; case QAbstractSocket::RemoteHostClosedError: ret="remote host closed connection"; break; case QAbstractSocket::HostNotFoundError: ret="remote host address not found"; break; case QAbstractSocket::SocketAccessError: ret="socket access error"; break; case QAbstractSocket::SocketResourceError: ret="socket resource error"; break; case QAbstractSocket::SocketTimeoutError: ret="remote host connection timed out"; break; case QAbstractSocket::DatagramTooLargeError: ret="datagram too large error"; break; case QAbstractSocket::NetworkError: ret="network error"; break; case QAbstractSocket::AddressInUseError: ret="unable to bind to specified port (port in use)"; break; case QAbstractSocket::SocketAddressNotAvailableError: ret="unable to bind to specfied port (insuffcient privileges)"; break; case QAbstractSocket::UnsupportedSocketOperationError: ret="unsupported socket operation"; break; case QAbstractSocket::ProxyAuthenticationRequiredError: ret="proxy authentication failed"; break; case QAbstractSocket::SslHandshakeFailedError: ret="SSL handshake failed"; break; case QAbstractSocket::UnfinishedSocketOperationError: ret="unfinished socket operations error"; break; case QAbstractSocket::ProxyConnectionRefusedError: ret="proxy connection refused"; break; case QAbstractSocket::ProxyConnectionClosedError: ret="proxy connection closed unexpectedly"; break; case QAbstractSocket::ProxyConnectionTimeoutError: ret="proxy connection timed out"; break; case QAbstractSocket::ProxyNotFoundError: ret="proxy not found"; break; case QAbstractSocket::ProxyProtocolError: ret="proxy protocol error"; break; case QAbstractSocket::OperationError: ret="socket operation error"; break; case QAbstractSocket::SslInternalError: ret="SSL internal error"; break; case QAbstractSocket::SslInvalidUserDataError: ret="SLL authentication error"; break; case QAbstractSocket::TemporaryError: ret="temporary socket error"; break; case QAbstractSocket::UnknownSocketError: break; } return ret; } QString Connector::timeStampString() { struct timespec ts; memset(&ts,0,sizeof(ts)); if(clock_gettime(CLOCK_REALTIME,&ts)!=0) { Log(LOG_WARNING, QString::asprintf("unable to get system time: %s",strerror(errno))); } return QString::asprintf("%020lu%09lu",ts.tv_sec,ts.tv_nsec); } void Connector::sendMetadata(MetaEvent *e) { QStringList keys=e->fieldKeys(); for(int i=0;ifield(keys.at(i)).toUtf8()); } // printf("StreamTitle: %s\n",(const char *)e->field(MetaEvent::StreamTitle).toString().toUtf8()); } void Connector::setStreamPrologue(const QByteArray &data) { } GlassCoder-2.0.1/src/common/connector.h000066400000000000000000000155421425562563600200070ustar00rootroot00000000000000// connector.h // // Abstract base class for streaming server source connections. // // (C) Copyright 2014-2020 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef CONNECTOR_H #define CONNECTOR_H #include #include #include #include #include #include #include #include #include #include "metaevent.h" #define RINGBUFFER_SERVICE_INTERVAL 50 class Connector : public QObject { Q_OBJECT; public: enum ServerType {HlsServer=0,Icecast2Server=1, Shoutcast1Server=2,Shoutcast2Server=3,FileServer=4, FileArchiveServer=5,IcecastStreamerServer=6, IcecastOutServer=7,LastServer=8}; Connector(QObject *parent=0); ~Connector(); virtual Connector::ServerType serverType() const=0; bool isConnected() const; bool serverExitOnLast() const; void setServerExitOnLast(bool state); int serverMaxConnections() const; void setServerMaxConnections(int max); QString serverUserAgent() const; void setServerUserAgent(const QString &str); QString serverUsername() const; void setServerUsername(const QString &str); QString serverPassword() const; void setServerPassword(const QString &str); QString serverBasicAuthString() const; QString serverMountpoint() const; void setServerMountpoint(const QString &str); QString serverPipe() const; void setServerPipe(const QString &str); int serverStartConnections() const; void setServerStartConnections(int conns); QString contentType() const; void setContentType(const QString &str); unsigned audioChannels() const; void setAudioChannels(unsigned chans); unsigned audioSamplerate() const; void setAudioSamplerate(unsigned rate); unsigned audioBitrate() const; void setAudioBitrate(unsigned rate); std::vector *audioBitrates(); void setAudioBitrates(std::vector *rates); QString streamName() const; void setStreamName(const QString &str); QString streamDescription() const; void setStreamDescription(const QString &str); QUrl streamUrl() const; void setStreamUrl(const QString &str); void setStreamUrl(const QUrl &url); QString streamIrc() const; void setStreamIrc(const QString &str); QString streamIcq() const; void setStreamIcq(const QString &str); QString streamAim() const; void setStreamAim(const QString &str); QString streamGenre() const; void setStreamGenre(const QString &str); bool streamPublic() const; void setStreamPublic(bool state); int streamTimestampOffset() const; void setStreamTimestampOffset(int msec); QString extension() const; void setExtension(const QString &str); QString formatIdentifier() const; void setFormatIdentifier(const QString &str); QUrl serverUrl() const; virtual void connectToServer(const QUrl &url); virtual int64_t writeData(int frames,const unsigned char *data,int64_t len); void stop(); QString scriptUp() const; void setScriptUp(const QString &cmd); QString scriptDown() const; void setScriptDown(const QString &cmd); bool dumpHeaders() const; void setDumpHeaders(bool state); static QString serverTypeText(Connector::ServerType); static QString optionKeyword(Connector::ServerType type); static bool requiresServerUrl(Connector::ServerType type); static Connector::ServerType serverType(const QString &key); static QString subMountpointName(const QString &mntpt,unsigned bitrate); static QString pathPart(const QString &fullpath); static QString basePart(const QString &fullpath); static QString urlEncode(const QString &str); static QString urlDecode(const QString &str); static QString base64Encode(const QString &str); static QString base64Decode(const QString &str,bool *ok=NULL); static QString curlStrError(int exit_code); static QString httpStrError(int status_code); static QString timezoneOffset(); static int id3TagSize(const QByteArray &data); static QString socketErrorText(QAbstractSocket::SocketError err); static QString timeStampString(); public slots: virtual void setStreamPrologue(const QByteArray &data); virtual void sendMetadata(MetaEvent *e); signals: void connected(bool state); void unmuteRequested(); void dataRequested(Connector *conn); void error(QAbstractSocket::SocketError err); void stopped(); private slots: void dataTimeoutData(); void watchdogTimeoutData(); void stopTimeoutData(); void scriptErrorData(QProcess::ProcessError err); void scriptUpFinishedData(int exit_code,QProcess::ExitStatus exit_status); void scriptUpCollectGarbageData(); void scriptDownFinishedData(int exit_code,QProcess::ExitStatus exit_status); void scriptDownCollectGarbageData(); protected: virtual void startStopping(); void setConnected(bool state); void setError(QAbstractSocket::SocketError err); virtual void connectToHostConnector(const QUrl &url)=0; virtual void disconnectFromHostConnector()=0; virtual int64_t writeDataConnector(int frames,const unsigned char *data, int64_t len)=0; private: bool conn_server_exit_on_last; QUrl conn_server_url; QString conn_server_username; QString conn_server_password; int conn_server_max_connections; QString conn_server_mountpoint; QString conn_server_user_agent; int conn_server_start_connections; QString conn_server_pipe; QString conn_content_type; unsigned conn_audio_channels; unsigned conn_audio_samplerate; std::vector conn_audio_bitrates; QString conn_stream_name; QString conn_stream_description; QUrl conn_stream_url; QString conn_stream_irc; QString conn_stream_icq; QString conn_stream_aim; QString conn_stream_genre; bool conn_stream_public; int conn_stream_timestamp_offset; QString conn_extension; QString conn_format_identifier; QTimer *conn_data_timer; QTimer *conn_watchdog_timer; bool conn_watchdog_active; bool conn_connected; // QString conn_host_hostname; // uint16_t conn_host_port; QTimer *conn_stop_timer; QString conn_script_up; QProcess *conn_script_up_process; QStringList conn_script_up_args; QTimer *conn_script_up_garbage_timer; QString conn_script_down; QProcess *conn_script_down_process; QStringList conn_script_down_args; bool conn_dump_headers; QTimer *conn_script_down_garbage_timer; bool conn_is_stopping; }; #endif // CONNECTOR_H GlassCoder-2.0.1/src/common/glasslimits.h000066400000000000000000000021401425562563600203360ustar00rootroot00000000000000// glasslimits.h // // System-wide limits and settings for GlassCoder // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef GLASSLIMITS_H #define GLASSLIMITS_H #define DEFAULT_SERVER_PORT 80 #define DEFAULT_AUDIO_BITRATE 128 #define DEFAULT_AUDIO_SAMPLERATE 44100 #define DEFAULT_AUDIO_DEVICE AudioDevice::Jack #define MAX_AUDIO_CHANNELS 2 #define RINGBUFFER_SIZE 262144 #define PROCESS_TERMINATION_TIMEOUT 30000 #endif // GLASSLIMITS_H GlassCoder-2.0.1/src/common/guiapplication.cpp000066400000000000000000000053731425562563600213610ustar00rootroot00000000000000// guiapplication.cpp // // Abstract base class for GUI applications in GlassCoder // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include "guiapplication.h" GuiApplication::GuiApplication(QWidget *parent) : QMainWindow(parent) { gui_settings_path=""; gui_settings_dir=NULL; checkSettingsDirectory(); } QDir *GuiApplication::settingsDirectory() const { return gui_settings_dir; } bool GuiApplication::setSettingsDirectory(const QString &dirname) { gui_settings_path=dirname; return checkSettingsDirectory(); } bool GuiApplication::checkSettingsDirectory() { QString path; if(gui_settings_path.isEmpty()) { path=QString("/")+GUI_SETTINGS_DIR; if(getenv("HOME")!=NULL) { path=QString(getenv("HOME"))+"/"+GUI_SETTINGS_DIR; } } else { path=gui_settings_path; } if((gui_settings_dir==NULL)||(gui_settings_dir->path()!=path)) { gui_settings_dir=new QDir(path); } if(!gui_settings_dir->exists()) { mkdir(path.toUtf8(),S_IRUSR|S_IWUSR|S_IXUSR); if(!gui_settings_dir->exists()) { return false; } } return true; } QString GuiApplication::settingsFilename(const QString &instance_name) { if(!checkSettingsDirectory()) { QMessageBox::critical(this,"GlassGui - "+tr("Error"), tr("Unable to create settings directory!")); exit(256); } QString ret=gui_settings_dir->path()+"/"+GUI_SETTINGS_FILE; if(!instance_name.isEmpty()) { ret+="-"+instance_name; } return ret; } void GuiApplication::deleteInstance(const QString &name) { if(checkSettingsDirectory()) { unlink((gui_settings_dir->path()+"/"+GUI_SETTINGS_FILE+"-"+name).toUtf8()); } } QStringList GuiApplication::listInstances() { QStringList files; QStringList ret; if(checkSettingsDirectory()) { files=gui_settings_dir-> entryList(QStringList(QString(GUI_SETTINGS_FILE)+"-*"), QDir::Files,QDir::Name); for(int i=0;i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef GUIAPPLICATION_H #define GUIAPPLICATION_H #include #include #include #define GUI_SETTINGS_DIR ".glassgui" #define GUI_SETTINGS_FILE "glassguirc" class GuiApplication : public QMainWindow { Q_OBJECT; public: GuiApplication(QWidget *parent=0); protected: QDir *settingsDirectory() const; bool setSettingsDirectory(const QString &dirname); bool checkSettingsDirectory(); QString settingsFilename(const QString &instance_name); void deleteInstance(const QString &name); QStringList listInstances(); private: QString gui_settings_path; QDir *gui_settings_dir; }; #endif // GUIAPPLICATION_H GlassCoder-2.0.1/src/common/hpiinputlistview.cpp000066400000000000000000000067151425562563600220010ustar00rootroot00000000000000// hpiinputlistview.cpp // // List widget for selecting HPI input streams // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #ifdef ASIHPI #include #endif // ASIHPI #include "hpiinputlistview.h" HpiInputListViewModel::HpiInputListViewModel(QObject *parent) : QAbstractListModel(parent) { #ifdef ASIHPI uint32_t index; uint16_t type; uint16_t inputs; uint16_t outputs; uint16_t version; uint32_t serial; int num=0; while(HPI_SubSysGetAdapter(NULL,num,&index,&type)==0) { if(HPI_AdapterGetInfo(NULL,index,&outputs,&inputs,&version,&serial,&type)==0) { for(uint16_t i=0;idata(currentIndex(), HpiInputListViewModel::AdapterRole).toUInt(); } unsigned HpiInputListView::selectedInputIndex() const { if(!currentIndex().isValid()) { return 0; } return hpi_model->data(currentIndex(), HpiInputListViewModel::InputRole).toUInt(); } void HpiInputListView::setSelected(unsigned adapter,unsigned input) { for(int i=0;irowCount();i++) { QModelIndex index=model()->index(i,0); if((model()->data(index,HpiInputListViewModel::AdapterRole)==adapter)&& (model()->data(index,HpiInputListViewModel::InputRole)==input)) { setCurrentIndex(index); } } } void HpiInputListView::keyPressEvent(QKeyEvent *e) { if(hpi_read_only) { e->accept(); } else { QListView::keyPressEvent(e); } } void HpiInputListView::mouseMoveEvent(QMouseEvent *e) { if(hpi_read_only) { e->accept(); } else { QListView::mouseMoveEvent(e); } } void HpiInputListView::mousePressEvent(QMouseEvent *e) { if(hpi_read_only) { e->accept(); } else { QListView::mousePressEvent(e); } } GlassCoder-2.0.1/src/common/hpiinputlistview.h000066400000000000000000000036611425562563600214430ustar00rootroot00000000000000// hpiinputlistview.h // // List widget for selecting HPI input streams // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef HPIINPUTLISTVIEW_H #define HPIINPUTLISTVIEW_H #include #include #include #include class HpiInputListViewModel : public QAbstractListModel { public: enum UserRole {AdapterRole=Qt::UserRole,InputRole=Qt::UserRole+1}; HpiInputListViewModel(QObject *parent=0); int rowCount(const QModelIndex &parent=QModelIndex()) const; QVariant data(const QModelIndex &index,int role=Qt::DisplayRole) const; private: std::vector hpi_input_names; std::vector hpi_adapter_indices; std::vector hpi_input_indices; }; class HpiInputListView : public QListView { Q_OBJECT; public: HpiInputListView(QWidget *parent); void setReadOnly(bool state); unsigned selectedAdapterIndex() const; unsigned selectedInputIndex() const; void setSelected(unsigned adapter,unsigned input); signals: void inputSelected(unsigned adapter,unsigned input); protected: void keyPressEvent(QKeyEvent *e); void mouseMoveEvent(QMouseEvent *e); void mousePressEvent(QMouseEvent *e); private: HpiInputListViewModel *hpi_model; bool hpi_read_only; }; #endif // HPIINPUTLISTVIEW_H GlassCoder-2.0.1/src/common/hpiwidget.cpp000066400000000000000000000331661425562563600203360ustar00rootroot00000000000000// hpiwidget.cpp // // Widget for configuring ASIHPI sources // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "hpiwidget.h" HpiWidget::HpiWidget(QWidget *parent) : QWidget(parent) { #ifdef ASIHPI hpi_adapter_index=0; hpi_input_index=0; hpi_mixer_handle=0; // // Fonts // QFont label_font("helvetica",14,QFont::Bold); label_font.setPixelSize(14); QFont readout_font("helvetica",14,QFont::Normal); readout_font.setPixelSize(14); // // Source // hpi_source_label=new QLabel(tr("Input Source")+":",this); hpi_source_label->setFont(label_font); hpi_source_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); hpi_source_box=new ComboBox(this); connect(hpi_source_box,SIGNAL(activated(int)), this,SLOT(sourceActivatedData(int))); // // Input Type // hpi_type_label=new QLabel(tr("Input Type")+":",this); hpi_type_label->setFont(label_font); hpi_type_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); hpi_type_box=new ComboBox(this); connect(hpi_type_box,SIGNAL(activated(int)), this,SLOT(typeActivatedData(int))); // // Input Mode // hpi_mode_label=new QLabel(tr("Input Mode")+":",this); hpi_mode_label->setFont(label_font); hpi_mode_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); hpi_mode_box=new ComboBox(this); connect(hpi_mode_box,SIGNAL(activated(int)), this,SLOT(modeActivatedData(int))); // // Input Gain // hpi_volume_label=new QLabel(tr("Input Gain")+":",this); hpi_volume_label->setFont(label_font); hpi_volume_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); hpi_volume_slider=new QSlider(Qt::Horizontal,this); connect(hpi_volume_slider,SIGNAL(valueChanged(int)), this,SLOT(volumeChangedData(int))); hpi_volume_readout_label=new QLabel(this); hpi_volume_readout_label->setFont(readout_font); hpi_volume_readout_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); hpi_view=new HpiInputListView(this); connect(hpi_view,SIGNAL(clicked(const QModelIndex &)), this,SLOT(listClickedData(const QModelIndex &))); #endif // ASIHPI } void HpiWidget::setReadOnly(bool state) { #ifdef ASIHPI hpi_view->setReadOnly(state); #endif // ASIHPI } unsigned HpiWidget::selectedAdapterIndex() const { #ifdef ASIHPI return hpi_view->selectedAdapterIndex(); #else return 0; #endif // ASIHPI } unsigned HpiWidget::selectedInputIndex() const { #ifdef ASIHPI return hpi_view->selectedInputIndex(); #else return 0; #endif // ASIHPI } void HpiWidget::setSelected(unsigned adapter,unsigned input) { #ifdef ASIHPI hpi_view->setSelected(adapter,input); listClickedData(QModelIndex()); #endif // ASIHPI } int HpiWidget::inputGain() const { #ifdef ASIHPI return hpi_volume_slider->value(); #else return 0; #endif // ASIHPI } void HpiWidget::setInputGain(int gain) { #ifdef ASIHPI hpi_volume_slider->setValue(gain); if(hpi_volume_found) { volumeChangedData(gain); } #endif // ASIHPI } unsigned HpiWidget::channelMode() const { #ifdef ASIHPI return hpi_mode_box->currentItemData().toUInt(); #else return 0; #endif // ASIHPI } void HpiWidget::setChannelMode(unsigned mode) { #ifdef ASIHPI if(hpi_mode_box->setCurrentItemData(mode)) { modeActivatedData(0); } #endif // ASIHPI } unsigned HpiWidget::inputSource() const { #ifdef ASIHPI return ASIHPI_UNPACK_TYPE(hpi_source_box->currentItemData().toUInt()); #else return 0; #endif // ASIHPI } void HpiWidget::setInputSource(unsigned src) { #ifdef ASIHPI if(hpi_source_box-> setCurrentItemData(ASIHPI_PACK_CONTROL(hpi_input_index,src))) { sourceActivatedData(0); } #endif // ASIHPI } unsigned HpiWidget::inputType() const { #ifdef ASIHPI return ASIHPI_UNPACK_TYPE(hpi_type_box->currentItemData().toUInt()); #else return 0; #endif // ASIHPI } void HpiWidget::setInputType(unsigned type) { #ifdef ASIHPI if(hpi_type_box-> setCurrentItemData(ASIHPI_PACK_CONTROL(hpi_input_index,type))) { typeActivatedData(0); } #endif // ASIHPI } QString HpiWidget::sourceNodeText(uint16_t src_node) { // // From HPI v4.16 // QString ret=tr("Unknown"); switch(src_node) { #ifdef ASIHPI case HPI_SOURCENODE_NONE: ret=tr("None"); break; case HPI_SOURCENODE_OSTREAM: ret=tr("Output Stream"); break; case HPI_SOURCENODE_LINEIN: ret=tr("Line In"); break; case HPI_SOURCENODE_AESEBU_IN: ret=tr("AES3 Input"); break; case HPI_SOURCENODE_TUNER: ret=tr("Tuner"); break; case HPI_SOURCENODE_RF: ret=tr("RF Input"); break; case HPI_SOURCENODE_CLOCK_SOURCE: ret=tr("Clock source"); break; case HPI_SOURCENODE_RAW_BITSTREAM: ret=tr("Raw bitstream"); break; case HPI_SOURCENODE_MICROPHONE: ret=tr("Microphone"); break; case HPI_SOURCENODE_COBRANET: ret=tr("CobraNet input"); break; case HPI_SOURCENODE_ANALOG: ret=tr("Analog input"); break; case HPI_SOURCENODE_ADAPTER: ret=tr("Adapter"); break; case HPI_SOURCENODE_RTP_DESTINATION: ret=tr("RTP stream input"); break; case HPI_SOURCENODE_INTERNAL: ret=tr("Internal"); break; /* case HPI_SOURCENODE_BLULINK: ret=tr("BLU-Link input"); break; case HPI_SOURCENODE_AVB_INPUT: ret=tr("AVB input"); break; case HPI_SOURCENODE_AVB_STREAM: ret=tr("AVB audio"); break; */ #endif // ASIHPI default: ret=tr("Unknown")+QString().sprintf(" [%u]",src_node); break; } return ret; } QString HpiWidget::channelModeText(uint16_t mode) { // // From HPI v4.16 // QString ret=tr("Unknown"); switch(mode) { #ifdef ASIHPI case HPI_CHANNEL_MODE_NORMAL: ret=tr("Normal"); break; case HPI_CHANNEL_MODE_SWAP: ret=tr("Swapped"); break; case HPI_CHANNEL_MODE_LEFT_TO_STEREO: ret=tr("Left Only"); break; case HPI_CHANNEL_MODE_RIGHT_TO_STEREO: ret=tr("Right Only"); break; case HPI_CHANNEL_MODE_STEREO_TO_LEFT: ret=tr("Left Only"); break; case HPI_CHANNEL_MODE_STEREO_TO_RIGHT: ret=tr("Right Only"); break; #endif // ASIHPI default: ret=tr("Unknown mode")+QString().sprintf(" [%u]",mode); break; } return ret; } void HpiWidget::resizeEvent(QResizeEvent *e) { #ifdef ASIHPI Redraw(); int ypos=0; hpi_view->setGeometry(0,0,size().width(),100); ypos+=105; if(hpi_source_box->isVisible()) { hpi_source_label->setGeometry(0,ypos,100,25); hpi_source_box->setGeometry(105,ypos,size().width()-115,25); ypos+=30; } if(hpi_mode_box->isVisible()) { hpi_mode_label->setGeometry(0,ypos,100,25); hpi_mode_box->setGeometry(105,ypos,size().width()-115,25); ypos+=30; } #endif // ASIHPI } void HpiWidget::listClickedData(const QModelIndex &index) { #ifdef ASIHPI LoadMixer(hpi_view->selectedAdapterIndex(),hpi_view->selectedInputIndex()); hpi_adapter_index=hpi_view->selectedAdapterIndex(); hpi_input_index=hpi_view->selectedInputIndex(); #endif // ASIHPI } void HpiWidget::sourceActivatedData(int n) { #ifdef ASIHPI uint16_t type=ASIHPI_UNPACK_TYPE(hpi_source_box->currentItemData().toUInt()); uint16_t index= ASIHPI_UNPACK_INDEX(hpi_source_box->currentItemData().toUInt()); HpiLog(HPI_Multiplexer_SetSource(NULL,hpi_mult_handle,type,index)); #endif // ASIHPI } void HpiWidget::typeActivatedData(int n) { #ifdef ASIHPI uint16_t type=ASIHPI_UNPACK_TYPE(hpi_type_box->currentItemData().toUInt()); uint16_t index= ASIHPI_UNPACK_INDEX(hpi_type_box->currentItemData().toUInt()); HpiLog(HPI_Multiplexer_SetSource(NULL,hpi_type_handle,type,index)); #endif // ASIHPI } void HpiWidget::modeActivatedData(int n) { #ifdef ASIHPI uint16_t mode=hpi_mode_box->currentItemData().toUInt(); HpiLog(HPI_ChannelModeSet(NULL,hpi_mode_handle,mode)); #endif // ASIHPI } void HpiWidget::volumeChangedData(int level) { #ifdef ASIHPI short lvls[HPI_MAX_CHANNELS]; for(unsigned i=0;isetText(tr("OFF")); } else { hpi_volume_readout_label->setText(QString().sprintf("%d dB",level/100)); } HpiLog(HPI_VolumeSetGain(NULL,hpi_volume_handle,lvls)); #endif // ASIHPI } #ifdef ASIHPI void HpiWidget::LoadMixer(unsigned adapter,unsigned input) { uint16_t index=0; uint16_t type=0; uint16_t mindex=0; short min=0; short max; short step=0; short gain=0; hpi_mode_found=false; hpi_mult_found=false; hpi_type_found=false; hpi_volume_found=false; if((hpi_adapter_index>0)&&(hpi_input_index>0)) { HpiLog(HPI_MixerClose(NULL,hpi_mixer_handle)); } if((adapter>0)&&(input>0)) { if((HpiLog(HPI_MixerOpen(NULL,adapter-1,&hpi_mixer_handle)))==0) { // // Input Source Multiplexer // hpi_source_box->clear(); if(HPI_MixerGetControl(NULL,hpi_mixer_handle,0,0, HPI_DESTNODE_ISTREAM,input-1, HPI_CONTROL_MULTIPLEXER, &hpi_mult_handle)==0) { hpi_mult_found=true; index=0; while(HPI_Multiplexer_QuerySource(NULL,hpi_mult_handle,index,&type, &mindex)==0) { hpi_source_box->insertItem(index,HpiWidget::sourceNodeText(type)+ QString().sprintf(" %u",mindex+1), ASIHPI_PACK_CONTROL(type,mindex)); index++; } if(HpiLog(HPI_Multiplexer_GetSource(NULL,hpi_mult_handle,&type, &mindex))==0) { hpi_source_box->setCurrentItemData(ASIHPI_PACK_CONTROL(type,mindex)); } } // // Input Type Multiplexer // if(HPI_MixerGetControl(NULL,hpi_mixer_handle, HPI_SOURCENODE_LINEIN,input-1, HPI_DESTNODE_NONE,0, HPI_CONTROL_MULTIPLEXER, &hpi_type_handle)==0) { hpi_type_found=true; index=0; while(HPI_Multiplexer_QuerySource(NULL,hpi_type_handle,index,&type, &mindex)==0) { hpi_type_box->insertItem(index,HpiWidget::sourceNodeText(type), ASIHPI_PACK_CONTROL(type,mindex)); index++; } if(HpiLog(HPI_Multiplexer_GetSource(NULL,hpi_type_handle,&type, &mindex))==0) { hpi_type_box->setCurrentItemData(ASIHPI_PACK_CONTROL(type,mindex)); } } // // Mode Control // hpi_mode_box->clear(); if(HPI_MixerGetControl(NULL,hpi_mixer_handle,0,0, HPI_DESTNODE_ISTREAM,input-1, HPI_CONTROL_CHANNEL_MODE, &hpi_mode_handle)==0) { hpi_mode_found=true; index=0; while(HPI_ChannelMode_QueryMode(NULL,hpi_mode_handle,index,&type)==0) { hpi_mode_box->insertItem(index,HpiWidget::channelModeText(type),type); index++; } if(HpiLog(HPI_ChannelModeGet(NULL,hpi_mode_handle,&type))==0) { hpi_mode_box->setCurrentItemData(type); } } // // Volume Control // if(HPI_MixerGetControl(NULL,hpi_mixer_handle, HPI_SOURCENODE_LINEIN,input-1, HPI_DESTNODE_NONE,0, HPI_CONTROL_VOLUME, &hpi_volume_handle)==0) { if(HpiLog(HPI_VolumeQueryRange(NULL,hpi_volume_handle, &min,&max,&step))==0) { hpi_volume_slider->setRange(min,max); hpi_volume_found=true; } if(HpiLog(HPI_VolumeGetGain(NULL,hpi_volume_handle,&gain))==0) { hpi_volume_slider->setValue(gain); volumeChangedData(gain); } } /* for(uint16_t i=HPI_SOURCENODE_NONE;isetGeometry(0,0,size().width(),100); ypos+=105; if(hpi_mult_found) { hpi_source_label->show(); hpi_source_label->setGeometry(0,ypos,100,25); hpi_source_box->show(); hpi_source_box->setGeometry(105,ypos,size().width()-115,25); ypos+=30; } else { hpi_source_label->hide(); hpi_source_box->hide(); } if(hpi_type_found) { hpi_type_label->show(); hpi_type_label->setGeometry(0,ypos,100,25); hpi_type_box->show(); hpi_type_box->setGeometry(105,ypos,size().width()-115,25); ypos+=30; } else { hpi_type_label->hide(); hpi_type_box->hide(); } if(hpi_mode_found) { hpi_mode_label->setGeometry(0,ypos,100,25); hpi_mode_label->show(); hpi_mode_box->setGeometry(105,ypos,size().width()-115,25); hpi_mode_box->show(); ypos+=30; } else { hpi_mode_label->hide(); hpi_mode_box->hide(); } if(hpi_volume_found) { hpi_volume_label->show(); hpi_volume_label->setGeometry(0,ypos,100,25); hpi_volume_slider->show(); hpi_volume_slider->setGeometry(105,ypos,size().width()-175,25); hpi_volume_readout_label->show(); hpi_volume_readout_label->setGeometry(size().width()-55,ypos,45,25); ypos+=30; } else { hpi_volume_label->hide(); hpi_volume_slider->hide(); hpi_volume_readout_label->hide(); } } hpi_err_t HpiWidget::HpiLog(hpi_err_t err,int priority) const { if(err!=0) { fprintf(stderr,"HPI error %d: \"%s\"\n",err,hpi_strerror(err)); } return err; } const char *HpiWidget::hpi_strerror(hpi_err_t err) const { static char err_text[200]; HPI_GetErrorText(err,err_text); return err_text; } #endif // ASIHPI GlassCoder-2.0.1/src/common/hpiwidget.h000066400000000000000000000055511425562563600200000ustar00rootroot00000000000000// hpiwidget.h // // Widget for configuring ASIHPI sources // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef HPIWIDGET_H #define HPIWIDGET_H #include #include #ifdef ASIHPI #include #endif // ASIHPI #include #include #include "combobox.h" #include "hpiinputlistview.h" #define ASIHPI_PACK_CONTROL(type,index) ((0xFFFF&(type))<<16)|(0xFFFF&(index)) #define ASIHPI_UNPACK_TYPE(value) (((value)>>16)&0xFFFF) #define ASIHPI_UNPACK_INDEX(value) ((value)&0xFFFF) class HpiWidget : public QWidget { Q_OBJECT; public: HpiWidget(QWidget *parent=0); void setReadOnly(bool state); unsigned selectedAdapterIndex() const; unsigned selectedInputIndex() const; void setSelected(unsigned adapter,unsigned input); int inputGain() const; void setInputGain(int gain); unsigned channelMode() const; void setChannelMode(unsigned mode); unsigned inputSource() const; void setInputSource(unsigned src); unsigned inputType() const; void setInputType(unsigned type); static QString sourceNodeText(uint16_t src_node); static QString channelModeText(uint16_t mode); protected: void resizeEvent(QResizeEvent *e); private slots: void listClickedData(const QModelIndex &index); void sourceActivatedData(int n); void typeActivatedData(int n); void modeActivatedData(int n); void volumeChangedData(int level); private: #ifdef ASIHPI void LoadMixer(unsigned adapter,unsigned input); void Redraw(); hpi_err_t HpiLog(hpi_err_t err,int priority=LOG_DEBUG) const; const char *hpi_strerror(hpi_err_t err) const; HpiInputListView *hpi_view; QLabel *hpi_source_label; ComboBox *hpi_source_box; QLabel *hpi_mode_label; ComboBox *hpi_mode_box; QLabel *hpi_type_label; ComboBox *hpi_type_box; QLabel *hpi_volume_label; QSlider *hpi_volume_slider; QLabel *hpi_volume_readout_label; unsigned hpi_adapter_index; unsigned hpi_input_index; uint32_t hpi_mixer_handle; hpi_handle_t hpi_mult_handle; bool hpi_mult_found; hpi_handle_t hpi_type_handle; bool hpi_type_found; hpi_handle_t hpi_mode_handle; bool hpi_mode_found; hpi_handle_t hpi_volume_handle; bool hpi_volume_found; #endif // ASIHPI }; #endif // HPIWIDGET_H GlassCoder-2.0.1/src/common/logging.cpp000066400000000000000000000030071425562563600177670ustar00rootroot00000000000000// logging.cpp // // Logging routines for glasscoder(1). // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include "logging.h" int global_log_to=LOG_TO_STDERR; QString global_log_string; bool global_log_verbose=false; void Log(int prio,const QString &msg) { QString sysmsg=msg; if(!global_log_string.isEmpty()) { sysmsg="["+global_log_string+"] "+msg; } switch(global_log_to) { case LOG_TO_SYSLOG: syslog(prio,"%s",sysmsg.toUtf8().constData()); break; case LOG_TO_STDOUT: if(global_log_verbose||(prio // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #define LOG_TO_STDERR 0 #define LOG_TO_SYSLOG 1 #define LOG_TO_STDOUT 2 #define CONNECTION_IDLE 0 #define CONNECTION_PENDING 1 #define CONNECTION_OK 2 #define CONNECTION_FAILED 3 #define CONNECTION_STOPPING 4 extern int global_log_to; extern QString global_log_string; extern bool global_log_verbose; void Log(int prio,const QString &msg); GlassCoder-2.0.1/src/common/messagewidget.cpp000066400000000000000000000026711425562563600211770ustar00rootroot00000000000000// messagewidget.cpp // // Connection message widget // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "messagewidget.h" MessageWidget::MessageWidget(QWidget *parent) : QLabel(parent) { msg_hang_timer=new QTimer(this); msg_hang_timer->setSingleShot(true); connect(msg_hang_timer,SIGNAL(timeout()),this,SLOT(hangData())); } MessageWidget::~MessageWidget() { delete msg_hang_timer; } void MessageWidget::addMessage(const QString &msg) { msg_messages.push(msg); if(!msg_hang_timer->isActive()) { msg_hang_timer->start(0); } } void MessageWidget::hangData() { if(msg_messages.size()>0) { setText(msg_messages.front()); msg_messages.pop(); msg_hang_timer->start(MESSAGEWIDGET_HANG_TIME); } else { setText(""); } } GlassCoder-2.0.1/src/common/messagewidget.h000066400000000000000000000022751425562563600206440ustar00rootroot00000000000000// messagewidget.h // // Connection message widget // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef MESSAGEWIDGET_H #define MESSAGEWIDGET_H #include #include #include #define MESSAGEWIDGET_HANG_TIME 5000 class MessageWidget : public QLabel { Q_OBJECT; public: MessageWidget(QWidget *parent=0); ~MessageWidget(); void addMessage(const QString &msg); private slots: void hangData(); private: std::queue msg_messages; QTimer *msg_hang_timer; }; #endif // MESSAGEWIDGET_H GlassCoder-2.0.1/src/common/metaevent.cpp000066400000000000000000000043321425562563600203330ustar00rootroot00000000000000// metaevent.cpp // // Container class for metadata updates. // // (C) Copyright 2016-2019 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "metaevent.h" MetaEvent::MetaEvent() { } MetaEvent::MetaEvent(const MetaEvent &e) { meta_fields=e.meta_fields; } QStringList MetaEvent::fieldKeys() const { QStringList keys; for(QMap::const_iterator it=meta_fields.begin(); it!=meta_fields.end();it++) { keys.push_back(it.key()); } return keys; } QString MetaEvent::field(const QString &key,bool *ok) const { if(ok!=NULL) { *ok=meta_fields.find(key)!=meta_fields.end(); } return meta_fields.value(key,QString()); } void MetaEvent::setField(const QString &key,const QString &v) { QString mkey=key; QString mv=v; if(key=="TXXX") { int index=v.indexOf("]"); if(index>=2) { QString subkey=v.mid(1,v.indexOf("]")-1); mkey=key+subkey; mv=v.right(v.length()-index-2); } } meta_fields[mkey]=mv; } QString MetaEvent::exportFields() const { QString ret=""; for(QMap::const_iterator it=meta_fields.begin(); it!=meta_fields.end();it++) { QString value=it.value(); ret+="Metadata|"+it.key()+":"+value+"\n"; } return ret; } bool MetaEvent::isEmpty() const { return meta_fields.size()==0; } QString MetaEvent::dump() const { QString str=""; for(QMap::const_iterator it=meta_fields.begin(); it!=meta_fields.end();it++) { str+=it.key()+": "+it.value()+"\n"; } return str; } void MetaEvent::clear() { meta_fields.clear(); } GlassCoder-2.0.1/src/common/metaevent.h000066400000000000000000000024261425562563600200020ustar00rootroot00000000000000// metaevent.h // // Container class for metadata updates. // // (C) Copyright 2016-2019 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef METAEVENT_H #define METAEVENT_H #include #include #include #include class MetaEvent { public: MetaEvent(); MetaEvent(const MetaEvent &e); QStringList fieldKeys() const; QString field(const QString &key,bool *ok=NULL) const; void setField(const QString &key,const QString &v); QString exportFields() const; bool isEmpty() const; QString dump() const; void clear(); private: QMap meta_fields; }; #endif // METAEVENT_H GlassCoder-2.0.1/src/common/paths.h.in000066400000000000000000000015701425562563600175350ustar00rootroot00000000000000// paths.h // // Build-specific settings for GlassCoder // // (C) Copyright 2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef PATHS_H #define PATHS_H #define GLASSCODER_PREFIX QString("@LOCAL_PREFIX@") #endif // PATHS_H GlassCoder-2.0.1/src/common/profile.cpp000066400000000000000000000157701425562563600200130ustar00rootroot00000000000000// profile.cpp // // A container class for profile lines. // // (C) Copyright 2013 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include "profile.h" ProfileLine::ProfileLine() { clear(); } QString ProfileLine::tag() const { return line_tag; } void ProfileLine::setTag(QString tag) { line_tag=tag; } QString ProfileLine::value() const { return line_value; } void ProfileLine::setValue(QString value) { line_value=value; } void ProfileLine::clear() { line_tag=""; line_value=""; } ProfileSection::ProfileSection() { clear(); } QString ProfileSection::name() const { return section_name; } void ProfileSection::setName(QString name) { section_name=name; } bool ProfileSection::getValue(QString tag,QString *value) const { for(unsigned i=0;iopen(QIODevice::ReadOnly)) { delete file; return false; } QTextStream *text=new QTextStream(file); QString line=text->readLine().trimmed(); while(!line.isNull()) { if((line.left(1)!=";")&&(line.left(1)!="#")) { if((line.left(1)=="[")&&(line.right(1)=="]")) { section=line.mid(1,line.length()-2); profile_section.push_back(ProfileSection()); profile_section.back().setName(section); } else if(((offset=line.indexOf('='))!=-1)) { // else if(((offset=line.indexOf('='))!=-1)&&(!section.isEmpty())) { profile_section.back(). addValue(line.left(offset), line.right(line.length()-offset-1).trimmed()); } } line=text->readLine().trimmed(); } delete text; delete file; return true; } bool Profile::setSource(std::vector *values) { QString section; int offset; profile_section.resize(0); profile_section.push_back(ProfileSection()); profile_section.back().setName(""); for(unsigned i=0;isize();i++) { if((values->at(i).left(1)!=";")&&(values->at(i).left(1)!="#")) { if((values->at(i).left(1)=="[")&&(values->at(i).right(1)=="]")) { section=values->at(i).mid(1,values->at(i).length()-2); profile_section.push_back(ProfileSection()); profile_section.back().setName(section); } else if(((offset=values->at(i).indexOf('='))!=-1)) { profile_section.back(). addValue(values->at(i).left(offset), values->at(i).right(values->at(i).length()-offset-1). trimmed()); } } } return true; } QString Profile::stringValue(const QString §ion,const QString &tag, const QString &default_str,bool *ok) const { QString result; for(unsigned i=0;i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef PROFILE_H #define PROFILE_H #include #include #include #include class ProfileLine { public: ProfileLine(); QString tag() const; void setTag(QString tag); QString value() const; void setValue(QString value); void clear(); private: QString line_tag; QString line_value; }; class ProfileSection { public: ProfileSection(); QString name() const; void setName(QString name); bool getValue(QString tag,QString *value) const; void addValue(QString tag,QString value); void clear(); private: QString section_name; std::vector section_line; }; class Profile { public: Profile(); QString source() const; bool setSource(const QString &filename); bool setSource(std::vector *values); QString stringValue(const QString §ion,const QString &tag, const QString &default_value="",bool *ok=0) const; int intValue(const QString §ion,const QString &tag, int default_value=0,bool *ok=0) const; int hexValue(const QString §ion,const QString &tag, int default_value=0,bool *ok=0) const; float floatValue(const QString §ion,const QString &tag, float default_value=0.0,bool *ok=0) const; double doubleValue(const QString §ion,const QString &tag, double default_value=0.0,bool *ok=0) const; bool boolValue(const QString §ion,const QString &tag, bool default_value=false,bool *ok=0) const; QTime timeValue(const QString §ion,const QString &tag, const QTime &default_value=QTime(),bool *ok=0); QHostAddress addressValue(const QString §ion,const QString &tag, const QHostAddress &default_value=QHostAddress(), bool *ok=0); QHostAddress addressValue(const QString §ion,const QString &tag, const QString &default_value="",bool *ok=0); void clear(); private: QString profile_source; std::vector profile_section; }; #endif // PROFILE_H GlassCoder-2.0.1/src/common/ringbuffer.cpp000066400000000000000000000246111425562563600204760ustar00rootroot00000000000000// ringbuffer.cpp // // A ringbuffer class for PCM audio // // (C) Copyright 2011-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License // version 2 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code. // This is safe for the case of one read thread and one write thread. // // Copyright (C) 2000 Paul Davis // Copyright (C) 2003 Rohan Drape // #include #include #include #ifdef USE_MLOCK #include #endif /* USE_MLOCK */ glass_ringbuffer_t *glass_ringbuffer_create(int sz); void glass_ringbuffer_free(glass_ringbuffer_t *rb); void glass_ringbuffer_get_read_vector(const glass_ringbuffer_t *rb, glass_ringbuffer_data_t *vec); void glass_ringbuffer_get_write_vector(const glass_ringbuffer_t *rb, glass_ringbuffer_data_t *vec); size_t glass_ringbuffer_read(glass_ringbuffer_t *rb, char *dest, size_t cnt); size_t glass_ringbuffer_peek(glass_ringbuffer_t *rb, char *dest, size_t cnt); void glass_ringbuffer_read_advance(glass_ringbuffer_t *rb, size_t cnt); size_t glass_ringbuffer_read_space(const glass_ringbuffer_t *rb); int glass_ringbuffer_mlock(glass_ringbuffer_t *rb); void glass_ringbuffer_reset(glass_ringbuffer_t *rb); void glass_ringbuffer_reset_size (glass_ringbuffer_t * rb, size_t sz); size_t glass_ringbuffer_write(glass_ringbuffer_t *rb, const char *src, size_t cnt); void glass_ringbuffer_write_advance(glass_ringbuffer_t *rb, size_t cnt); size_t glass_ringbuffer_write_space(const glass_ringbuffer_t *rb); /* Create a new ringbuffer to hold at least `sz' bytes of data. The actual buffer size is rounded up to the next power of two. */ glass_ringbuffer_t * glass_ringbuffer_create (int sz) { int power_of_two; glass_ringbuffer_t *rb; if ((rb = (glass_ringbuffer_t *) malloc (sizeof (glass_ringbuffer_t))) == NULL) { return NULL; } for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); rb->size = 1 << power_of_two; rb->size_mask = rb->size; rb->size_mask -= 1; rb->write_ptr = 0; rb->read_ptr = 0; if ((rb->buf = (char *) malloc (rb->size)) == NULL) { free (rb); return NULL; } rb->mlocked = 0; return rb; } /* Free all data associated with the ringbuffer `rb'. */ void glass_ringbuffer_free (glass_ringbuffer_t * rb) { #ifdef USE_MLOCK if (rb->mlocked) { munlock (rb->buf, rb->size); } #endif /* USE_MLOCK */ free (rb->buf); free (rb); } /* Lock the data block of `rb' using the system call 'mlock'. */ int glass_ringbuffer_mlock (glass_ringbuffer_t * rb) { #ifdef USE_MLOCK if (mlock (rb->buf, rb->size)) { return -1; } #endif /* USE_MLOCK */ rb->mlocked = 1; return 0; } /* Reset the read and write pointers to zero. This is not thread safe. */ void glass_ringbuffer_reset (glass_ringbuffer_t * rb) { rb->read_ptr = 0; rb->write_ptr = 0; memset(rb->buf, 0, rb->size); } /* Reset the read and write pointers to zero. This is not thread safe. */ void glass_ringbuffer_reset_size (glass_ringbuffer_t * rb, size_t sz) { rb->size = sz; rb->size_mask = rb->size; rb->size_mask -= 1; rb->read_ptr = 0; rb->write_ptr = 0; } /* Return the number of bytes available for reading. This is the number of bytes in front of the read pointer and behind the write pointer. */ size_t glass_ringbuffer_read_space (const glass_ringbuffer_t * rb) { size_t w, r; w = rb->write_ptr; r = rb->read_ptr; if (w > r) { return w - r; } else { return (w - r + rb->size) & rb->size_mask; } } /* Return the number of bytes available for writing. This is the number of bytes in front of the write pointer and behind the read pointer. */ size_t glass_ringbuffer_write_space (const glass_ringbuffer_t * rb) { size_t w, r; w = rb->write_ptr; r = rb->read_ptr; if (w > r) { return ((r - w + rb->size) & rb->size_mask) - 1; } else if (w < r) { return (r - w) - 1; } else { return rb->size - 1; } } /* The copying data reader. Copy at most `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes copied. */ size_t glass_ringbuffer_read (glass_ringbuffer_t * rb, char *dest, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_read; size_t n1, n2; if ((free_cnt = glass_ringbuffer_read_space (rb)) == 0) { return 0; } to_read = cnt > free_cnt ? free_cnt : cnt; cnt2 = rb->read_ptr + to_read; if (cnt2 > rb->size) { n1 = rb->size - rb->read_ptr; n2 = cnt2 & rb->size_mask; } else { n1 = to_read; n2 = 0; } memcpy (dest, &(rb->buf[rb->read_ptr]), n1); rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask; if (n2) { memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask; } return to_read; } /* The copying data reader w/o read pointer advance. Copy at most `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes copied. */ size_t glass_ringbuffer_peek (glass_ringbuffer_t * rb, char *dest, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_read; size_t n1, n2; size_t tmp_read_ptr; tmp_read_ptr = rb->read_ptr; if ((free_cnt = glass_ringbuffer_read_space (rb)) == 0) { return 0; } to_read = cnt > free_cnt ? free_cnt : cnt; cnt2 = tmp_read_ptr + to_read; if (cnt2 > rb->size) { n1 = rb->size - tmp_read_ptr; n2 = cnt2 & rb->size_mask; } else { n1 = to_read; n2 = 0; } memcpy (dest, &(rb->buf[tmp_read_ptr]), n1); tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask; if (n2) { memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2); } return to_read; } /* The copying data writer. Copy at most `cnt' bytes to `rb' from `src'. Returns the actual number of bytes copied. */ size_t glass_ringbuffer_write (glass_ringbuffer_t * rb, const char *src, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_write; size_t n1, n2; if ((free_cnt = glass_ringbuffer_write_space (rb)) == 0) { return 0; } to_write = cnt > free_cnt ? free_cnt : cnt; cnt2 = rb->write_ptr + to_write; if (cnt2 > rb->size) { n1 = rb->size - rb->write_ptr; n2 = cnt2 & rb->size_mask; } else { n1 = to_write; n2 = 0; } memcpy (&(rb->buf[rb->write_ptr]), src, n1); rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask; if (n2) { memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask; } return to_write; } /* Advance the read pointer `cnt' places. */ void glass_ringbuffer_read_advance (glass_ringbuffer_t * rb, size_t cnt) { size_t tmp = (rb->read_ptr + cnt) & rb->size_mask; rb->read_ptr = tmp; } /* Advance the write pointer `cnt' places. */ void glass_ringbuffer_write_advance (glass_ringbuffer_t * rb, size_t cnt) { size_t tmp = (rb->write_ptr + cnt) & rb->size_mask; rb->write_ptr = tmp; } /* The non-copying data reader. `vec' is an array of two places. Set the values at `vec' to hold the current readable data at `rb'. If the readable data is in one segment the second segment has zero length. */ void glass_ringbuffer_get_read_vector (const glass_ringbuffer_t * rb, glass_ringbuffer_data_t * vec) { size_t free_cnt; size_t cnt2; size_t w, r; w = rb->write_ptr; r = rb->read_ptr; if (w > r) { free_cnt = w - r; } else { free_cnt = (w - r + rb->size) & rb->size_mask; } cnt2 = r + free_cnt; if (cnt2 > rb->size) { /* Two part vector: the rest of the buffer after the current write ptr, plus some from the start of the buffer. */ vec[0].buf = &(rb->buf[r]); vec[0].len = rb->size - r; vec[1].buf = rb->buf; vec[1].len = cnt2 & rb->size_mask; } else { /* Single part vector: just the rest of the buffer */ vec[0].buf = &(rb->buf[r]); vec[0].len = free_cnt; vec[1].len = 0; } } /* The non-copying data writer. `vec' is an array of two places. Set the values at `vec' to hold the current writeable data at `rb'. If the writeable data is in one segment the second segment has zero length. */ void glass_ringbuffer_get_write_vector (const glass_ringbuffer_t * rb, glass_ringbuffer_data_t * vec) { size_t free_cnt; size_t cnt2; size_t w, r; w = rb->write_ptr; r = rb->read_ptr; if (w > r) { free_cnt = ((r - w + rb->size) & rb->size_mask) - 1; } else if (w < r) { free_cnt = (r - w) - 1; } else { free_cnt = rb->size - 1; } cnt2 = w + free_cnt; if (cnt2 > rb->size) { /* Two part vector: the rest of the buffer after the current write ptr, plus some from the start of the buffer. */ vec[0].buf = &(rb->buf[w]); vec[0].len = rb->size - w; vec[1].buf = rb->buf; vec[1].len = cnt2 & rb->size_mask; } else { vec[0].buf = &(rb->buf[w]); vec[0].len = free_cnt; vec[1].len = 0; } } Ringbuffer::Ringbuffer(size_t bytes,unsigned channels) { ring_channels=channels; ring_ring=glass_ringbuffer_create(bytes); } Ringbuffer::~Ringbuffer() { glass_ringbuffer_free(ring_ring); } unsigned Ringbuffer::size() const { return 0; } unsigned Ringbuffer::read(float *data,unsigned frames) { return glass_ringbuffer_read(ring_ring,(char *)data, frames*sizeof(float)*ring_channels)/ (sizeof(float)*ring_channels); } unsigned Ringbuffer::readSpace() const { return glass_ringbuffer_read_space(ring_ring)/(sizeof(float)*ring_channels); } unsigned Ringbuffer::write(float *data,unsigned frames) { return glass_ringbuffer_write(ring_ring,(const char *)data, frames*sizeof(float)*ring_channels)/ (sizeof(float)*ring_channels); } unsigned Ringbuffer::writeSpace() const { return glass_ringbuffer_write_space(ring_ring)/(sizeof(float)*ring_channels); } unsigned Ringbuffer::dump(unsigned frames) { size_t bytes=frames*ring_channels*sizeof(float); unsigned ret=frames; if(glass_ringbuffer_read_space(ring_ring) // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License // version 2 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code. // This is safe for the case of one read thread and one write thread. // // Copyright (C) 2000 Paul Davis // Copyright (C) 2003 Rohan Drape // #ifndef RINGBUFFER_H #define RINGBUFFER_H #include #ifdef __cplusplus extern "C" { #endif typedef struct { char *buf; size_t len; } glass_ringbuffer_data_t ; typedef struct { char *buf; volatile size_t write_ptr; volatile size_t read_ptr; size_t size; size_t size_mask; int mlocked; } glass_ringbuffer_t ; glass_ringbuffer_t *glass_ringbuffer_create(int sz); void glass_ringbuffer_free(glass_ringbuffer_t *rb); void glass_ringbuffer_get_read_vector(const glass_ringbuffer_t *rb, glass_ringbuffer_data_t *vec); void glass_ringbuffer_get_write_vector(const glass_ringbuffer_t *rb, glass_ringbuffer_data_t *vec); size_t glass_ringbuffer_read(glass_ringbuffer_t *rb, char *dest, size_t cnt); size_t glass_ringbuffer_peek(glass_ringbuffer_t *rb, char *dest, size_t cnt); void glass_ringbuffer_read_advance(glass_ringbuffer_t *rb, size_t cnt); size_t glass_ringbuffer_read_space(const glass_ringbuffer_t *rb); int glass_ringbuffer_mlock(glass_ringbuffer_t *rb); void glass_ringbuffer_reset(glass_ringbuffer_t *rb); void glass_ringbuffer_reset_size (glass_ringbuffer_t * rb, size_t sz); size_t glass_ringbuffer_write(glass_ringbuffer_t *rb, const char *src, size_t cnt); void glass_ringbuffer_write_advance(glass_ringbuffer_t *rb, size_t cnt); size_t glass_ringbuffer_write_space(const glass_ringbuffer_t *rb); #ifdef __cplusplus } #endif class Ringbuffer { public: Ringbuffer(size_t bytes,unsigned channels); ~Ringbuffer(); unsigned size() const; unsigned read(float *data,unsigned frames); unsigned readSpace() const; unsigned write(float *data,unsigned frames); unsigned writeSpace() const; unsigned dump(unsigned frames); private: glass_ringbuffer_t *ring_ring; unsigned ring_channels; }; #endif // RINGBUFFER_H GlassCoder-2.0.1/src/common/segmeter.cpp000066400000000000000000000246171425562563600201660ustar00rootroot00000000000000// segmeter.cpp // // An audio meter display widget. // // (C) Copyright 2002-2019 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License // version 2 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // #include #include #include #include #include #include #include #include #include #include #include "segmeter.h" SegMeter::SegMeter(SegMeter::Orientation o,QWidget *parent) : QWidget(parent) { orient=o; dark_low_color=QColor(DEFAULT_DARK_LOW_COLOR); dark_high_color=QColor(DEFAULT_DARK_HIGH_COLOR); dark_clip_color=QColor(DEFAULT_DARK_CLIP_COLOR); low_color=QColor(DEFAULT_LOW_COLOR); high_color=QColor(DEFAULT_HIGH_COLOR); clip_color=QColor(DEFAULT_CLIP_COLOR); high_threshold=-14; clip_threshold=0; seg_size=2; seg_gap=1; range_min=-3000; range_max=0; solid_bar=-10000; floating_bar=-10000; seg_mode=SegMeter::Independent; peak_timer=new QTimer(this); connect(peak_timer,SIGNAL(timeout()),this,SLOT(peakData())); } QSize SegMeter::sizeHint() const { return QSize(0,0); } QSizePolicy SegMeter::sizePolicy() const { return QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); } void SegMeter::setRange(int min,int max) { range_min=min; range_max=max; repaint(); } void SegMeter::setDarkLowColor(QColor color) { if(dark_low_color!=color) { dark_low_color=color; repaint(); } } void SegMeter::setDarkHighColor(QColor color) { if(dark_high_color!=color) { dark_high_color=color; repaint(); } } void SegMeter::setDarkClipColor(QColor color) { if(dark_clip_color!=color) { dark_clip_color=color; repaint(); } } void SegMeter::setLowColor(QColor color) { if(low_color!=color) { low_color=color; repaint(); } } void SegMeter::setHighColor(QColor color) { if(high_color!=color) { high_color=color; repaint(); } } void SegMeter::setClipColor(QColor color) { if(clip_color!=color) { clip_color=color; repaint(); } } void SegMeter::setHighThreshold(int level) { if(high_threshold!=level) { high_threshold=level; repaint(); } } void SegMeter::setClipThreshold(int level) { if(clip_threshold!=level) { clip_threshold=level; repaint(); } } SegMeter::Mode SegMeter::mode() const { return seg_mode; } void SegMeter::setMode(SegMeter::Mode mode) { seg_mode=mode; switch(seg_mode) { case SegMeter::Independent: if(peak_timer->isActive()) { peak_timer->stop(); } break; case SegMeter::Peak: if(!peak_timer->isActive()) { peak_timer->start(PEAK_HOLD_TIME); } break; } } void SegMeter::setSolidBar(int level) { if((seg_mode==SegMeter::Independent)&&(solid_bar!=level)) { solid_bar=level; repaint(); } } void SegMeter::setFloatingBar(int level) { if((seg_mode==SegMeter::Independent)&&(solid_bar!=level)) { floating_bar=level; repaint(); } } void SegMeter::setPeakBar(int level) { if((seg_mode==SegMeter::Peak)&&(solid_bar!=level)) { solid_bar=level; if(solid_bar>floating_bar) { floating_bar=solid_bar; } if(solid_barsize()); int seg_total=seg_size+seg_gap; QPainter *p=new QPainter(&pix); p->fillRect(0,0,width(),height(),Qt::black); low_region=0; high_region=0; clip_region=0; p->setBrush(low_color); p->setPen(low_color); // // Set Orientation // switch(orient) { case SegMeter::Left: case SegMeter::Up: p->translate(width(),height()); p->rotate(180); break; default: break; } // // The low range // if(solid_bar>high_threshold) { op_pt=high_threshold; } else { op_pt=solid_bar; } switch(orient) { case SegMeter::Left: case SegMeter::Right: low_region=(int)((double)(op_pt-range_min)/ (double)(range_max-range_min)*width()/seg_total); if(op_pt>range_min) { for(int i=0;ifillRect(i*seg_total,0,seg_size,height(),low_color); } } break; case SegMeter::Down: case SegMeter::Up: low_region=(int)((double)(op_pt-range_min)/ (double)(range_max-range_min)*height()/seg_total); if(op_pt>range_min) { for(int i=0;ifillRect(0,i*seg_total,width(),seg_size,low_color); } } break; } // // The high range // if(solid_bar>clip_threshold) { op_pt=clip_threshold; } else { op_pt=solid_bar; } switch(orient) { case SegMeter::Left: case SegMeter::Right: high_region=(int)((double)(op_pt-high_threshold)/ (double)(range_max-range_min)*width()/seg_total); if(op_pt>high_threshold) { for(int i=low_region;ifillRect(i*seg_total,0,seg_size,height(),high_color); } } break; case SegMeter::Down: case SegMeter::Up: high_region=(int)((double)(op_pt-high_threshold)/ (double)(range_max-range_min)*height()/seg_total); if(op_pt>high_threshold) { for(int i=low_region;ifillRect(0,i*seg_total,width(),seg_size,high_color); } } break; } // // The clip range // if(solid_bar>range_max) { op_pt=range_max; } else { op_pt=solid_bar; } switch(orient) { case SegMeter::Left: case SegMeter::Right: clip_region=(int)((double)(op_pt-clip_threshold)/ (double)(range_max-range_min)*width()/seg_total); if(op_pt>clip_threshold) { for(int i=low_region+high_region; ifillRect(i*seg_total,0,seg_size,height(),clip_color); } } break; case SegMeter::Down: case SegMeter::Up: clip_region=(int)((double)(op_pt-range_min)/ (double)(range_max-range_min)*height()/seg_total); if(op_pt>clip_threshold) { for(int i=low_region+high_region; ifillRect(0,i*seg_total,width(),seg_size,clip_color); } } break; } // // The dark low range // switch(orient) { case SegMeter::Left: case SegMeter::Right: dark_low_region=(int)((double)(high_threshold-range_min)/ (double)(range_max-range_min)*width()/seg_total); if(op_ptfillRect(i*seg_total,0,seg_size,height(),dark_low_color); } } break; case SegMeter::Down: case SegMeter::Up: dark_low_region=(int)((double)(high_threshold-range_min)/ (double)(range_max-range_min)*height()/seg_total); if(op_ptfillRect(0,i*seg_total,width(),seg_size,dark_low_color); } } break; } // // The dark high range // if(solid_bar>=high_threshold) { op_pt=low_region+high_region; } else { op_pt=dark_low_region; } switch(orient) { case SegMeter::Left: case SegMeter::Right: dark_high_region=(int)((double)(clip_threshold-range_min)/ (double)(range_max-range_min)*width()/seg_total); if(solid_barfillRect(i*seg_total,0,seg_size,height(),dark_high_color); } } break; case SegMeter::Down: case SegMeter::Up: dark_high_region=(int)((double)(clip_threshold-range_min)/ (double)(range_max-range_min)*height()/seg_total); if(solid_barfillRect(0,i*seg_total,width(),seg_size,dark_high_color); } } break; } // // The dark clip range // if(solid_bar>clip_threshold) { op_pt=low_region+high_region+clip_region; } else { op_pt=dark_high_region; } switch(orient) { case SegMeter::Left: case SegMeter::Right: dark_clip_region=(int)((double)(range_max-range_min)/ (double)(range_max-range_min)*width()/seg_total); if(solid_barfillRect(i*seg_total,0,seg_size,height(),dark_clip_color); } } break; case SegMeter::Down: case SegMeter::Up: dark_clip_region=(int)((double)(range_max-range_min)/ (double)(range_max-range_min)*height()/seg_total); if(solid_barfillRect(0,i*seg_total,width(),seg_size,dark_clip_color); } } break; } // // The floating segment // if(floating_bar>solid_bar) { if(floating_bar<=high_threshold) { float_color=low_color; } if((floating_bar>high_threshold)&&(floating_bar<=clip_threshold)) { float_color=high_color; } if(floating_bar>clip_threshold) { float_color=clip_color; } switch(orient) { case SegMeter::Left: case SegMeter::Right: float_region=(int)((double)(floating_bar-range_min)/ (double)(range_max-range_min)*width()); float_region=seg_total*(float_region/seg_total); p->fillRect(float_region,0,seg_size,height(),float_color); break; case SegMeter::Down: case SegMeter::Up: float_region=(int)((double)(floating_bar-range_min)/ (double)(range_max-range_min)*height()); float_region=seg_total*(float_region/seg_total); p->fillRect(0,float_region,width(),seg_size,float_color); break; } } p->end(); p->begin(this); p->drawPixmap(0,0,pix); p->end(); delete p; } void SegMeter::peakData() { floating_bar=solid_bar; repaint(); } GlassCoder-2.0.1/src/common/segmeter.h000066400000000000000000000132061425562563600176230ustar00rootroot00000000000000// segmeter.h // // An audio meter display widget. // // (C) Copyright 2002-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License // version 2 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // #ifndef SEGMETER_H #define SEGMETER_H #include #include #include #include #include #include /* * Default Colors */ #define DEFAULT_LOW_COLOR Qt::green #define DEFAULT_DARK_LOW_COLOR 0,80,0 #define DEFAULT_HIGH_COLOR Qt::yellow #define DEFAULT_DARK_HIGH_COLOR 75,75,0 #define DEFAULT_CLIP_COLOR Qt::red #define DEFAULT_DARK_CLIP_COLOR 85,0,0 /* * Global Settings */ #define PEAK_HOLD_TIME 750 /** * @short An audio level meter * @author Fred Gleason * * This class implements an audio level meter, styled after the traditional * LED based meters found on many types of audio gear. The meter can * be subdivided into up to three different color bands, calibrated to * correspond to various audio threshold levels. **/ class SegMeter : public QWidget { Q_OBJECT public: /** * Meter modes. * Independent - Operation of the solid and floating bar segments are * completely independent of each other. * * Peak - The solid bar shows current level, while the floating bar shows * the peak level achieved over the previous half second. **/ enum Mode {Independent=0,Peak=1}; enum Orientation {Left=0,Right=1,Up=2,Down=3}; /** * Instantiates the widget. * @param o The orientation of the meter, meaning the direction of * increasing signal level on the meter. **/ SegMeter(SegMeter::Orientation o,QWidget *parent=0); /** * Returns a suggested size for the meter. **/ QSize sizeHint() const; /** * Returns the sizing policy of the meter. This widget can be freely * resized to fit the requiements of the calling application. */ QSizePolicy sizePolicy() const; /** * Set the range of the audio meter. * @param min The minimum value which will register on the meter. * @param max The "full scale" value of the meter. **/ void setRange(int min,int max); /** * Set the color of the "dark" (i.e. non-lit) segments of the low level * range on the meter. * @param color Color of the dark segments **/ void setDarkLowColor(QColor color); /** * Set the color of the "dark" (i.e. non-lit) segments of the high level * range on the meter. * @param color Color of the dark segments **/ void setDarkHighColor(QColor color); /** * Set the color of the "dark" (i.e. non-lit) segments of the clip level * range on the meter. * @param color Color of the dark segments **/ void setDarkClipColor(QColor color); /** * Set the color of the first level range segments on the meter. * @param color Color of the first level range segments **/ void setLowColor(QColor color); /** * Set the color of the second level range segments on the meter. * @param color Color of the second level range segments **/ void setHighColor(QColor color); /** * Set the color of the third level range segments on the meter. * @param color Color of the third level range segments **/ void setClipColor(QColor color); /** * Set the transition point between the first and second range segments. * @param level Transition point between first and second range segments. **/ void setHighThreshold(int level); /** * Set the transition point between the second and third range segments. * @param level Transition point between second and third range segments. **/ void setClipThreshold(int level); /** * Set the thickness of the segments in the meter. * @param size Thickness of segments in meter. **/ void setSegmentSize(int size); /** * Set the size of the gap between segments in the meter. * @param size Thickness of gap in meter. **/ void setSegmentGap(int gap); /** * Get the current meter mode **/ SegMeter::Mode mode() const; /** * Set the meter mode. * @param mode = The mode to set **/ void setMode(SegMeter::Mode mode); public slots: /** * Set the level of the "solid" display on the meter. * @param level Level of "solid" display on the meter. **/ void setSolidBar(int level); /** * Set the level of the "floating" display on the meter. * @param level Level of "floating" display on the meter. **/ void setFloatingBar(int level); /** * Set the level of the peak display on the meter. This is only valid * when the meter is in "peak" mode. * @param level Level of "floating" display on the meter. **/ void setPeakBar(int level); protected: void paintEvent(QPaintEvent *); private slots: void peakData(); private: SegMeter::Orientation orient; SegMeter::Mode seg_mode; QTimer *peak_timer; int range_min,range_max; QColor dark_low_color; QColor dark_high_color; QColor dark_clip_color; QColor low_color; QColor high_color; QColor clip_color; int high_threshold,clip_threshold; int solid_bar,floating_bar; int seg_size,seg_gap; }; #endif // SEGMETER_H GlassCoder-2.0.1/src/common/serverdialog.cpp000066400000000000000000000355651425562563600210450ustar00rootroot00000000000000// serverdialog.cpp // // Configuration dialog for server settings // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include "serverdialog.h" ServerDialog::ServerDialog(QDir *temp_dir,const QString &caption,QWidget *parent) : QDialog(parent) { srv_temp_dir=temp_dir; srv_caption=caption; // // Fonts // QFont label_font("helvetica",14,QFont::Bold); label_font.setPixelSize(14); setWindowTitle(srv_caption+" - "+tr("Server Settings")); srv_identity_path="/"; if(getenv("HOME")!=NULL) { srv_identity_path=QString(getenv("HOME"))+"/.ssh"; } // // Server Type // srv_server_type_label=new QLabel(tr("Type")+":",this); srv_server_type_label->setFont(label_font); srv_server_type_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); srv_server_type_box=new ComboBox(this); for(int i=0;i insertItem(i,Connector::serverTypeText((Connector::ServerType)i),i); } connect(srv_server_type_box,SIGNAL(activated(int)), this,SLOT(serverTypeChanged(int))); // // Verbose Logging // srv_verbose_check=new QCheckBox(this); srv_verbose_label=new QLabel(tr("Enable verbose logging"),this); srv_verbose_label->setFont(label_font); srv_verbose_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Server Location // srv_server_location_label=new QLabel(tr("Server URL")+":",this); srv_server_location_label->setFont(label_font); srv_server_location_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); srv_server_location_edit=new QLineEdit(this); connect(srv_server_location_edit,SIGNAL(textEdited(const QString &)), this,SLOT(locationChanged(const QString &))); // // Server Username // srv_server_username_label=new QLabel(tr("User Name")+":",this); srv_server_username_label->setFont(label_font); srv_server_username_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); srv_server_username_edit=new QLineEdit(this); // // Server Password // srv_server_password_label=new QLabel(tr("Password")+":",this); srv_server_password_label->setFont(label_font); srv_server_password_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); srv_server_password_edit=new QLineEdit(this); srv_server_password_edit->setEchoMode(QLineEdit::Password); // // SSH Identity // srv_use_identity_check=new QCheckBox(this); srv_use_identity_label= new QLabel(tr("Use SSH Identity To Authenticate"),this); srv_use_identity_label->setFont(label_font); srv_use_identity_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); connect(srv_use_identity_check,SIGNAL(toggled(bool)), this,SLOT(useIdentityChanged(bool))); srv_identity_label=new QLabel(tr("SSH Identity File")+":",this); srv_identity_label->setFont(label_font); srv_identity_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); srv_identity_edit=new QLineEdit(this); srv_identity_button=new QPushButton(tr("Select"),this); connect(srv_identity_button,SIGNAL(clicked()), this,SLOT(selectIdentityFile())); // // Server Script Up // srv_server_script_up_label=new QLabel(tr("CONNECTED Script")+":",this); srv_server_script_up_label->setFont(label_font); srv_server_script_up_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); srv_server_script_up_edit=new QLineEdit(this); // // Server Script Down // srv_server_script_down_label=new QLabel(tr("DISCONNECTED Script")+":",this); srv_server_script_down_label->setFont(label_font); srv_server_script_down_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); srv_server_script_down_edit=new QLineEdit(this); // // Metadata Port // srv_server_metadata_port_label= new QLabel(tr("Local MetaData Port")+":",this); srv_server_metadata_port_label->setFont(label_font); srv_server_metadata_port_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); srv_server_metadata_port_spin=new QSpinBox(this); srv_server_metadata_port_spin->setRange(0,0xFFFF); srv_server_metadata_port_spin->setSpecialValueText(tr("Disabled")); // // Maximum Player Connections // srv_server_maxconns_label= new QLabel(tr("Max Player Connections")+":",this); srv_server_maxconns_label->setFont(label_font); srv_server_maxconns_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); srv_server_maxconns_spin=new QSpinBox(this); srv_server_maxconns_spin->setRange(-1,0xFFFF); srv_server_maxconns_spin->setSpecialValueText(tr("Unlimited")); // // Publish Point Cleanup // srv_cleanup_check=new QCheckBox(this); srv_cleanup_label= new QLabel(tr("Purge stale files on publishing point"),this); srv_cleanup_label->setFont(label_font); srv_cleanup_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Close Button // srv_close_button=new QPushButton(tr("Close"),this); srv_close_button->setFont(label_font); connect(srv_close_button,SIGNAL(clicked()),this,SLOT(hide())); setMinimumHeight(sizeHint().height()); setMaximumHeight(sizeHint().height()); setMinimumSize(sizeHint()); } QSize ServerDialog::sizeHint() const { return QSize(600,330); } bool ServerDialog::makeArgs(QStringList *args,bool escape_args) { QUrl url(srv_server_location_edit->text()); if(!url.isValid()) { return false; } QString esc=""; if(escape_args) { esc="\""; } Connector::ServerType type=(Connector::ServerType) srv_server_type_box->itemData(srv_server_type_box->currentIndex()).toInt(); args->push_back("--server-type="+Connector::optionKeyword(type)); args->push_back("--server-url="+url.toString()); args->push_back("--credentials-file="+credentialsFilename()); if(srv_use_identity_check->isChecked()&&(url.scheme().toLower()=="sftp")) { args->push_back("--ssh-identity="+srv_identity_edit->text()); } if(!srv_server_script_down_edit->text().isEmpty()) { args->push_back("--server-script-down="+ esc+srv_server_script_down_edit->text()+esc); } if(!srv_server_script_up_edit->text().isEmpty()) { args->push_back("--server-script-up="+ esc+srv_server_script_up_edit->text()+esc); } if(srv_server_metadata_port_spin->value()>0) { args->push_back("--metadata-port="+ QString().sprintf("%d",srv_server_metadata_port_spin->value())); } if(srv_server_maxconns_spin->value()>=0) { args->push_back("--server-max-connections="+ QString().sprintf("%d",srv_server_maxconns_spin->value())); } if(!srv_cleanup_check->isChecked()) { args->push_back("--server-no-deletes"); } if(srv_verbose_check->isChecked()) { args->push_back("--verbose"); } return true; } bool ServerDialog::writeCredentials() const { QUrl url(srv_server_location_edit->text()); FILE *f=NULL; if((f=fopen(credentialsFilename().toUtf8(),"w"))==NULL) { return false; } fprintf(f,"[Credentials]\n"); fprintf(f,"Username=%s\n", srv_server_username_edit->text().toUtf8().constData()); fprintf(f,"Password=%s\n", srv_server_password_edit->text().toUtf8().constData()); if(srv_use_identity_check->isChecked()&&(url.scheme().toLower()=="sftp")) { //args->push_back("--ssh-identity="+srv_identity_edit->text()); } fclose(f); return true; } void ServerDialog::setControlsLocked(bool state) { srv_server_type_box->setReadOnly(state); srv_server_location_edit->setReadOnly(state); srv_server_username_edit->setReadOnly(state); srv_server_password_edit->setReadOnly(state); srv_identity_edit->setReadOnly(state); } QString ServerDialog::credentialsFilename() const { QUrl url(srv_server_location_edit->text()); return srv_temp_dir->path()+"/creds-"+url.toString(QUrl::RemoveScheme). replace("//","").replace("/","_").replace("?","_"); } void ServerDialog::load(Profile *p) { bool ok=false; srv_server_type_box-> setCurrentItemData(Connector::serverType(p->stringValue("GlassGui", "ServerType"))); serverTypeChanged(srv_server_type_box->currentIndex()); srv_server_location_edit-> setText(p->stringValue("GlassGui","ServerLocation")); srv_server_username_edit-> setText(QByteArray::fromBase64(p->stringValue("GlassGui", "ServerUsernameSecret","",&ok).toUtf8())); if(!ok) { srv_server_username_edit-> setText(p->stringValue("GlassGui","ServerUsername")); } srv_server_password_edit-> setText(QByteArray::fromBase64(p->stringValue("GlassGui", "ServerPasswordSecret","",&ok).toUtf8())); if(!ok) { srv_server_password_edit-> setText(p->stringValue("GlassGui","ServerPassword")); } srv_use_identity_check-> setChecked(p->intValue("GlassGui","ServerUseIdentity")!=0); srv_identity_edit-> setText(p->stringValue("GlassGui","ServerSshIdentity")); srv_server_script_down_edit-> setText(p->stringValue("GlassGui","ServerScriptDown")); srv_server_script_up_edit-> setText(p->stringValue("GlassGui","ServerScriptUp")); srv_server_maxconns_spin-> setValue(p->intValue("GlassGui","ServerMaxConnections",-1)); srv_cleanup_check-> setChecked(p->intValue("GlassGui","ServerNoDeletes",1)!=0); srv_verbose_check->setChecked(p->boolValue("GlassGui","VerboseLogging")); srv_server_metadata_port_spin-> setValue(p->intValue("GlassGui","MetadataPort",-1)); useIdentityChanged(srv_use_identity_check->isChecked()); } void ServerDialog::save(FILE *f) { fprintf(f,"ServerType=%s\n", Connector::optionKeyword((Connector::ServerType) srv_server_type_box->currentItemData().toInt()).toUtf8().constData()); fprintf(f,"ServerLocation=%s\n", srv_server_location_edit->text().toUtf8().constData()); fprintf(f,"ServerUsernameSecret=%s\n", srv_server_username_edit->text().toUtf8().toBase64().constData()); fprintf(f,"ServerPasswordSecret=%s\n", srv_server_password_edit->text().toUtf8().toBase64().constData()); fprintf(f,"ServerUseIdentity=%u\n",srv_use_identity_check->isChecked()); fprintf(f,"ServerSshIdentity=%s\n", srv_identity_edit->text().toUtf8().constData()); fprintf(f,"ServerScriptDown=%s\n", srv_server_script_down_edit->text().toUtf8().constData()); fprintf(f,"ServerScriptUp=%s\n", srv_server_script_up_edit->text().toUtf8().constData()); fprintf(f,"ServerMaxConnections=%d\n",srv_server_maxconns_spin->value()); fprintf(f,"ServerNoDeletes=%d\n",srv_cleanup_check->isChecked()); fprintf(f,"MetadataPort=%d\n",srv_server_metadata_port_spin->value()); fprintf(f,"VerboseLogging=%d\n",srv_verbose_check->isChecked()); } void ServerDialog::resizeEvent(QResizeEvent *e) { int ypos=10; srv_server_type_label->setGeometry(10,ypos,110,24); srv_server_type_box->setGeometry(125,ypos,250,24); srv_verbose_check->setGeometry(395,ypos,25,25); srv_verbose_label->setGeometry(420,ypos+1,size().width()-430,24); ypos+=26; srv_server_location_label->setGeometry(10,ypos,180,24); srv_server_location_edit->setGeometry(195,ypos,size().width()-205,24); ypos+=26; srv_server_username_label->setGeometry(10,ypos,180,24); srv_server_username_edit->setGeometry(195,ypos,size().width()-205,24); ypos+=26; srv_server_password_label->setGeometry(10,ypos,180,24); srv_server_password_edit->setGeometry(195,ypos,size().width()-205,24); ypos+=22; srv_use_identity_check->setGeometry(195,ypos+5,15,15); srv_use_identity_label->setGeometry(215,ypos+1,size().width()-225,24); ypos+=26; srv_identity_label->setGeometry(10,ypos,180,24); srv_identity_edit->setGeometry(195,ypos,size().width()-275,24); srv_identity_button->setGeometry(size().width()-70,ypos-2,60,28); ypos+=26; srv_server_script_up_label->setGeometry(10,ypos,180,24); srv_server_script_up_edit->setGeometry(195,ypos,size().width()-205,24); ypos+=26; srv_server_script_down_label->setGeometry(10,ypos,180,24); srv_server_script_down_edit->setGeometry(195,ypos,size().width()-205,24); ypos+=26; srv_server_metadata_port_label->setGeometry(10,ypos,180,24); srv_server_metadata_port_spin->setGeometry(195,ypos,100,24); ypos+=26; srv_server_maxconns_label->setGeometry(10,ypos,180,24); srv_server_maxconns_spin->setGeometry(195,ypos,100,24); ypos+=26; srv_cleanup_check->setGeometry(105,ypos+5,15,15); srv_cleanup_label->setGeometry(125,ypos+1,size().width()-225,24); ypos+=35; srv_close_button->setGeometry(size().width()-80,size().height()-50,70,40); } void ServerDialog::serverTypeChanged(int index) { Connector::ServerType type= (Connector::ServerType)srv_server_type_box->itemData(index).toInt(); bool authfields=false; bool cleanup=false; switch(type) { case Connector::HlsServer: authfields=true; cleanup=true; break; case Connector::IcecastStreamerServer: authfields=false; cleanup=false; break; case Connector::Shoutcast1Server: case Connector::Shoutcast2Server: case Connector::IcecastOutServer: case Connector::Icecast2Server: case Connector::FileServer: case Connector::FileArchiveServer: authfields=true; cleanup=false; break; case Connector::LastServer: break; } srv_server_script_up_label->setEnabled(authfields); srv_server_script_up_edit->setEnabled(authfields); srv_server_script_down_label->setEnabled(authfields); srv_server_script_down_edit->setEnabled(authfields); srv_server_maxconns_label->setDisabled(authfields); srv_server_maxconns_spin->setDisabled(authfields); srv_cleanup_check->setEnabled(cleanup); srv_cleanup_label->setEnabled(cleanup); emit typeChanged(type); } void ServerDialog::locationChanged(const QString &str) { useIdentityChanged(srv_use_identity_check->isChecked()); emit settingsChanged(); } void ServerDialog::useIdentityChanged(bool state) { bool is_sftp= QUrl(srv_server_location_edit->text()).scheme().toLower()=="sftp"; if(state&&is_sftp) { srv_server_password_label->setText(tr("Identity Passphrase")+":"); } else { srv_server_password_label->setText(tr("Password")+":"); } srv_use_identity_check->setEnabled(is_sftp); srv_use_identity_label->setEnabled(is_sftp); srv_identity_label->setEnabled(state&&is_sftp); srv_identity_edit->setEnabled(state&&is_sftp); srv_identity_button->setEnabled(state&&is_sftp); } void ServerDialog::selectIdentityFile() { QString filename=srv_identity_edit->text(); if(filename.isEmpty()) { filename=srv_identity_path; } filename=QFileDialog::getOpenFileName(this,"GlassGui - "+ tr("Choose SSH Identity"), filename); if(!filename.isNull()) { srv_identity_edit->setText(filename); } } GlassCoder-2.0.1/src/common/serverdialog.h000066400000000000000000000054531425562563600205030ustar00rootroot00000000000000// serverdialog.h // // Configuration dialog for server settings // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef SERVERDIALOG_H #define SERVERDIALOG_H #include #include #include #include #include #include #include #include #include #include #include "combobox.h" #include "connector.h" #include "profile.h" class ServerDialog : public QDialog { Q_OBJECT; public: ServerDialog(QDir *temp_dir,const QString &caption,QWidget *parent); QSize sizeHint() const; bool makeArgs(QStringList *args,bool escape_args); bool writeCredentials() const; void setControlsLocked(bool state); QString credentialsFilename() const; void load(Profile *p); void save(FILE *f); signals: void typeChanged(Connector::ServerType type); void settingsChanged(); protected: void resizeEvent(QResizeEvent *e); private slots: void serverTypeChanged(int index); void locationChanged(const QString &str); void useIdentityChanged(bool state); void selectIdentityFile(); private: QLabel *srv_server_label; QLabel *srv_server_type_label; ComboBox *srv_server_type_box; QCheckBox *srv_verbose_check; QLabel *srv_verbose_label; QLabel *srv_server_location_label; QLineEdit *srv_server_location_edit; QLabel *srv_server_username_label; QLineEdit *srv_server_username_edit; QLabel *srv_server_password_label; QLineEdit *srv_server_password_edit; QCheckBox *srv_use_identity_check; QLabel *srv_use_identity_label; QLabel *srv_identity_label; QLineEdit *srv_identity_edit; QPushButton *srv_identity_button; QLabel *srv_server_script_up_label; QLineEdit *srv_server_script_up_edit; QLabel *srv_server_script_down_label; QLineEdit *srv_server_script_down_edit; QLabel *srv_server_metadata_port_label; QSpinBox *srv_server_metadata_port_spin; QLabel *srv_server_maxconns_label; QSpinBox *srv_server_maxconns_spin; QCheckBox *srv_cleanup_check; QLabel *srv_cleanup_label; QPushButton *srv_close_button; QDir *srv_temp_dir; QString srv_identity_path; QString srv_caption; }; #endif // SERVERDIALOG_H GlassCoder-2.0.1/src/common/sourcedialog.cpp000066400000000000000000000321741425562563600210300ustar00rootroot00000000000000// sourcedialog.cpp // // Configuration dialog for source settings // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "asihpi.h" #include "sourcedialog.h" SourceDialog::SourceDialog(const QString &caption,QWidget *parent) : QDialog(parent) { gui_caption=caption; // // Fonts // QFont label_font("helvetica",14,QFont::Bold); label_font.setPixelSize(14); setWindowTitle(gui_caption+" - "+tr("Audio Sources")); // // Source Type // gui_source_type_label=new QLabel(tr("Type")+":",this); gui_source_type_label->setFont(label_font); gui_source_type_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_source_type_box=new ComboBox(this); connect(gui_source_type_box,SIGNAL(activated(int)), this,SLOT(sourceTypeChanged(int))); // // ALSA Fields // gui_alsa_device_label=new QLabel(tr("ALSA Device")+":",this); gui_alsa_device_label->setFont(label_font); gui_alsa_device_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_alsa_device_label->hide(); gui_alsa_device_edit=new QLineEdit(this); connect(gui_alsa_device_edit,SIGNAL(textEdited(const QString &)), this,SLOT(checkArgs(const QString &))); gui_alsa_device_edit->hide(); // // FILE Fields // gui_file_name_label=new QLabel(tr("Filename")+":",this); gui_file_name_label->setFont(label_font); gui_file_name_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_file_name_label->hide(); gui_file_name_edit=new QLineEdit(this); connect(gui_file_name_edit,SIGNAL(textEdited(const QString &)), this,SLOT(checkArgs(const QString &))); gui_file_name_edit->hide(); gui_file_select_button=new QPushButton(tr("Select"),this); connect(gui_file_select_button,SIGNAL(clicked()),this,SLOT(fileSelectName())); gui_file_select_button->hide(); // // HPI Fields // gui_asihpi_widget=new HpiWidget(this); gui_asihpi_widget->hide(); // // JACK Fields // gui_jack_server_name_label=new QLabel(tr("JACK Server Name")+":",this); gui_jack_server_name_label->setFont(label_font); gui_jack_server_name_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_jack_server_name_label->hide(); gui_jack_server_name_edit=new QLineEdit(this); gui_jack_server_name_edit->hide(); gui_jack_client_name_label=new QLabel(tr("JACK Client Name")+":",this); gui_jack_client_name_label->setFont(label_font); gui_jack_client_name_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_jack_client_name_label->hide(); gui_jack_client_name_edit=new QLineEdit(this); gui_jack_client_name_edit->hide(); gui_jack_gain_label=new QLabel(tr("Gain")+":",this); gui_jack_gain_label->setFont(label_font); gui_jack_gain_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_jack_gain_label->hide(); gui_jack_gain_spin=new QSpinBox(this); gui_jack_gain_spin->setRange(-100,100); gui_jack_gain_spin->hide(); // // Close Button // gui_close_button=new QPushButton(tr("Close"),this); gui_close_button->setFont(label_font); connect(gui_close_button,SIGNAL(clicked()),this,SLOT(hide())); } QSize SourceDialog::sizeHint() const { QSize ret=QSize(500,150); switch((AudioDevice::DeviceType) gui_source_type_box->itemData(gui_source_type_box->currentIndex()).toInt()) { break; case AudioDevice::AsiHpi: ret=QSize(500,320); break; case AudioDevice::Alsa: case AudioDevice::File: ret=QSize(500,150); break; case AudioDevice::Jack: ret=QSize(500,175); break; case AudioDevice::LastType: break; } return ret; } bool SourceDialog::makeArgs(QStringList *args,bool escape_args) { QString quote=""; if(escape_args) { quote="\""; } AudioDevice::DeviceType type=(AudioDevice::DeviceType) gui_source_type_box->itemData(gui_source_type_box->currentIndex()).toInt(); args->push_back("--audio-device="+AudioDevice::optionKeyword(type)); switch(type) { case AudioDevice::Alsa: if(!gui_alsa_device_edit->text().isEmpty()) { args->push_back("--alsa-device="+gui_alsa_device_edit->text()); } break; case AudioDevice::AsiHpi: #ifdef ASIHPI if((gui_asihpi_widget->selectedAdapterIndex()==0)|| (gui_asihpi_widget->selectedInputIndex()==0)) { return false; } args->push_back("--asihpi-adapter-index="+ QString().sprintf("%u",gui_asihpi_widget->selectedAdapterIndex())); args->push_back("--asihpi-input-index="+ QString().sprintf("%u",gui_asihpi_widget->selectedInputIndex())); args->push_back("--asihpi-input-gain="+ QString().sprintf("%d",gui_asihpi_widget->inputGain()/100)); args->push_back("--asihpi-input-source="+ AsihpiSourceName(gui_asihpi_widget->inputSource())); if(!AsihpiSourceName(gui_asihpi_widget->inputType()).isEmpty()) { args->push_back("--asihpi-input-type="+ AsihpiSourceName(gui_asihpi_widget->inputType())); } switch(gui_asihpi_widget->channelMode()) { case HPI_CHANNEL_MODE_NORMAL: args->push_back("--asihpi-channel-mode=NORMAL"); break; case HPI_CHANNEL_MODE_SWAP: args->push_back("--asihpi-channel-mode=SWAP"); break; case HPI_CHANNEL_MODE_LEFT_TO_STEREO: args->push_back("--asihpi-channel-mode=LEFT"); break; case HPI_CHANNEL_MODE_RIGHT_TO_STEREO: args->push_back("--asihpi-channel-mode=RIGHT"); break; } #endif // ASIHPI break; case AudioDevice::File: if(gui_file_name_edit->text().isEmpty()) { return false; } args->push_back("--file-name="+quote+gui_file_name_edit->text()+quote); break; case AudioDevice::Jack: if(!gui_jack_server_name_edit->text().isEmpty()) { args->push_back("--jack-server-name="+quote+ gui_jack_server_name_edit->text()+quote); } if(!gui_jack_client_name_edit->text().isEmpty()) { args->push_back("--jack-client-name="+quote+ gui_jack_client_name_edit->text()+quote); } args->push_back(QString().sprintf("--jack-gain=%d", gui_jack_gain_spin->value())); break; case AudioDevice::LastType: break; } return true; } void SourceDialog::setControlsLocked(bool state) { gui_source_type_box->setReadOnly(state); gui_jack_server_name_edit->setReadOnly(state); gui_jack_client_name_edit->setReadOnly(state); if(state) { gui_jack_gain_spin->setRange(gui_jack_gain_spin->value(), gui_jack_gain_spin->value()); } else { gui_jack_gain_spin->setRange(-100,100); } gui_file_name_edit->setReadOnly(state); gui_file_select_button->setDisabled(state); gui_asihpi_widget->setReadOnly(state); gui_alsa_device_edit->setReadOnly(state); } void SourceDialog::addSourceTypes(const QString &types) { QStringList f0; f0=QString(types).split("\n"); for(int i=0;i insertItem(-1,AudioDevice::deviceTypeText((AudioDevice::DeviceType)j), j); } } } } void SourceDialog::load(Profile *p) { gui_source_type_box-> setCurrentItemData(AudioDevice::deviceType(p->stringValue("GlassGui", "AudioDevice"))); sourceTypeChanged(gui_source_type_box->currentIndex()); #ifdef ALSA gui_alsa_device_edit->setText(p->stringValue("GlassGui","AlsaDevice")); #endif // ALSA #ifdef ASIHPI gui_asihpi_widget->setSelected(p->intValue("GlassGui","AsihpiAdapterIndex"), p->intValue("GlassGui","AsihpiInputIndex")); gui_asihpi_widget-> setInputGain(100*p->intValue("GlassGui","AsihpiInputGain")); gui_asihpi_widget->setChannelMode(p->intValue("GlassGui","AsihpiChannelMode", HPI_CHANNEL_MODE_NORMAL)); gui_asihpi_widget->setInputSource(p->intValue("GlassGui","AsihpiInputSource", HPI_SOURCENODE_LINEIN)); gui_asihpi_widget->setInputType(p->intValue("GlassGui","AsihpiInputType", HPI_SOURCENODE_LINEIN)); #endif // ASIHPI #ifdef SNDFILE gui_file_name_edit->setText(p->stringValue("GlassGui","FileName")); #endif // SNDFILE #ifdef JACK gui_jack_server_name_edit-> setText(p->stringValue("GlassGui","JackServerName")); gui_jack_client_name_edit-> setText(p->stringValue("GlassGui","JackClientName")); gui_jack_gain_spin->setValue(p->intValue("GlassGui","JackGain")); #endif // JACK } void SourceDialog::save(FILE *f) { fprintf(f,"AudioDevice=%s\n", (const char *)AudioDevice::optionKeyword((AudioDevice::DeviceType) gui_source_type_box->currentItemData().toInt()).toUtf8()); fprintf(f,"AlsaDevice=%s\n", (const char *)gui_alsa_device_edit->text().toUtf8()); fprintf(f,"AsihpiAdapterIndex=%u\n", gui_asihpi_widget->selectedAdapterIndex()); fprintf(f,"AsihpiInputIndex=%u\n",gui_asihpi_widget->selectedInputIndex()); fprintf(f,"AsihpiInputGain=%d\n",gui_asihpi_widget->inputGain()/100); fprintf(f,"AsihpiChannelMode=%u\n",gui_asihpi_widget->channelMode()); fprintf(f,"AsihpiInputSource=%u\n",gui_asihpi_widget->inputSource()); fprintf(f,"AsihpiInputType=%u\n",gui_asihpi_widget->inputType()); fprintf(f,"FileName=%s\n", (const char *)gui_file_name_edit->text().toUtf8()); fprintf(f,"JackServerName=%s\n", (const char *)gui_jack_server_name_edit->text().toUtf8()); fprintf(f,"JackClientName=%s\n", (const char *)gui_jack_client_name_edit->text().toUtf8()); fprintf(f,"JackGain=%d\n",gui_jack_gain_spin->value()); } void SourceDialog::show() { ChangeSize(); QDialog::show(); } void SourceDialog::resizeEvent(QResizeEvent *e) { int ypos=10; gui_source_type_label->setGeometry(10,ypos,60,20); gui_source_type_box->setGeometry(75,ypos,350,24); ypos+=26; int ypos_base=ypos; // // ALSA Controls // ypos=ypos_base; gui_alsa_device_label->setGeometry(70,ypos,110,24); gui_alsa_device_edit->setGeometry(185,ypos,100,24); ypos+=26; // // FILE Controls // ypos=ypos_base; gui_file_select_button->setGeometry(size().width()-90,ypos+2,80,40); ypos+=10; gui_file_name_label->setGeometry(10,ypos,160,20); gui_file_name_edit->setGeometry(175,ypos,size().width()-275,24); ypos+=26; // // ASIHPI Controls // ypos=ypos_base; gui_asihpi_widget->setGeometry(75,ypos,400,220); // // JACK Controls // ypos=ypos_base; gui_jack_server_name_label->setGeometry(75,ypos,145,20); gui_jack_server_name_edit->setGeometry(225,ypos,size().width()-260,24); ypos+=26; gui_jack_client_name_label->setGeometry(75,ypos,145,20); gui_jack_client_name_edit->setGeometry(225,ypos,size().width()-260,24); ypos+=26; gui_jack_gain_label->setGeometry(75,ypos,145,20); gui_jack_gain_spin->setGeometry(225,ypos,60,24); ypos+=26; gui_close_button->setGeometry(size().width()-80,size().height()-50,70,40); } void SourceDialog::sourceTypeChanged(int n) { gui_alsa_device_label->hide(); gui_alsa_device_edit->hide(); gui_file_select_button->hide(); gui_file_name_label->hide(); gui_file_name_edit->hide(); gui_asihpi_widget->hide(); gui_jack_server_name_label->hide(); gui_jack_server_name_edit->hide(); gui_jack_client_name_label->hide(); gui_jack_client_name_edit->hide(); gui_jack_gain_label->hide(); gui_jack_gain_spin->hide(); AudioDevice::DeviceType type= (AudioDevice::DeviceType)gui_source_type_box->itemData(n).toInt(); switch(type) { case AudioDevice::Alsa: gui_alsa_device_label->show(); gui_alsa_device_edit->show(); break; case AudioDevice::AsiHpi: gui_asihpi_widget->show(); break; case AudioDevice::File: gui_file_select_button->show(); gui_file_name_label->show(); gui_file_name_edit->show(); break; case AudioDevice::Jack: gui_jack_server_name_label->show(); gui_jack_server_name_edit->show(); gui_jack_client_name_label->show(); gui_jack_client_name_edit->show(); gui_jack_gain_label->show(); gui_jack_gain_spin->show(); break; case AudioDevice::LastType: break; } ChangeSize(); } void SourceDialog::fileSelectName() { QString filename; if(getenv("HOME")!=NULL) { filename=getenv("HOME"); } if(!gui_file_name_edit->text().isEmpty()) { filename=gui_file_name_edit->text(); } filename=QFileDialog::getOpenFileName(this,"GlassGui - "+tr("Select File"), filename, tr("Audio Files")+" (*.aiff *.AIFF *.wav *.WAV);;All Files (*)"); if(!filename.isEmpty()) { gui_file_name_edit->setText(filename); } } void SourceDialog::checkArgs(const QString &str) { emit updated(); } void SourceDialog::ChangeSize() { QRect g=geometry(); setGeometry(g.x(),g.y(),sizeHint().width(),sizeHint().height()); /* setMinimumHeight(sizeHint().height()); setMaximumHeight(sizeHint().height()); setMinimumSize(sizeHint()); */ } GlassCoder-2.0.1/src/common/sourcedialog.h000066400000000000000000000043221425562563600204670ustar00rootroot00000000000000// sourcedialog.h // // Configuration dialog for source settings // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef SOURCEDIALOG_H #define SOURCEDIALOG_H #include #include #include #include #include #include #include #include #include "audiodevice.h" #include "combobox.h" #include "hpiwidget.h" #include "profile.h" class SourceDialog : public QDialog { Q_OBJECT; public: SourceDialog(const QString &caption,QWidget *parent); QSize sizeHint() const; bool makeArgs(QStringList *args,bool escape_args); void setControlsLocked(bool state); void addSourceTypes(const QString &types); void load(Profile *p); void save(FILE *f); public slots: void show(); signals: void updated(); protected: void resizeEvent(QResizeEvent *e); private slots: void sourceTypeChanged(int n); void fileSelectName(); void checkArgs(const QString &str); private: void ChangeSize(); QLabel *gui_source_type_label; ComboBox *gui_source_type_box; QLabel *gui_alsa_device_label; QLineEdit *gui_alsa_device_edit; QLabel *gui_file_name_label; QLineEdit *gui_file_name_edit; QPushButton *gui_file_select_button; HpiWidget *gui_asihpi_widget; QLabel *gui_jack_server_name_label; QLineEdit *gui_jack_server_name_edit; QLabel *gui_jack_client_name_label; QLineEdit *gui_jack_client_name_edit; QLabel *gui_jack_gain_label; QSpinBox *gui_jack_gain_spin; QPushButton *gui_close_button; QString gui_caption; }; #endif // SOURCEDIALOG_H GlassCoder-2.0.1/src/common/spinbox.cpp000066400000000000000000000023071425562563600200250ustar00rootroot00000000000000// spinbox.cpp // // SpinBox widget for GlassGui // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "spinbox.h" SpinBox::SpinBox(QWidget *parent) : QSpinBox(parent) { spin_read_only=false; } bool SpinBox::isReadOnly() const { return spin_read_only; } void SpinBox::setReadOnly(bool state) { if(state!=spin_read_only) { if(state) { spin_maximum=maximum(); spin_minimum=minimum(); setRange(value(),value()); } else { setRange(spin_minimum,spin_maximum); } spin_read_only=state; } } GlassCoder-2.0.1/src/common/spinbox.h000066400000000000000000000020621425562563600174700ustar00rootroot00000000000000// spinbox.h // // SpinBox widget for GlassGui // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef SPINBOX_H #define SPINBOX_H #include class SpinBox : public QSpinBox { Q_OBJECT; public: SpinBox(QWidget *parent=0); bool isReadOnly() const; void setReadOnly(bool state); private: bool spin_read_only; int spin_maximum; int spin_minimum; }; #endif // SPINBOX_H GlassCoder-2.0.1/src/common/statuswidget.cpp000066400000000000000000000040341425562563600210710ustar00rootroot00000000000000// statuswidget.cpp // // Connection status widget // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "logging.h" #include "statuswidget.h" StatusWidget::StatusWidget(QWidget *parent) : QLabel(parent) { setAlignment(Qt::AlignCenter); // // Create Stylesheets // stat_idle_style=""; stat_connected_style="background-color: green;color: lightGray"; stat_connecting_style="background-color: #BBBB00;color: black"; stat_disconnecting_style="background-color: #BBBB00;color: black"; stat_failed_style="background-color: red;color: lightGray"; setStatus(CONNECTION_IDLE); } int StatusWidget::status() const { return stat_status; } bool StatusWidget::setStatus(int status) { bool ret=false; switch(status) { case CONNECTION_IDLE: setText(tr("IDLE")); setStyleSheet(stat_idle_style); ret=true; break; case CONNECTION_PENDING: setText(tr("CONNECTING...")); setStyleSheet(stat_connecting_style); ret=true; break; case CONNECTION_STOPPING: setText(tr("STOPPING...")); setStyleSheet(stat_connecting_style); ret=true; break; case CONNECTION_OK: setText(tr("CONNECTED")); setStyleSheet(stat_connected_style); ret=true; break; case CONNECTION_FAILED: setText(tr("RECONNECTING...")); setStyleSheet(stat_failed_style); ret=true; break; } stat_status=status; return ret; } GlassCoder-2.0.1/src/common/statuswidget.h000066400000000000000000000023141425562563600205350ustar00rootroot00000000000000// statuswidget.h // // Connection status widget // // (C) Copyright 2015-2020 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef STATUSWIDGET_H #define STATUSWIDGET_H #include #include class StatusWidget : public QLabel { Q_OBJECT; public: StatusWidget(QWidget *parent=0); int status() const; bool setStatus(int status); private: int stat_status; QString stat_idle_style; QString stat_connecting_style; QString stat_connected_style; QString stat_disconnecting_style; QString stat_failed_style; }; #endif // STATUSWIDGET_H GlassCoder-2.0.1/src/common/stereometer.cpp000066400000000000000000000146721425562563600207110ustar00rootroot00000000000000// stereometer.cpp // // This implements a widget that represents a stereo audio level meter, // complete with labels and scale. // // (C) Copyright 2002-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License // version 2 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "segmeter.h" #include "stereometer.h" StereoMeter::StereoMeter(QWidget *parent) : QWidget(parent) { ref_level=0; clip_light_level=1600; clip_light_on=false; label_x=0; meter_label=QString(""); left_meter=new SegMeter(SegMeter::Right,this); left_meter->setGeometry(25,10,300,10); left_meter->setRange(-4600,-800); left_meter->setHighThreshold(-1600); left_meter->setClipThreshold(-1100); left_meter->setSegmentSize(5); left_meter->setSegmentGap(1); left_meter->setSolidBar(-10000); left_meter->setFloatingBar(-10000); right_meter=new SegMeter(SegMeter::Right,this); right_meter->setGeometry(25,40,300,10); right_meter->setRange(-4600,-800); right_meter->setHighThreshold(-1600); right_meter->setClipThreshold(-1100); right_meter->setSegmentSize(5); right_meter->setSegmentGap(1); right_meter->setSolidBar(-10000); right_meter->setFloatingBar(-10000); setFixedSize(335,60); // // Generate Fonts // meter_label_font=QFont("System",18,QFont::Bold); meter_label_font.setPixelSize(18); meter_scale_font=QFont("System",12,QFont::Bold); meter_scale_font.setPixelSize(12); // // Set Colors // QPalette p=palette(); p.setColor(QPalette::Window,Qt::black); setPalette(p); } QSize StereoMeter::sizeHint() const { if(meter_label==QString("")) { return QSize(335,60); } else { return QSize(335,80); } } QSizePolicy StereoMeter::sizePolicy() const { return QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); } void StereoMeter::setReference(int level) { ref_level=level; } void StereoMeter::setClipLight(int level) { clip_light_level=level-1600+ref_level; } void StereoMeter::setDarkLowColor(QColor color) { left_meter->setDarkLowColor(color); right_meter->setDarkLowColor(color); } void StereoMeter::setDarkHighColor(QColor color) { left_meter->setDarkHighColor(color); right_meter->setDarkHighColor(color); } void StereoMeter::setDarkClipColor(QColor color) { left_meter->setDarkClipColor(color); right_meter->setDarkClipColor(color); } void StereoMeter::setLowColor(QColor color) { left_meter->setLowColor(color); right_meter->setLowColor(color); } void StereoMeter::setHighColor(QColor color) { left_meter->setHighColor(color); right_meter->setHighColor(color); } void StereoMeter::setClipColor(QColor color) { left_meter->setClipColor(color); right_meter->setClipColor(color); } void StereoMeter::setHighThreshold(int level) { left_meter->setHighThreshold(level); right_meter->setHighThreshold(level); } void StereoMeter::setClipThreshold(int level) { left_meter->setClipThreshold(level); right_meter->setClipThreshold(level); } void StereoMeter::setLabel(QString label) { meter_label=label; if(meter_label!=QString("")) { QFont meter_font=QFont("System",18,QFont::Normal); meter_font.setPixelSize(18); QFontMetrics meter_metrics=QFontMetrics(meter_font); label_x=(335-meter_metrics.width(meter_label))/2; setFixedSize(335,80); } else { setFixedSize(335,60); } } SegMeter::Mode StereoMeter::mode() const { return left_meter->mode(); } void StereoMeter::setMode(SegMeter::Mode mode) { left_meter->setMode(mode); right_meter->setMode(mode); } void StereoMeter::setLeftSolidBar(int level) { left_meter->setSolidBar(level-ref_level); if((level>=0)&&(!clip_light_on)) { clip_light_on=true; emit clip(); update(); } } void StereoMeter::setRightSolidBar(int level) { right_meter->setSolidBar(level-ref_level); if((level>=clip_light_level)&&(!clip_light_on)) { clip_light_on=true; emit clip(); update(); } } void StereoMeter::setLeftFloatingBar(int level) { left_meter->setFloatingBar(level-ref_level); if((level>=clip_light_level)&&(!clip_light_on)) { clip_light_on=true; emit clip(); update(); } } void StereoMeter::setRightFloatingBar(int level) { right_meter->setFloatingBar(level-ref_level); if((level>=clip_light_level)&&(!clip_light_on)) { clip_light_on=true; emit clip(); update(); } } void StereoMeter::setLeftPeakBar(int level) { left_meter->setPeakBar(level-ref_level); if((level>=clip_light_level)&&(!clip_light_on)) { clip_light_on=true; emit clip(); update(); } } void StereoMeter::setRightPeakBar(int level) { right_meter->setPeakBar(level-ref_level); if((level>=clip_light_level)&&(!clip_light_on)) { clip_light_on=true; emit clip(); update(); } } void StereoMeter::resetClipLight() { clip_light_on=false; update(); } void StereoMeter::setSegmentSize(int size) { left_meter->setSegmentSize(size); right_meter->setSegmentSize(size); } void StereoMeter::setSegmentGap(int gap) { left_meter->setSegmentGap(gap); right_meter->setSegmentGap(gap); } void StereoMeter::paintEvent(QPaintEvent *paintEvent) { // // Setup // QPixmap pix(this->size()); QPainter *p=new QPainter(&pix); p->fillRect(0,0,width(),height(),Qt::black); p->setBrush(QColor(Qt::white)); p->setPen(QColor(Qt::white)); p->setFont(meter_scale_font); p->drawText(10,20,tr("L")); p->drawText(10,50,tr("R")); p->drawText(38,35,"-35"); p->drawText(77,35,"-30"); p->drawText(116,35,"-25"); p->drawText(153,35,"-20"); p->drawText(191,35,"-15"); p->drawText(229,35,"-10"); p->drawText(267,35,"-5"); p->drawText(317,35,"0"); if(meter_label!=QString("")) { p->setFont(meter_label_font); p->drawText(label_x,72,meter_label); } if(clip_light_on) { p->setFont(meter_scale_font); p->setPen(QColor(CLIP_LIGHT_COLOR)); p->drawText(274,35,tr("CLIP")); } p->end(); p->begin(this); p->drawPixmap(0,0,pix); p->end(); delete p; } GlassCoder-2.0.1/src/common/stereometer.h000066400000000000000000000041541425562563600203500ustar00rootroot00000000000000// stereometer.h // // A Stereo Audio Meter Widget // // (C) Copyright 2002-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License // version 2 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // #ifndef STEREOMETER_H #define STEREOMETER_H #include #include "segmeter.h" #define CLIP_LIGHT_COLOR Qt::red class StereoMeter : public QWidget { Q_OBJECT public: StereoMeter(QWidget *parent=0); QSize sizeHint() const; QSizePolicy sizePolicy() const; void setReference(int level); void setClipLight(int level); void setDarkLowColor(QColor color); void setDarkHighColor(QColor color); void setDarkClipColor(QColor color); void setLowColor(QColor color); void setHighColor(QColor color); void setClipColor(QColor color); void setHighThreshold(int level); void setClipThreshold(int level); void setSegmentSize(int size); void setSegmentGap(int gap); void setLabel(QString label); SegMeter::Mode mode() const; void setMode(SegMeter::Mode mode); public slots: void setLeftSolidBar(int level); void setRightSolidBar(int level); void setLeftFloatingBar(int level); void setRightFloatingBar(int level); void setLeftPeakBar(int level); void setRightPeakBar(int level); void resetClipLight(); signals: void clip(); protected: void paintEvent(QPaintEvent *); private: SegMeter *left_meter,*right_meter; int ref_level; int clip_light_level; bool clip_light_on; int label_x; QString meter_label; QFont meter_label_font; QFont meter_scale_font; }; #endif // STEREOMETER_H GlassCoder-2.0.1/src/common/streamdialog.cpp000066400000000000000000000324771425562563600210310ustar00rootroot00000000000000// streamdialog.cpp // // Configuration dialog for stream metadata settings // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "streamdialog.h" StreamDialog::StreamDialog(const QString &caption,QWidget *parent) : QDialog(parent) { gui_caption=caption; // // Fonts // QFont label_font("helvetica",14,QFont::Bold); label_font.setPixelSize(14); setWindowTitle(gui_caption+" - "+tr("Stream Metadata Settings")); // // Stream Name // gui_stream_name_label=new QLabel(tr("Name")+":",this); gui_stream_name_label->setFont(label_font); gui_stream_name_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_stream_name_edit=new QLineEdit(this); // // Stream Description // gui_stream_description_label=new QLabel(tr("Description")+":",this); gui_stream_description_label->setFont(label_font); gui_stream_description_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_stream_description_edit=new QLineEdit(this); // // Stream URL // gui_stream_url_label=new QLabel(tr("URL")+":",this); gui_stream_url_label->setFont(label_font); gui_stream_url_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_stream_url_edit=new QLineEdit(this); // // Stream Genre // gui_stream_genre_label=new QLabel(tr("Genre")+":",this); gui_stream_genre_label->setFont(label_font); gui_stream_genre_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_stream_genre_edit=new QLineEdit(this); // // Stream Icq // gui_stream_icq_label=new QLabel(tr("ICQ ID")+":",this); gui_stream_icq_label->setFont(label_font); gui_stream_icq_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_stream_icq_edit=new QLineEdit(this); // // Stream AOL Instant Messager ID // gui_stream_aim_label=new QLabel(tr("AOL IM ID")+":",this); gui_stream_aim_label->setFont(label_font); gui_stream_aim_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_stream_aim_edit=new QLineEdit(this); // // Internet Relay Chat ID // gui_stream_irc_label=new QLabel(tr("IRC ID")+":",this); gui_stream_irc_label->setFont(label_font); gui_stream_irc_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_stream_irc_edit=new QLineEdit(this); // // Timestamp Offset // gui_stream_timestamp_offset_label=new QLabel(tr("Timestamp Offset")+":",this); gui_stream_timestamp_offset_label->setFont(label_font); gui_stream_timestamp_offset_label-> setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_stream_timestamp_offset_spin=new SpinBox(this); gui_stream_timestamp_offset_spin->setRange(-300,300); gui_stream_timestamp_offset_unit=new QLabel(tr("seconds"),this); gui_stream_timestamp_offset_unit->setFont(label_font); gui_stream_timestamp_offset_unit-> setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Close Button // gui_close_button=new QPushButton(tr("Close"),this); gui_close_button->setFont(label_font); connect(gui_close_button,SIGNAL(clicked()),this,SLOT(hide())); setMinimumHeight(sizeHint().height()); setMaximumHeight(sizeHint().height()); setMinimumSize(sizeHint()); } QSize StreamDialog::sizeHint() const { return QSize(400,276); } void StreamDialog::makeArgs(QStringList *args,bool escape_args) { QString quote=""; if(escape_args) { quote="\""; } if(!gui_stream_name_edit->text().isEmpty()) { args->push_back("--stream-name="+quote+gui_stream_name_edit->text()+quote); } if(!gui_stream_description_edit->text().isEmpty()) { args->push_back("--stream-description="+quote+ gui_stream_description_edit->text()+quote); } if(!gui_stream_url_edit->text().isEmpty()) { args->push_back("--stream-url="+quote+gui_stream_url_edit->text()+quote); } if(!gui_stream_genre_edit->text().isEmpty()) { args->push_back("--stream-genre="+quote+gui_stream_genre_edit->text()+ quote); } if(!gui_stream_icq_edit->text().isEmpty()) { args->push_back("--stream-icq="+quote+gui_stream_icq_edit->text()+quote); } if(!gui_stream_aim_edit->text().isEmpty()) { args->push_back("--stream-aim="+quote+gui_stream_aim_edit->text()+quote); } if(!gui_stream_irc_edit->text().isEmpty()) { args->push_back("--stream-irc="+quote+gui_stream_irc_edit->text()+quote); } if(gui_stream_timestamp_offset_spin->value()!=0) { args->push_back("--stream-timestamp-offset="+ QString().sprintf("%d",gui_stream_timestamp_offset_spin->value())); } } void StreamDialog::setServerType(Connector::ServerType type) { switch(type) { case Connector::HlsServer: gui_stream_name_label->setEnabled(false); gui_stream_name_edit->setEnabled(false); gui_stream_description_label->setEnabled(false); gui_stream_description_edit->setEnabled(false); gui_stream_url_label->setEnabled(false); gui_stream_url_edit->setEnabled(false); gui_stream_genre_label->setEnabled(false); gui_stream_genre_edit->setEnabled(false); gui_stream_icq_label->setEnabled(false); gui_stream_icq_edit->setEnabled(false); gui_stream_aim_label->setEnabled(false); gui_stream_aim_edit->setEnabled(false); gui_stream_irc_label->setEnabled(false); gui_stream_irc_edit->setEnabled(false); gui_stream_timestamp_offset_label->setEnabled(true); gui_stream_timestamp_offset_spin->setEnabled(true); gui_stream_timestamp_offset_unit->setEnabled(true); break; case Connector::FileServer: case Connector::FileArchiveServer: gui_stream_name_label->setEnabled(false); gui_stream_name_edit->setEnabled(false); gui_stream_description_label->setEnabled(false); gui_stream_description_edit->setEnabled(false); gui_stream_url_label->setEnabled(false); gui_stream_url_edit->setEnabled(false); gui_stream_genre_label->setEnabled(false); gui_stream_genre_edit->setEnabled(false); gui_stream_icq_label->setEnabled(false); gui_stream_icq_edit->setEnabled(false); gui_stream_aim_label->setEnabled(false); gui_stream_aim_edit->setEnabled(false); gui_stream_irc_label->setEnabled(false); gui_stream_irc_edit->setEnabled(false); gui_stream_timestamp_offset_label->setEnabled(false); gui_stream_timestamp_offset_spin->setEnabled(false); gui_stream_timestamp_offset_unit->setEnabled(false); break; case Connector::Shoutcast1Server: gui_stream_name_label->setEnabled(true); gui_stream_name_edit->setEnabled(true); gui_stream_description_label->setEnabled(false); gui_stream_description_edit->setEnabled(false); gui_stream_url_label->setEnabled(true); gui_stream_url_edit->setEnabled(true); gui_stream_genre_label->setEnabled(true); gui_stream_genre_edit->setEnabled(true); gui_stream_icq_label->setEnabled(true); gui_stream_icq_edit->setEnabled(true); gui_stream_aim_label->setEnabled(true); gui_stream_aim_edit->setEnabled(true); gui_stream_irc_label->setEnabled(true); gui_stream_irc_edit->setEnabled(true); gui_stream_timestamp_offset_label->setEnabled(false); gui_stream_timestamp_offset_spin->setEnabled(false); gui_stream_timestamp_offset_unit->setEnabled(false); break; case Connector::Shoutcast2Server: gui_stream_name_label->setEnabled(true); gui_stream_name_edit->setEnabled(true); gui_stream_description_label->setEnabled(false); gui_stream_description_edit->setEnabled(false); gui_stream_url_label->setEnabled(false); gui_stream_url_edit->setEnabled(false); gui_stream_genre_label->setEnabled(true); gui_stream_genre_edit->setEnabled(true); gui_stream_icq_label->setEnabled(true); gui_stream_icq_edit->setEnabled(true); gui_stream_aim_label->setEnabled(true); gui_stream_aim_edit->setEnabled(true); gui_stream_irc_label->setEnabled(true); gui_stream_irc_edit->setEnabled(true); gui_stream_timestamp_offset_label->setEnabled(false); gui_stream_timestamp_offset_spin->setEnabled(false); gui_stream_timestamp_offset_unit->setEnabled(false); break; case Connector::Icecast2Server: case Connector::IcecastOutServer: gui_stream_name_label->setEnabled(true); gui_stream_name_edit->setEnabled(true); gui_stream_description_label->setEnabled(true); gui_stream_description_edit->setEnabled(true); gui_stream_url_label->setEnabled(true); gui_stream_url_edit->setEnabled(true); gui_stream_genre_label->setEnabled(true); gui_stream_genre_edit->setEnabled(true); gui_stream_icq_label->setEnabled(false); gui_stream_icq_edit->setEnabled(false); gui_stream_aim_label->setEnabled(false); gui_stream_aim_edit->setEnabled(false); gui_stream_irc_label->setEnabled(false); gui_stream_irc_edit->setEnabled(false); gui_stream_timestamp_offset_label->setEnabled(false); gui_stream_timestamp_offset_spin->setEnabled(false); gui_stream_timestamp_offset_unit->setEnabled(false); break; case Connector::IcecastStreamerServer: gui_stream_name_label->setEnabled(true); gui_stream_name_edit->setEnabled(true); gui_stream_description_label->setEnabled(true); gui_stream_description_edit->setEnabled(true); gui_stream_url_label->setEnabled(true); gui_stream_url_edit->setEnabled(true); gui_stream_genre_label->setEnabled(true); gui_stream_genre_edit->setEnabled(true); gui_stream_icq_label->setEnabled(false); gui_stream_icq_edit->setEnabled(false); gui_stream_aim_label->setEnabled(false); gui_stream_aim_edit->setEnabled(false); gui_stream_irc_label->setEnabled(false); gui_stream_irc_edit->setEnabled(false); gui_stream_timestamp_offset_label->setEnabled(false); gui_stream_timestamp_offset_spin->setEnabled(false); gui_stream_timestamp_offset_unit->setEnabled(false); break; case Connector::LastServer: break; } } void StreamDialog::setControlsLocked(bool state) { gui_stream_name_edit->setReadOnly(state); gui_stream_description_edit->setReadOnly(state); gui_stream_url_edit->setReadOnly(state); gui_stream_genre_edit->setReadOnly(state); gui_stream_icq_edit->setReadOnly(state); gui_stream_aim_edit->setReadOnly(state); gui_stream_irc_edit->setReadOnly(state); gui_stream_timestamp_offset_spin->setReadOnly(state); } void StreamDialog::load(Profile *p) { gui_stream_name_edit->setText(p->stringValue("GlassGui","StreamName")); gui_stream_description_edit-> setText(p->stringValue("GlassGui","StreamDescription")); gui_stream_url_edit->setText(p->stringValue("GlassGui","StreamUrl")); gui_stream_genre_edit->setText(p->stringValue("GlassGui","StreamGenre")); gui_stream_icq_edit->setText(p->stringValue("GlassGui","StreamIcq")); gui_stream_aim_edit->setText(p->stringValue("GlassGui","StreamAim")); gui_stream_irc_edit->setText(p->stringValue("GlassGui","StreamIrc")); gui_stream_timestamp_offset_spin-> setValue(p->intValue("GlassGui","StreamTimestampOffset")); } void StreamDialog::save(FILE *f) { fprintf(f,"StreamName=%s\n", (const char *)gui_stream_name_edit->text().toUtf8()); fprintf(f,"StreamDescription=%s\n", (const char *)gui_stream_description_edit->text().toUtf8()); fprintf(f,"StreamUrl=%s\n", (const char *)gui_stream_url_edit->text().toUtf8()); fprintf(f,"StreamGenre=%s\n", (const char *)gui_stream_genre_edit->text().toUtf8()); fprintf(f,"StreamIcq=%s\n", (const char *)gui_stream_icq_edit->text().toUtf8()); fprintf(f,"StreamAim=%s\n", (const char *)gui_stream_aim_edit->text().toUtf8()); fprintf(f,"StreamIrc=%s\n", (const char *)gui_stream_irc_edit->text().toUtf8()); fprintf(f,"StreamTimestampOffset=%d\n", gui_stream_timestamp_offset_spin->value()); } void StreamDialog::resizeEvent(QResizeEvent *e) { int ypos=10; gui_stream_name_label->setGeometry(10,ypos,110,24); gui_stream_name_edit->setGeometry(125,ypos,size().width()-145,24); ypos+=26; gui_stream_description_label->setGeometry(10,ypos,110,24); gui_stream_description_edit->setGeometry(125,ypos,size().width()-145,24); ypos+=26; gui_stream_url_label->setGeometry(10,ypos,110,24); gui_stream_url_edit->setGeometry(125,ypos,size().width()-145,24); ypos+=26; gui_stream_genre_label->setGeometry(10,ypos,110,24); gui_stream_genre_edit->setGeometry(125,ypos,size().width()-145,24); ypos+=26; gui_stream_icq_label->setGeometry(10,ypos,110,24); gui_stream_icq_edit->setGeometry(125,ypos,size().width()-145,24); ypos+=26; gui_stream_aim_label->setGeometry(10,ypos,110,24); gui_stream_aim_edit->setGeometry(125,ypos,size().width()-145,24); ypos+=26; gui_stream_irc_label->setGeometry(10,ypos,110,24); gui_stream_irc_edit->setGeometry(125,ypos,size().width()-145,24); ypos+=26; gui_stream_timestamp_offset_label->setGeometry(10,ypos,160,24); gui_stream_timestamp_offset_spin->setGeometry(175,ypos,60,24); gui_stream_timestamp_offset_unit->setGeometry(240,ypos,60,24); ypos+=35; gui_close_button->setGeometry(size().width()-80,size().height()-50,70,40); } GlassCoder-2.0.1/src/common/streamdialog.h000066400000000000000000000042101425562563600204560ustar00rootroot00000000000000// streamdialog.h // // Configuration dialog for stream metadata settings // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef STREAMDIALOG_H #define STREAMDIALOG_H #include #include #include #include #include #include #include #include "connector.h" #include "profile.h" #include "spinbox.h" class StreamDialog : public QDialog { Q_OBJECT; public: StreamDialog(const QString &caption,QWidget *parent); QSize sizeHint() const; void makeArgs(QStringList *args,bool escape_args); void setServerType(Connector::ServerType type); void setControlsLocked(bool state); void load(Profile *p); void save(FILE *f); protected: void resizeEvent(QResizeEvent *e); private: QLabel *gui_stream_label; QLabel *gui_stream_name_label; QLineEdit *gui_stream_name_edit; QLabel *gui_stream_description_label; QLineEdit *gui_stream_description_edit; QLabel *gui_stream_url_label; QLineEdit *gui_stream_url_edit; QLabel *gui_stream_genre_label; QLineEdit *gui_stream_genre_edit; QLabel *gui_stream_icq_label; QLineEdit *gui_stream_icq_edit; QLabel *gui_stream_aim_label; QLineEdit *gui_stream_aim_edit; QLabel *gui_stream_irc_label; QLineEdit *gui_stream_irc_edit; QPushButton *gui_close_button; QLabel *gui_stream_timestamp_offset_label; SpinBox *gui_stream_timestamp_offset_spin; QLabel *gui_stream_timestamp_offset_unit; QString gui_caption; }; #endif // STREAMDIALOG_H GlassCoder-2.0.1/src/glasscoder/000077500000000000000000000000001425562563600164735ustar00rootroot00000000000000GlassCoder-2.0.1/src/glasscoder/Makefile.am000066400000000000000000000150241425562563600205310ustar00rootroot00000000000000## automake.am ## ## Makefile for the glasscoder(1) Audio Encoder. ## ## (C) Copyright 2014-2022 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Use automake to process this into a Makefile.in AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -Wno-strict-aliasing -std=c++11 -fPIC @QT5_CLI_CFLAGS@ @LIBCURL_CFLAGS@ @SNDFILE_CFLAGS@ @TAGLIB_CFLAGS@ @OPENSSL_CFLAGS@ @ALSA_CFLAGS@ MOC = @QT5_MOC@ # The dependency for qt's Meta Object Compiler (moc) moc_%.cpp: %.h @MOC@ $< -o $@ bin_PROGRAMS = glasscoder\ glassconv dist_glasscoder_SOURCES = alsadevice.cpp alsadevice.h\ asihpidevice.cpp asihpidevice.h\ audiodevice.cpp audiodevice.h\ audiodevicefactory.cpp audiodevicefactory.h\ codec.cpp codec.h\ codecfactory.cpp codecfactory.h\ config.cpp config.h\ connector.cpp connector.h\ connectorfactory.cpp connectorfactory.h\ fdkcodec.cpp fdkcodec.h\ fileconnector.cpp fileconnector.h\ filearchiveconnector.cpp filearchiveconnector.h\ filedevice.cpp filedevice.h\ getconveyor.cpp getconveyor.h\ glasscoder.cpp glasscoder.h\ hlsconnector.cpp hlsconnector.h\ httpconnection.cpp httpconnection.h\ httpserver.cpp httpserver.h\ httpuser.cpp httpuser.h\ iceconnector.cpp iceconnector.h\ iceoutconnector.cpp iceoutconnector.h\ icestreamconnector.cpp icestreamconnector.h\ icyconnector.cpp icyconnector.h\ jackdevice.cpp jackdevice.h\ metaserver.cpp metaserver.h\ meteraverage.cpp meteraverage.h\ mpegl2codec.cpp mpegl2codec.h\ mpegl3codec.cpp mpegl3codec.h\ netconveyor.cpp netconveyor.h\ pcm16codec.cpp pcm16codec.h\ opuscodec.cpp opuscodec.h\ profile.cpp profile.h\ socketmessage.cpp socketmessage.h\ socketserver.cpp socketserver.h\ vorbiscodec.cpp vorbiscodec.h nodist_glasscoder_SOURCES = asihpi.cpp asihpi.h\ cmdswitch.cpp cmdswitch.h\ glasslimits.h\ logging.cpp logging.h\ metaevent.cpp metaevent.h\ moc_alsadevice.cpp\ moc_asihpidevice.cpp\ moc_audiodevice.cpp\ moc_codec.cpp\ moc_connector.cpp\ moc_fdkcodec.cpp\ moc_fileconnector.cpp\ moc_filearchiveconnector.cpp\ moc_filedevice.cpp\ moc_getconveyor.cpp\ moc_glasscoder.cpp\ moc_hlsconnector.cpp\ moc_httpconnection.cpp\ moc_httpserver.cpp\ moc_iceconnector.cpp\ moc_iceoutconnector.cpp\ moc_icestreamconnector.cpp\ moc_icyconnector.cpp\ moc_jackdevice.cpp\ moc_metaserver.cpp\ moc_mpegl2codec.cpp\ moc_mpegl3codec.cpp\ moc_netconveyor.cpp\ moc_opuscodec.cpp\ moc_pcm16codec.cpp\ moc_socketserver.cpp\ moc_vorbiscodec.cpp\ paths.h\ ringbuffer.cpp ringbuffer.h glasscoder_LDADD = @LIBJACK@ @LIBCURL_LIBS@ @SNDFILE_LIBS@ @TAGLIB_LIBS@ @OPENSSL_LIBS@ @ALSA_LIBS@ @ASIHPI_LIBS@ @QT5_CLI_LIBS@ -lsamplerate -ldl -lpthread #glasscoder_LDFLAGS = dist_glassconv_SOURCES = glassconv.cpp glassconv.h nodist_glassconv_SOURCES = cmdswitch.cpp cmdswitch.h\ logging.cpp logging.h\ moc_glassconv.cpp\ profile.cpp profile.h glassconv_LDADD = @LIBCURL_LIBS@ @QT5_CLI_LIBS@ -ldl -lpthread CLEANFILES = *~\ moc_*\ *.obj\ *.idb\ *.pdb\ *ilk DISTCLEANFILES = asihpi.cpp asihpi.h\ audiodevice.cpp audiodevice.h\ cmdswitch.cpp cmdswitch.h\ codec.cpp codec.h\ codecdialog.cpp codecdialog.h\ codeviewer.cpp codeviewer.h\ combobox.cpp combobox.h\ connector.cpp connector.h\ glasslimits.h\ guiapplication.cpp guiapplication.h\ hpiinputlistview.cpp hpiinputlistview.h\ hpiwidget.cpp hpiwidget.h\ logging.cpp logging.h\ messagewidget.cpp messagewidget.h\ metaevent.cpp metaevent.h\ paths.h\ profile.cpp profile.h\ ringbuffer.cpp ringbuffer.h\ segmeter.cpp segmeter.h\ serverdialog.cpp serverdialog.h\ sourcedialog.cpp sourcedialog.h\ spinbox.cpp spinbox.h\ statuswidget.cpp statuswidget.h\ stereometer.cpp stereometer.h\ streamdialog.cpp streamdialog.h MAINTAINERCLEANFILES = *~\ Makefile.in GlassCoder-2.0.1/src/glasscoder/alsadevice.cpp000066400000000000000000000153261425562563600213060ustar00rootroot00000000000000// alsadevice.cpp // // Audio source for the Advanced Linux Sound Architecture // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "alsadevice.h" #include "glasslimits.h" #include "logging.h" void *AlsaCallback(void *ptr) { #ifdef ALSA static AlsaDevice *dev=(AlsaDevice *)ptr; static int n; static float pcm1[32768]; static float pcm2[32768]; static float lvls[MAX_AUDIO_CHANNELS]; static unsigned i; while(1==1) { n=snd_pcm_readi(dev->alsa_pcm,dev->alsa_pcm_buffer, dev->alsa_buffer_size/(dev->alsa_period_quantity*2)); if((snd_pcm_state(dev->alsa_pcm)!=SND_PCM_STATE_RUNNING)&&(n<0)) { snd_pcm_drop(dev->alsa_pcm); snd_pcm_prepare(dev->alsa_pcm); if(n==-EPIPE) { Log(LOG_NOTICE,"****** ALSA Capture Xrun ******"); } else { Log(LOG_WARNING,QString().sprintf("ALSA Error [%s]",snd_strerror(n))); } } else { if(dev->alsa_format!=AudioDevice::FLOAT) { dev->convertToFloat(pcm1,dev->alsa_pcm_buffer,dev->alsa_format,n, dev->alsa_channels); } if(dev->alsa_channels==dev->channels()) { dev->ringBuffer()->write(pcm1,n); dev->peakLevels(lvls,pcm1,n,dev->channels()); } else { dev->remixChannels(pcm2,dev->channels(),pcm1,dev->alsa_channels,n); dev->ringBuffer()->write(pcm2,n); dev->peakLevels(lvls,pcm2,n,dev->channels()); } for(i=0;ichannels();i++) { dev->alsa_meter_avg[i]->addValue(lvls[i]); } dev->setMeterLevels(lvls); } } #endif // ALSA return NULL; } AlsaDevice::AlsaDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent) : AudioDevice(chans,samprate,ring,parent) { #ifdef ALSA alsa_device=ALSA_DEFAULT_DEVICE; alsa_pcm_buffer=NULL; for(int i=0;istart(AUDIO_METER_INTERVAL); return true; #else return false; #endif // ALSA } unsigned AlsaDevice::deviceSamplerate() const { #ifdef ALSA return alsa_samplerate; #else return DEFAULT_AUDIO_SAMPLERATE; #endif // ALSA } void AlsaDevice::meterData() { float lvls[MAX_AUDIO_CHANNELS]; for(unsigned i=0;iaverage(); } setMeterLevels(lvls); } GlassCoder-2.0.1/src/glasscoder/alsadevice.h000066400000000000000000000035261425562563600207520ustar00rootroot00000000000000// alsadevice.h // // Audio source for the Advance Linux Sound Architecture // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef ALSADEVICE_H #define ALSADEVICE_H #ifdef ALSA #include #include #endif // ALSA #include #include "audiodevice.h" #include "meteraverage.h" #define ALSA_DEFAULT_DEVICE "hw:0" #define ALSA_PERIOD_QUANTITY 4 class AlsaDevice : public AudioDevice { Q_OBJECT; public: AlsaDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent=0); ~AlsaDevice(); bool processOptions(QString *err,const QStringList &keys, const QStringList &values); bool start(QString *err); unsigned deviceSamplerate() const; private slots: void meterData(); private: #ifdef ALSA QString alsa_device; snd_pcm_t *alsa_pcm; AudioDevice::Format alsa_format; unsigned alsa_samplerate; unsigned alsa_channels; unsigned alsa_period_quantity; snd_pcm_uframes_t alsa_buffer_size; float *alsa_pcm_buffer; pthread_t alsa_pthread; MeterAverage *alsa_meter_avg[MAX_AUDIO_CHANNELS]; QTimer *alsa_meter_timer; friend void *AlsaCallback(void *ptr); #endif // ALSA }; #endif // ALSADEVICE_H GlassCoder-2.0.1/src/glasscoder/asihpidevice.cpp000066400000000000000000000223111425562563600216330ustar00rootroot00000000000000// asihpidevice.cpp // // Audio source for AudioScience HPI devices // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include "asihpi.h" #include "asihpidevice.h" #include "logging.h" AsiHpiDevice::AsiHpiDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent) : AudioDevice(chans,samprate,ring,parent) { #ifdef ASIHPI struct hpi_format fmt; asihpi_adapter_index=ASIHPI_DEFAULT_INDEX; asihpi_input_index=ASIHPI_DEFAULT_INPUT_INDEX; asihpi_input_gain=0; asihpi_channel_mode=HPI_CHANNEL_MODE_NORMAL; asihpi_input_source=HPI_SOURCENODE_LINEIN; asihpi_input_type=HPI_SOURCENODE_LINEIN; asihpi_pcm_buffer=NULL; asihpi_dma_buffer_size=0; // // Calculate DMA Buffer Size // memset(&fmt,0,sizeof(fmt)); // Worst case situation fmt.dwSampleRate=48000; fmt.wChannels=2; fmt.wFormat=HPI_FORMAT_PCM32_FLOAT; if(HPI_StreamEstimateBufferSize(&fmt,ASIHPI_READ_INTERVAL,&asihpi_dma_buffer_size)!=0) { asihpi_dma_buffer_size=0; } asihpi_read_timer=new QTimer(this); connect(asihpi_read_timer,SIGNAL(timeout()),this,SLOT(readData())); asihpi_meter_timer=new QTimer(this); connect(asihpi_meter_timer,SIGNAL(timeout()),this,SLOT(meterData())); #endif // ASIHPI } AsiHpiDevice::~AsiHpiDevice() { #ifdef ASIHPI if(asihpi_pcm_buffer!=NULL) { delete asihpi_pcm_buffer; } delete asihpi_meter_timer; delete asihpi_read_timer; #endif // ASIHPI } bool AsiHpiDevice::isAvailable() const { #ifdef ASIHPI uint32_t index; uint16_t type; bool ret=(HPI_SubSysGetAdapter(NULL,0,&index,&type)==0); return ret; #else return false; #endif // ASIHPI } bool AsiHpiDevice::processOptions(QString *err,const QStringList &keys, const QStringList &values) { #ifdef ASIHPI for(int i=0;i=HPI_MAX_ADAPTERS)) { *err=tr("invalid")+" --asihpi-adapter-index "+tr("argument"); return false; } processed=true; } if(keys[i]=="--asihpi-input-index") { asihpi_input_index=values[i].toUInt(&ok)-1; if((!ok)||(asihpi_input_index>HPI_MAX_STREAMS)) { *err=tr("invalid")+" --asihpi-input-index "+tr("argument"); return false; } processed=true; } if(keys[i]=="--asihpi-input-gain") { asihpi_input_gain=values[i].toInt(&ok); if((!ok)||((asihpi_input_gain<-100)&&(asihpi_input_gain>20))) { *err=tr("invalid")+" --asihpi-input-gain "+tr("argument"); return false; } processed=true; } if(keys[i]=="--asihpi-channel-mode") { if(values[i].toLower()=="normal") { asihpi_channel_mode=HPI_CHANNEL_MODE_NORMAL; processed=true; } if(values[i].toLower()=="swap") { asihpi_channel_mode=HPI_CHANNEL_MODE_SWAP; processed=true; } if(values[i].toLower()=="left") { asihpi_channel_mode=HPI_CHANNEL_MODE_LEFT_TO_STEREO; processed=true; } if(values[i].toLower()=="right") { asihpi_channel_mode=HPI_CHANNEL_MODE_RIGHT_TO_STEREO; processed=true; } if(!processed) { *err=tr("invalid value for")+" --asihpi-channel-mode"; return false; } } if(keys[i]=="--asihpi-input-source") { if((asihpi_input_source=AsihpiSourceNode(values[i]))==0) { *err=tr("invalid value for --asihpi-input-source"); return false; } processed=true; } if(keys[i]=="--asihpi-input-type") { if((asihpi_input_type=AsihpiSourceNode(values[i]))==0) { *err=tr("invalid value for --asihpi-input-type"); return false; } processed=true; } if(!processed) { *err=tr("unrecognized option")+" "+keys[i]+"\""; return false; } } return true; #else return false; #endif // ASIHPI } bool AsiHpiDevice::start(QString *err) { #ifdef ASIHPI hpi_err_t herr; struct hpi_format fmt; uint16_t state=0; uint32_t buffer_size=0; uint32_t data_recorded=0; uint32_t samples_recorded=0; uint32_t aux_data_recorded=0; hpi_handle_t handle; short lvls[HPI_MAX_CHANNELS]; // // Open Mixer // if(HpiLog(HPI_MixerOpen(NULL,asihpi_adapter_index,&asihpi_mixer))==0) { // // Input Gain // if(HPI_MixerGetControl(NULL,asihpi_mixer, HPI_SOURCENODE_LINEIN,asihpi_input_index, HPI_DESTNODE_NONE,0,HPI_CONTROL_VOLUME, &handle)==0) { for(unsigned i=0;istart(100); } } // // Open Input Stream // if((herr=HPI_InStreamOpen(NULL,asihpi_adapter_index,asihpi_input_index,&asihpi_input_stream))!=0) { *err=tr("HPI error")+": "+hpi_strerror(herr); return false; } if(asihpi_dma_buffer_size>0) { if(HpiLog(HPI_InStreamHostBufferAllocate(NULL,asihpi_input_stream,asihpi_dma_buffer_size))!=0) { return false; } } // // Find Supported Format // MakeFormat(&fmt,HPI_FORMAT_PCM32_FLOAT); if((herr=HPI_InStreamQueryFormat(NULL,asihpi_input_stream,&fmt))!=0) { *err=tr("HPI error")+": "+hpi_strerror(herr); return false; } // // Set Format // if((herr=HPI_InStreamSetFormat(NULL,asihpi_input_stream,&fmt))!=0) { *err=tr("HPI error")+": "+hpi_strerror(herr); return false; } // // Start input stream // if((herr=HPI_InStreamStart(NULL,asihpi_input_stream))!=0) { *err=tr("HPI error")+": "+hpi_strerror(herr); return false; } // // Create PCM buffer // if((herr=HPI_InStreamGetInfoEx(NULL,asihpi_input_stream,&state,&buffer_size, &data_recorded,&samples_recorded, &aux_data_recorded))!=0) { *err=tr("HPI error")+": "+hpi_strerror(herr); return false; } asihpi_pcm_buffer=new uint8_t[buffer_size]; asihpi_read_timer->start(ASIHPI_READ_INTERVAL); return true; #else return false; #endif // ASIHPI } void AsiHpiDevice::readData() { #ifdef ASIHPI uint16_t state=0; uint32_t buffer_size=0; uint32_t data_recorded=0; uint32_t samples_recorded=0; uint32_t aux_data_recorded=0; HpiLog(HPI_InStreamGetInfoEx(NULL,asihpi_input_stream,&state,&buffer_size, &data_recorded,&samples_recorded, &aux_data_recorded)); if(state==HPI_STATE_RECORDING) { if(HpiLog(HPI_InStreamReadBuf(NULL,asihpi_input_stream,asihpi_pcm_buffer, data_recorded))==0) { ringBuffer()->write((float *)asihpi_pcm_buffer, data_recorded/(sizeof(float)*channels())); } } else { Log(LOG_WARNING,"not in recording state"+ QString().sprintf(" [state: %u]",state)); } #endif // ASIHPI } void AsiHpiDevice::meterData() { #ifdef ASIHPI short levels[HPI_MAX_CHANNELS]; int lvls[MAX_AUDIO_CHANNELS]; if(HpiLog(HPI_MeterGetRms(NULL,asihpi_input_meter,levels))==0) { for(unsigned i=0;idwSampleRate=samplerate(); fmt->wChannels=channels(); fmt->wFormat=hfmt; } #endif // ASIHPI #ifdef ASIHPI hpi_err_t AsiHpiDevice::HpiLog(hpi_err_t err,int priority) const { if(err!=0) { Log(priority, QString().sprintf("HPI error %d: \"%s\"",err,hpi_strerror(err))); } return err; } #endif // ASIHPI #ifdef ASIHPI const char *AsiHpiDevice::hpi_strerror(hpi_err_t err) const { static char err_text[200]; HPI_GetErrorText(err,err_text); return err_text; } #endif // ASIHPI GlassCoder-2.0.1/src/glasscoder/asihpidevice.h000066400000000000000000000042101425562563600212760ustar00rootroot00000000000000// asihpidevice.h // // Audio source for AudioScience HPI devices // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef ASIHPIDEVICE_H #define ASIHPIDEVICE_H #include #include #ifdef ASIHPI #include #endif // ASIHPI #include #include "audiodevice.h" #define ASIHPI_DEFAULT_INDEX 0 #define ASIHPI_DEFAULT_INPUT_INDEX 0 #define ASIHPI_READ_INTERVAL 10 class AsiHpiDevice : public AudioDevice { Q_OBJECT; public: AsiHpiDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent=0); ~AsiHpiDevice(); bool isAvailable() const; bool processOptions(QString *err,const QStringList &keys, const QStringList &values); bool start(QString *err); private slots: void readData(); void meterData(); private: #ifdef ASIHPI void MakeFormat(struct hpi_format *fmt,uint16_t hfmt); hpi_err_t HpiLog(hpi_err_t err,int priority=LOG_DEBUG) const; const char *hpi_strerror(hpi_err_t err) const; // // Arguments // uint16_t asihpi_adapter_index; uint16_t asihpi_input_index; uint16_t asihpi_input_mode; uint16_t asihpi_input_gain; uint16_t asihpi_channel_mode; uint16_t asihpi_input_source; uint16_t asihpi_input_type; hpi_handle_t asihpi_input_stream; hpi_handle_t asihpi_mixer; hpi_handle_t asihpi_input_meter; uint8_t *asihpi_pcm_buffer; QTimer *asihpi_read_timer; QTimer *asihpi_meter_timer; uint32_t asihpi_dma_buffer_size; #endif // ASIHPI }; #endif // ASIHPIDEVICE_H GlassCoder-2.0.1/src/glasscoder/audiodevicefactory.cpp000066400000000000000000000033321425562563600230510ustar00rootroot00000000000000// audiodevicefactory.cpp // // Instantiate AudioDevice classes // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "alsadevice.h" #include "asihpidevice.h" #include "filedevice.h" #include "jackdevice.h" #include "audiodevicefactory.h" AudioDevice *AudioDeviceFactory(AudioDevice::DeviceType type, unsigned chans,unsigned samprate, Ringbuffer *ring, QObject *parent) { AudioDevice *dev=NULL; switch(type) { case AudioDevice::Alsa: #ifdef ALSA dev=new AlsaDevice(chans,samprate,ring,parent); #endif // ALSA break; case AudioDevice::AsiHpi: #ifdef ASIHPI dev=new AsiHpiDevice(chans,samprate,ring,parent); #endif // ASIHPI break; case AudioDevice::File: #ifdef SNDFILE dev=new FileDevice(chans,samprate,ring,parent); #endif // SNDFILE break; case AudioDevice::Jack: #ifdef JACK dev=new JackDevice(chans,samprate,ring,parent); #endif // JACK break; case AudioDevice::LastType: break; } if(dev!=NULL) { if(!dev->isAvailable()) { delete dev; dev=NULL; } } return dev; } GlassCoder-2.0.1/src/glasscoder/audiodevicefactory.h000066400000000000000000000020351425562563600225150ustar00rootroot00000000000000// audiodevicefactory.h // // Instantiate AudioDevice classes // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef AUDIODEVICEFACTORY_H #define AUDIODEVICEFACTORY_H #include "audiodevice.h" AudioDevice *AudioDeviceFactory(AudioDevice::DeviceType type, unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent=0); #endif // AUDIODEVICEFACTORY_H GlassCoder-2.0.1/src/glasscoder/codecfactory.cpp000066400000000000000000000030341425562563600216440ustar00rootroot00000000000000// codecfactory.cpp // // Instantiate Codec classes // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "codecfactory.h" #include "fdkcodec.h" #include "mpegl2codec.h" #include "mpegl3codec.h" #include "opuscodec.h" #include "pcm16codec.h" #include "vorbiscodec.h" Codec *CodecFactory(Codec::Type type,Ringbuffer *ring,QObject *parent) { Codec *cdc=NULL; switch(type) { case Codec::TypeFdk: cdc=new FdkCodec(ring,parent); break; case Codec::TypeMpegL2: cdc=new MpegL2Codec(ring,parent); break; case Codec::TypeMpegL3: cdc=new MpegL3Codec(ring,parent); break; case Codec::TypeOpus: cdc=new OpusCodec(ring,parent); break; case Codec::TypePcm16: cdc=new Pcm16Codec(ring,parent); break; case Codec::TypeVorbis: cdc=new VorbisCodec(ring,parent); break; case Codec::TypeLast: break; } return cdc; } GlassCoder-2.0.1/src/glasscoder/codecfactory.h000066400000000000000000000016661425562563600213220ustar00rootroot00000000000000// codecfactory.h // // Instantiate Codec classes // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef CODECFACTORY_H #define CODECFACTORY_H #include "codec.h" Codec *CodecFactory(Codec::Type type,Ringbuffer *ring,QObject *parent=0); #endif // CODECFACTORY_H GlassCoder-2.0.1/src/glasscoder/config.cpp000066400000000000000000000335621425562563600204550ustar00rootroot00000000000000// config.cpp // // Configuration Class for glasscoder(1) // // (C) Copyright 2016-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include "audiodevicefactory.h" #include "codecfactory.h" #include "config.h" #include "logging.h" #include "profile.h" Config::Config() { unsigned num; bool ok=false; audio_atomic_frames=false; audio_bitrate=0; audio_channels=MAX_AUDIO_CHANNELS; audio_device=DEFAULT_AUDIO_DEVICE; audio_format=Codec::TypeVorbis; audio_quality=-1.0; audio_samplerate=DEFAULT_AUDIO_SAMPLERATE; server_exit_on_last=false; server_max_connections=-1; server_password=""; credentials_file=""; delete_credentials=false; ssh_identity=""; server_type=Connector::Icecast2Server; server_script_down=""; server_script_up=""; server_start_connections=0; server_pipe=""; server_start_connections=0; server_no_deletes=false; stream_aim=""; stream_genre=""; stream_icq=""; stream_irc=""; stream_name=""; stream_timestamp_offset=0; stream_url=""; list_codecs=false; list_devices=false; metadata_port=0; global_log_string=""; meter_data=false; dump_headers=false; show_verbose=false; server_user_agent=QString("GlassCoder/")+VERSION; CmdSwitch *cmd=new CmdSwitch("glasscoder",GLASSCODER_USAGE); for(unsigned i=0;ikeys();i++) { if(cmd->key(i)=="--audio-atomic-frames") { audio_atomic_frames=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--audio-bitrate") { num=cmd->value(i).toUInt(&ok); if(ok) { audio_bitrate=num; } else { Log(LOG_ERR,"invalid --audio-bitrate value"); exit(256); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--audio-channels") { audio_channels=cmd->value(i).toUInt(&ok); if((!ok)||(audio_channels==0)||(audio_channels>MAX_AUDIO_CHANNELS)) { Log(LOG_ERR,"invalid --audio-channels value"); exit(256); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--audio-device") { for(unsigned j=0;jvalue(i).toLower()== AudioDevice::optionKeyword((AudioDevice::DeviceType)j)) { audio_device=(AudioDevice::DeviceType)j; cmd->setProcessed(i,true); } } } if(cmd->key(i)=="--audio-format") { for(int j=0;jvalue(i).toLower()) { audio_format=(Codec::Type)j; cmd->setProcessed(i,true); } } if(!cmd->processed(i)) { Log(LOG_ERR, QString().sprintf("unknown --audio-format value \"%s\"", (const char *)cmd->value(i).toUtf8())); exit(256); } } if(cmd->key(i)=="--audio-quality") { audio_quality=cmd->value(i).toDouble(&ok); if((!ok)||(audio_quality<0.0)||(audio_quality>1.0)) { Log(LOG_ERR,"invalid --audio-quality value"); exit(256); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--audio-samplerate") { audio_samplerate=cmd->value(i).toUInt(&ok); if(!ok) { Log(LOG_ERR,"invalid --audio-samplerate value"); exit(256); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--credentials-file") { credentials_file=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--delete-credentials") { delete_credentials=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--ssh-identity") { ssh_identity=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--dump-headers") { dump_headers=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--errors-string") { global_log_string=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--errors-to") { if(cmd->value(i).toLower()=="stderr") { global_log_to=LOG_TO_STDERR; cmd->setProcessed(i,true); } if(cmd->value(i).toLower()=="syslog") { global_log_to=LOG_TO_SYSLOG; openlog("glasscoder",0,LOG_DAEMON); cmd->setProcessed(i,true); } if(cmd->value(i).toLower()=="stdout") { global_log_to=LOG_TO_STDOUT; cmd->setProcessed(i,true); } } if(cmd->key(i)=="--list-codecs") { list_codecs=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--list-devices") { list_devices=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--metadata-port") { metadata_port=cmd->value(i).toUInt(&ok); if((!ok)||(metadata_port>0xFFFF)) { Log(LOG_ERR,"invalid --metadata-port argument"); exit(256); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--meter-data") { meter_data=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-auth") { QStringList f0=cmd->value(i).split(":"); if(f0.size()==2) { server_username=f0[0]; server_password=f0[1]; } else { server_username=f0[0]; } cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-exit-on-last") { server_exit_on_last=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-max-connections") { server_max_connections=cmd->value(i).toInt(&ok); if((!ok)||(server_max_connections<0)) { Log(LOG_ERR,"invalid argument for --server-max-connections"); exit(256); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-url") { server_url=QUrl(cmd->value(i)); if(server_url.port()<0) { if(server_url.scheme().toLower()=="file") { server_url.setPort(0); } if(server_url.scheme().toLower()=="http") { server_url.setPort(80); } if(server_url.scheme().toLower()=="https") { server_url.setPort(443); } if(server_url.scheme().toLower()=="sftp") { server_url.setPort(22); } if(server_url.port()<0) { Log(LOG_ERR, "unknown/unsupported URL scheme \""+server_url.scheme()+"\""); exit(256); } } if(cmd->value(i).right(1)=="/") { server_base_url=cmd->value(i).left(cmd->value(i).length()-1); } else { QStringList f0=cmd->value(i).split("/",QString::KeepEmptyParts); f0.removeLast(); server_base_url=f0.join("/"); } cmd->setProcessed(i,true); if(!server_url.isValid()) { Log(LOG_ERR,"invalid argument for --server-url"); exit(256); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-user-agent") { server_user_agent=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-script-down") { server_script_down=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-script-up") { server_script_up=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-start-connections") { server_start_connections=cmd->value(i).toInt(&ok); if((!ok)||(server_start_connections<0)) { Log(LOG_ERR,"invalid --server-start-connections value \""+ cmd->value(i)+"\""); exit(256); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-type") { for(int j=0;jvalue(i).toLower()) { server_type=(Connector::ServerType)j; cmd->setProcessed(i,true); } } if(!cmd->processed(i)) { Log(LOG_ERR, QString().sprintf("unknown --server-type value \"%s\"", (const char *)cmd->value(i).toUtf8())); exit(256); } } if(cmd->key(i)=="--server-pipe") { server_pipe=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--server-no-deletes") { server_no_deletes=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--stream-description") { stream_description=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--stream-genre") { stream_genre=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--stream-name") { stream_name=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--stream-url") { stream_url=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--stream-irc") { stream_irc=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--stream-icq") { stream_icq=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--stream-aim") { stream_aim=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--stream-timestamp-offset") { stream_timestamp_offset=cmd->value(i).toInt(&ok); if(!ok) { fprintf(stderr,"invalid --stream-timestamp-offset\n"); exit(256); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--verbose") { global_log_verbose=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--verbose") { show_verbose=true; cmd->setProcessed(i,true); } if(!cmd->processed(i)) { device_keys.push_back(cmd->key(i)); device_values.push_back(cmd->value(i)); } } // // Read Credentials // if(!credentials_file.isEmpty()) { Profile *p=new Profile(); if(!p->setSource(credentials_file)) { Log(LOG_ERR,"credentials file not found"); exit(256); } server_username=p->stringValue("Credentials","Username"); server_password=p->stringValue("Credentials","Password"); delete p; if(delete_credentials) { unlink(credentials_file.toUtf8()); } } // // Sanity Checks // for(int i=0;i=0.0)&&(audio_bitrate>0)) { Log(LOG_ERR,"--audio-quality and --audio-bitrate are mutually exclusive"); exit(256); } if((audio_quality<0.0)&&(audio_bitrate==0)) { audio_bitrate=DEFAULT_AUDIO_BITRATE; } } bool Config::audioAtomicFrames() const { return audio_atomic_frames; } unsigned Config::audioBitrate() const { return audio_bitrate; } unsigned Config::audioChannels() const { return audio_channels; } AudioDevice::DeviceType Config::audioDevice() const { return audio_device; } Codec::Type Config::audioFormat() const { return audio_format; } unsigned Config::audioQuality() const { return audio_quality; } unsigned Config::audioSamplerate() const { return audio_samplerate; } bool Config::serverExitOnLast() const { return server_exit_on_last; } int Config::serverMaxConnections() const { return server_max_connections; } QString Config::serverPassword() const { return server_password; } QString Config::credentialsFile() const { return credentials_file; } bool Config::deleteCredentials() const { return delete_credentials; } QString Config::sshIdentity() const { return ssh_identity; } QString Config::serverScriptDown() const { return server_script_down; } QString Config::serverScriptUp() const { return server_script_up; } int Config::serverStartConnections() const { return server_start_connections; } Connector::ServerType Config::serverType() const { return server_type; } QUrl Config::serverUrl() const { return server_url; } QString Config::serverBaseUrl() const { return server_base_url; } QString Config::serverUserAgent() const { return server_user_agent; } QString Config::serverUsername() const { return server_username; } QString Config::serverPipe() const { return server_pipe; } bool Config::serverNoDeletes() const { return server_no_deletes; } QString Config::streamAim() const { return stream_aim; } QString Config::streamDescription() const { return stream_description; } QString Config::streamGenre() const { return stream_genre; } QString Config::streamIcq() const { return stream_icq; } QString Config::streamIrc() const { return stream_irc; } QString Config::streamName() const { return stream_name; } int Config::streamTimestampOffset() const { return stream_timestamp_offset; } QString Config::streamUrl() const { return stream_url; } QStringList Config::deviceKeys() const { return device_keys; } QStringList Config::deviceValues() const { return device_values; } bool Config::listCodecs() const { return list_codecs; } bool Config::listDevices() const { return list_devices; } unsigned Config::metadataPort() const { return metadata_port; } bool Config::dumpHeaders() const { return dump_headers; } bool Config::verbose() const { return show_verbose; } bool Config::meterData() const { return meter_data; } void Config::ListCodecs() const { for(int i=0;iisAvailable()) { printf("%s\n", (const char *)Codec::optionKeyword((Codec::Type)i).toUtf8()); } } } void Config::ListDevices() const { for(int i=0;i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef CONFIG_H #define CONFIG_H #include #include "audiodevice.h" #include "cmdswitch.h" #include "codec.h" #include "connector.h" #define GLASSCODER_CREDENTIALS "creds" #define GLASSCODER_USAGE "[options]\n" class Config { public: enum ExitCode {ExitOk=0,ExitRetry=1,ExitFatal=2}; Config(); bool audioAtomicFrames() const; unsigned audioBitrate() const; unsigned audioChannels() const; AudioDevice::DeviceType audioDevice() const; Codec::Type audioFormat() const; unsigned audioQuality() const; unsigned audioSamplerate() const; bool serverExitOnLast() const; int serverMaxConnections() const; QString serverPassword() const; QString credentialsFile() const; bool deleteCredentials() const; QString sshIdentity() const; QString serverScriptDown() const; QString serverScriptUp() const; int serverStartConnections() const; Connector::ServerType serverType() const; QUrl serverUrl() const; QString serverBaseUrl() const; QString serverUserAgent() const; QString serverUsername() const; QString serverPipe() const; bool serverNoDeletes() const; QString streamAim() const; QString streamDescription() const; QString streamGenre() const; QString streamIcq() const; QString streamIrc() const; QString streamName() const; int streamTimestampOffset() const; QString streamUrl() const; QStringList deviceKeys() const; QStringList deviceValues() const; bool listCodecs() const; bool listDevices() const; unsigned metadataPort() const; bool meterData() const; bool dumpHeaders() const; bool verbose() const; private: void ListCodecs() const; void ListDevices() const; // // Audio Arguments // bool audio_atomic_frames; unsigned audio_bitrate; unsigned audio_channels; AudioDevice::DeviceType audio_device; Codec::Type audio_format; double audio_quality; unsigned audio_samplerate; // // Server Arguments // bool server_exit_on_last; int server_max_connections; QString server_password; QString credentials_file; bool delete_credentials; QString ssh_identity; QString server_script_down; QString server_script_up; int server_start_connections; Connector::ServerType server_type; QUrl server_url; QString server_base_url; QString server_user_agent; QString server_username; QString server_pipe; bool server_no_deletes; // // Stream Arguments // QString stream_aim; QString stream_description; QString stream_genre; QString stream_icq; QString stream_irc; QString stream_name; int stream_timestamp_offset; QString stream_url; // // Device Arguments // QStringList device_keys; QStringList device_values; // // Miscellaneous Arguments // bool list_codecs; bool list_devices; unsigned metadata_port; bool meter_data; bool dump_headers; bool show_verbose; }; #endif // CONFIG_H GlassCoder-2.0.1/src/glasscoder/connectorfactory.cpp000066400000000000000000000035611425562563600225660ustar00rootroot00000000000000// connectorfactory.cpp // // Instantiate Connector classes. // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "filearchiveconnector.h" #include "fileconnector.h" #include "hlsconnector.h" #include "iceconnector.h" #include "iceoutconnector.h" #include "icestreamconnector.h" #include "icyconnector.h" #include "connectorfactory.h" Connector *ConnectorFactory(Connector::ServerType type,Config *conf, QObject *parent) { Connector *conn=NULL; switch(type) { case Connector::HlsServer: conn=new HlsConnector(conf,parent); break; case Connector::Shoutcast1Server: conn=new IcyConnector(1,parent); break; case Connector::Shoutcast2Server: conn=new IcyConnector(2,parent); break; case Connector::Icecast2Server: conn=new IceConnector(parent); break; case Connector::IcecastOutServer: conn=new IceOutConnector(parent); break; case Connector::IcecastStreamerServer: conn=new IceStreamConnector(parent); break; case Connector::FileServer: conn=new FileConnector(parent); break; case Connector::FileArchiveServer: conn=new FileArchiveConnector(parent); break; case Connector::LastServer: break; } return conn; } GlassCoder-2.0.1/src/glasscoder/connectorfactory.h000066400000000000000000000017701425562563600222330ustar00rootroot00000000000000// connectorfactory.h // // Instantiate Connector classes // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef CONNECTORFACTORY_H #define CONNECTORFACTORY_H #include "config.h" #include "connector.h" Connector *ConnectorFactory(Connector::ServerType type,Config *conf, QObject *parent=0); #endif // CONNECTORFACTORY_H GlassCoder-2.0.1/src/glasscoder/fdkcodec.cpp000066400000000000000000000127211425562563600207440ustar00rootroot00000000000000// fdkcodec.cpp // // Codec class for MPEG-4 Advanced Audio Coding High Efficiency Profile // // (C) Copyright 2014-2020 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "fdkcodec.h" #include "logging.h" FdkCodec::FdkCodec(Ringbuffer *ring,QObject *parent) : Codec(Codec::TypeFdk,ring,parent) { #ifdef HAVE_FDKAAC fdk_input_buffer=NULL; fdk_output_buffer=NULL; #endif // HAVE_FDKAAC } FdkCodec::~FdkCodec() { #ifdef HAVE_FDKAAC if(fdk_input_buffer!=NULL) { delete fdk_input_buffer; } if(fdk_output_buffer!=NULL) { delete fdk_output_buffer; } #endif // HAVE_FDKAAC } bool FdkCodec::isAvailable() const { #ifdef HAVE_FDKAAC return (dlopen("libfdk-aac.so.2",RTLD_LAZY)!=NULL)|| (dlopen("libfdk-aac.so.1",RTLD_LAZY)!=NULL); #else return false; #endif // HAVE_FDKAAC } QString FdkCodec::contentType() const { return "audio/aacp"; } unsigned FdkCodec::pcmFrames() const { #ifdef HAVE_FDKAAC return fdk_info.frameLength; #else return 0; #endif // HAVE_FDKAAC } QString FdkCodec::defaultExtension() const { return QString("aac"); } QString FdkCodec::formatIdentifier() const { // // From ISO/IEC 14496-3 // (see the summary at https://en.wikipedia.org/wiki/MPEG-4_Part_3) // return QString("mp4a.40.29"); } bool FdkCodec::startCodec() { #ifdef HAVE_FDKAAC AACENC_ERROR err; // // Sanity Checks // if(bitrate()==0) { Log(LOG_ERR,"VBR encoding not supported"); return false; } // // Load Library // if((fdk_handle=dlopen("libfdk-aac.so.2",RTLD_LAZY))==NULL) { fdk_handle=dlopen("libfdk-aac.so.1",RTLD_LAZY); } if(fdk_handle==NULL) { Log(LOG_ERR,"unsupported audio format (library not found)"); return false; } *(void **)(&aacEncOpen)=dlsym(fdk_handle,"aacEncOpen"); *(void **)(&aacEncClose)=dlsym(fdk_handle,"aacEncClose"); *(void **)(&aacEncoder_GetParam)=dlsym(fdk_handle,"aacEncoder_GetParam"); *(void **)(&aacEncoder_SetParam)=dlsym(fdk_handle,"aacEncoder_SetParam"); *(void **)(&aacEncEncode)=dlsym(fdk_handle,"aacEncEncode"); *(void **)(&aacEncInfo)=dlsym(fdk_handle,"aacEncInfo"); *(void **)(&aacEncGetLibInfo)=dlsym(fdk_handle,"aacEncGetLibInfo"); // // Initialize Encoder Instance // fdk_encoder=NULL; if((err=aacEncOpen(&fdk_encoder,0,0))!=AACENC_OK) { Log(LOG_ERR, QString().sprintf("unable to open encoder instance [0x%04X]",err)); return false; } fdk_input_buffer=new INT_PCM[2048*channels()]; fdk_input_ids[0]=IN_AUDIO_DATA; fdk_input_sizes[0]=2048*channels(); fdk_inputel_sizes[0]=sizeof(INT_PCM); memset(&fdk_input_desc,0,sizeof(fdk_input_desc)); fdk_input_desc.numBufs=1; fdk_input_desc.bufs=(void **)&fdk_input_buffer; fdk_input_desc.bufferIdentifiers=fdk_input_ids; fdk_input_desc.bufSizes=fdk_input_sizes; fdk_input_desc.bufElSizes=fdk_inputel_sizes; fdk_output_buffer=new UCHAR[6144*channels()]; fdk_output_ids[0]=OUT_BITSTREAM_DATA; fdk_output_sizes[0]=6144*channels(); fdk_outputel_sizes[0]=sizeof(UCHAR); memset(&fdk_output_desc,0,sizeof(fdk_output_desc)); fdk_output_desc.numBufs=1; fdk_output_desc.bufs=(void **)&fdk_output_buffer; fdk_output_desc.bufferIdentifiers=fdk_output_ids; fdk_output_desc.bufSizes=fdk_output_sizes; fdk_output_desc.bufElSizes=fdk_outputel_sizes; switch(channels()) { case 1: aacEncoder_SetParam(fdk_encoder,AACENC_AOT,5); // MPEG-4 HE-AAC aacEncoder_SetParam(fdk_encoder,AACENC_CHANNELMODE,MODE_1); break; case 2: aacEncoder_SetParam(fdk_encoder,AACENC_AOT,29); // MPEG-4 HE-AAC/PS aacEncoder_SetParam(fdk_encoder,AACENC_CHANNELMODE,MODE_2); break; default: Log(LOG_ERR,"unsupported channel count"); return false; } aacEncoder_SetParam(fdk_encoder,AACENC_BITRATE,1000*bitrate()); aacEncoder_SetParam(fdk_encoder,AACENC_SAMPLERATE,streamSamplerate()); aacEncoder_SetParam(fdk_encoder,AACENC_AFTERBURNER,1); aacEncoder_SetParam(fdk_encoder,AACENC_TRANSMUX,2); // ADTS bitstream if((err=aacEncEncode(fdk_encoder,NULL,NULL,NULL,NULL))!=AACENC_OK) { Log(LOG_ERR, QString().sprintf("unable to initialize encoder instance [0x%04X]",err)); return false; } aacEncInfo(fdk_encoder,&fdk_info); return true; #else Log(LOG_ERR,"unsupported audio format (no build support)"); return false; #endif // HAVE_FDKAAC } void FdkCodec::encodeData(Connector *conn,const float *pcm,int frames) { #ifdef HAVE_FDKAAC AACENC_InArgs inargs; AACENC_OutArgs outargs; AACENC_ERROR err; inargs.numInSamples=frames*channels(); inargs.numAncBytes=0; src_float_to_short_array(pcm,fdk_input_buffer,frames*channels()); if((err=aacEncEncode(fdk_encoder,&fdk_input_desc,&fdk_output_desc,&inargs,&outargs))== AACENC_OK) { conn->writeData(frames,fdk_output_buffer,outargs.numOutBytes); } else { Log(LOG_WARNING,QString().sprintf("fdk_aac encoding error %d",err)); } #endif // HAVE_FDKAAC } GlassCoder-2.0.1/src/glasscoder/fdkcodec.h000066400000000000000000000045401425562563600204110ustar00rootroot00000000000000// fdkcodec.h // // Codec class for MPEG-4 Advanced Audio Coding High Efficiency Profile v2 // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef FDKCODEC_H #define FDKCODEC_H #ifdef HAVE_FDKAAC #include #endif // HAVE_FDKAAC #include "codec.h" class FdkCodec : public Codec { Q_OBJECT; public: FdkCodec(Ringbuffer *ring,QObject *parent=0); ~FdkCodec(); bool isAvailable() const; QString contentType() const; unsigned pcmFrames() const; QString defaultExtension() const; QString formatIdentifier() const; bool startCodec(); protected: void encodeData(Connector *conn,const float *pcm,int frames); private: #ifdef HAVE_FDKAAC void *fdk_handle; HANDLE_AACENCODER fdk_encoder; AACENC_ERROR (*aacEncOpen)(HANDLE_AACENCODER *,const UINT,const UINT); AACENC_ERROR (*aacEncClose)(HANDLE_AACENCODER *); UINT (*aacEncoder_GetParam)(const HANDLE_AACENCODER,const AACENC_PARAM); AACENC_ERROR (*aacEncoder_SetParam)(const HANDLE_AACENCODER, const AACENC_PARAM,const UINT); AACENC_ERROR (*aacEncEncode)(const HANDLE_AACENCODER, const AACENC_BufDesc *,const AACENC_BufDesc *, const AACENC_InArgs *,AACENC_OutArgs *); AACENC_ERROR (*aacEncInfo)(const HANDLE_AACENCODER,AACENC_InfoStruct *); AACENC_ERROR (*aacEncGetLibInfo)(LIB_INFO *); unsigned long fdk_input_samples; AACENC_BufDesc fdk_input_desc; INT_PCM *fdk_input_buffer; INT fdk_input_ids[1]; INT fdk_input_sizes[1]; INT fdk_inputel_sizes[1]; AACENC_BufDesc fdk_output_desc; UCHAR *fdk_output_buffer; INT fdk_output_ids[1]; INT fdk_output_sizes[1]; INT fdk_outputel_sizes[1]; AACENC_InfoStruct fdk_info; #endif // HAVE_FDKAAC }; #endif // FDKCODEC_H GlassCoder-2.0.1/src/glasscoder/filearchiveconnector.cpp000066400000000000000000000112001425562563600233650ustar00rootroot00000000000000// filearchiveconnector.cpp // // Source connector class for local file archives // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include "filearchiveconnector.h" #include "logging.h" FileArchiveConnector::FileArchiveConnector(QObject *parent) : Connector(parent) { archive_fd=-1; archive_snd=NULL; archive_hour=-1; archive_rotate_timer=new QTimer(this); archive_rotate_timer->setSingleShot(true); connect(archive_rotate_timer,SIGNAL(timeout()),this,SLOT(rotateFile())); } FileArchiveConnector::~FileArchiveConnector() { delete archive_rotate_timer; if(archive_snd!=NULL) { sf_close(archive_snd); } if(archive_fd>=0) { close(archive_fd); } } FileArchiveConnector::ServerType FileArchiveConnector::serverType() const { return Connector::FileArchiveServer; } void FileArchiveConnector::rotateFile() { QDateTime now(QDate::currentDate(),QTime::currentTime()); QString filename=GetFilename(now); if(!DidHourAdvance(now)) { // We haven't crossed into new hour, holdoff archive_rotate_timer->start(100); return; } if(contentType()=="audio/x-wav") { if(archive_snd!=NULL) { sf_close(archive_snd); archive_snd=NULL; } if((archive_snd=sf_open(filename.toUtf8(),SFM_WRITE,&archive_sf))!=NULL) { setConnected(true); Log(LOG_DEBUG,"now writing to \""+filename+"\""); } else { Log(LOG_WARNING,("unable to open destination file \""+filename+ "\" ["+sf_strerror(archive_snd)+"]").toUtf8()); setConnected(false); } } else { if(archive_fd>=0) { close(archive_fd); archive_fd=-1; } if((archive_fd=open(filename.toUtf8(),O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))>=0) { setConnected(true); Log(LOG_DEBUG,"now writing to \""+filename+"\""); } else { Log(LOG_WARNING,("unable to open destination file \""+filename+ "\" ["+strerror(errno)+"]").toUtf8()); setConnected(false); } } archive_rotate_timer-> start(now.time().msecsTo(QTime(now.time().hour(),59,59,999))); } void FileArchiveConnector::connectToHostConnector(const QUrl &url) { // // Validate Mountpoint // if(serverMountpoint().right(1)=="/") { Log(LOG_ERR,"invalid --server-url"); exit(256); } if((archive_fd=open(serverMountpoint().toUtf8(),O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))<0) { Log(LOG_ERR,("unable to write to archive location \""+serverMountpoint()+ "\" ["+strerror(errno)+"]").toUtf8()); exit(256); } close(archive_fd); archive_fd=-1; unlink(serverMountpoint().toUtf8()); // // Initialize libsndfile // if(contentType()=="audio/x-wav") { memset(&archive_sf,0,sizeof(archive_sf)); archive_sf.samplerate=audioSamplerate(); archive_sf.channels=audioChannels(); archive_sf.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16; } rotateFile(); emit unmuteRequested(); } void FileArchiveConnector::disconnectFromHostConnector() { archive_rotate_timer->stop(); if(archive_fd>=0) { close(archive_fd); archive_fd=-1; } if(archive_snd!=NULL) { sf_close(archive_snd); } } int64_t FileArchiveConnector::writeDataConnector(int frames, const unsigned char *data, int64_t len) { if(contentType()=="audio/x-wav") { short pcm[frames*audioChannels()]; for(int i=0;i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef FILEARCHIVECONNECTOR_H #define FILEARCHIVECONNECTOR_H #include #include #include #include "connector.h" #define FILEARCHIVE_DATETIME_PATTERN "yyyy-MM-dd-hh" class FileArchiveConnector : public Connector { Q_OBJECT; public: FileArchiveConnector(QObject *parent=0); ~FileArchiveConnector(); FileArchiveConnector::ServerType serverType() const; private slots: void rotateFile(); protected: void connectToHostConnector(const QUrl &url); void disconnectFromHostConnector(); int64_t writeDataConnector(int frames,const unsigned char *data,int64_t len); private: bool DidHourAdvance(const QDateTime &dt); QString GetFilename(const QDateTime &dt) const; QTimer *archive_rotate_timer; int archive_fd; SNDFILE *archive_snd; SF_INFO archive_sf; int archive_hour; }; #endif // FILEARCHIVECONNECTOR_H GlassCoder-2.0.1/src/glasscoder/fileconnector.cpp000066400000000000000000000053171425562563600220370ustar00rootroot00000000000000// fileconnector.cpp // // Source connector class for local files // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include "fileconnector.h" #include "logging.h" FileConnector::FileConnector(QObject *parent) : Connector(parent) { file_fd=-1; file_snd=NULL; } FileConnector::~FileConnector() { if(file_snd!=NULL) { sf_close(file_snd); } if(file_fd>=0) { close(file_fd); } } FileConnector::ServerType FileConnector::serverType() const { return Connector::FileServer; } void FileConnector::connectToHostConnector(const QUrl &url) { SF_INFO sf; if(contentType()=="audio/x-wav") { memset(&sf,0,sizeof(sf)); sf.samplerate=audioSamplerate(); sf.channels=audioChannels(); sf.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16; if((file_snd=sf_open(serverMountpoint().toUtf8(),SFM_WRITE,&sf))!=NULL) { setConnected(true); } else { Log(LOG_ERR,("unable to open destination file \""+serverMountpoint()+ "\" ["+sf_strerror(file_snd)+"]").toUtf8()); setConnected(false); } } else { if((file_fd=open(serverMountpoint().toUtf8(),O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))>=0) { setConnected(true); } else { Log(LOG_ERR,("unable to open destination file \""+serverMountpoint()+ "\" ["+strerror(errno)+"]").toUtf8()); setConnected(false); } } emit unmuteRequested(); } void FileConnector::disconnectFromHostConnector() { if(file_snd==NULL) { close(file_fd); file_fd=-1; } else { sf_close(file_snd); file_snd=NULL; } } int64_t FileConnector::writeDataConnector(int frames,const unsigned char *data, int64_t len) { if(file_snd==NULL) { return write(file_fd,data,len); } short pcm[frames*audioChannels()]; for(int i=0;i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef FILECONNECTOR_H #define FILECONNECTOR_H #include #include "connector.h" class FileConnector : public Connector { Q_OBJECT; public: FileConnector(QObject *parent=0); ~FileConnector(); FileConnector::ServerType serverType() const; protected: void connectToHostConnector(const QUrl &url); void disconnectFromHostConnector(); int64_t writeDataConnector(int frames,const unsigned char *data,int64_t len); private: int file_fd; SNDFILE *file_snd; }; #endif // FILECONNECTOR_H GlassCoder-2.0.1/src/glasscoder/filedevice.cpp000066400000000000000000000074141425562563600213040ustar00rootroot00000000000000// filedevice.cpp // // Audio source for streaming direct from a file. // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "filedevice.h" #include "glasslimits.h" FileDevice::FileDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent) : AudioDevice(chans,samprate,ring,parent) { #ifdef SNDFILE file_sndfile=NULL; file_muted=true; memset(&file_sfinfo,0,sizeof(file_sfinfo)); for(int i=0;iMAX_AUDIO_CHANNELS) { *err=tr("unsupported channel count"); sf_close(file_sndfile); return false; } file_read_timer->start(1000*SNDFILE_BUFFER_SIZE/file_sfinfo.samplerate); return true; #else return false; #endif // SNDFILE } unsigned FileDevice::deviceSamplerate() const { #ifdef SNDFILE return file_sfinfo.samplerate; #else return DEFAULT_AUDIO_SAMPLERATE; #endif // SNDFILE } void FileDevice::unmute() { file_muted=false; } void FileDevice::readTimerData() { #ifdef SNDFILE float pcm1[SNDFILE_BUFFER_SIZE*MAX_AUDIO_CHANNELS]; float pcm2[SNDFILE_BUFFER_SIZE*MAX_AUDIO_CHANNELS]; float *pcm=pcm1; sf_count_t nframes; float levels[MAX_AUDIO_CHANNELS]; if(file_muted) { memset(pcm,0,sizeof(SNDFILE_BUFFER_SIZE*sizeof(float)*channels())); ringBuffer()->write(pcm,SNDFILE_BUFFER_SIZE); } else { if((nframes=sf_readf_float(file_sndfile,pcm1,SNDFILE_BUFFER_SIZE))>0) { if(file_sfinfo.channels!=(int)channels()) { remixChannels(pcm2,channels(),pcm1,file_sfinfo.channels,nframes); pcm=pcm2; } ringBuffer()->write(pcm,nframes); peakLevels(levels,pcm,nframes,channels()); for(unsigned i=0;iaddValue(levels[i]); levels[i]=file_meter_avg[i]->average(); } setMeterLevels(levels); } else { sf_close(file_sndfile); file_sndfile=NULL; emit hasStopped(); } } #endif // SNDFILE } GlassCoder-2.0.1/src/glasscoder/filedevice.h000066400000000000000000000033311425562563600207430ustar00rootroot00000000000000// filedevice.h // // Audio source for streaming direct from a file. // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef FILEDEVICE_H #define FILEDEVICE_H #include #include #include #include #include #ifdef SNDFILE #include #endif // SNDFILE #include "audiodevice.h" #include "meteraverage.h" #include "ringbuffer.h" #define SNDFILE_BUFFER_SIZE 1024 class FileDevice : public AudioDevice { Q_OBJECT; public: FileDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent=0); ~FileDevice(); bool processOptions(QString *err,const QStringList &keys, const QStringList &values); bool start(QString *err); unsigned deviceSamplerate() const; public slots: void unmute(); private slots: void readTimerData(); private: #ifdef SNDFILE QString file_name; SNDFILE *file_sndfile; SF_INFO file_sfinfo; QTimer *file_read_timer; MeterAverage *file_meter_avg[MAX_AUDIO_CHANNELS]; bool file_muted; #endif // SNDFILE }; #endif // FILEDEVICE_H GlassCoder-2.0.1/src/glasscoder/getconveyor.cpp000066400000000000000000000112031425562563600215400ustar00rootroot00000000000000// getconveyor.cpp // // Serialized service for processing http GET transactions // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include "getconveyor.h" GetConveyor::GetConveyor(QObject *parent) : QObject(parent) { conv_process=NULL; // // Garbage Timer // conv_garbage_timer=new QTimer(this); conv_garbage_timer->setSingleShot(true); connect(conv_garbage_timer,SIGNAL(timeout()), this,SLOT(processCollectGarbageData())); } GetConveyor::~GetConveyor() { delete conv_garbage_timer; if(conv_process!=NULL) { conv_process->kill(); delete conv_process; } } void GetConveyor::setUsername(const QString &str) { conv_username=str; } void GetConveyor::setPassword(const QString &str) { conv_password=str; } void GetConveyor::setUserAgent(const QString &str) { conv_user_agent=str; } void GetConveyor::setAddedHeaders(const QStringList &hdrs) { conv_added_headers=hdrs; } void GetConveyor::push(const QUrl &url) { conv_events.push(url); if(conv_events.size()==1) { Dispatch(); } } void GetConveyor::processErrorData(QProcess::ProcessError err) { emit error(conv_events.front(),err,conv_arguments); conv_garbage_timer->start(0); } void GetConveyor::processFinishedData(int exit_code, QProcess::ExitStatus exit_status) { bool ok=false; if(exit_status==QProcess::CrashExit) { emit eventFinished(conv_events.front(),256,0,conv_arguments); } else { if(exit_code!=0) { emit eventFinished(conv_events.front(),exit_code,0,conv_arguments); } else { if(conv_process==NULL) { emit eventFinished(conv_events.front(),0,200,QStringList()); } else { QString response=conv_process->readAllStandardOutput(); if((conv_events.front().scheme().toLower()=="sftp")&& (response.toInt(&ok)==0)) { if(ok) { response="200"; } } emit eventFinished(conv_events.front(),0,response.toInt(), conv_arguments); } } } conv_garbage_timer->start(0); } void GetConveyor::processCollectGarbageData() { if(conv_process!=NULL) { delete conv_process; conv_process=NULL; } conv_events.pop(); if(conv_events.size()>0) { Dispatch(); } } void GetConveyor::Dispatch() { QUrl url=conv_events.front(); /* printf("Dispatch (via \"%s\"): %s => %s\n", (const char *)evt.url().scheme().toUtf8(), (const char *)evt.filename().toUtf8(), (const char *)evt.url().toString().toUtf8()); */ if((url.scheme().toLower()=="http")|| (url.scheme().toLower()=="https")) { conv_arguments.clear(); AddCurlAuthArgs(&conv_arguments,url); if((!conv_user_agent.isEmpty())&&(!conv_password.isEmpty())) { conv_arguments.push_back("--user-agent"); conv_arguments.push_back(conv_user_agent); } AddHeaders(&conv_arguments,conv_added_headers); conv_arguments.push_back("--write-out"); conv_arguments.push_back("%{response_code}"); conv_arguments.push_back("--silent"); conv_arguments.push_back("--output"); conv_arguments.push_back("/dev/null"); conv_arguments.push_back(url.toEncoded()); conv_process=new QProcess(this); connect(conv_process,SIGNAL(error(QProcess::ProcessError)), this,SLOT(processErrorData(QProcess::ProcessError))); connect(conv_process,SIGNAL(finished(int,QProcess::ExitStatus)), this,SLOT(processFinishedData(int,QProcess::ExitStatus))); conv_process->start("curl",conv_arguments); } } void GetConveyor::AddHeaders(QStringList *arglist,const QStringList &hdrs) { for(int i=0;ipush_back("-H"); arglist->push_back(hdrs[i]); } } void GetConveyor::AddCurlAuthArgs(QStringList *arglist,const QUrl &url) { if(!conv_username.isEmpty()) { arglist->push_back("-u"); if(conv_password.isEmpty()) { arglist->push_back(conv_username); } else { arglist->push_back(conv_username+":"+conv_password); } } } GlassCoder-2.0.1/src/glasscoder/getconveyor.h000066400000000000000000000041301425562563600212060ustar00rootroot00000000000000// getconveyor.h // // Serialized service for processing http GET transactions // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef GETCONVEYOR_H #define GETCONVEYOR_H #include #include #include #include #include #include #include #include #include class GetConveyor : public QObject { Q_OBJECT; public: GetConveyor(QObject *parent=0); ~GetConveyor(); void setUsername(const QString &str); void setPassword(const QString &str); void setUserAgent(const QString &str); void setAddedHeaders(const QStringList &hdrs); void push(const QUrl &url); signals: void eventFinished(const QUrl &url,int exit_code,int resp_code, const QStringList &args); void error(const QUrl &url,QProcess::ProcessError err, const QStringList &args); private slots: void processErrorData(QProcess::ProcessError err); void processFinishedData(int exit_code,QProcess::ExitStatus exit_status); void processCollectGarbageData(); private: void Dispatch(); void AddHeaders(QStringList *arglist,const QStringList &hdrs); void AddCurlAuthArgs(QStringList *arglist,const QUrl &url); std::queue conv_events; QProcess *conv_process; QStringList conv_arguments; QTimer *conv_garbage_timer; QString conv_username; QString conv_password; QString conv_user_agent; QStringList conv_added_headers; }; #endif // GETCONVEYOR_H GlassCoder-2.0.1/src/glasscoder/glasscoder.cpp000066400000000000000000000220571425562563600213330ustar00rootroot00000000000000// glasscoder.cpp // // glasscoder(1) Audio Encoder // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include "audiodevicefactory.h" #include "cmdswitch.h" #include "codecfactory.h" #include "connectorfactory.h" #include "glasscoder.h" #include "logging.h" // // Globals // bool glasscoder_exiting=false; void SignalHandler(int signo) { switch(signo) { case SIGTERM: case SIGINT: glasscoder_exiting=true; break; } } MainObject::MainObject(QObject *parent) : QObject(parent) { sir_exit_count=0; sir_meta_server=NULL; sir_config=new Config(); if(!StartAudioDevice()) { exit(256); } // // Initialize CURL // curl_global_init(CURL_GLOBAL_ALL); // // Stdin Interface // int flags=fcntl(0,F_GETFL,NULL); flags=flags|O_NONBLOCK; fcntl(0,F_SETFL,flags); sir_stdin_notify=new QSocketNotifier(0,QSocketNotifier::Read,this); connect(sir_stdin_notify,SIGNAL(activated(int)), this,SLOT(stdinActivatedData(int))); // // Metadata Processor // if(sir_config->metadataPort()>0) { sir_meta_server=new MetaServer(sir_config,this); if(!sir_meta_server->listen(sir_config->metadataPort())) { Log(LOG_ERR,QString().sprintf("unable to bind port %u", sir_config->metadataPort())); exit(256); } } // // Start Server Connection // if(!StartSingleStream()) { exit(256); } // // Set Signals // sir_exit_timer=new QTimer(this); connect(sir_exit_timer,SIGNAL(timeout()),this,SLOT(exitTimerData())); sir_exit_timer->start(250); ::signal(SIGINT,SignalHandler); ::signal(SIGTERM,SignalHandler); } void MainObject::stdinActivatedData(int sock) { char data[1501]; int n; while((n=read(sock,data,1500))>0) { for(int i=0;imeterLevels(lvls); switch(sir_codec->channels()) { case 1: printf("ME %04X%04X\n",lvls[0],lvls[0]); break; case 2: printf("ME %04X%04X\n",lvls[0],lvls[1]); break; } fflush(stdout); } void MainObject::connectedData(bool state) { if(global_log_to==LOG_TO_STDOUT) { if(state) { printf("CS %d\n",CONNECTION_OK); } else { printf("CS %d\n",CONNECTION_FAILED); } } } void MainObject::exitTimerData() { if(glasscoder_exiting) { bool connected=sir_connectors.at(0)->isConnected(); for(unsigned i=0;istop(); } sir_exit_timer->stop(); if((!sir_config->serverScriptDown().isEmpty())&&connected) { QString cmd=sir_config->serverScriptDown(); if(fork()==0) { if(system(cmd.toUtf8())==0); // FIXME: Replace this with QProcess _exit(0); // _exit(2) NOT exit(3), to avoid racing with the parent } } } } bool MainObject::StartAudioDevice() { // // Create Ringbuffer // if(sir_config->audioBitrate()==0) { // For VBR modes sir_ringbuffer=new Ringbuffer(RINGBUFFER_SIZE,sir_config->audioChannels()); } else { sir_ringbuffer=new Ringbuffer(RINGBUFFER_SIZE,sir_config->audioChannels()); } // // Start Audio Device // QString err; if((sir_audio_device= AudioDeviceFactory(sir_config->audioDevice(),sir_config->audioChannels(), sir_config->audioSamplerate(), // &sir_ringbuffers,this))==NULL) { sir_ringbuffer,this))==NULL) { Log(LOG_ERR, QString().sprintf("%s devices not supported", (const char *)AudioDevice::deviceTypeText(sir_config->audioDevice()).toUtf8())); exit(256); } connect(sir_audio_device,SIGNAL(hasStopped()), this,SLOT(audioDeviceStoppedData())); if(!sir_audio_device->processOptions(&err,sir_config->deviceKeys(), sir_config->deviceValues())) { Log(LOG_ERR,err); exit(256); } if(!sir_audio_device->start(&err)) { Log(LOG_ERR,err); exit(256); } sir_meter_timer=new QTimer(this); connect(sir_meter_timer,SIGNAL(timeout()),this,SLOT(meterData())); if(sir_config->meterData()) { sir_meter_timer->start(AUDIO_METER_INTERVAL); } return true; } bool MainObject::StartCodec() { if((sir_codec= CodecFactory(sir_config->audioFormat(),sir_ringbuffer,this))==NULL) { Log(LOG_ERR, QString().sprintf("unsupported codec type \"%s\"", (const char *)Codec::codecTypeText(Codec::TypeMpegL3).toUtf8())); return false; } if(sir_config->audioBitrate()>0) { sir_codec->setBitrate(sir_config->audioBitrate()); } else { sir_codec->setBitrate(0); } sir_codec->setChannels(sir_config->audioChannels()); sir_codec->setQuality(sir_config->audioQuality()); sir_codec->setSourceSamplerate(sir_audio_device->deviceSamplerate()); sir_codec->setStreamSamplerate(sir_config->audioSamplerate()); sir_codec->setCompleteFrames(sir_config->audioAtomicFrames()); return sir_codec->start(); } void MainObject::StartServerConnection(const QString &mntpt) { Connector *conn; // // Create Connector Instance // conn=ConnectorFactory(sir_config->serverType(),sir_config,this); connect(conn,SIGNAL(stopped()),this,SLOT(connectorStoppedData())); connect(conn,SIGNAL(unmuteRequested()),sir_audio_device,SLOT(unmute())); connect(conn,SIGNAL(dataRequested(Connector *)), sir_codec,SLOT(encode(Connector *))); connect(conn,SIGNAL(connected(bool)),this,SLOT(connectedData(bool))); connect(conn,SIGNAL(stopped()),this,SLOT(connectorStoppedData())); conn->setStreamPrologue(sir_codec->streamPrologue()); sir_codec->setCompleteFrames(sir_config->serverType()==Connector::HlsServer); if(sir_meta_server!=NULL) { connect(sir_meta_server,SIGNAL(metadataReceived(MetaEvent *)), conn,SLOT(sendMetadata(MetaEvent *))); } // // Set Configuration // if(mntpt.isEmpty()) { conn->setServerMountpoint(sir_config->serverUrl().path()); } else { conn->setServerMountpoint(mntpt); } conn->setServerExitOnLast(sir_config->serverExitOnLast()); conn->setServerUsername(sir_config->serverUsername()); conn->setServerPassword(sir_config->serverPassword()); conn->setServerMaxConnections(sir_config->serverMaxConnections()); conn->setServerPipe(sir_config->serverPipe()); conn->setServerStartConnections(sir_config->serverStartConnections()); conn->setServerUserAgent(sir_config->serverUserAgent()); conn->setDumpHeaders(sir_config->dumpHeaders()); conn->setContentType(sir_codec->contentType()); conn->setExtension(sir_codec->defaultExtension()); conn->setFormatIdentifier(sir_codec->formatIdentifier()); conn->setAudioBitrate(sir_config->audioBitrate()); conn->setAudioChannels(sir_config->audioChannels()); conn->setAudioSamplerate(sir_config->audioSamplerate()); conn->setScriptUp(sir_config->serverScriptUp()); conn->setScriptDown(sir_config->serverScriptDown()); conn->setStreamDescription(sir_config->streamDescription()); conn->setStreamGenre(sir_config->streamGenre()); conn->setStreamName(sir_config->streamName()); conn->setStreamUrl(sir_config->streamUrl()); conn->setStreamIrc(sir_config->streamIrc()); conn->setStreamIcq(sir_config->streamIcq()); conn->setStreamAim(sir_config->streamAim()); conn->setStreamTimestampOffset(sir_config->streamTimestampOffset()); // // Open the server connection // sir_connectors.push_back(conn); sir_connectors.back()->connectToServer(sir_config->serverUrl()); } bool MainObject::StartSingleStream() { if(!StartCodec()) { return false; } StartServerConnection(); return true; } void MainObject::ProcessCommand(const QString &cmd) { QStringList cmds=cmd.split(" "); if(cmds[0]=="MD") { // Metadata update MetaEvent *e=new MetaEvent(); cmds.erase(cmds.begin()); e->setField("StreamTitle",cmds.join(" ")); for(unsigned i=0;isendMetadata(e); } delete e; } } int main(int argc,char *argv[]) { QCoreApplication a(argc,argv); new MainObject(); return a.exec(); } GlassCoder-2.0.1/src/glasscoder/glasscoder.h000066400000000000000000000041021425562563600207670ustar00rootroot00000000000000// glasscoder.h // // glasscoder(1) Audio Encoder // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef GLASSCODER_H #define GLASSCODER_H #include #include #include #include #include #include #include "audiodevice.h" #include "codec.h" #include "config.h" #include "connector.h" #include "glasslimits.h" #include "metaserver.h" #include "ringbuffer.h" class MainObject : public QObject { Q_OBJECT; public: MainObject(QObject *parent=0); private slots: void stdinActivatedData(int sock); void audioDeviceStoppedData(); void connectorStoppedData(); void meterData(); void connectedData(bool state); void exitTimerData(); private: Config *sir_config; // // Audio Device // bool StartAudioDevice(); Ringbuffer *sir_ringbuffer; AudioDevice *sir_audio_device; // // Server Connection // void StartServerConnection(const QString &mntpt=""); std::vector sir_connectors; // // Codec // bool StartCodec(); Codec * sir_codec; // // Metadata Processor // MetaServer *sir_meta_server; // // Miscelaneous // bool StartSingleStream(); QTimer *sir_meter_timer; QTimer *sir_exit_timer; unsigned sir_exit_count; // // Stdin Processor // void ProcessCommand(const QString &cmd); QSocketNotifier *sir_stdin_notify; QString sir_stdin_accum; }; #endif // GLASSCODER_H GlassCoder-2.0.1/src/glasscoder/glassconv.cpp000066400000000000000000000257211425562563600212050ustar00rootroot00000000000000// glassconv.cpp // // glassconv(1) File Conveyor Service // // (C) Copyright 2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include #include "cmdswitch.h" #include "config.h" #include "glassconv.h" #include "profile.h" MainObject::MainObject(QObject *parent) : QObject(parent) { d_source_dir=NULL; d_dest_url=NULL; int syslog_option=0; // // Process Command Arguments // CmdSwitch *cmd=new CmdSwitch("glassconv",GLASSCONV_USAGE); for(unsigned i=0;ikeys();i++) { if(cmd->key(i)=="--source-dir") { d_source_dir=new QDir(cmd->value(i)); cmd->setProcessed(i,true); } if(cmd->key(i)=="--dest-url") { d_dest_url=new QUrl(cmd->value(i)); cmd->setProcessed(i,true); } if(cmd->key(i)=="--debug") { syslog_option+=LOG_PERROR; cmd->setProcessed(i,true); } if(!cmd->processed(i)) { fprintf(stderr,"unrecognized option \"%s\"", cmd->key(i).toUtf8().constData()); exit(Config::ExitFatal); } } openlog("glassconv",syslog_option,LOG_DAEMON); Log(LOG_DEBUG,"starting up"); if(d_source_dir==NULL) { Log(LOG_ERR,"\"--source-dir\" argument required"); exit(Config::ExitFatal); } if(d_dest_url==NULL) { Log(LOG_ERR,"\"--dest-url\" argument required"); exit(Config::ExitFatal); } if(!d_source_dir->exists()) { Log(LOG_ERR,"source directory does not exist"); exit(Config::ExitFatal); } if((!d_dest_url->isValid())||d_dest_url->isRelative()) { Log(LOG_ERR,"destination url is invalid"); exit(Config::ExitFatal); } if((d_dest_url->scheme().toLower()!="http")&& (d_dest_url->scheme().toLower()!="https")&& (d_dest_url->scheme().toLower()!="sftp")&& (d_dest_url->scheme().toLower()!="file")) { Log(LOG_ERR,"destination url has unsupported scheme"); exit(Config::ExitFatal); } // // Initialize CURL // if(curl_global_init(CURL_GLOBAL_ALL)!=0) { Log(LOG_ERR,"curl global initialization failed"); exit(Config::ExitRetry); } if((d_curl_handle=curl_easy_init())==NULL) { Log(LOG_ERR,"curl initialization failed"); exit(Config::ExitRetry); } // // Load Credentials // Profile *p=new Profile(); if(p->setSource(d_source_dir->path()+"/"+GLASSCODER_CREDENTIALS)) { Log(LOG_DEBUG,"reading transfer credentials from \"%s\"", p->source().toUtf8().constData()); d_username=p->stringValue("Credentials","Username"); d_password=p->stringValue("Credentials","Password"); d_ssh_identity=p->stringValue("Credentials","SshIdentity"); d_user_agent=p->stringValue("Credentials","UserAgent", QString("GlassCoder/")+VERSION); UnlinkLocalFile(p->source()); } delete p; // // Set Directory Filters // d_source_dir->setFilter(QDir::Files|QDir::Readable|QDir::NoDotAndDotDot); d_source_dir->setSorting(QDir::Name); // // Scan Timer // d_scan_timer=new QTimer(this); d_scan_timer->setSingleShot(true); connect(d_scan_timer,SIGNAL(timeout()),this,SLOT(scanData())); // // Signal Disposition // ::signal(SIGTERM,SIG_IGN); ::signal(SIGINT,SIG_IGN); d_scan_timer->start(0); } void MainObject::scanData() { QStringList files=d_source_dir->entryList(QStringList()); for(int i=0;ipath()+"/"+files.at(i)); } d_scan_timer->start(1000); } void MainObject::ProcessFile(const QString &filename) { QStringList f0=filename.split("/",QString::SkipEmptyParts); QStringList f1=f0.last().split("-",QString::KeepEmptyParts); if(f1.size()<3) { Log(LOG_WARNING, "unrecognized file naming scheme \"%s\", skipping", filename.toUtf8().constData()); UnlinkLocalFile(filename); return; } for(int i=3;i3) { f1.removeLast(); } QString method=f1.at(1).trimmed(); QString destname=f1.at(2); printf("method: %s destname: %s\n",method.toUtf8().constData(), destname.toUtf8().constData()); if(method=="DELETE") { Delete(destname,filename); UnlinkLocalFile(filename); return; } if(method=="PUT") { Put(destname,filename); UnlinkLocalFile(filename); return; } if(method=="STOP") { // // Clean up temp directory // QStringList files=d_source_dir->entryList(QDir::Files); for(int i=0;ipath()+"/"+files.at(i)).toUtf8()); } rmdir(d_source_dir->path().toUtf8()); Log(LOG_DEBUG,"exiting normally"); exit(Config::ExitOk); } Log(LOG_WARNING, "unsupported transfer method in \"%s\", skipping", filename.toUtf8().constData()); UnlinkLocalFile(filename); } void MainObject::Put(const QString &destname,const QString &srcname) { Log(LOG_DEBUG,"uploading \"%s\" to \"%s/%s\"", srcname.toUtf8().constData(), d_dest_url->toDisplayString().toUtf8().constData(), destname.toUtf8().constData()); long resp_code=0; FILE *f=fopen(srcname.toUtf8(),"r"); if(f==NULL) { Log(LOG_WARNING,"upload of \"%s\" failed: %s", srcname.toUtf8().constData(),strerror(errno)); return; } struct stat st; memset(&st,0,sizeof(st)); stat(srcname.toUtf8(),&st); QUrl url(d_dest_url->toDisplayString()+"/"+destname); curl_easy_reset(d_curl_handle); // // Authentication // SetCurlAuthentication(d_curl_handle); // // Transaction // curl_easy_setopt(d_curl_handle,CURLOPT_ERRORBUFFER,d_curl_errorbuffer); curl_easy_setopt(d_curl_handle,CURLOPT_READDATA,(void *)f); if(st.st_size>0) { curl_easy_setopt(d_curl_handle,CURLOPT_INFILESIZE,st.st_size); } curl_easy_setopt(d_curl_handle,CURLOPT_URL,url.toEncoded().constData()); curl_easy_setopt(d_curl_handle,CURLOPT_UPLOAD,1); curl_easy_setopt(d_curl_handle,CURLOPT_FOLLOWLOCATION,1); curl_easy_setopt(d_curl_handle,CURLOPT_USERAGENT, d_user_agent.toUtf8().constData()); CURLcode code=curl_easy_perform(d_curl_handle); if(code==CURLE_OK) { curl_easy_getinfo(d_curl_handle,CURLINFO_RESPONSE_CODE,&resp_code); if(((resp_code<200)||(resp_code>=300))&&(resp_code!=0)) { Log(LOG_WARNING,"upload of \"%s\" returned code %lu", srcname.toUtf8().constData(),resp_code); } } else { Log(LOG_WARNING,"upload of \"%s\" failed: %s", srcname.toUtf8().constData(),d_curl_errorbuffer); } fclose(f); } void MainObject::Delete(const QString &destname,const QString &srcname) { Log(LOG_DEBUG,"removing \"%s\" from \"%s/%s\"", srcname.toUtf8().constData(), d_dest_url->toDisplayString().toUtf8().constData(), destname.toUtf8().constData()); QString scheme=d_dest_url->scheme().toLower(); if((scheme=="http")||(scheme=="https")) { DeleteHttp(destname,srcname); } if(scheme=="file") { DeleteFile(destname,srcname); } if(scheme=="sftp") { DeleteSftp(destname,srcname); } } void MainObject::DeleteHttp(const QString &destname,const QString &srcname) { long resp_code=0; QUrl url(d_dest_url->toDisplayString()+"/"+destname); curl_easy_reset(d_curl_handle); curl_easy_setopt(d_curl_handle,CURLOPT_ERRORBUFFER,d_curl_errorbuffer); if(!d_username.isEmpty()) { curl_easy_setopt(d_curl_handle,CURLOPT_USERPWD, (d_username+":"+d_password).toUtf8().constData()); } curl_easy_setopt(d_curl_handle,CURLOPT_URL,url.toEncoded().constData()); curl_easy_setopt(d_curl_handle,CURLOPT_CUSTOMREQUEST,"DELETE"); curl_easy_setopt(d_curl_handle,CURLOPT_FOLLOWLOCATION,1); curl_easy_setopt(d_curl_handle,CURLOPT_USERAGENT, d_user_agent.toUtf8().constData()); CURLcode code=curl_easy_perform(d_curl_handle); if(code==CURLE_OK) { curl_easy_getinfo(d_curl_handle,CURLINFO_RESPONSE_CODE,&resp_code); if((resp_code<200)||(resp_code>=300)) { Log(LOG_WARNING,"removal of \"%s\" returned code %lu", srcname.toUtf8().constData(),resp_code); } } else { Log(LOG_WARNING,"removal of \"%s\" failed: %s", url.toDisplayString().toUtf8().constData(),d_curl_errorbuffer); } } void MainObject::DeleteFile(const QString &destname,const QString &srcname) { QUrl url(d_dest_url->toDisplayString()+"/"+destname); if((unlink(url.path().toUtf8())!=0)&&(errno!=ENOENT)) { Log(LOG_WARNING,"removal of \"%s\" failed: %s", url.toDisplayString().toUtf8().constData(),strerror(errno)); } } void MainObject::DeleteSftp(const QString &destname,const QString &srcname) { struct curl_slist *cmds=NULL; QUrl url(d_dest_url->toDisplayString()+"/"+destname); // // Set Up The Transaction // curl_easy_reset(d_curl_handle); SetCurlAuthentication(d_curl_handle); curl_easy_setopt(d_curl_handle,CURLOPT_URL,url.toEncoded().constData()); curl_easy_setopt(d_curl_handle,CURLOPT_HTTPAUTH,CURLAUTH_ANY); curl_easy_setopt(d_curl_handle,CURLOPT_USERAGENT, d_user_agent.toUtf8().constData()); cmds=curl_slist_append(cmds,(QString("rm ")+url.path()).toUtf8()); curl_easy_setopt(d_curl_handle,CURLOPT_QUOTE,cmds); // // Execute It // CURLcode code=curl_easy_perform(d_curl_handle); if((code!=CURLE_OK)&&(code!=CURLE_REMOTE_FILE_NOT_FOUND)) { Log(LOG_WARNING,"removal of \"%s\" failed: [%d] %s", url.toDisplayString().toUtf8().constData(),code,d_curl_errorbuffer); } } void MainObject::SetCurlAuthentication(CURL *handle) const { if(d_ssh_identity.isEmpty()) { curl_easy_setopt(d_curl_handle,CURLOPT_USERPWD, (d_username+":"+d_password).toUtf8().constData()); } else { curl_easy_setopt(d_curl_handle, CURLOPT_USERNAME,d_username.toUtf8().constData()); curl_easy_setopt(d_curl_handle,CURLOPT_SSH_PRIVATE_KEYFILE, d_ssh_identity.toUtf8().constData()); curl_easy_setopt(d_curl_handle,CURLOPT_KEYPASSWD, d_password.toUtf8().constData()); } } void MainObject::UnlinkLocalFile(const QString &pathname) const { Log(LOG_DEBUG,"unlinking \"%s\"",pathname.toUtf8().constData()); unlink(pathname.toUtf8()); } void MainObject::Log(int prio,const char *fmt,...) const { char line[1024]; va_list args; // // Send to parent controller // va_start(args,fmt); if(vsnprintf(line,1023,fmt,args)>0) { printf("ER %d %s",prio,line); fflush(stdout); } va_end(args); // // Send to syslog // va_start(args,fmt); vsyslog(prio,fmt,args); va_end(args); } int main(int argv,char *argc[]) { QCoreApplication a(argv,argc); new MainObject(); return a.exec(); } GlassCoder-2.0.1/src/glasscoder/glassconv.h000066400000000000000000000036051425562563600206470ustar00rootroot00000000000000// glassconv.h // // glassconv(1) File Conveyor Service // // (C) Copyright 2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef GLASSCONV_H #define GLASSCONV_H #include #include #include #include #include #include #define GLASSCONV_USAGE "--source-dir= --dest-url= [--debug]" class MainObject : public QObject { Q_OBJECT; public: MainObject(QObject *parent=0); private slots: void scanData(); private: void ProcessFile(const QString &filename); void Put(const QString &destname,const QString &srcname); void Delete(const QString &destname,const QString &srcname); void DeleteHttp(const QString &destname,const QString &srcname); void DeleteFile(const QString &destname,const QString &srcname); void DeleteSftp(const QString &destname,const QString &srcname); void SetCurlAuthentication(CURL *handle) const; void UnlinkLocalFile(const QString &pathname) const; void Log(int prio,const char *fmt,...) const; QDir *d_source_dir; QUrl *d_dest_url; QString d_username; QString d_password; QString d_ssh_identity; QTimer *d_scan_timer; CURL *d_curl_handle; char d_curl_errorbuffer[CURL_ERROR_SIZE]; QString d_user_agent; }; #endif // GLASSCONV_H GlassCoder-2.0.1/src/glasscoder/hlsconnector.cpp000066400000000000000000000255701425562563600217110ustar00rootroot00000000000000// hlsconnector.cpp // // HLS/HTTP streaming connector for GlassCoder // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include #include #include "hlsconnector.h" #include "logging.h" HlsConnector::HlsConnector(Config *conf,QObject *parent) : Connector(parent) { hls_config=conf; hls_sequence_head=0; hls_sequence_back=0; hls_media_frames=0; hls_total_media_frames=0; hls_metadata_updated=false; hls_conveyor=new NetConveyor(conf,this); connect(hls_conveyor,SIGNAL(stopped()),this,SLOT(conveyorStoppedData())); // // Create working directory // char tempdir[PATH_MAX]; strncpy(tempdir,"/tmp",PATH_MAX); if(getenv("TEMP")!=NULL) { strncpy(tempdir,getenv("TEMP"),PATH_MAX-1); } strncat(tempdir,"/glasscoder-XXXXXX",PATH_MAX-strlen(tempdir)); if(mkdtemp(tempdir)==NULL) { Log(LOG_ERR,QString().sprintf("unable to create temporary directory [%s]", strerror(errno))); exit(256); } hls_temp_dir=new QDir(tempdir); Log(LOG_INFO,QString().sprintf("using working directory \"%s\"", (const char *)hls_temp_dir->path().toUtf8())); // // Start DateTime // QTimeZone tz(QTimeZone::systemTimeZoneId()); hls_start_datetime=QDateTime::currentDateTime(). addSecs(-tz.offsetFromUtc(QDateTime::currentDateTime())); hls_start_datetime.setTimeZone(QTimeZone::utc()); } HlsConnector::~HlsConnector() { QStringList files=hls_temp_dir->entryList(QDir::Files); for(int i=0;ipath()+"/"+files[i]).toUtf8()); } rmdir(hls_temp_dir->path().toUtf8()); delete hls_temp_dir; } Connector::ServerType HlsConnector::serverType() const { return Connector::HlsServer; } void HlsConnector::sendMetadata(MetaEvent *e) { TagLib::ID3v2::Tag *tag=new TagLib::ID3v2::Tag(); QStringList keys=e->fieldKeys(); AddTXXXFrame(tag,"ach",QString().sprintf("%d",audioChannels())); AddTXXXFrame(tag,"adr",QString().sprintf("%d",audioBitrate())); // AddTXXXFrame(tag,"aot","5"); AddTXXXFrame(tag,"asr",QString().sprintf("%d",audioSamplerate())); AddTXXXFrame(tag,"crb",QString("GlassCoder ")+VERSION); AddTXXXFrame(tag,"crd", hls_start_datetime.toString("yyyyMMdd hh:mm:ss")+" UTC"); AddTXXXFrame(tag,"dev","AudioScience HPI"); AddTXXXFrame(tag,"enc",QString("GlassCoder ")+VERSION); for(int i=0;ifield(keys.at(i))); } AddTextIdFrame(tag,"TFLT","MPG/AAC"); TagLib::ByteVector bytes=tag->render(4); hls_metadata_tag=QByteArray(bytes.data(),bytes.size()); int tag_size=Connector::id3TagSize(hls_metadata_tag); if(tag_sizestop(); } void HlsConnector::connectToHostConnector(const QUrl &url) { // // Calculate publish point info // QStringList f0=serverMountpoint().split("/",QString::SkipEmptyParts); hls_put_basename=f0[f0.size()-1]; hls_put_basestamp=QString().sprintf("%ld",time(NULL)); QStringList f1=hls_put_basename.split("."); if((f1[f1.size()-1]!="m3u8")&&(f1[f1.size()-1]!="m3u")) { hls_put_basename+=".m3u8"; } hls_put_directory=""; for(int i=0;ipath()+"/"+hls_put_basename; // // Create initial media file // hls_media_filename=GetMediaFilename(hls_sequence_back); if((hls_media_handle= fopen((hls_temp_dir->path()+"/"+hls_media_filename).toUtf8(),"w"))== NULL) { Log(LOG_WARNING, QString().sprintf("unable to write media data to \"%s\" [%s]", (const char *)(hls_temp_dir->path()+"/"+ hls_media_filename).toUtf8(),strerror(errno))); } // // Write ID3 tag(s) // #ifndef HLS_OMIT_ID3_TIMESTAMPS uint8_t id3_header[HLS_ID3_HEADER_SIZE]; hls_total_media_frames=HLS_SEGMENT_SIZE*audioSamplerate(); GetStreamTimestamp(id3_header,hls_total_media_frames); fwrite(id3_header,1,HLS_ID3_HEADER_SIZE,hls_media_handle); #endif // HLS_OMIT_ID3_TIMESTAMPS setConnected(true); emit unmuteRequested(); } void HlsConnector::disconnectFromHostConnector() { } int64_t HlsConnector::writeDataConnector(int frames,const unsigned char *data, int64_t len) { int frame_start=-1; QByteArray sdata((const char *)data,len); if((hls_media_frames+(uint64_t)frames)>(HLS_SEGMENT_SIZE*audioSamplerate())) { RotateMediaFile(); } hls_media_frames+=(uint64_t)frames; hls_total_media_frames+=(uint64_t)frames; if(hls_metadata_updated) { if((frame_start=sdata.indexOf(0xFF))>=0) { sdata.insert(frame_start,hls_metadata_tag); hls_metadata_updated=false; } } return fwrite(sdata.constData(),sdata.size(),1,hls_media_handle); } void HlsConnector::conveyorStoppedData() { emit stopped(); } void HlsConnector::RotateMediaFile() { // // Update working files // fclose(hls_media_handle); hls_media_datetimes[hls_sequence_back]= QDateTime(QDate::currentDate(),QTime::currentTime()); hls_media_durations[hls_sequence_back]= (double)hls_media_frames/(double)audioSamplerate(); if((hls_sequence_back-hls_sequence_head)>=HLS_MINIMUM_SEGMENT_QUAN) { // Schedule garbage collection hls_media_killtimes[hls_sequence_head++]=hls_total_media_frames; } WritePlaylistFile(); // // HTTP Uploads // hls_conveyor->push(this,hls_temp_dir->path()+"/"+hls_media_filename, NetConveyorEvent::PutMethod); unlink((hls_temp_dir->path()+"/"+hls_media_filename).toUtf8()); hls_conveyor->push(this,hls_playlist_filename,NetConveyorEvent::PutMethod); unlink(hls_playlist_filename.toUtf8()); // // Take out the trash // if(!hls_config->serverNoDeletes()) { std::map::iterator ci=hls_media_killtimes.begin(); while(ci!=hls_media_killtimes.end()) { if(ci->second::iterator cj=hls_media_durations.begin(); while(cj!=hls_media_durations.end()) { if(cj->first==ci->first) { hls_media_durations.erase(cj++); } else { ++cj; } } std::map::iterator dj=hls_media_datetimes.begin(); while(dj!=hls_media_datetimes.end()) { if(dj->first==ci->first) { hls_media_datetimes.erase(dj++); } else { ++dj; } } hls_conveyor-> push(this,hls_temp_dir->path()+"/"+GetMediaFilename(ci->first), NetConveyorEvent::DeleteMethod); hls_media_killtimes.erase(ci++); } else { ++ci; } } } // // Initialize for next segment // hls_sequence_back++; hls_media_frames=0; hls_media_filename=GetMediaFilename(hls_sequence_back); if((hls_media_handle= fopen((hls_temp_dir->path()+"/"+hls_media_filename).toUtf8(),"w"))== NULL) { Log(LOG_WARNING, QString().sprintf("unable to write media data to \"%s\" [%s]", (const char *)(hls_temp_dir->path()+"/"+hls_media_filename).toUtf8(), strerror(errno))); } #ifndef HLS_OMIT_ID3_TIMESTAMPS uint8_t id3_header[HLS_ID3_HEADER_SIZE]; GetStreamTimestamp(id3_header,hls_total_media_frames); fwrite(id3_header,1,HLS_ID3_HEADER_SIZE,hls_media_handle); if(hls_metadata_tag.size()>0) { fwrite(hls_metadata_tag.data(),1,hls_metadata_tag.size(), hls_media_handle); hls_metadata_updated=false; } #endif // HLS_OMIT_ID3_TIMESTAMPS } void HlsConnector::WritePlaylistFile() { FILE *f=NULL; if((f=fopen(hls_playlist_filename.toUtf8(),"w"))==NULL) { Log(LOG_ERR, QString().sprintf("unable to write playlist data to \"%s\" [%s]", (const char *)(hls_temp_dir->path()+"/"+ hls_playlist_filename).toUtf8(),strerror(errno))); exit(256); } fprintf(f,"#EXTM3U\n"); fprintf(f,"#EXT-X-TARGETDURATION:%d\n",HLS_SEGMENT_SIZE); fprintf(f,"#EXT-X-VERSION:%d\n",HLS_VERSION); fprintf(f,"#EXT-X-MEDIA-SEQUENCE:%d\n",hls_sequence_head); for(int i=hls_sequence_head;i<=hls_sequence_back;i++) { fprintf(f,"#EXT-X-PROGRAM-DATE-TIME:%s%s\n",(const char *) hls_media_datetimes[i].addSecs(streamTimestampOffset()). toString("yyyy-MM-ddThh:mm:ss.zzz").toUtf8(), (const char *)Connector::timezoneOffset().toUtf8()); fprintf(f,"#EXTINF:%7.5lf,\n%s\n", hls_media_durations[i],(const char *)GetMediaFilename(i).toUtf8()); } fclose(f); } QString HlsConnector::GetMediaFilename(int seqno) { return hls_put_basename+"-"+hls_put_basestamp+"-"+ QString().sprintf("%d.",seqno)+extension(); } void HlsConnector::GetStreamTimestamp(uint8_t *bytes,uint64_t frames) { // // ID3 PRIV tag (see HTTP Live Streaming Draft sec. 4) // static uint8_t id3_header[HLS_ID3_HEADER_SIZE]= {0x49,0x44,0x33,0x04,0x00,0x00,0x00,0x00, 0x00,0x3F,0x50,0x52,0x49,0x56,0x00,0x00, 0x00,0x35,0x00,0x00,0x63,0x6F,0x6D,0x2E, 0x61,0x70,0x70,0x6C,0x65,0x2E,0x73,0x74, 0x72,0x65,0x61,0x6D,0x69,0x6E,0x67,0x2E, 0x74,0x72,0x61,0x6E,0x73,0x70,0x6F,0x72, 0x74,0x53,0x74,0x72,0x65,0x61,0x6D,0x54, 0x69,0x6D,0x65,0x73,0x74,0x61,0x6D,0x70, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00}; memcpy(bytes,id3_header,HLS_ID3_HEADER_SIZE); // // MPEG-2 timestamps are expressed in 1/90000ths of a second // uint64_t stamp=(uint64_t)(90000.0*(double)frames/(double)audioSamplerate())& (0x1FFFFFFFF); for(int i=0;i<8;i++) { // Use network byte order! bytes[(HLS_ID3_HEADER_SIZE-8)+i]=0xFF&(stamp>>(56-8*i)); } } void HlsConnector::AddTextIdFrame(TagLib::ID3v2::Tag *tag,const QString &id, const QString &value) const { TagLib::ID3v2::TextIdentificationFrame *frame=NULL; frame=new TagLib::ID3v2::TextIdentificationFrame(TagLib::ByteVector((const char *)id.toUtf8()),TagLib::String::UTF8); TagLib::String str((const char *)value.toUtf8(),TagLib::String::UTF8); frame->setText(str); tag->addFrame(frame); } void HlsConnector::AddTXXXFrame(TagLib::ID3v2::Tag *tag,const QString &desc, const QString &value) const { TagLib::ID3v2::UserTextIdentificationFrame *uframe= new TagLib::ID3v2::UserTextIdentificationFrame((const char *)desc.toUtf8(), TagLib::StringList((const char *)value.toUtf8()),TagLib::String::UTF8); tag->addFrame(uframe); } GlassCoder-2.0.1/src/glasscoder/hlsconnector.h000066400000000000000000000056731425562563600213600ustar00rootroot00000000000000// hlsconnector.h // // HLS/HTTP streaming connector for GlassCoder // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef HLSCONNECTOR_H #define HLSCONNECTOR_H #define HLS_SEGMENT_SIZE 10 #define HLS_VERSION 3 #define HLS_MINIMUM_SEGMENT_QUAN 4 #define HLS_ID3_HEADER_SIZE 73 // // The Pantos & May draft HLS spec calls for placing an ID3 PRIV timestamp // at the start of each media segment [Section 3]. However, doing so causes // certain players -- e.g. VLC -- to produce audable glitches in the playout. // // Define this to suppress generation of such timestamps. // // #define HLS_OMIT_ID3_TIMESTAMPS #include #include #include #include #include #include #include #include #include #include "config.h" #include "connector.h" #include "netconveyor.h" class HlsConnector : public Connector { Q_OBJECT; public: HlsConnector(Config *conf,QObject *parent); ~HlsConnector(); Connector::ServerType serverType() const; public slots: void sendMetadata(MetaEvent *e); protected: void startStopping(); void connectToHostConnector(const QUrl &url); void disconnectFromHostConnector(); int64_t writeDataConnector(int frames,const unsigned char *data,int64_t len); private slots: void conveyorStoppedData(); private: void RotateMediaFile(); void WritePlaylistFile(); QString GetMediaFilename(int seqno); void GetStreamTimestamp(uint8_t *bytes,uint64_t frames); void AddTextIdFrame(TagLib::ID3v2::Tag *tag,const QString &id, const QString &value) const; void AddTXXXFrame(TagLib::ID3v2::Tag *tag,const QString &desc, const QString &value) const; QDir *hls_temp_dir; QString hls_playlist_filename; int hls_sequence_head; int hls_sequence_back; std::map hls_media_durations; std::map hls_media_datetimes; std::map hls_media_killtimes; QString hls_media_filename; FILE *hls_media_handle; uint64_t hls_media_frames; uint64_t hls_total_media_frames; QString hls_put_directory; QString hls_put_basename; QString hls_put_basestamp; NetConveyor *hls_conveyor; QDateTime hls_start_datetime; QByteArray hls_metadata_tag; bool hls_metadata_updated; Config *hls_config; }; #endif // HLSCONNECTOR_H GlassCoder-2.0.1/src/glasscoder/httpconnection.cpp000066400000000000000000000375141425562563600222500ustar00rootroot00000000000000// httpconnection.cpp // // HTTP connection state for HttpServer // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include "httpserver.h" HttpConnection::HttpConnection(int id,QTcpSocket *sock,bool dump_trans, QObject *parent) : QObject(parent) { conn_id=id; conn_dump_transactions=dump_trans; conn_method=HttpConnection::None; conn_websocket=false; conn_major_protocol_version=0; conn_minor_protocol_version=0; conn_host_port=0; conn_auth_type=HttpConnection::AuthNone; conn_content_length=0; conn_content_type="application/octet-stream"; conn_socket=sock; conn_cgi_process=NULL; conn_cgi_headers_active=true; conn_parse_state=0; conn_app_socket_message=new SocketMessage(); conn_cntl_socket_message=new SocketMessage(); } HttpConnection::~HttpConnection() { delete conn_cntl_socket_message; delete conn_app_socket_message; if(conn_cgi_process!=NULL) { delete conn_cgi_process; } delete conn_socket; } int HttpConnection::id() const { return conn_id; } unsigned HttpConnection::majorProtocolVersion() const { return conn_major_protocol_version; } unsigned HttpConnection::minorProtocolVersion() const { return conn_minor_protocol_version; } bool HttpConnection::protocolAtLeast(int major,int minor) const { return QString().sprintf("%u.%u", conn_major_protocol_version,conn_minor_protocol_version). toFloat()<=QString().sprintf("%u.%u",major,minor).toFloat(); } bool HttpConnection::setProtocolVersion(const QString &str) { QStringList f0=str.split("."); bool ok=false; if(f0.size()!=2) { return false; } conn_major_protocol_version=f0[0].toInt(&ok); if(!ok) { return false; } conn_minor_protocol_version=f0[1].toInt(&ok); if(!ok) { return false; } return true; } HttpConnection::Method HttpConnection::method() const { return conn_method; } void HttpConnection::setMethod(Method meth) { conn_method=meth; } bool HttpConnection::isWebsocket() const { return conn_websocket; } void HttpConnection::setWebsocket(bool state) { conn_websocket=state; } QString HttpConnection::uri() const { return conn_uri; } void HttpConnection::setUri(const QString &uri) { conn_uri=uri; } QString HttpConnection::hostName() const { return conn_host_name; } uint16_t HttpConnection::hostPort() const { return conn_host_port; } bool HttpConnection::setHost(const QString &str) { QStringList f0=str.split(":"); bool ok=false; conn_socket_close_status=1005; if(f0.size()>2) { return false; } conn_host_name=f0[0]; if(f0.size()==2) { conn_host_port=f0[1].toUInt(&ok); if((!ok)||(conn_host_port==0)) { return false; } } else { conn_host_port=80; } return true; } HttpConnection::AuthType HttpConnection::authType() const { return conn_auth_type; } QString HttpConnection::authName() const { return conn_auth_name; } QString HttpConnection::authPassword() const { return conn_auth_password; } bool HttpConnection::setAuthorization(const QString &str) { bool ret=false; QStringList f0=str.trimmed().split(" "); if((f0[0].toLower()=="basic")&&(f0.size()==2)) { QStringList f1=QString(QByteArray::fromBase64(f0[1].toUtf8())). split(":",QString::KeepEmptyParts); if(f1.size()>=2) { conn_auth_name=f1[0]; f1.erase(f1.begin()); conn_auth_password=f1.join(":"); ret=true; } } return ret; } int64_t HttpConnection::contentLength() const { return conn_content_length; } void HttpConnection::setContentLength(int64_t len) { conn_content_length=len; } QString HttpConnection::contentType() const { return conn_content_type; } void HttpConnection::setContentType(const QString &mimetype) { conn_content_type=mimetype; } QString HttpConnection::referrer() const { return conn_referrer; } void HttpConnection::setReferrer(const QString &str) { conn_referrer=str; } QString HttpConnection::subProtocol() const { return conn_sub_protocol; } void HttpConnection::setSubProtocol(const QString &str) { conn_sub_protocol=str; } QString HttpConnection::upgrade() const { return conn_upgrade; } void HttpConnection::setUpgrade(const QString &str) { conn_upgrade=str; } QString HttpConnection::userAgent() const { return conn_user_agent; } void HttpConnection::setUserAgent(const QString &str) { conn_user_agent=str; } uint16_t HttpConnection::socketCloseStatus() const { return conn_socket_close_status; } void HttpConnection::setSocketCloseStatus(uint16_t status) { conn_socket_close_status=status; } QByteArray HttpConnection::socketCloseBody() const { return conn_socket_close_body; } void HttpConnection::setSocketCloseBody(const QByteArray &data) { conn_socket_close_body=data; } QStringList HttpConnection::headerNames() const { return conn_header_names; } QStringList HttpConnection::headerValues() const { return conn_header_values; } QString HttpConnection::headerValue(const QString &name) const { for(int i=0;i=0) { env.insert("CONTENT_LENGTH", QString().sprintf("%ld",contentLength())); env.insert("CONTENT_TYPE",contentType()); } env.insert("GATEWAY_INTERFACE","1.1"); env.insert("HTTP_HOST",socket()->localAddress().toString()); if(!referrer().isEmpty()) { env.insert("HTTP_REFERER",referrer()); } if(!userAgent().isEmpty()) { env.insert("HTTP_USER_AGENT",userAgent()); } env.insert("REMOTE_ADDR",socket()->peerAddress().toString()); env.insert("REMOTE_HOST",socket()->peerAddress().toString()); env.insert("REMOTE_PORT",QString().sprintf("%u",0xFFFF&socket()->peerPort())); if(method()==HttpConnection::Post) { env.insert("REQUEST_METHOD","POST"); } else { env.insert("REQUEST_METHOD","GET"); } env.insert("REQUEST_URI",uri()); env.insert("SCRIPT_FILENAME",filename); env.insert("SCRIPT_NAME",filename); env.insert("SERVER_ADMIN","noone@nowhere.com"); env.insert("SERVER_NAME",socket()->localAddress().toString()); env.insert("SERVER_PORT", QString().sprintf("%u",0xFFFF&socket()->localPort())); env.insert("SERVER_SOFTWARE",QString("Webhost-")+VERSION); conn_cgi_process->setProcessEnvironment(env); conn_cgi_process->start(filename); } void HttpConnection::sendResponseHeader(int stat_code,const QString &mimetype) { QString statline=QString().sprintf("HTTP/1.1 %d ",stat_code)+ HttpConnection::statusText(stat_code)+"\r\n"; if(conn_dump_transactions) { fprintf(stderr,"STATUS-LINE: %s",(const char *)statline.toUtf8()); } socket()->write(statline.toUtf8()); sendHeader("Date",HttpConnection:: datetimeStamp(QDateTime(QDate::currentDate(),QTime::currentTime()))); sendHeader("Server",QString("Webhost/")+VERSION); sendHeader("Connection","close");// FIXME: Implement persistent connections if(!mimetype.isEmpty()) { sendHeader("Content-Type",mimetype); } } void HttpConnection::sendResponse(int stat_code, const QStringList &hdr_names, const QStringList &hdr_values, const QByteArray &body, const QString &mimetype) { sendResponseHeader(stat_code,mimetype); if(body.length()>0) { sendHeader("Content-Length",QString().sprintf("%d",body.length())); } for(int i=0;iwrite(body); } void HttpConnection::sendResponse(int stat_code, const QByteArray &body, const QString &mimetype) { sendResponse(stat_code,QStringList(),QStringList(),body,mimetype); } void HttpConnection::sendError(int stat_code,const QString &msg, const QStringList &hdr_names, const QStringList &hdr_values) { QString err_text=msg; if(err_text.isEmpty()) { err_text= QString().sprintf("%d ",stat_code)+HttpConnection::statusText(stat_code); } sendResponse(stat_code,hdr_names,hdr_values,err_text.toUtf8()); } QTcpSocket *HttpConnection::socket() { return conn_socket; } SocketMessage *HttpConnection::appSocketMessage() { return conn_app_socket_message; } SocketMessage *HttpConnection::cntlSocketMessage() { return conn_cntl_socket_message; } void HttpConnection::cgiStartedData() { if(method()==HttpConnection::Post) { conn_cgi_process->write(body()); conn_cgi_process->write("\r\n"); } } void HttpConnection::cgiReadyReadData() { QByteArray data; if(conn_cgi_headers_active) { data=conn_cgi_process->readLine().trimmed(); if(data.length()==0) { sendResponseHeader(200); for(int i=0;iwrite((conn_cgi_headers[i]+"\r\n").toUtf8()); } socket()->write("\r\n",2); conn_cgi_headers_active=false; while(conn_cgi_process->bytesAvailable()>0) { data=conn_cgi_process->read(1024); socket()->write(data); } } else { conn_cgi_headers.push_back(data); } } else { while(conn_cgi_process->bytesAvailable()>0) { data=conn_cgi_process->read(1024); socket()->write(data); } } } void HttpConnection::cgiFinishedData(int exit_code, QProcess::ExitStatus status) { if(status!=QProcess::NormalExit) { sendError(500,"CGI process crashed"); } else { if(exit_code!=0) { sendError(500,QString(). sprintf("CGI process returned non-zero exit code [%d]",exit_code)); } else { cgiReadyReadData(); } } socket()->close(); emit cgiFinished(); } void HttpConnection::cgiErrorData(QProcess::ProcessError err) { QString err_text=QString().sprintf("unknown process error %d",err); switch(err) { case QProcess::FailedToStart: err_text="failed to start"; break; case QProcess::Crashed: err_text="crashed"; break; case QProcess::Timedout: err_text="Timed out"; break; case QProcess::WriteError: err_text="write error"; break; case QProcess::ReadError: err_text="read error"; break; case QProcess::UnknownError: err_text="unknown error"; break; } sendError(500,"500 CGI process error ["+err_text+"]"); socket()->close(); emit cgiFinished(); } void HttpConnection::sendHeader(const QString &name,const QString &value) { if(name.isEmpty()&&value.isEmpty()) { if(conn_dump_transactions) { fprintf(stderr,"HEADER:\r\n"); } socket()->write("\r\n"); } else { QString line=name+": "+value+"\r\n"; if(conn_dump_transactions) { fprintf(stderr,"HEADER: %s",(const char *)line.toUtf8()); } socket()->write(line.toUtf8()); } } int HttpConnection::parseState() const { return conn_parse_state; } void HttpConnection::setParseState(int state) { conn_parse_state=state; } QString HttpConnection::statusText(int stat_code) { // // From RFC 2616 Section 10 // QString ret="Unknown"; if((stat_code>=100)&&(stat_code<200)) { ret="Continue"; } if((stat_code>=200)&&(stat_code<300)) { ret="OK"; } if((stat_code>=300)&&(stat_code<400)) { ret="Multiple Choices"; } if((stat_code>=400)&&(stat_code<500)) { ret="Bad Request"; } if((stat_code>=500)&&(stat_code<600)) { ret="Internal Server Error"; } switch(stat_code) { case 100: ret="Continue"; break; case 101: ret="Switching Protocols"; break; case 200: ret="OK"; break; case 201: ret="Created"; break; case 202: ret="Accepted"; break; case 203: ret="Non-Authoritative Information"; break; case 204: ret="No Content"; break; case 205: ret="Reset Content"; break; case 206: ret="Partial Content"; break; case 300: ret="Multiple Choices"; break; case 301: ret="Moved Permanently"; break; case 302: ret="Found"; break; case 303: ret="See Other"; break; case 304: ret="Not Modified"; break; case 305: ret="Use Proxy"; break; case 306: ret="(Unused)"; break; case 307: ret="Temporary Redirect"; break; case 400: ret="Bad Request"; break; case 401: ret="Unauthorized"; break; case 402: ret="Payment Required"; break; case 403: ret="Forbidden"; break; case 404: ret="Not Found"; break; case 405: ret="Method Not Allowed"; break; case 406: ret="Not Acceptable"; break; case 407: ret="Proxy Authentication Required"; break; case 408: ret="Request Timeout"; break; case 409: ret="Conflict"; break; case 410: ret="Gone"; break; case 411: ret="Length Required"; break; case 412: ret="Precondition Failed"; break; case 413: ret="Request Entity Too Large"; break; case 414: ret="Request-URI Too Long"; break; case 415: ret="Unsupported Media Type"; break; case 416: ret="Requested Range Not Satisfiable"; break; case 417: ret="Expectation Failed"; break; case 500: ret="Internal Server Error"; break; case 501: ret="Not Implemented"; break; case 502: ret="Bad Gateway"; break; case 503: ret="Service Unavailable"; break; case 504: ret="Gateway Timeout"; break; case 505: ret="HTTP Version Not Supported"; break; } return ret; } int HttpConnection::timezoneOffset() { time_t t=time(&t); struct tm *tm=localtime(&t); time_t local_time=3600*tm->tm_hour+60*tm->tm_min+tm->tm_sec; tm=gmtime(&t); time_t gmt_time=3600*tm->tm_hour+60*tm->tm_min+tm->tm_sec; return gmt_time-local_time; } QString HttpConnection::datetimeStamp(const QDateTime &dt) { return dt.addSecs(HttpConnection::timezoneOffset()). toString("ddd, dd MMM yyyy hh:mm:ss")+" GMT"; } GlassCoder-2.0.1/src/glasscoder/httpconnection.h000066400000000000000000000113201425562563600217000ustar00rootroot00000000000000// httpconnection.h // // HTTP connection state for HttpServer // // (C) Copyright 2016-2019 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef HTTPCONNECTION_H #define HTTPCONNECTION_H #include #include #include #include #include #include #include #include "socketmessage.h" class HttpConnection : public QObject { Q_OBJECT; public: enum Method {None=0,Get=1,Post=2,Head=3,Put=4,Delete=5}; enum AuthType {AuthNone=0,AuthBasic=1,AuthDigest=2}; HttpConnection(int id,QTcpSocket *sock,bool dump_trans,QObject *parent=0); ~HttpConnection(); int id() const; unsigned majorProtocolVersion() const; unsigned minorProtocolVersion() const; bool protocolAtLeast(int major,int minor) const; bool setProtocolVersion(const QString &str); Method method() const; void setMethod(Method meth); bool isWebsocket() const; void setWebsocket(bool state); QString uri() const; void setUri(const QString &uri); QString hostName() const; uint16_t hostPort() const; bool setHost(const QString &str); AuthType authType() const; QString authName() const; QString authPassword() const; bool setAuthorization(const QString &str); int64_t contentLength() const; void setContentLength(int64_t len); QString contentType() const; void setContentType(const QString &mimetype); QString referrer() const; void setReferrer(const QString &str); QString subProtocol() const; void setSubProtocol(const QString &str); QString upgrade() const; void setUpgrade(const QString &str); QString userAgent() const; void setUserAgent(const QString &str); uint16_t socketCloseStatus() const; void setSocketCloseStatus(uint16_t); QByteArray socketCloseBody() const; void setSocketCloseBody(const QByteArray &data); QStringList headerNames() const; QStringList headerValues() const; QString headerValue(const QString &name) const; void addHeader(const QString &name,const QString &value); QByteArray body() const; void appendBody(const QByteArray &data); QString dump() const; QTcpSocket *socket(); SocketMessage *appSocketMessage(); SocketMessage *cntlSocketMessage(); void startCgiScript(const QString &filename); void sendResponseHeader(int stat_code,const QString &mimetype=""); void sendResponse(int stat_code, const QStringList &hdr_names,const QStringList &hdr_values, const QByteArray &body=QByteArray(), const QString &mimetype=""); void sendResponse(int stat_code, const QByteArray &body=QByteArray(), const QString &mimetype=""); void sendError(int stat_code,const QString &msg="", const QStringList &hdr_names=QStringList(), const QStringList &hdr_values=QStringList()); void sendHeader(const QString &name="",const QString &value=""); int parseState() const; void setParseState(int state); static QString statusText(int stat_code); static int timezoneOffset(); static QString datetimeStamp(const QDateTime &dt); signals: void cgiFinished(); private slots: void cgiStartedData(); void cgiReadyReadData(); void cgiFinishedData(int exit_code,QProcess::ExitStatus status); void cgiErrorData(QProcess::ProcessError err); private: int conn_id; Method conn_method; bool conn_websocket; unsigned conn_major_protocol_version; unsigned conn_minor_protocol_version; QString conn_uri; QString conn_host_name; uint16_t conn_host_port; AuthType conn_auth_type; QString conn_auth_name; QString conn_auth_password; int64_t conn_content_length; QString conn_content_type; QString conn_referrer; QString conn_sub_protocol; QString conn_upgrade; QString conn_user_agent; uint16_t conn_socket_close_status; QByteArray conn_socket_close_body; QStringList conn_header_names; QStringList conn_header_values; QByteArray conn_body; QTcpSocket *conn_socket; SocketMessage *conn_app_socket_message; SocketMessage *conn_cntl_socket_message; QProcess *conn_cgi_process; QStringList conn_cgi_headers; bool conn_cgi_headers_active; int conn_parse_state; bool conn_dump_transactions; }; #endif // HTTPCONNECTION_H GlassCoder-2.0.1/src/glasscoder/httpserver.cpp000066400000000000000000000517411425562563600214150ustar00rootroot00000000000000// httpserver.cpp // // HTTP Server // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include "httpserver.h" #include "profile.h" HttpServer::HttpServer(QObject *parent) : QObject(parent) { http_dump_transactions=getenv("Webhost_ShowHttpTransaction")!=NULL; http_server=new QTcpServer(this); connect(http_server,SIGNAL(newConnection()),this,SLOT(newConnectionData())); http_read_mapper=new QSignalMapper(this); connect(http_read_mapper,SIGNAL(mapped(int)),this,SLOT(readyReadData(int))); http_disconnect_mapper=new QSignalMapper(this); connect(http_disconnect_mapper,SIGNAL(mapped(int)), this,SLOT(disconnectedData(int))); http_cgi_finished_mapper=new QSignalMapper(this); connect(http_cgi_finished_mapper,SIGNAL(mapped(int)), this,SLOT(cgiFinishedData(int))); http_garbage_timer=new QTimer(this); http_garbage_timer->setSingleShot(true); connect(http_garbage_timer,SIGNAL(timeout()),this,SLOT(garbageData())); } HttpServer::~HttpServer() { for(unsigned i=0;ilisten(QHostAddress::Any,port); } bool HttpServer::listen(const QHostAddress &iface,uint16_t port) { return http_server->listen(iface,port); } void HttpServer::sendSocketMessage(int conn_id,SocketMessage::OpCode opcode, const QByteArray &data) { QByteArray packet; HttpConnection *conn=http_connections[conn_id]; // // OpCode // packet.append(0x80|(0x0F&opcode)); // // Payload Length // // WARNING: This breaks with messages longer than 4,294,967,295 bytes! // See RFC 6455 Section 5.2 // // DO NOT MASK return messages, as per RFC 6455 Sect. 5.1 // if(data.length()<126) { packet.append(data.length()); } else { if(data.length()<65536) { packet.append(126); packet.append(data.length()>>8); packet.append(0xFF&data.length()); } else { packet.append(127); packet.append((char)0); packet.append((char)0); packet.append((char)0); packet.append((char)0); packet.append(data.length()>>24); packet.append(0xFF&(data.length()>>16)); packet.append(0xFF&(data.length()>>8)); packet.append(0xFF&data.length()); } } // // Payload // packet.append(data); conn->socket()->write(packet); } void HttpServer::sendSocketMessage(int conn_id,const QByteArray &data) { sendSocketMessage(conn_id,SocketMessage::Binary,data); } void HttpServer::sendSocketMessage(int conn_id,const QString &str) { sendSocketMessage(conn_id,SocketMessage::Text,str.toUtf8()); } void HttpServer::closeSocketConnection(int conn_id,uint16_t status, const QByteArray &body) { QByteArray payload; payload.append(0xFF&(status>>8)); payload.append(0xFF&status); payload.append(body); sendSocketMessage(conn_id,SocketMessage::Close,payload); } QStringList HttpServer::userRealms() const { QStringList ret; for(std::map >::const_iterator it=http_users.begin(); it!=http_users.end();it++) { ret.push_back(it->first); } return ret; } QStringList HttpServer::userNames(const QString &realm) { QStringList ret; for(unsigned i=0;iname()); } return ret; } void HttpServer::addUser(const QString &realm,const QString &name, const QString &passwd) { for(unsigned i=0;iname()==name) { http_users[realm][i]->setPassword(passwd); return; } } http_users[realm].push_back(new HttpUser(name,passwd)); } void HttpServer::removeUser(const QString &realm,const QString &name) { for(unsigned i=0;iname()==name) { delete http_users[realm][i]; http_users[realm].erase(http_users[realm].begin()+i); return; } } } bool HttpServer::loadUsers(const QString &filename) { Profile *p=new Profile(); if(!p->setSource(filename)) { return false; } for(std::map >::const_iterator it=http_users.begin(); it!=http_users.end();it++) { for(unsigned i=0;isecond.size();i++) { delete it->second[i]; } } http_users.clear(); int count=0; QString realm; QString name; QString passwd; bool ok=false; QString section=QString().sprintf("WebHostUser%d",count+1); realm=p->stringValue(section,"Realm","",&ok); while(ok) { name=p->stringValue(section,"Name"); passwd=p->stringValue(section,"Password"); addUser(realm,name,passwd); count++; section=QString().sprintf("WebHostUser%d",count+1); realm=p->stringValue(section,"Realm","",&ok); } return true; } bool HttpServer::saveUsers(const QString &filename) { FILE *f=NULL; int count=0; if((f=fopen((filename+".back").toUtf8(),"w"))==NULL) { return false; } for(std::map >::const_iterator it=http_users.begin(); it!=http_users.end();it++) { for(unsigned i=0;isecond.size();i++) { fprintf(f,"[WebHostUser%d]\n",count+1); fprintf(f,"Realm=%s\n",(const char *)it->first.toUtf8()); fprintf(f,"Name=%s\n",(const char *)it->second[i]->name().toUtf8()); fprintf(f,"Password=%s\n", (const char *)it->second[i]->password().toUtf8()); fprintf(f,"\n"); count++; } } fclose(f); rename((filename+".back").toUtf8(),filename.toUtf8()); return true; } void HttpServer::addStaticSource(const QString &uri,const QString &mimetype, const QString &filename,const QString &realm) { http_static_uris.push_back(uri); http_static_mimetypes.push_back(mimetype); http_static_filenames.push_back(filename); http_static_realms.push_back(realm); } void HttpServer::addCgiSource(const QString &uri,const QString &filename, const QString &realm) { http_cgi_uris.push_back(uri); http_cgi_filenames.push_back(filename); http_cgi_realms.push_back(realm); } void HttpServer::addSocketSource(const QString &uri,const QString &proto, const QString &realm) { http_socket_uris.push_back(uri); http_socket_protocols.push_back(proto); http_socket_realms.push_back(realm); } void HttpServer::requestReceived(HttpConnection *conn) { switch(conn->method()) { case HttpConnection::Get: getRequestReceived(conn); return; case HttpConnection::Post: postRequestReceived(conn); return; case HttpConnection::Head: headRequestReceived(conn); return; case HttpConnection::Put: putRequestReceived(conn); return; case HttpConnection::Delete: deleteRequestReceived(conn); return; case HttpConnection::None: break; } fprintf(stderr,"URI \"%s\" not found\n", (const char *)conn->uri().toUtf8()); conn->sendError(404,"404 Not found"); } void HttpServer::getRequestReceived(HttpConnection *conn) { fprintf(stderr,"URI \"%s\" not found\n", (const char *)conn->uri().toUtf8()); conn->sendError(404,"404 Not found"); } void HttpServer::postRequestReceived(HttpConnection *conn) { fprintf(stderr,"URI \"%s\" not found\n", (const char *)conn->uri().toUtf8()); conn->sendError(404,"404 Not found"); } void HttpServer::headRequestReceived(HttpConnection *conn) { fprintf(stderr,"URI \"%s\" not found\n", (const char *)conn->uri().toUtf8()); conn->sendError(404,"404 Not found"); } void HttpServer::putRequestReceived(HttpConnection *conn) { fprintf(stderr,"URI \"%s\" not found\n", (const char *)conn->uri().toUtf8()); conn->sendError(404,"404 Not found"); } void HttpServer::deleteRequestReceived(HttpConnection *conn) { fprintf(stderr,"URI \"%s\" not found\n", (const char *)conn->uri().toUtf8()); conn->sendError(404,"404 Not found"); } bool HttpServer::authenticateUser(const QString &realm,const QString &name, const QString &passwd) { for(unsigned i=0;iisValid(name,passwd)) { return true; } } return false; } void HttpServer::newConnectionData() { int id=-1; for(unsigned i=0;inextPendingConnection(), http_dump_transactions,this); id=i; break; } } if(id<0) { id=http_connections.size(); http_connections. push_back(new HttpConnection(id,http_server->nextPendingConnection(), http_dump_transactions,this)); } connect(http_connections[id]->socket(),SIGNAL(readyRead()), http_read_mapper,SLOT(map())); http_read_mapper->setMapping(http_connections[id]->socket(),id); connect(http_connections[id]->socket(),SIGNAL(disconnected()), http_disconnect_mapper,SLOT(map())); http_disconnect_mapper->setMapping(http_connections[id]->socket(),id); connect(http_connections[id],SIGNAL(cgiFinished()), http_cgi_finished_mapper,SLOT(map())); http_cgi_finished_mapper->setMapping(http_connections[id]->socket(),id); } void HttpServer::readyReadData(int id) { HttpConnection *conn=http_connections[id]; switch(conn->parseState()) { case 0: ReadMethodLine(conn); break; case 1: ReadHeaders(conn); break; case 2: ReadBody(conn); break; case 10: ReadWebsocket(conn); break; } } void HttpServer::disconnectedData(int id) { HttpConnection *conn=http_connections[id]; if(http_connections[id]->isWebsocket()) { emit socketConnectionClosed(id,conn->socketCloseStatus(), conn->socketCloseBody()); } http_garbage_timer->start(0); } void HttpServer::cgiFinishedData(int id) { http_garbage_timer->start(0); } void HttpServer::garbageData() { for(unsigned i=0;isocket()->state()!= QAbstractSocket::ConnectedState) { delete http_connections[i]; http_connections[i]=NULL; } } } } void HttpServer::ReadMethodLine(HttpConnection *conn) { QStringList hdr_names; QStringList hdr_values; QString line; if(conn->socket()->canReadLine()) { line=QString(conn->socket()->readLine()).trimmed(); if(http_dump_transactions) { fprintf(stderr,"REQUEST-LINE: %s\n",(const char *)line.toUtf8()); } QStringList f0=line.split(" "); if(f0.size()!=3) { conn->sendError(400,"400 Bad Request
Malformed HTTP request"); return; } // // The HTTP Method // if(f0[0].trimmed()=="GET") { conn->setMethod(HttpConnection::Get); } if(f0[0].trimmed()=="POST") { conn->setMethod(HttpConnection::Post); } if(f0[0].trimmed()=="HEAD") { conn->setMethod(HttpConnection::Head); } if(conn->method()==HttpConnection::None) { hdr_names.push_back("Allow"); hdr_values.push_back("GET"); if(IsCgiScript(f0[1].trimmed())) { hdr_values.back()+=",POST"; } conn->sendError(501,"501 Not implemented",hdr_names,hdr_values); return; } conn->setUri(f0[1].trimmed()); QStringList f1=f0[2].trimmed().split("/"); if(f1.size()!=2) { conn->sendError(400,"400 Bad Request
Malformed HTTP request"); return; } if(!conn->setProtocolVersion(f1[1])) { conn->sendError(400,"400 Bad Request
Malformed HTTP request"); return; } if((conn->majorProtocolVersion()!=1)||(conn->minorProtocolVersion()>1)) { conn->sendError(505,"505 HTTP Version Not Supported
This server only supports HTTP v1.x"); return; } conn->setParseState(1); ReadHeaders(conn); } } void HttpServer::ReadHeaders(HttpConnection *conn) { QStringList hdr_names; QStringList hdr_values; QString line; bool ok=false; while(conn->socket()->canReadLine()) { line=QString(conn->socket()->readLine()).trimmed(); if(http_dump_transactions) { fprintf(stderr,"HEADER: %s\n",(const char *)line.toUtf8()); } bool processed=false; QStringList f0=line.split(": ",QString::KeepEmptyParts); if(f0.size()>=2) { QString hdr=f0[0].trimmed().toLower(); f0.erase(f0.begin()); QString value=f0.join(": "); if(hdr=="authorization") { conn->setAuthorization(value); processed=true; } if(hdr=="content-length") { int64_t len=value.toLongLong(&ok); if(!ok) { conn-> sendError(400,"400 Bad Request Malformed Content-Length: header"); return; } conn->setContentLength(len); processed=true; } if(hdr=="content-type") { QStringList f1=value.split(";"); conn->setContentType(f1[0]); processed=true; } if(hdr=="host") { if(!conn->setHost(value)) { conn->sendError(400,"400 Bad Request Malformed Host: header"); return; } processed=true; } if(hdr=="referer") { conn->setReferrer(value); processed=true; } if(hdr=="sec-websocket-protocol") { conn->setSubProtocol(value); processed=true; } if(hdr=="upgrade") { conn->setUpgrade(value); processed=true; } if(hdr=="user-agent") { conn->setUserAgent(value); processed=true; } if(!processed) { conn->addHeader(hdr,value); } } else { if(line.isEmpty()) { // End of headers if((conn->minorProtocolVersion()==1)&&(conn->hostPort()==0)) { conn-> sendError(400,"400 Bad Request -- Missing/malformed Host: header"); return; } if(conn->method()==HttpConnection::Get) { ProcessRequest(conn); } else { conn->setParseState(2); ReadBody(conn); } return; } } } } void HttpServer::ReadWebsocket(HttpConnection *conn) { QByteArray data=conn->socket()->readAll(); uint32_t plen=0; int offset=0; SocketMessage *msg=NULL; if((0x70&data[0])!=0) { // Extension bits set conn->socket()->close(); } if((0x80&data[1])==0) { // Mask not set conn->socket()->close(); } SocketMessage::OpCode opcode=(SocketMessage::OpCode)(0x0F&data[0]); if(SocketMessage::isControlMessage(opcode)) { msg=conn->cntlSocketMessage(); } else { msg=conn->appSocketMessage(); } bool finished=(0x80&data[0])!=0; if(opcode!=SocketMessage::Continuation) { msg->setOpCode(opcode); msg->clearPayload(); } // // Payload Length // // WARNING: This breaks with messages longer than 4,294,967,295 bytes! // See RFC 6455 Section 5.2 // plen=0x7F&data[1]; if(plen==126) { plen=((0xFF&data[2])<<8)+(0xFF&data[3]); offset=2; } else { if(plen==127) { plen=((0xFF&data[6])<<24)+((0xFF&data[7])<<16)+((0xFF&data[8])<<8)+ (0xFF&data[9]); offset=8; } } // // Extract Payload // for(uint32_t i=0;iappendPayload(data[offset+6+i]^data[offset+2+(i%4)]); } // // Disposition // switch(msg->opCode()) { case SocketMessage::Text: case SocketMessage::Binary: case SocketMessage::AppReserv3: case SocketMessage::AppReserv4: case SocketMessage::AppReserv5: case SocketMessage::AppReserv6: case SocketMessage::AppReserv7: if(finished) { emit socketMessageReceived(conn->id(),msg); } break; case SocketMessage::Close: if(msg->payload().length()>=2) { uint16_t status=((0xFF&msg->payload()[0])<<8)+(0xFF&msg->payload()[1]); conn->setSocketCloseStatus(status); conn->setSocketCloseBody(msg->payload(). mid(2,msg->payload().length()-2).constData()); } sendSocketMessage(conn->id(),SocketMessage::Close,msg->payload()); conn->socket()->close(); break; case SocketMessage::Ping: sendSocketMessage(conn->id(),SocketMessage::Pong,msg->payload()); break; case SocketMessage::Continuation: case SocketMessage::Pong: case SocketMessage::CntlReserv11: case SocketMessage::CntlReserv12: case SocketMessage::CntlReserv13: case SocketMessage::CntlReserv14: case SocketMessage::CntlReserv15: break; } } void HttpServer::ReadBody(HttpConnection *conn) { conn->appendBody(conn->socket()-> read(conn->contentLength()-conn->body().length())); if(conn->body().length()==conn->contentLength()) { ProcessRequest(conn); } } void HttpServer::ProcessRequest(HttpConnection *conn) { if(conn->upgrade().toLower()=="websocket") { for(int i=0;iuri()==http_socket_uris[i])|| (conn->subProtocol()==http_socket_protocols[i])) { StartWebsocket(conn,i); return; } } requestReceived(conn); return; } for(int i=0;iuri()==http_static_uris[i]) { SendStaticSource(conn,i); return; } } for(int i=0;iuri()==http_cgi_uris[i]) { SendCgiSource(conn,i); return; } } requestReceived(conn); } void HttpServer::StartWebsocket(HttpConnection *conn,int n) { QStringList hdrs=conn->headerNames(); QStringList values=conn->headerValues(); QString key; QByteArray resp; if(!AuthenticateRealm(conn,http_socket_realms[n], conn->authName(),conn->authPassword())) { return; } for(int i=0;iprotocolAtLeast(1,1))||(conn->method()!=HttpConnection::Get)|| key.isEmpty()) { conn->sendError(400); return; } resp=GetWebsocketHandshake(key); if(resp.length()==0) { conn->sendError(500,"500 Internal processing error"); return; } QString statline="HTTP/1.1 101 Switching Protocols\r\n"; if(http_dump_transactions) { fprintf(stderr,"STATUS-LINE: %s",(const char *)statline.toUtf8()); } conn->socket()->write(statline.toUtf8()); conn->sendHeader("Upgrade","websocket"); conn->sendHeader("Connection","upgrade"); conn->sendHeader("Sec-WebSocket-Accept",resp); conn->sendHeader("Sec-WebSocket-Protocol",conn->subProtocol()); conn->sendHeader(); conn->setParseState(10); conn->setWebsocket(true); emit newSocketConnection(conn->id(),conn->uri(),conn->subProtocol()); } void HttpServer::SendStaticSource(HttpConnection *conn,int n) { QFile file(http_static_filenames[n]); QByteArray data; if(!AuthenticateRealm(conn,http_static_realms[n], conn->authName(),conn->authPassword())) { return; } if(!file.exists()) { conn->sendError(404); return; } if(!file.open(QIODevice::ReadOnly)) { conn->sendError(500); return; } switch(conn->method()) { case HttpConnection::Get: case HttpConnection::Post: data=file.readAll(); conn->sendResponse(200,data,http_static_mimetypes[n]); file.close(); return; case HttpConnection::Head: conn->sendResponseHeader(200,http_static_mimetypes[n]); conn->sendHeader(); return; case HttpConnection::Put: case HttpConnection::Delete: case HttpConnection::None: break; } conn->sendResponse(405,"Method not allowed"); } void HttpServer::SendCgiSource(HttpConnection *conn,int n) { if(!AuthenticateRealm(conn,http_cgi_realms[n], conn->authName(),conn->authPassword())) { return; } if(conn->method()==HttpConnection::Head) { conn->sendResponse(405,"405 Method Not Allowd"); return; } conn->startCgiScript(http_cgi_filenames[n]); } bool HttpServer::IsCgiScript(const QString &uri) const { for(int i=0;isendResponse(401,hdrs,values,"401 Unauthorized"); return false; } QByteArray HttpServer::GetWebsocketHandshake(const QString &key) const { EVP_MD_CTX *mdctx; const EVP_MD *md; unsigned char md_value[EVP_MAX_MD_SIZE]; unsigned md_len; OpenSSL_add_all_digests(); if((md=EVP_get_digestbyname("sha1"))==NULL) { return QByteArray(); } mdctx=EVP_MD_CTX_create(); EVP_DigestInit_ex(mdctx,md,NULL); EVP_DigestUpdate(mdctx,(key+WEBSOCKET_MAGIC_STRING).toUtf8(), (key+WEBSOCKET_MAGIC_STRING).length()); EVP_DigestFinal_ex(mdctx,md_value,&md_len); EVP_MD_CTX_destroy(mdctx); return QByteArray((const char *)md_value).toBase64(); } GlassCoder-2.0.1/src/glasscoder/httpserver.h000066400000000000000000000105621425562563600210560ustar00rootroot00000000000000// httpserver.h // // HTTP Server // // (C) Copyright 2016-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef HTTPSERVER_H #define HTTPSERVER_H #include #include #include #include #include #include #include #include #include #include "httpconnection.h" #include "httpuser.h" #include "socketmessage.h" #define WEBSOCKET_VERSION 13 #define WEBSOCKET_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" class HttpServer : public QObject { Q_OBJECT; public: HttpServer(QObject *parent=0); ~HttpServer(); bool listen(uint16_t port); bool listen(const QHostAddress &iface,uint16_t port); void sendSocketMessage(int conn_id,SocketMessage::OpCode opcode, const QByteArray &data); void sendSocketMessage(int conn_id,const QByteArray &data); void sendSocketMessage(int conn_id,const QString &str); void closeSocketConnection(int conn_id,uint16_t status, const QByteArray &body=QByteArray()); QStringList userRealms() const; QStringList userNames(const QString &realm); void addUser(const QString &realm,const QString &name,const QString &passwd); void removeUser(const QString &realm,const QString &name); bool loadUsers(const QString &filename); bool saveUsers(const QString &filename); void addStaticSource(const QString &uri,const QString &mimetype, const QString &filename,const QString &realm=""); void addCgiSource(const QString &uri,const QString &filename, const QString &realm=""); void addSocketSource(const QString &uri,const QString &proto, const QString &realm=""); signals: void newSocketConnection(int conn_id,const QString &uri,const QString &proto); void socketMessageReceived(int conn_id,SocketMessage *msg); void socketConnectionClosed(int conn_id,uint16_t stat_code, const QByteArray &body); protected: virtual void requestReceived(HttpConnection *conn); virtual void getRequestReceived(HttpConnection *conn); virtual void postRequestReceived(HttpConnection *conn); virtual void headRequestReceived(HttpConnection *conn); virtual void putRequestReceived(HttpConnection *conn); virtual void deleteRequestReceived(HttpConnection *conn); virtual bool authenticateUser(const QString &realm,const QString &name, const QString &passwd); private slots: void newConnectionData(); void readyReadData(int id); void disconnectedData(int id); void cgiFinishedData(int id); void garbageData(); private: void ReadMethodLine(HttpConnection *conn); void ReadHeaders(HttpConnection *conn); void ReadBody(HttpConnection *conn); void ReadWebsocket(HttpConnection *conn); void ProcessRequest(HttpConnection *conn); void StartWebsocket(HttpConnection *conn,int n); void SendStaticSource(HttpConnection *conn,int n); void SendCgiSource(HttpConnection *conn,int n); bool IsCgiScript(const QString &uri) const; bool AuthenticateRealm(HttpConnection *conn,const QString &realm, const QString &name,const QString &passwd); QByteArray GetWebsocketHandshake(const QString &key) const; QStringList http_static_filenames; QStringList http_static_uris; QStringList http_static_mimetypes; QStringList http_static_realms; QStringList http_cgi_filenames; QStringList http_cgi_uris; QStringList http_cgi_realms; QStringList http_socket_uris; QStringList http_socket_protocols; QStringList http_socket_realms; QTcpServer *http_server; QSignalMapper *http_read_mapper; QSignalMapper *http_disconnect_mapper; QSignalMapper *http_cgi_finished_mapper; std::vector http_connections; QTimer *http_garbage_timer; std::map > http_users; bool http_dump_transactions; }; #endif // HTTPSERVER_H GlassCoder-2.0.1/src/glasscoder/httpuser.cpp000066400000000000000000000023121425562563600210530ustar00rootroot00000000000000// httpuser.cpp // // Abstract an HTTP user // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "httpuser.h" HttpUser::HttpUser(const QString &name,const QString &passwd) { user_name=name; user_password=passwd; } QString HttpUser::name() const { return user_name; } QString HttpUser::password() const { return user_password; } void HttpUser::setPassword(const QString &passwd) { user_password=passwd; } bool HttpUser::isValid(const QString &name,const QString &passwd) { return (name==user_name)&&(passwd==user_password); } GlassCoder-2.0.1/src/glasscoder/httpuser.h000066400000000000000000000021701425562563600205220ustar00rootroot00000000000000// httpuser.h // // Abstract an HTTP user // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef HTTPUSER_H #define HTTPUSER_H #include class HttpUser { public: HttpUser(const QString &name,const QString &passwd); QString name() const; QString password() const; void setPassword(const QString &passwd); bool isValid(const QString &name,const QString &passwd); private: QString user_name; QString user_password; }; #endif // HTTPUSER_H GlassCoder-2.0.1/src/glasscoder/iceconnector.cpp000066400000000000000000000171401425562563600216550ustar00rootroot00000000000000// iceconnector.cpp // // Source connector class for IceCast2 servers // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "iceconnector.h" #include "logging.h" IceConnector::IceConnector(QObject *parent) : Connector(parent) { ice_recv_buffer=""; ice_socket=NULL; // // Metadata File Conveyor // ice_conveyor=new GetConveyor(this); connect(ice_conveyor,SIGNAL(eventFinished(const QUrl &,int,int, const QStringList &)), this,SLOT(conveyorEventFinished(const QUrl &,int,int, const QStringList &))); connect(ice_conveyor, SIGNAL(error(const QUrl &,QProcess::ProcessError, const QStringList &)), this, SLOT(conveyorError(const QUrl &,QProcess::ProcessError, const QStringList &))); } IceConnector::~IceConnector() { delete ice_socket; } IceConnector::ServerType IceConnector::serverType() const { return Connector::Icecast2Server; } void IceConnector::sendMetadata(MetaEvent *e) { if(e->fieldKeys().contains("StreamTitle")) { QString url=QString("http://")+ serverUrl().host()+ QString().sprintf(":%u",serverUrl().port())+ "/admin/metadata?"+ "mount="+serverMountpoint()+"&"+ "mode=updinfo&"+ "song="+Connector::urlEncode(e->field("StreamTitle")); ice_conveyor->push(url); } } void IceConnector::connectToHostConnector(const QUrl &url) { // // Create Socket // if(ice_socket!=NULL) { ice_socket-> disconnect(SIGNAL(connected()),this,SLOT(socketConnectedData())); ice_socket-> disconnect(SIGNAL(disconnected()),this,SLOT(socketDisconnectedData())); ice_socket-> disconnect(SIGNAL(readyRead()),this,SLOT(socketReadyReadData())); ice_socket-> disconnect(SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(socketErrorData(QAbstractSocket::SocketError))); ice_socket->deleteLater(); } ice_socket=new QTcpSocket(this); connect(ice_socket,SIGNAL(connected()),this,SLOT(socketConnectedData())); connect(ice_socket,SIGNAL(disconnected()), this,SLOT(socketDisconnectedData())); connect(ice_socket,SIGNAL(readyRead()),this,SLOT(socketReadyReadData())); connect(ice_socket,SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(socketErrorData(QAbstractSocket::SocketError))); // // Initiate the Connection // ice_conveyor->setUsername(serverUsername()); ice_conveyor->setPassword(serverPassword()); ice_socket->connectToHost(url.host(),url.port()); emit unmuteRequested(); } void IceConnector::disconnectFromHostConnector() { ice_socket->disconnectFromHost(); } int64_t IceConnector::writeDataConnector(int frames,const unsigned char *data, int64_t len) { if(ice_socket->state()==QAbstractSocket::ConnectedState) { return ice_socket->write((const char *)data,len); } return len; } void IceConnector::socketConnectedData() { QString username=serverUsername(); if(username.isEmpty()) { username="source"; } WriteHeader("SOURCE "+serverMountpoint()+" HTTP/1.0"); WriteHeader(QString("Authorization: Basic ")+ Connector::base64Encode(username+":"+serverPassword())); WriteHeader("User-Agent: "+serverUserAgent()); WriteHeader("Content-Type: "+contentType()); WriteHeader("ice-name: "+streamName()); WriteHeader("ice-description: "+streamDescription()); WriteHeader("ice-genre: "+streamGenre()); WriteHeader("ice-public: "+QString().sprintf("%d",streamPublic())); WriteHeader(QString("ice-audio-info: ")+ QString().sprintf("bitrate=%u;",audioBitrate())+ QString().sprintf("channels=%u;",audioChannels())+ QString().sprintf("samplerate=%u",audioSamplerate())); WriteHeader(""); } void IceConnector::socketDisconnectedData() { setConnected(false); } void IceConnector::socketReadyReadData() { char data[1501]; int64_t n; while((n=ice_socket->read(data,1500))>0) { data[n]=0; //printf("recvd %ld bytes: |%s|\n",n,data); for(int64_t i=0;i=2)&& (ice_recv_buffer.toUtf8().at(ice_recv_buffer.length()-3)==13)) { ice_recv_buffer=ice_recv_buffer.left(ice_recv_buffer.length()-1); ProcessHeaders(ice_recv_buffer); ice_recv_buffer=""; } else { ice_recv_buffer+=data[i]; } break; default: ice_recv_buffer+=data[i]; break; } } } } void IceConnector::socketErrorData(QAbstractSocket::SocketError err) { setError(err); } void IceConnector::conveyorEventFinished(const QUrl &url,int exit_code, int resp_code,const QStringList &args) { // // Exit code handler // if(exit_code!=0) { setConnected(false); if(global_log_verbose) { Log(LOG_WARNING, QString().sprintf("curl(1) error: %s, cmd: \"curl %s\"", (const char *)Connector::curlStrError(exit_code).toUtf8(), (const char *)args.join(" ").toUtf8())); } else { Log(LOG_WARNING,QString().sprintf("CURL error: %s", (const char *)Connector::curlStrError(exit_code).toUtf8())); } } else { // // Reponse code handler // if((resp_code<200)||(resp_code>299)) { setConnected(false); if(global_log_verbose) { Log(LOG_WARNING,"curl(1) response error: "+ Connector::httpStrError(resp_code)+ ", cmd: \"curl "+args.join(" ")+"\""); } else { Log(LOG_WARNING,"curl(1) response error: "+ Connector::httpStrError(resp_code)); } } else { setConnected(true); } } } void IceConnector::conveyorError(const QUrl &url, QProcess::ProcessError err, const QStringList &args) { Log(LOG_ERR, QString().sprintf("curl(1) process error: %d, cmd: \"curl %s\"",err, (const char *)args.join(" ").toUtf8())); setConnected(false); exit(256); } void IceConnector::ProcessHeaders(const QString &hdrs) { QStringList f0; QStringList f1; QString txt; f0=hdrs.split("\r\n"); for(int i=0;i %s\n",(const char *)f0.at(i).toUtf8()); } f1=f0[i].split(" "); if(f1[0].left(7)=="HTTP/1.") { for(int i=2;iwrite((str+"\r\n").toUtf8()); } GlassCoder-2.0.1/src/glasscoder/iceconnector.h000066400000000000000000000035161425562563600213240ustar00rootroot00000000000000// iceconnector.h // // Source connector class for IceCast2 servers. // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef ICECONNECTOR_H #define ICECONNECTOR_H #include "connector.h" #include "getconveyor.h" class IceConnector : public Connector { Q_OBJECT; public: IceConnector(QObject *parent=0); ~IceConnector(); IceConnector::ServerType serverType() const; public slots: void sendMetadata(MetaEvent *e); protected: void connectToHostConnector(const QUrl &url); void disconnectFromHostConnector(); int64_t writeDataConnector(int frames,const unsigned char *data,int64_t len); private slots: void socketConnectedData(); void socketDisconnectedData(); void socketReadyReadData(); void socketErrorData(QAbstractSocket::SocketError err); void conveyorEventFinished(const QUrl &url,int exit_code, int resp_code,const QStringList &args); void conveyorError(const QUrl &url,QProcess::ProcessError err, const QStringList &args); private: void ProcessHeaders(const QString &hdrs); void WriteHeader(const QString &str); QTcpSocket *ice_socket; QString ice_recv_buffer; GetConveyor *ice_conveyor; }; #endif // ICECONNECTOR_H GlassCoder-2.0.1/src/glasscoder/iceoutconnector.cpp000066400000000000000000000043741425562563600224120ustar00rootroot00000000000000// iceoutconnector.cpp // // Glasscoder connector for a single Icecast stream // // (C) Copyright 2014-2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include #include "iceoutconnector.h" IceOutConnector::IceOutConnector(QObject *parent) : Connector(parent) { } IceOutConnector::~IceOutConnector() { } Connector::ServerType IceOutConnector::serverType() const { return Connector::IcecastOutServer; } void IceOutConnector::startStopping() { emit stopped(); } void IceOutConnector::connectToHostConnector(const QUrl &url) { StartStream(); setConnected(true); emit unmuteRequested(); } void IceOutConnector::disconnectFromHostConnector() { } int64_t IceOutConnector::writeDataConnector(int frames, const unsigned char *data, int64_t len) { fwrite(data,len,1,stdout); return len; } void IceOutConnector::SendHeader(const QString &hdr) const { printf("%s\r\n",(const char *)hdr.toUtf8()); } void IceOutConnector::StartStream() { // // Send Headers // SendHeader("Content-Type: "+contentType()); SendHeader("icy-br: "+QString().sprintf("%u",audioBitrate())); SendHeader("ice-audio-info: "+ QString().sprintf("bitrate=%u",audioBitrate())); SendHeader("icy-description: "+streamDescription()); SendHeader("icy-genre: "+streamGenre()); SendHeader("icy-name: "+streamName()); SendHeader("icy-pub: "+QString().sprintf("%u",streamPublic())); SendHeader("icy-url: "+streamUrl().toString()); SendHeader(); } GlassCoder-2.0.1/src/glasscoder/iceoutconnector.h000066400000000000000000000025321425562563600220510ustar00rootroot00000000000000// iceoutconnector.h // // Glasscoder connector for a single IceCast stream // // (C) Copyright 2014-2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef ICEOUTCONNECTOR_H #define ICEOUTCONNECTOR_H #include "connector.h" class IceOutConnector : public Connector { Q_OBJECT; public: IceOutConnector(QObject *parent=0); ~IceOutConnector(); Connector::ServerType serverType() const; protected: void startStopping(); void connectToHostConnector(const QUrl &url); void disconnectFromHostConnector(); int64_t writeDataConnector(int frames,const unsigned char *data,int64_t len); private: void SendHeader(const QString &hdr="") const; void StartStream(); }; #endif // ICEOUTCONNECTOR_H GlassCoder-2.0.1/src/glasscoder/icestreamconnector.cpp000066400000000000000000000317261425562563600230770ustar00rootroot00000000000000// icestreamconnector.cpp // // Glasscoder connector for an integrated IceCast server // // (C) Copyright 2014-2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include #include #include "icestreamconnector.h" IceStream::IceStream(QTcpSocket *sock,IceStream::Type type) { ice_socket=sock; ice_type=type; ice_is_negotiated=false; ice_is_authenticated=false; ice_type=IceStream::New; ice_metadata_enabled=false; ice_metadata_bytes=0; ice_timeout_timer=new QTimer(); ice_timeout_timer->setSingleShot(true); ice_timeout_timer->start(ICESTREAM_CONNECTION_TIMEOUT); } IceStream::~IceStream() { delete ice_socket; delete ice_timeout_timer; } QTcpSocket *IceStream::socket() const { return ice_socket; } QTimer *IceStream::timeoutTimer() const { return ice_timeout_timer; } IceStream::Type IceStream::type() const { return ice_type; } void IceStream::setType(IceStream::Type type) { ice_type=type; } bool IceStream::isNegotiated() const { return ice_is_negotiated; } void IceStream::setNegotiated() { ice_timeout_timer->stop(); ice_is_negotiated=true; } bool IceStream::isAuthenticated() const { return ice_is_authenticated; } void IceStream::setAuthenticated(bool state) { ice_is_authenticated=state; } QString IceStream::streamTitle() const { return ice_stream_title; } void IceStream::setStreamTitle(const QString &str) { ice_stream_title=str; } bool IceStream::metadataEnabled() const { return ice_metadata_enabled; } void IceStream::setMetadataEnabled(bool state) { ice_metadata_enabled=state; } int IceStream::addMetadataBytes(int bytes) { ice_metadata_bytes+=bytes; if((!metadataEnabled())||(ice_metadata_bytessetSingleShot(true); connect(iceserv_garbage_timer,SIGNAL(timeout()),this,SLOT(garbageData())); } IceStreamConnector::~IceStreamConnector() { if(iceserv_socket_server!=NULL) { delete iceserv_socket_server; } delete iceserv_garbage_timer; delete iceserv_readyread_mapper; for(unsigned i=0;ifield("StreamTitle")); } void IceStreamConnector::newConnectionData() { // // Accept Connection // QTcpSocket *sock=iceserv_server->nextPendingConnection(); if((int)iceserv_streams.size()==serverMaxConnections()) { sock->disconnectFromHost(); return; } int id=GetFreeStreamId(); iceserv_streams[id]=new IceStream(sock); connect(sock,SIGNAL(disconnected()),this,SLOT(disconnectedData())); iceserv_readyread_mapper->setMapping(sock,id); connect(sock,SIGNAL(readyRead()),iceserv_readyread_mapper,SLOT(map())); iceserv_timeout_mapper->setMapping(iceserv_streams.at(id)->timeoutTimer(),id); connect(iceserv_streams.at(id)->timeoutTimer(),SIGNAL(timeout()), iceserv_timeout_mapper,SLOT(map())); } void IceStreamConnector::newPipeConnectionData() { // // Accept Connection // QTcpSocket *sock=iceserv_socket_server->nextPendingConnection(); if((int)iceserv_streams.size()==serverMaxConnections()) { sock->disconnectFromHost(); return; } int id=GetFreeStreamId(); iceserv_streams[id]=new IceStream(sock,IceStream::Player); connect(sock,SIGNAL(disconnected()),this,SLOT(disconnectedData())); StartStream(iceserv_streams[id]); } void IceStreamConnector::readyReadData(int id) { IceStream *strm=iceserv_streams.at(id); QByteArray data=iceserv_streams.at(id)->socket()->readAll(); if(!strm->isNegotiated()) { for(int i=0;iaccum=""; break; default: strm->accum+=data[i]; break; } } } } void IceStreamConnector::timeoutData(int id) { iceserv_streams.at(id)->socket()->disconnectFromHost(); iceserv_garbage_timer->start(1); } void IceStreamConnector::disconnectedData() { iceserv_garbage_timer->start(1); } void IceStreamConnector::garbageData() { for(unsigned i=0;isocket()->state()!= QAbstractSocket::ConnectedState) { delete iceserv_streams.at(i); iceserv_streams[i]=NULL; } } } if(serverExitOnLast()) { for(unsigned i=0;ilisten(serverPipe())) { fprintf(stderr,"glasscoder: unable to create server pipe [%s]\n", strerror(errno)); exit(256); } } if(!url.host().isEmpty()) { if(!addr.setAddress(url.host())) { fprintf(stderr,"glasscoder: invalid interface address in URL\n"); exit(256); } if(!iceserv_server->listen(addr,url.port())) { fprintf(stderr,"glasscoder: unable to bind TCP port %u\n", 0xFFFF&url.port()); exit(256); } } setConnected(true); if(serverStartConnections()==0) { emit unmuteRequested(); } } void IceStreamConnector::disconnectFromHostConnector() { for(unsigned i=0;iisNegotiated())) { if((offset=strm->addMetadataBytes(len))<0) { strm->socket()->write((const char *)data,len); } else { strm->socket()->write((const char *)data,offset); strm->socket()->write(iceserv_metadata); strm->socket()->write((const char *)data+offset,len-offset); } } } return len; } void IceStreamConnector::SetMetadata(const QString &title) { // // This has to be the lamest metadata protocol in existence. // Codecs have ancillary channels for this sort of thing! ** BAD LLAMA!! ** // iceserv_metadata=("StreamTitle='"+title+"';").toUtf8(); while((iceserv_metadata.length()%16)!=0) { iceserv_metadata.append((char)0); } iceserv_metadata.prepend((char)(iceserv_metadata.length()/16)); } void IceStreamConnector::SendHeader(IceStream *strm,const QString &hdr) const { strm->socket()->write((hdr+"\r\n").toUtf8()); } void IceStreamConnector::ProcessHeader(IceStream *strm) { QStringList f0; QStringList f1; QStringList hdrs; bool ok=false; if(strm->type()==IceStream::New) { f0=strm->accum.split(" ",QString::SkipEmptyParts); if(f0.size()==3) { if(f0.at(0)=="GET") { f1=f0.at(2).split("/"); if((f1.size()==2)&&(f1.at(0)=="HTTP")) { QUrl url(f0.at(1)); if(url.path()==serverMountpoint()) { strm->setType(IceStream::Player); ok=true; } if(url.path()=="/admin/metadata") { QUrlQuery query(url.query()); if(("/"+query.queryItemValue("mount")==serverMountpoint())&& (query.queryItemValue("mode")=="updinfo")) { strm->setType(IceStream::Updinfo); strm->setStreamTitle(query.queryItemValue("song")); // printf("SONG: %s\n",(const char *)url.queryItemValue("song").toUtf8()); ok=true; } } } } } if(!ok) { CloseConnection(strm,400,"Malformed request"); } } else { if(strm->accum.isEmpty()) { switch(strm->type()) { case IceStream::Player: StartStream(strm); break; case IceStream::Updinfo: if(strm->isAuthenticated()) { SetMetadata(strm->streamTitle()); CloseConnection(strm,200,"OK"); } else { hdrs.push_back("WWW-Authenticate: Basic realm="+ serverMountpoint().right(serverMountpoint().length()-1)); CloseConnection(strm,401,"Unauthorized",hdrs); } break; default: CloseConnection(strm,404,"Not Found"); } } else { f0=strm->accum.split(":",QString::SkipEmptyParts); if((f0.size()==2)&&(f0.at(0).trimmed()=="icy-metadata")) { bool state=f0.at(1).trimmed().toUInt(&ok); if(ok&&((contentType()=="audio/mpeg")||(contentType()=="audio/aacp"))) { strm->setMetadataEnabled(state); } } if((f0.size()==2)&&(f0.at(0).trimmed()=="Authorization")) { f1=f0.at(1).trimmed().split(" "); if((f1.size()==2)&&(f1.at(0).toLower()=="basic")) { strm->setAuthenticated(f1.at(1)==serverBasicAuthString()); } } } } } void IceStreamConnector::CloseConnection(IceStream *strm,int code, const QString &str, const QStringList &hdrs) { QString msg=QString().sprintf("%d ",code)+str+"\r\n"; SendHeader(strm,QString().sprintf("HTTP/1.0 %d ",code)+str); SendHeader(strm,"Server: GlassCoder "+QString(VERSION)); SendHeader(strm,"Date: "+ QDateTime::currentDateTime().addSecs(4*3600). toString("ddd, dd MM yyyy hh:mm:ss GMT").toUtf8()); SendHeader(strm, QString().sprintf("Content-Length: %d",msg.toUtf8().length())); for(int i=0;isocket()->write(msg.toUtf8()); strm->socket()->disconnectFromHost(); iceserv_garbage_timer->start(1); } void IceStreamConnector::StartStream(IceStream *strm) { // // Send Headers // SendHeader(strm,"HTTP/1.0 200 OK"); SendHeader(strm,"Server: Icecast 2.4.0"); SendHeader(strm,"Date: "+ QDateTime::currentDateTime().toUTC(). toString("ddd, dd MM yyyy hh:mm:ss GMT").toUtf8()); SendHeader(strm,"Content-Type: "+contentType()); SendHeader(strm,"Cache-Control: no-cache"); SendHeader(strm,"Pragma: no-cache"); SendHeader(strm,"icy-br: "+QString().sprintf("%u",audioBitrate())); SendHeader(strm,"ice-audio-info: "+ QString().sprintf("bitrate=%u",audioBitrate())+ QString().sprintf(";channels=%u",audioChannels())+ QString().sprintf(";samplerate=%u",audioSamplerate())); SendHeader(strm,"icy-description: "+streamDescription()); SendHeader(strm,"icy-genre: "+streamGenre()); SendHeader(strm,"icy-name: "+streamName()); SendHeader(strm,"icy-pub: "+QString().sprintf("%u",streamPublic())); SendHeader(strm,"icy-url: "+streamUrl().toString()); if(strm->metadataEnabled()) { SendHeader(strm,"icy-metaint: "+ QString().sprintf("%u",ICESTREAM_METADATA_INTERVAL)); } SendHeader(strm); strm->setNegotiated(); strm->socket()->write(iceserv_stream_prologue); if((int)iceserv_streams.size()==serverStartConnections()) { emit unmuteRequested(); } } int IceStreamConnector::GetFreeStreamId() { for(unsigned i=0;i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef ICESTREAMCONNECTOR_H #define ICESTREAMCONNECTOR_H #include #include #include #include #include "connector.h" #include "socketserver.h" #define ICESTREAM_METADATA_INTERVAL 16000 #define ICESTREAM_CONNECTION_TIMEOUT 10000 class IceStream { public: enum Type {New=0,Player=1,Updinfo=2}; IceStream(QTcpSocket *sock,Type type=New); ~IceStream(); QTcpSocket *socket() const; QTimer *timeoutTimer() const; Type type() const; void setType(Type type); bool isNegotiated() const; void setNegotiated(); bool isAuthenticated() const; void setAuthenticated(bool state); QString streamTitle() const; void setStreamTitle(const QString &str); bool metadataEnabled() const; void setMetadataEnabled(bool state); int addMetadataBytes(int bytes); QString accum; private: unsigned ice_id; QTcpSocket *ice_socket; QTimer *ice_timeout_timer; Type ice_type; bool ice_is_negotiated; bool ice_is_authenticated; QString ice_stream_title; bool ice_metadata_enabled; int ice_metadata_bytes; }; class IceStreamConnector : public Connector { Q_OBJECT; public: IceStreamConnector(QObject *parent=0); ~IceStreamConnector(); Connector::ServerType serverType() const; public slots: void setStreamPrologue(const QByteArray &data); void sendMetadata(MetaEvent *e); private slots: void newConnectionData(); void newPipeConnectionData(); void readyReadData(int id); void timeoutData(int id); void disconnectedData(); void garbageData(); protected: void startStopping(); void connectToHostConnector(const QUrl &url); void disconnectFromHostConnector(); int64_t writeDataConnector(int frames,const unsigned char *data,int64_t len); private: void SetMetadata(const QString &title); void SendHeader(IceStream *strm,const QString &hdr="") const; void ProcessHeader(IceStream *strm); void CloseConnection(IceStream *strm,int code,const QString &str, const QStringList &hdrs=QStringList()); void StartStream(IceStream *strm); int GetFreeStreamId(); QTcpServer *iceserv_server; std::vector iceserv_streams; QSignalMapper *iceserv_readyread_mapper; QSignalMapper *iceserv_timeout_mapper; QTimer *iceserv_garbage_timer; QByteArray iceserv_metadata; SocketServer *iceserv_socket_server; QByteArray iceserv_stream_prologue; }; #endif // ICESTREAMCONNECTOR_H GlassCoder-2.0.1/src/glasscoder/icyconnector.cpp000066400000000000000000000162711425562563600217050ustar00rootroot00000000000000// icyconnector.cpp // // Source connector class for Shoutcast servers // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "icyconnector.h" #include "logging.h" IcyConnector::IcyConnector(int version,QObject *parent) : Connector(parent) { icy_protocol_version=version; icy_recv_buffer=""; icy_authenticated=false; icy_socket=new QTcpSocket(this); connect(icy_socket,SIGNAL(connected()),this,SLOT(socketConnectedData())); connect(icy_socket,SIGNAL(disconnected()), this,SLOT(socketDisconnectedData())); connect(icy_socket,SIGNAL(readyRead()),this,SLOT(socketReadyReadData())); connect(icy_socket,SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(socketErrorData(QAbstractSocket::SocketError))); // // Metadata File Conveyor // icy_conveyor=new GetConveyor(this); QStringList hdrs; // // D.N.A.S v1.9.8 refuses to process updates with the default CURL // user-agent value, hence we lie to it. // hdrs.push_back("User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2"); icy_conveyor->setAddedHeaders(hdrs); connect(icy_conveyor,SIGNAL(eventFinished(const QUrl &,int,int, const QStringList &)), this,SLOT(conveyorEventFinished(const QUrl &,int,int, const QStringList &))); connect(icy_conveyor, SIGNAL(error(const QUrl &,QProcess::ProcessError, const QStringList &)), this, SLOT(conveyorError(const QUrl &,QProcess::ProcessError, const QStringList &))); } IcyConnector::~IcyConnector() { delete icy_socket; } IcyConnector::ServerType IcyConnector::serverType() const { return Connector::Shoutcast1Server; } void IcyConnector::sendMetadata(MetaEvent *e) { if(e->fieldKeys().contains("StreamTitle")|| e->fieldKeys().contains("StreamUrl")) { QString url=QString("http://")+ serverUrl().host()+ QString().sprintf(":%u",serverUrl().port())+ "/admin.cgi?"+ "pass="+Connector::urlEncode(serverPassword())+"&"+ "mode=updinfo"; if(e->fieldKeys().contains("StreamTitle")) { url+="&song="+Connector::urlEncode(e->field("StreamTitle")); } if(e->fieldKeys().contains("StreamUrl")) { url+="&url="+Connector::urlEncode(e->field("StreamUrl")); } icy_conveyor->push(url); } } void IcyConnector::connectToHostConnector(const QUrl &url) { icy_socket->connectToHost(url.host(),url.port()+1); emit unmuteRequested(); } void IcyConnector::disconnectFromHostConnector() { icy_socket->disconnectFromHost(); } int64_t IcyConnector::writeDataConnector(int frames,const unsigned char *data, int64_t len) { if(icy_socket->state()==QAbstractSocket::ConnectedState) { return icy_socket->write((const char *)data,len); } return len; } void IcyConnector::socketConnectedData() { QString auth=serverPassword()+"\r\n"; if(!serverUsername().isEmpty()) { auth=serverUsername()+":"+serverPassword()+"\r\n"; } icy_socket->write(auth.toUtf8()); } void IcyConnector::socketDisconnectedData() { if(!icy_authenticated) { Log(LOG_WARNING, QString().sprintf("login to \"%s:%d\" rejected: bad password", (const char *)serverUrl().host().toUtf8(), 0xFFFF&serverUrl().port())); } icy_authenticated=false; setConnected(false); } void IcyConnector::socketReadyReadData() { char data[1501]; int64_t n; while((n=icy_socket->read(data,1500))>0) { data[n]=0; for(int64_t i=0;i=2)&& (icy_recv_buffer.toUtf8().at(icy_recv_buffer.length()-3)==13)) { icy_recv_buffer=icy_recv_buffer.left(icy_recv_buffer.length()-1); ProcessHeaders(icy_recv_buffer); icy_recv_buffer=""; } else { icy_recv_buffer+=data[i]; } break; case 13: if(QString(icy_recv_buffer)=="invalid password") { Log(LOG_WARNING, QString().sprintf("login to \"%s:%d\" rejected: invalid password", (const char *)serverUrl().host().toUtf8(), 0xFFFF&serverUrl().port())); } else { icy_recv_buffer+=data[i]; } break; default: icy_recv_buffer+=data[i]; break; } } } } void IcyConnector::socketErrorData(QAbstractSocket::SocketError err) { setError(err); } void IcyConnector::conveyorEventFinished(const QUrl &url,int exit_code, int resp_code,const QStringList &args) { // // Exit code handler // if((exit_code!=0)&&(exit_code!=CURLE_GOT_NOTHING)) { setConnected(false); if(global_log_verbose) { Log(LOG_WARNING, QString().sprintf("curl(1) error: %s, cmd: \"curl %s\"", (const char *)Connector::curlStrError(exit_code).toUtf8(), (const char *)args.join(" ").toUtf8())); } else { Log(LOG_WARNING,QString().sprintf("CURL error: %s", (const char *)Connector::curlStrError(exit_code).toUtf8())); } } else { // // Response code handler // if(((resp_code<200)||(resp_code>299))&&(resp_code!=0)) { setConnected(false); if(global_log_verbose) { Log(LOG_WARNING,"curl(1) response error: "+ Connector::httpStrError(resp_code)+ ", cmd: \"curl "+args.join(" ")+"\""); } else { Log(LOG_WARNING,"curl(1) response error: "+ Connector::httpStrError(resp_code)); } } else { setConnected(true); } } } void IcyConnector::conveyorError(const QUrl &url, QProcess::ProcessError err, const QStringList &args) { Log(LOG_ERR, QString().sprintf("curl(1) process error: %d, cmd: \"curl %s\"",err, (const char *)args.join(" ").toUtf8())); setConnected(false); exit(256); } void IcyConnector::ProcessHeaders(const QString &hdrs) { QStringList f0; QStringList f1; QString txt; f0=hdrs.split("\r\n"); if((!icy_authenticated)&&(f0[0]!="OK2")) { Log(LOG_WARNING, QString().sprintf("login to \"%s:%d\" rejected: %s", (const char *)serverUrl().host().toUtf8(), 0xFFFF&serverUrl().port(), (const char *)f0[0].toUtf8())); return; } icy_authenticated=true; WriteHeader("icy-name: "+streamName()); WriteHeader("icy-genre: "+streamGenre()); WriteHeader("icy-pub: "+QString().sprintf("%d",streamPublic())); WriteHeader("icy-br: "+QString().sprintf("%u",audioBitrate())); if(icy_protocol_version==1) { WriteHeader("icy-url: "+streamUrl().toString()); } WriteHeader("icy-irc: "+streamIrc()); WriteHeader("icy-icq: "+streamIcq()); WriteHeader("icy-aim: "+streamAim()); WriteHeader("Content-Type: "+contentType()); WriteHeader(""); setConnected(true); } void IcyConnector::WriteHeader(const QString &str) { icy_socket->write((str+"\r\n").toUtf8()); } GlassCoder-2.0.1/src/glasscoder/icyconnector.h000066400000000000000000000036201425562563600213440ustar00rootroot00000000000000// icyconnector.h // // Source connector class for ShoutCast v1 servers. // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef ICYCONNECTOR_H #define ICYCONNECTOR_H #include "connector.h" #include "getconveyor.h" class IcyConnector : public Connector { Q_OBJECT; public: IcyConnector(int ver,QObject *parent=0); ~IcyConnector(); IcyConnector::ServerType serverType() const; public slots: void sendMetadata(MetaEvent *e); protected: void connectToHostConnector(const QUrl &url); void disconnectFromHostConnector(); int64_t writeDataConnector(int frames,const unsigned char *data,int64_t len); private slots: void socketConnectedData(); void socketDisconnectedData(); void socketReadyReadData(); void socketErrorData(QAbstractSocket::SocketError err); void conveyorEventFinished(const QUrl &url,int exit_code, int resp_code,const QStringList &args); void conveyorError(const QUrl &url,QProcess::ProcessError err, const QStringList &args); private: void ProcessHeaders(const QString &hdrs); void WriteHeader(const QString &str); QTcpSocket *icy_socket; QString icy_recv_buffer; int icy_protocol_version; bool icy_authenticated; GetConveyor *icy_conveyor; }; #endif // ICYCONNECTOR_H GlassCoder-2.0.1/src/glasscoder/jackdevice.cpp000066400000000000000000000142561425562563600212770ustar00rootroot00000000000000// jackdevice.cpp // // Audio source for the Jack Audio Connection Kit // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include "glasslimits.h" #include "jackdevice.h" #include "logging.h" // // JACK Callback // #ifdef JACK jack_default_audio_sample_t *jack_cb_buffers[MAX_AUDIO_CHANNELS]; float jack_cb_interleave_buffer[MAX_AUDIO_CHANNELS*RINGBUFFER_SIZE]; int JackProcess(jack_nframes_t nframes, void *arg) { static unsigned i; static jack_nframes_t j; static JackDevice *obj=(JackDevice *)arg; static float lvls[MAX_AUDIO_CHANNELS]; // // Get Buffers // for(i=0;ichannels();i++) { jack_cb_buffers[i]=(jack_default_audio_sample_t *) jack_port_get_buffer(obj->jack_jack_ports[i],nframes); } // // Interleave Channels // for(i=0;ichannels();i++) { if(jack_cb_buffers[i]!=NULL) { for(j=0;jchannels()*j+i]= (float)jack_cb_buffers[i][j]*obj->jack_gain; } } } // // Write It // obj->ringBuffer()->write(jack_cb_interleave_buffer,nframes); obj->peakLevels(lvls,jack_cb_interleave_buffer,nframes,obj->channels()); for(i=0;ichannels();i++) { obj->jack_meter_avg[i]->addValue(lvls[i]); } return 0; } #endif // JACK JackDevice::JackDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent) : AudioDevice(chans,samprate,ring,parent) { #ifdef JACK jack_server_name=""; jack_client_name=DEFAULT_JACK_CLIENT_NAME; jack_gain=1.0; for(int i=0;istart(AUDIO_METER_INTERVAL); return true; #endif // JACK return false; } unsigned JackDevice::deviceSamplerate() const { #ifdef JACK return jack_jack_sample_rate; #else return DEFAULT_AUDIO_SAMPLERATE; #endif // JACK } void JackDevice::meterData() { #ifdef JACK float lvls[MAX_AUDIO_CHANNELS]; for(unsigned i=0;iaverage(); } setMeterLevels(lvls); #endif // JACK } GlassCoder-2.0.1/src/glasscoder/jackdevice.h000066400000000000000000000034521425562563600207400ustar00rootroot00000000000000// jackdevice.h // // Audio source for the Jack Audio Connection Kit // // (C) Copyright 2014-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef JACKDEVICE_H #define JACKDEVICE_H #ifdef JACK #include #endif // JACK #include #include #include "audiodevice.h" #include "glasslimits.h" #include "meteraverage.h" #define DEFAULT_JACK_CLIENT_NAME "glasscoder" class JackDevice : public AudioDevice { Q_OBJECT; public: JackDevice(unsigned chans,unsigned samprate, Ringbuffer *ring,QObject *parent=0); ~JackDevice(); bool processOptions(QString *err,const QStringList &keys, const QStringList &values); bool start(QString *err); unsigned deviceSamplerate() const; private slots: void meterData(); private: #ifdef JACK QString jack_server_name; QString jack_client_name; float jack_gain; jack_client_t *jack_jack_client; jack_nframes_t jack_jack_sample_rate; jack_port_t *jack_jack_ports[MAX_AUDIO_CHANNELS]; MeterAverage *jack_meter_avg[MAX_AUDIO_CHANNELS]; QTimer *jack_meter_timer; friend int JackProcess(jack_nframes_t nframes, void *arg); #endif // JACK }; #endif // JACKDEVICE_H GlassCoder-2.0.1/src/glasscoder/metaserver.cpp000066400000000000000000000067541425562563600213700ustar00rootroot00000000000000// metaserver.cpp // // HTTP Server for Metadata Processing // // (C) Copyright 2016-2019 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include "metaserver.h" MetaServer::MetaServer(Config *config,QObject *parent) : HttpServer(parent) { meta_config=config; } void MetaServer::getRequestReceived(HttpConnection *conn) { //printf("HTTP: %s\n",(const char *)conn->dump().toUtf8()); int resp_code=404; QString resp_str="Not Found"; QUrl url(conn->uri()); QUrlQuery query(url); if(url.path()=="/admin.cgi") { // Shoutcast Style if(query.queryItemValue("pass",QUrl::FullyDecoded)== meta_config->serverPassword()) { if(query.queryItemValue("mode")=="updinfo") { MetaEvent *e=new MetaEvent(); e->setField("StreamTitle", query.queryItemValue("song",QUrl::FullyDecoded)); e->setField("StreamUrl", query.queryItemValue("url",QUrl::FullyDecoded)); emit metadataReceived(e); delete e; conn->sendError(200,"OK"); return; } else { resp_code=403; resp_str="Unrecognized Mode"; } } else { resp_code=403; resp_str="Bad Password"; } } if(url.path()=="/admin/metadata") { // Icecast Style if(query.queryItemValue("mode")=="updinfo") { MetaEvent *e=new MetaEvent(); e->setField("StreamTitle", query.queryItemValue("song",QUrl::FullyDecoded)); emit metadataReceived(e); delete e; conn->sendError(200,"OK"); return; } else { resp_code=403; resp_str="Unrecognized Mode"; } } conn->sendError(resp_code,resp_str); } void MetaServer::postRequestReceived(HttpConnection *conn) { // printf("HTTP: %s\n",(const char *)conn->dump().toUtf8()); int resp_code=404; QString resp_str="Not Found"; QUrl url(conn->uri()); if(url.path()=="/json_pad") { // JSON Articulated Metadata resp_code=400; resp_str="Invalid Data"; QJsonDocument doc=QJsonDocument::fromJson(conn->body()); if(!doc.isNull()) { if(doc.isObject()) { QJsonObject obj=doc.object(); if(obj.keys().at(0)=="Metadata") { if(ProcessJsonMetadataUpdates(obj.value("Metadata").toObject())) { resp_code=200; resp_str="OK"; } } } } } conn->sendError(resp_code,resp_str); } bool MetaServer::authenticateUser(const QString &realm,const QString &name, const QString &passwd) { return true; } bool MetaServer::ProcessJsonMetadataUpdates(const QJsonObject &obj) { QStringList keys=obj.keys(); MetaEvent *e=new MetaEvent(); for(int i=0;isetField(keys.at(i),obj.value(keys.at(i)).toString()); } else { e->setField(keys.at(i), QString().sprintf("%d",obj.value(keys.at(i)).toInt())); } } emit metadataReceived(e); delete e; return true; } GlassCoder-2.0.1/src/glasscoder/metaserver.h000066400000000000000000000025501425562563600210230ustar00rootroot00000000000000// metaserver.h // // HTTP Server for Metadata Processing // // (C) Copyright 2016-2019 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef METASERVER_H #define METASERVER_H #include "config.h" #include "httpserver.h" #include "metaevent.h" class MetaServer : public HttpServer { Q_OBJECT; public: MetaServer(Config *config,QObject *parent=0); signals: void metadataReceived(MetaEvent *e); protected: void getRequestReceived(HttpConnection *conn); void postRequestReceived(HttpConnection *conn); bool authenticateUser(const QString &realm,const QString &name, const QString &passwd); private: bool ProcessJsonMetadataUpdates(const QJsonObject &obj); Config *meta_config; }; #endif // METASERVER_H GlassCoder-2.0.1/src/glasscoder/meteraverage.cpp000066400000000000000000000023641425562563600216530ustar00rootroot00000000000000// meteraverage.cpp // // Average sucessive levels for a meter. // // (C) Copyright 2007-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "meteraverage.h" MeterAverage::MeterAverage(int maxsize) { avg_maxsize=maxsize; avg_total=0.0; } float MeterAverage::average() const { if(avg_values.size()==0) { return 0.0; } return avg_total/((float)avg_values.size()); } void MeterAverage::addValue(float value) { avg_total+=value; avg_values.push(value); int size=avg_values.size()-avg_maxsize; for(int i=0;i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef METERAVERAGE_H #define METERAVERAGE_H #include class MeterAverage { public: MeterAverage(int maxsize); float average() const; void addValue(float value); private: int avg_maxsize; float avg_total; std::queue avg_values; }; #endif // METERAVERAGE_H GlassCoder-2.0.1/src/glasscoder/mpegl2codec.cpp000066400000000000000000000104241425562563600213640ustar00rootroot00000000000000// mpegl2codec.cpp // // Codec class for MPEG-1 Layer 2 // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "logging.h" #include "mpegl2codec.h" MpegL2Codec::MpegL2Codec(Ringbuffer *ring,QObject *parent) : Codec(Codec::TypeMpegL3,ring,parent) { #ifdef HAVE_TWOLAME twolame_handle=NULL; #endif // HAVE_TWOLAME } bool MpegL2Codec::isAvailable() const { #ifdef HAVE_TWOLAME return dlopen("libtwolame.so.0",RTLD_LAZY)!=NULL; #else return false; #endif // HAVE_TWOLAME } QString MpegL2Codec::contentType() const { return "audio/mpeg"; } unsigned MpegL2Codec::pcmFrames() const { return 1152; } QString MpegL2Codec::defaultExtension() const { return QString("mp2"); } QString MpegL2Codec::formatIdentifier() const { // // See RFC 6381 // return QString("mp4a/40.33"); } bool MpegL2Codec::startCodec() { #ifdef HAVE_TWOLAME // // Load Library // if((twolame_handle=dlopen("libtwolame.so.0",RTLD_LAZY))==NULL) { Log(LOG_ERR,"unsupported audio format (library not found)"); return false; } *(void **)(&twolame_init)=dlsym(twolame_handle,"twolame_init"); *(void **)(&twolame_set_mode)=dlsym(twolame_handle,"twolame_set_mode"); *(void **)(&twolame_set_num_channels)= dlsym(twolame_handle,"twolame_set_num_channels"); *(void **)(&twolame_set_in_samplerate)= dlsym(twolame_handle,"twolame_set_in_samplerate"); *(void **)(&twolame_set_out_samplerate)= dlsym(twolame_handle,"twolame_set_out_samplerate"); *(void **)(&twolame_set_bitrate)= dlsym(twolame_handle,"twolame_set_bitrate"); *(void **)(&twolame_init_params)= dlsym(twolame_handle,"twolame_init_params"); *(void **)(&twolame_close)=dlsym(twolame_handle,"twolame_close"); *(void **)(&twolame_encode_buffer_interleaved)= dlsym(twolame_handle,"twolame_encode_buffer_interleaved"); *(void **)(&twolame_encode_buffer_float32_interleaved)= dlsym(twolame_handle,"twolame_encode_buffer_float32_interleaved"); *(void **)(&twolame_encode_flush)= dlsym(twolame_handle,"twolame_encode_flush"); *(void **)(&twolame_set_energy_levels)= dlsym(twolame_handle,"twolame_set_energy_levels"); *(void **)(&twolame_set_VBR)=dlsym(twolame_handle,"twolame_set_VBR"); *(void **)(&twolame_set_VBR_level)= dlsym(twolame_handle,"twolame_set_VBR_level"); // // Initialize Encoder Instance // TWOLAME_MPEG_mode mpeg_mode=TWOLAME_STEREO; switch(channels()) { case 1: mpeg_mode=TWOLAME_MONO; break; case 2: mpeg_mode=TWOLAME_STEREO; break; } if((twolame_lameopts=twolame_init())==NULL) { Log(LOG_ERR,"unable to initialize MP2 encoder"); return false; } twolame_set_mode(twolame_lameopts,mpeg_mode); twolame_set_num_channels(twolame_lameopts,channels()); twolame_set_in_samplerate(twolame_lameopts,streamSamplerate()); twolame_set_out_samplerate(twolame_lameopts,streamSamplerate()); if(bitrate()==0) { twolame_set_VBR(twolame_lameopts,TRUE); twolame_set_VBR_level(twolame_lameopts,(int)(20.0*quality()-10.0)); } else { twolame_set_bitrate(twolame_lameopts,bitrate()); } twolame_set_energy_levels(twolame_lameopts,1); if(twolame_init_params(twolame_lameopts)!=0) { Log(LOG_ERR,"unable to start MP2 encoder"); return false; } return true; #else Log(LOG_ERR,"unsupported audio format (no build support)"); return false; #endif // HAVE_TWOLAME } void MpegL2Codec::encodeData(Connector *conn,const float *pcm,int frames) { #ifdef HAVE_TWOLAME int s; unsigned char mpeg[8640]; if((s=twolame_encode_buffer_float32_interleaved(twolame_lameopts,pcm,frames, mpeg,8640))>=0) { conn->writeData(frames,mpeg,s); } #endif // HAVE_TWOLAME } GlassCoder-2.0.1/src/glasscoder/mpegl2codec.h000066400000000000000000000044301425562563600210310ustar00rootroot00000000000000// mpegl2codec.h // // Codec class for MPEG-1 Layer 3 // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef MPEGL2CODEC_H #define MPEGL2CODEC_H #ifdef HAVE_TWOLAME #include #endif // HAVE_TWOLAME #include "codec.h" class MpegL2Codec : public Codec { Q_OBJECT; public: MpegL2Codec(Ringbuffer *ring,QObject *parent=0); bool isAvailable() const; QString contentType() const; unsigned pcmFrames() const; QString defaultExtension() const; QString formatIdentifier() const; bool startCodec(); protected: void encodeData(Connector *conn,const float *pcm,int frames); private: #ifdef HAVE_TWOLAME void *twolame_handle; twolame_options *twolame_lameopts; twolame_options *(*twolame_init)(void); void (*twolame_set_mode)(twolame_options *,TWOLAME_MPEG_mode); void (*twolame_set_num_channels)(twolame_options *,int); void (*twolame_set_in_samplerate)(twolame_options *,int); void (*twolame_set_out_samplerate)(twolame_options *,int); void (*twolame_set_bitrate)(twolame_options *,int); int (*twolame_init_params)(twolame_options *); void (*twolame_close)(twolame_options **); int (*twolame_encode_buffer_interleaved)(twolame_options *,const short int[], int,unsigned char *,int); int (*twolame_encode_buffer_float32_interleaved) (twolame_options *,const float[],int,unsigned char *,int); int (*twolame_encode_flush)(twolame_options *,unsigned char *,int); int (*twolame_set_energy_levels)(twolame_options *,int); int (*twolame_set_VBR)(twolame_options *, int); int (*twolame_set_VBR_level)(twolame_options *, float); #endif // HAVE_TWOLAME }; #endif // MPEGL2CODEC_H GlassCoder-2.0.1/src/glasscoder/mpegl3codec.cpp000066400000000000000000000132301425562563600213630ustar00rootroot00000000000000// mpegl3codec.cpp // // Codec class for MPEG-1 Layer 3 // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "logging.h" #include "mpegl3codec.h" MpegL3Codec::MpegL3Codec(Ringbuffer *ring,QObject *parent) : Codec(Codec::TypeMpegL3,ring,parent) { #ifdef HAVE_LAME l3_lameopts=NULL; l3_lame_handle=NULL; #endif // HAVE_LAME } bool MpegL3Codec::isAvailable() const { #ifdef HAVE_LAME return dlopen("libmp3lame.so.0",RTLD_LAZY)!=NULL; #else return false; #endif // HAVE_LAME } QString MpegL3Codec::contentType() const { return "audio/mpeg"; } unsigned MpegL3Codec::pcmFrames() const { return 1152; } QString MpegL3Codec::defaultExtension() const { return QString("mp3"); } QString MpegL3Codec::formatIdentifier() const { // // From ISO/IEC 14496-3 // (see the summary at https://en.wikipedia.org/wiki/MPEG-4_Part_3) // return QString("mp4a.40.34"); } bool MpegL3Codec::startCodec() { #ifdef HAVE_LAME MPEG_mode mpeg_mode=STEREO; // // Load Library // l3_lame_handle=dlopen("libmp3lame.so.0",RTLD_LAZY); if(l3_lame_handle==NULL) { Log(LOG_ERR,"unsupported audio format (library not found)"); return false; } *(void **)(&lame_init)=dlsym(l3_lame_handle,"lame_init"); *(void **)(&lame_set_mode)= dlsym(l3_lame_handle,"lame_set_mode"); *(void **)(&lame_set_num_channels)= dlsym(l3_lame_handle,"lame_set_num_channels"); *(void **)(&lame_set_in_samplerate)= dlsym(l3_lame_handle,"lame_set_in_samplerate"); *(void **)(&lame_set_out_samplerate)= dlsym(l3_lame_handle,"lame_set_out_samplerate"); *(void **)(&lame_set_brate)=dlsym(l3_lame_handle,"lame_set_brate"); *(void **)(&lame_init_params)=dlsym(l3_lame_handle,"lame_init_params"); *(void **)(&lame_close)=dlsym(l3_lame_handle,"lame_close"); *(void **)(&lame_encode_buffer_interleaved)= dlsym(l3_lame_handle,"lame_encode_buffer_interleaved"); *(void **)(&lame_encode_buffer)= dlsym(l3_lame_handle,"lame_encode_buffer"); *(void **)(&lame_encode_flush)=dlsym(l3_lame_handle,"lame_encode_flush"); *(void **)(&lame_set_bWriteVbrTag)= dlsym(l3_lame_handle,"lame_set_bWriteVbrTag"); *(void **)(&lame_encode_buffer_float)= dlsym(l3_lame_handle,"lame_encoder_buffer_float"); *(void **)(&lame_encode_buffer_ieee_float)= dlsym(l3_lame_handle,"lame_encode_buffer_ieee_float"); *(void **)(&lame_encode_buffer_interleaved_ieee_float)= dlsym(l3_lame_handle,"lame_encode_buffer_interleaved_ieee_float"); *(void **)(&lame_encode_buffer_ieee_double)= dlsym(l3_lame_handle,"lame_encode_buffer_ieee_double"); *(void **)(&lame_encode_buffer_long)= dlsym(l3_lame_handle,"lame_encode_buffer_long"); *(void **)(&lame_encode_buffer_long2)= dlsym(l3_lame_handle,"lame_encode_buffer_long2"); *(void **)(&lame_encode_buffer_int)= dlsym(l3_lame_handle,"lame_encode_buffer_int"); *(void **)(&lame_encode_flush_nogap)= dlsym(l3_lame_handle,"lame_encode_flush_nogap"); *(void **)(&lame_init_bitstream)= dlsym(l3_lame_handle,"lame_init_bitstream"); *(void **)(&lame_set_VBR)=dlsym(l3_lame_handle,"lame_set_VBR"); *(void **)(&lame_set_VBR_quality)= dlsym(l3_lame_handle,"lame_set_VBR_quality"); *(void **)(&lame_set_disable_reservoir)= dlsym(l3_lame_handle,"lame_set_disable_reservoir"); *(void **)(&lame_get_disable_reservoir)= dlsym(l3_lame_handle,"lame_get_disable_reservoir"); if(lame_encode_buffer_ieee_float==NULL) { // Earlier versions of LAME didn't include this! return false; } // // Initialize Encoder Instance // switch(channels()) { case 1: mpeg_mode=MONO; break; case 2: mpeg_mode=STEREO; break; default: Log(LOG_ERR,"invalid audio channels"); return false; } if((l3_lameopts=lame_init())==NULL) { Log(LOG_ERR,"unable to initialize MP3 encoder"); return false; } if(completeFrames()) { lame_set_disable_reservoir(l3_lameopts,1); } lame_set_mode(l3_lameopts,mpeg_mode); lame_set_num_channels(l3_lameopts,channels()); lame_set_in_samplerate(l3_lameopts,streamSamplerate()); lame_set_out_samplerate(l3_lameopts,streamSamplerate()); if(bitrate()==0) { lame_set_VBR(l3_lameopts,vbr_default); lame_set_VBR_quality(l3_lameopts,(int)(9.0*(1.0-quality()))); } else { lame_set_brate(l3_lameopts,bitrate()); } lame_set_bWriteVbrTag(l3_lameopts,0); if(lame_init_params(l3_lameopts)!=0) { lame_close(l3_lameopts); Log(LOG_ERR,"unable to start MP3 encoder"); return false; } return true; #else Log(LOG_ERR,"unsupported audio format (no build support)"); return false; #endif // HAVE_LAME } void MpegL3Codec::encodeData(Connector *conn,const float *pcm,int frames) { #ifdef HAVE_LAME int s; unsigned char mpeg[8640]; if(channels()==2) { if((s=lame_encode_buffer_interleaved_ieee_float(l3_lameopts,pcm, frames,mpeg,8640))>=0) { conn->writeData(frames,mpeg,s); } } else { if((s=lame_encode_buffer_ieee_float(l3_lameopts,pcm,NULL,frames, mpeg,8640))>=0) { conn->writeData(frames,mpeg,s); } } #endif // HAVE_LAME } GlassCoder-2.0.1/src/glasscoder/mpegl3codec.h000066400000000000000000000064471425562563600210440ustar00rootroot00000000000000// mpegl3codec.h // // Codec class for MPEG-1 Layer 3 // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef MPEGL3CODEC_H #define MPEGL3CODEC_H #ifdef HAVE_LAME #include #endif // HAVE_LAME #include "codec.h" class MpegL3Codec : public Codec { Q_OBJECT; public: MpegL3Codec(Ringbuffer *ring,QObject *parent=0); bool isAvailable() const; QString contentType() const; unsigned pcmFrames() const; QString defaultExtension() const; QString formatIdentifier() const; bool startCodec(); protected: void encodeData(Connector *conn,const float *pcm,int frames); private: #ifdef HAVE_LAME lame_global_flags *l3_lameopts; void *l3_lame_handle; lame_global_flags *(*lame_init)(void); void (*lame_set_mode)(lame_global_flags *,int); void (*lame_set_num_channels)(lame_global_flags *,int); void (*lame_set_in_samplerate)(lame_global_flags *,int); void (*lame_set_out_samplerate)(lame_global_flags *,int); void (*lame_set_brate)(lame_global_flags *,int); int (*lame_init_params)(lame_global_flags *); void (*lame_close)(lame_global_flags *); int (*lame_encode_buffer_interleaved) (lame_global_flags *,short int[],int,unsigned char *,int); int (*lame_encode_buffer) (lame_global_flags *,short int[],short int[],int,unsigned char *,int); int (*lame_encode_buffer_float) (lame_global_flags *,const float,const float,const int,unsigned char *, const int); int (*lame_encode_buffer_ieee_float) (lame_t,const float[],const float[],const int,unsigned char *,const int); int (*lame_encode_buffer_interleaved_ieee_float) (lame_t,const float[],const int,unsigned char *,const int); int (*lame_encode_buffer_ieee_double) (lame_t,const double,const double,const int,unsigned char *,const int); int (*lame_encode_buffer_long) (lame_global_flags *,const long,const long,const int,unsigned char *, const int); int (*lame_encode_buffer_long2) (lame_global_flags *,const long,const long,const int,unsigned char *, const int); int (*lame_encode_buffer_int) (lame_global_flags *,const int,const int,const int,unsigned char *, const int); int(*lame_encode_flush_nogap)(lame_global_flags *,unsigned char *,int); int (*lame_init_bitstream)(lame_global_flags *); int (*lame_encode_flush)(lame_global_flags *,unsigned char *,int); int (*lame_set_bWriteVbrTag)(lame_global_flags *, int); int (*lame_set_VBR)(lame_global_flags *, vbr_mode); int (*lame_set_VBR_quality)(lame_global_flags *, float); int (*lame_set_disable_reservoir)(lame_global_flags *,int); int (*lame_get_disable_reservoir)(const lame_global_flags *); #endif // HAVE_LAME }; #endif // MPEGL3CODEC_H GlassCoder-2.0.1/src/glasscoder/netconveyor.cpp000066400000000000000000000162641425562563600215630ustar00rootroot00000000000000// netconveyor.cpp // // Serialized service for uploading files // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include "connector.h" #include "logging.h" #include "netconveyor.h" #include "paths.h" NetConveyorEvent::NetConveyorEvent(void *orig,const QString &pathname, HttpMethod meth) { evt_originator=orig; evt_pathname=pathname; evt_method=meth; } void *NetConveyorEvent::originator() const { return evt_originator; } QString NetConveyorEvent::pathname() const { return evt_pathname; } NetConveyorEvent::HttpMethod NetConveyorEvent::method() const { return evt_method; } QString NetConveyorEvent::dump() const { return QString::asprintf("NetConveyorEvent %p\n",this)+ "pathname: "+pathname()+"\n"+ "method: "+NetConveyorEvent::httpMethodString(method()); } QString NetConveyorEvent::httpMethodString(HttpMethod method) { QString ret="UNKNOWN"; switch(method) { case NetConveyorEvent::PutMethod: ret="PUT"; break; case NetConveyorEvent::DeleteMethod: ret="DELETE"; break; case NetConveyorEvent::StopMethod: ret="STOP"; break; } return ret; } NetConveyor::NetConveyor(Config *conf,QObject *parent) : QObject(parent) { conv_config=conf; conv_process=NULL; // // Create temp directory // char tempdir[PATH_MAX]; strncpy(tempdir,"/tmp",PATH_MAX); if(getenv("TEMP")!=NULL) { strncpy(tempdir,getenv("TEMP"),PATH_MAX-1); } strncat(tempdir,"/glasscoder-XXXXXX",PATH_MAX-strlen(tempdir)); if(mkdtemp(tempdir)==NULL) { syslog(LOG_ERR,"unable to create temporary directory [%s]", strerror(errno)); exit(256); } conv_temp_dir=new QDir(tempdir); // // Create Timers // conv_restart_timer=new QTimer(this); conv_restart_timer->setSingleShot(true); connect(conv_restart_timer,SIGNAL(timeout()), this,SLOT(startConveyorProcess())); // // Start glassconv(1) Instance // conv_restart_timer->start(0); } NetConveyor::~NetConveyor() { if(conv_process!=NULL) { conv_process->kill(); delete conv_process; } } void NetConveyor::push(void *orig,const QString &pathname, NetConveyorEvent::HttpMethod meth) { NetConveyorEvent evt(orig,pathname,meth); push(evt); } void NetConveyor::push(const NetConveyorEvent &evt) { // printf("============================================\n"); // printf("pushing: %s\n",evt.dump().toUtf8().constData()); int fd=-1; QString timestamp=Connector::timeStampString(); QString temp_pathname=conv_temp_dir->path()+"/"+ timestamp+"-"+ NetConveyorEvent::httpMethodString(evt.method())+"-"+ evt.pathname().split("/",QString::SkipEmptyParts).last(); // printf("sourcePathname: %s\n",evt.pathname().toUtf8().constData()); // printf("temp_pathname: %s\n",temp_pathname.toUtf8().constData()); switch(evt.method()) { case NetConveyorEvent::DeleteMethod: if((fd=open(temp_pathname.toUtf8(),O_CREAT|O_WRONLY,S_IRUSR|S_IWUSR))>=0) { close(fd); conv_putted_files.removeAll(evt.pathname()); } break; case NetConveyorEvent::PutMethod: if(link(evt.pathname().toUtf8(),temp_pathname.toUtf8())==0) { if((!conv_putted_files.contains(evt.pathname()))&& (!conv_config->serverNoDeletes())) { conv_putted_files.push_back(evt.pathname()); } } else { Log(LOG_WARNING, QString::asprintf("link() call failed for \"%s\": %s", evt.pathname().toUtf8().constData(), strerror(errno))); } break; case NetConveyorEvent::StopMethod: // Tell glassconv(1) to exit if((fd=open(temp_pathname.toUtf8(),O_CREAT|O_WRONLY,S_IRUSR|S_IWUSR))>=0) { close(fd); } break; } } void NetConveyor::stop() { // // Clean up files on the publishing point // QStringList files=conv_putted_files; // Get an invariant copy for(int i=0;ipath()+"/creds").toUtf8(),"w"))==NULL) { syslog(LOG_ERR,"unable to write to temp directory \"%s\": %s", (conv_temp_dir->path()+"/creds").toUtf8().constData(), strerror(errno)); exit(1); } fprintf(f,"[Credentials]\n"); if(!conv_config->serverUsername().isEmpty()) { fprintf(f,"Username=%s\n", conv_config->serverUsername().toUtf8().constData()); fprintf(f,"Password=%s\n", conv_config->serverPassword().toUtf8().constData()); } if(!conv_config->sshIdentity().isEmpty()) { fprintf(f,"SshIdentity=%s\n", conv_config->sshIdentity().toUtf8().constData()); } fprintf(f,"UserAgent=%s\n", conv_config->serverUserAgent().toUtf8().constData()); fclose(f); // // Start glassconv(1) // QStringList args; args.push_back("--dest-url="+conv_config->serverBaseUrl()); args.push_back("--source-dir="+conv_temp_dir->path()); conv_process=new QProcess(this); connect(conv_process,SIGNAL(readyReadStandardOutput()), this,SLOT(processReadyReadData())); connect(conv_process,SIGNAL(finished(int,QProcess::ExitStatus)), this,SLOT(processFinishedData(int,QProcess::ExitStatus))); conv_process->start(GLASSCODER_PREFIX+"/bin/glassconv",args); } void NetConveyor::processFinishedData(int exit_code, QProcess::ExitStatus exit_status) { if(exit_status!=QProcess::NormalExit) { Log(LOG_WARNING, "process \""+conv_process->program()+" "+ conv_process->arguments().join(" ")+"\" crashed, restarting"); conv_process->deleteLater(); conv_restart_timer->start(1000); } else { switch((Config::ExitCode)conv_process->exitCode()) { case Config::ExitOk: emit stopped(); case Config::ExitRetry: Log(LOG_WARNING,"program \""+conv_process->program()+"\""+ " exited unexpectedly, restarting"); conv_process->deleteLater(); conv_restart_timer->start(1000); break; case Config::ExitFatal: Log(LOG_ERR,"fatal conveyor error, exiting"); exit(1); break; } } } void NetConveyor::processReadyReadData() { QString msg=QString::fromUtf8(conv_process->readAllStandardOutput()); QStringList f0=msg.split(" ",QString::KeepEmptyParts); bool ok=false; if((f0.size()>=3)&&(f0.at(0)=="ER")) { int prio=f0.at(1).toInt(&ok); if(ok) { f0.removeFirst(); f0.removeFirst(); Log(prio,f0.join(" ")); } } } GlassCoder-2.0.1/src/glasscoder/netconveyor.h000066400000000000000000000042071425562563600212220ustar00rootroot00000000000000// netconveyor.h // // Serialized service for uploading files // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef NETCONVEYOR_H #define NETCONVEYOR_H #include #include #include #include #include #include #include "config.h" class NetConveyorEvent { public: // enum HttpMethod {NoMethod=0,GetMethod=1,PutMethod=2,DeleteMethod=3, // PostMethod=4,HeadMethod=5,StopMethod=6}; enum HttpMethod {PutMethod=0,DeleteMethod=1,StopMethod=2}; NetConveyorEvent(void *orig,const QString &pathname,HttpMethod meth); void *originator() const; QString pathname() const; NetConveyorEvent::HttpMethod method() const; QString dump() const; static QString httpMethodString(HttpMethod method); private: void *evt_originator; QString evt_pathname; NetConveyorEvent::HttpMethod evt_method; }; class NetConveyor : public QObject { Q_OBJECT; public: NetConveyor(Config *conf,QObject *parent=0); ~NetConveyor(); void push(void *orig,const QString &pathname, NetConveyorEvent::HttpMethod meth); void push(const NetConveyorEvent &evt); void stop(); signals: void stopped(); private slots: void startConveyorProcess(); void processFinishedData(int exit_code,QProcess::ExitStatus exit_status); void processReadyReadData(); private: QProcess *conv_process; QTimer *conv_restart_timer; QDir *conv_temp_dir; QStringList conv_putted_files; Config *conv_config; }; #endif // NETCONVEYOR_H GlassCoder-2.0.1/src/glasscoder/opuscodec.cpp000066400000000000000000000266631425562563600212000ustar00rootroot00000000000000// opuscodec.cpp // // Codec class for Advanced Audio Coding (AAC) // // (C) Copyright 2014 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "logging.h" #include "opuscodec.h" OpusCodec::OpusCodec(Ringbuffer *ring,QObject *parent) : Codec(Codec::TypeOpus,ring,parent) { opus_packet_number=0; opus_packet_granulepos=0; } QByteArray OpusCodec::streamPrologue() const { return opus_stream_prologue; } bool OpusCodec::isAvailable() const { #ifdef HAVE_OPUS return (dlopen("libopus.so.0",RTLD_LAZY)!=NULL)&& (dlopen("libogg.so.0",RTLD_LAZY)!=NULL); #else return false; #endif // HAVE_OPUS } QString OpusCodec::contentType() const { return "audio/ogg"; } unsigned OpusCodec::pcmFrames() const { return 2880; } QString OpusCodec::defaultExtension() const { return QString("ogg"); } QString OpusCodec::formatIdentifier() const { return QString(); } bool OpusCodec::startCodec() { #ifdef HAVE_OPUS int err; // // Load Libraries // opus_handle=dlopen("libopus.so.0",RTLD_LAZY); if(opus_handle==NULL) { Log(LOG_ERR,"unsupported audio format (library not found)"); return false; } *(void **)(&opus_encoder_create)=dlsym(opus_handle,"opus_encoder_create"); *(void **)(&opus_encoder_init)=dlsym(opus_handle,"opus_encoder_init"); *(void **)(&opus_encode)=dlsym(opus_handle,"opus_encode"); *(void **)(&opus_encode_float)=dlsym(opus_handle,"opus_encode_float"); *(void **)(&opus_encoder_destroy)=dlsym(opus_handle,"opus_encoder_destroy"); *(void **)(&opus_encoder_ctl)=dlsym(opus_handle,"opus_encoder_ctl"); *(void **)(&opus_strerror)=dlsym(opus_handle,"opus_strerror"); opus_ogg_handle=dlopen("libogg.so.0",RTLD_LAZY); if(opus_ogg_handle==NULL) { Log(LOG_ERR,"unsupported audio format (library not found)"); return false; } *(void **)(&oggpack_writeinit)=dlsym(opus_ogg_handle,"oggpack_writeint"); *(void **)(&oggpack_writecheck)=dlsym(opus_ogg_handle,"oggpack_writecheck"); *(void **)(&oggpack_writetrunc)=dlsym(opus_ogg_handle,"oggpack_writetrunc"); *(void **)(&oggpack_writealign)=dlsym(opus_ogg_handle,"oggpack_writealign"); *(void **)(&oggpack_writecopy)=dlsym(opus_ogg_handle,"oggpack_writecopy"); *(void **)(&oggpack_reset)=dlsym(opus_ogg_handle,"oggpack_reset"); *(void **)(&oggpack_writeclear)=dlsym(opus_ogg_handle,"oggpack_writeclear"); *(void **)(&oggpack_readinit)=dlsym(opus_ogg_handle,"oggpack_readinit"); *(void **)(&oggpack_write)=dlsym(opus_ogg_handle,"oggpack_write"); *(void **)(&oggpack_look)=dlsym(opus_ogg_handle,"oggpack_look"); *(void **)(&oggpack_look1)=dlsym(opus_ogg_handle,"oggpack_look1"); *(void **)(&oggpack_adv)=dlsym(opus_ogg_handle,"oggpack_adv"); *(void **)(&oggpack_adv1)=dlsym(opus_ogg_handle,"oggpack_adv1"); *(void **)(&oggpack_read)=dlsym(opus_ogg_handle,"oggpack_read"); *(void **)(&oggpack_read1)=dlsym(opus_ogg_handle,"oggpack_read1"); *(void **)(&oggpack_bytes)=dlsym(opus_ogg_handle,"oggpack_bytes"); *(void **)(&oggpack_bits)=dlsym(opus_ogg_handle,"oggpack_bits"); *(void **)(&oggpack_get_buffer)= dlsym(opus_ogg_handle,"oggpack_get_buffer"); *(void **)(&oggpackB_writeinit)=dlsym(opus_ogg_handle,"oggpackB_writeinit"); *(void **)(&oggpackB_writecheck)= dlsym(opus_ogg_handle,"oggpackB_writecheck"); *(void **)(&oggpackB_writetrunc)= dlsym(opus_ogg_handle,"oggpackB_writetrunc"); *(void **)(&oggpackB_writealign)= dlsym(opus_ogg_handle,"oggpackB_writealign"); *(void **)(&oggpackB_writecopy)=dlsym(opus_ogg_handle,"oggpackB_writecopy"); *(void **)(&oggpackB_reset)=dlsym(opus_ogg_handle,"oggpackB_reset"); *(void **)(&oggpackB_writeclear)= dlsym(opus_ogg_handle,"oggpackB_writeclear"); *(void **)(&oggpackB_readinit)=dlsym(opus_ogg_handle,"oggpackB_readini"); *(void **)(&oggpackB_write)=dlsym(opus_ogg_handle,"oggpackB_write"); *(void **)(&oggpackB_look)=dlsym(opus_ogg_handle,"oggpackB_look"); *(void **)(&oggpackB_look1)=dlsym(opus_ogg_handle,"oggpackB_look1"); *(void **)(&oggpackB_adv)=dlsym(opus_ogg_handle,"oggpackB_adv"); *(void **)(&oggpackB_adv1)=dlsym(opus_ogg_handle,"oggpackB_adv1"); *(void **)(&oggpackB_read)=dlsym(opus_ogg_handle,"oggpackB_read"); *(void **)(&oggpackB_read1)=dlsym(opus_ogg_handle,"oggpackB_read1"); *(void **)(&oggpackB_bytes)=dlsym(opus_ogg_handle,"oggpackB_bytes"); *(void **)(&oggpackB_bits)=dlsym(opus_ogg_handle,"oggpackB_bits"); *(void **)(&oggpackB_get_buffer)= dlsym(opus_ogg_handle,"oggpackB_get_buffer"); *(void **)(&ogg_stream_packetin)= dlsym(opus_ogg_handle,"ogg_stream_packetin"); *(void **)(&ogg_stream_iovecin)=dlsym(opus_ogg_handle,"ogg_stream_iovecin"); *(void **)(&ogg_stream_pageout)=dlsym(opus_ogg_handle,"ogg_stream_pageout"); *(void **)(&ogg_stream_flush)=dlsym(opus_ogg_handle,"ogg_stream_flush"); *(void **)(&ogg_sync_init)=dlsym(opus_ogg_handle,"ogg_sync_init"); *(void **)(&ogg_sync_clear)=dlsym(opus_ogg_handle,"ogg_sync_clear"); *(void **)(&ogg_sync_reset)=dlsym(opus_ogg_handle,"ogg_sync_reset"); *(void **)(&ogg_sync_destroy)=dlsym(opus_ogg_handle,"ogg_sync_destroy"); *(void **)(&ogg_sync_check)=dlsym(opus_ogg_handle,"ogg_sync_check"); *(void **)(&ogg_sync_buffer)=dlsym(opus_ogg_handle,"ogg_sync_buffer"); *(void **)(&ogg_sync_wrote)=dlsym(opus_ogg_handle,"ogg_sync_wrote"); *(void **)(&ogg_sync_pageseek)=dlsym(opus_ogg_handle,"ogg_sync_pageseek"); *(void **)(&ogg_sync_pageout)=dlsym(opus_ogg_handle,"ogg_sync_pageout"); *(void **)(&ogg_stream_pagein)=dlsym(opus_ogg_handle,"ogg_stream_pagein"); *(void **)(&ogg_stream_packetout)= dlsym(opus_ogg_handle,"ogg_stream_packetout"); *(void **)(&ogg_stream_packetpeek)= dlsym(opus_ogg_handle,"ogg_stream_packetpeek"); *(void **)(&ogg_stream_init)=dlsym(opus_ogg_handle,"ogg_stream_init"); *(void **)(&ogg_stream_clear)=dlsym(opus_ogg_handle,"ogg_stream_clear"); *(void **)(&ogg_stream_reset)=dlsym(opus_ogg_handle,"ogg_stream_reset"); *(void **)(&ogg_stream_reset_serialno)= dlsym(opus_ogg_handle,"ogg_stream_reset_serialn"); *(void **)(&ogg_stream_destroy)=dlsym(opus_ogg_handle,"ogg_stream_destroy"); *(void **)(&ogg_stream_check)=dlsym(opus_ogg_handle,"ogg_stream_check"); *(void **)(&ogg_stream_eos)=dlsym(opus_ogg_handle,"ogg_stream_eos"); *(void **)(&ogg_page_checksum_set)= dlsym(opus_ogg_handle,"ogg_page_checksum_set"); *(void **)(&ogg_page_version)=dlsym(opus_ogg_handle,"ogg_page_version"); *(void **)(&ogg_page_continued)=dlsym(opus_ogg_handle,"ogg_page_continued"); *(void **)(&ogg_page_bos)=dlsym(opus_ogg_handle,"ogg_page_bos"); *(void **)(&ogg_page_eos)=dlsym(opus_ogg_handle,"ogg_page_eos"); *(void **)(&ogg_page_granulepos)= dlsym(opus_ogg_handle,"ogg_page_granulepos"); *(void **)(&ogg_page_serialno)=dlsym(opus_ogg_handle,"ogg_page_serialno"); *(void **)(&ogg_page_pageno)=dlsym(opus_ogg_handle,"ogg_page_pageno"); *(void **)(&ogg_page_packets)=dlsym(opus_ogg_handle,"ogg_page_packets"); *(void **)(&ogg_packet_clear)=dlsym(opus_ogg_handle,"ogg_packet_clear"); // // Initialize Encoder Instance // if((opus_encoder=opus_encoder_create(streamSamplerate(),channels(), OPUS_APPLICATION_AUDIO,&err))==NULL) { Log(LOG_ERR,QString().sprintf("unable to create codec [%s]", opus_strerror(err))); return false; } if(bitrate()==0) { opus_encoder_ctl(opus_encoder,OPUS_SET_VBR(1)); opus_encoder_ctl(opus_encoder,OPUS_SET_COMPLEXITY(10.0*quality())); } else { opus_encoder_ctl(opus_encoder,OPUS_SET_VBR(0)); opus_encoder_ctl(opus_encoder,OPUS_SET_BITRATE(1000*bitrate())); } opus_encoder_ctl(opus_encoder, OPUS_SET_COMPLEXITY(OPUSCODEC_ENCODER_COMPLEXITY)); // // Initialize the stream // ogg_stream_init(&opus_ogg_stream,rand()); // // Header Packet // QByteArray info=MakeInfoHeader(channels(),streamSamplerate()); opus_ogg_packet.packet=(unsigned char *)info.constData(); opus_ogg_packet.bytes=info.length(); opus_ogg_packet.b_o_s=1; opus_ogg_packet.e_o_s=0; opus_ogg_packet.granulepos=0; opus_ogg_packet.packetno=opus_packet_number++; ogg_stream_packetin(&opus_ogg_stream,&opus_ogg_packet); while(ogg_stream_flush(&opus_ogg_stream,&opus_ogg_page)) { opus_stream_prologue.append((const char *)opus_ogg_page.header, opus_ogg_page.header_len); opus_stream_prologue.append((const char *)opus_ogg_page.body, opus_ogg_page.body_len); } // // Comment Packet // QByteArray comment=MakeCommentHeader(); opus_ogg_packet.packet=(unsigned char *)comment.constData(); opus_ogg_packet.bytes=comment.length(); opus_ogg_packet.b_o_s=0; opus_ogg_packet.e_o_s=0; opus_ogg_packet.granulepos=0; opus_ogg_packet.packetno=opus_packet_number++; ogg_stream_packetin(&opus_ogg_stream,&opus_ogg_packet); while(ogg_stream_flush(&opus_ogg_stream,&opus_ogg_page)!=0) { opus_stream_prologue.append((const char *)opus_ogg_page.header, opus_ogg_page.header_len); opus_stream_prologue.append((const char *)opus_ogg_page.body, opus_ogg_page.body_len); } return true; #else Log(LOG_ERR,"unsupported audio format (no build support)"); return false; #endif // HAVE_OPUS } void OpusCodec::encodeData(Connector *conn,const float *pcm,int frames) { #ifdef HAVE_OPUS int s; unsigned char data[4096]; if(!opus_prologue_sent) { conn->writeData(0,(const unsigned char *)opus_stream_prologue.constData(), opus_stream_prologue.size()); opus_prologue_sent=true; } if((s=opus_encode_float(opus_encoder,pcm,frames,data,4096))>1) { opus_packet_granulepos+=frames; opus_ogg_packet.packet=data; opus_ogg_packet.bytes=s; opus_ogg_packet.b_o_s=0; opus_ogg_packet.e_o_s=0; opus_ogg_packet.granulepos=opus_packet_granulepos; opus_ogg_packet.packetno=opus_packet_number++; ogg_stream_packetin(&opus_ogg_stream,&opus_ogg_packet); while(ogg_stream_pageout(&opus_ogg_stream,&opus_ogg_page)!=0) { conn-> writeData(frames,opus_ogg_page.header,opus_ogg_page.header_len); conn->writeData(0,opus_ogg_page.body,opus_ogg_page.body_len); } } else { Log(LOG_WARNING,QString().sprintf("opus encoding error %d",s)); } #endif // HAVE_OPUS } QByteArray OpusCodec::MakeInfoHeader(unsigned chans,unsigned samprate) { QByteArray hdr(19,0); hdr[0]='O'; hdr[1]='p'; hdr[2]='u'; hdr[3]='s'; hdr[4]='H'; hdr[5]='e'; hdr[6]='a'; hdr[7]='d'; hdr[8]=1; hdr[9]=0xFF&chans; hdr[12]=0xFF&samprate; hdr[13]=0xFF&(samprate>>8); hdr[14]=0xFF&(samprate>>16); hdr[15]=0xFF&(samprate>>24); return hdr; } QByteArray OpusCodec::MakeCommentHeader() { QString version=QString("GlassCoder ")+VERSION; QByteArray hdr(12,0); hdr[0]='O'; hdr[1]='p'; hdr[2]='u'; hdr[3]='s'; hdr[4]='T'; hdr[5]='a'; hdr[6]='g'; hdr[7]='s'; hdr[8]=version.toUtf8().length(); hdr[9]=0; hdr[10]=0; hdr[11]=0; hdr.append(version.toUtf8()); hdr.append((char)0); hdr.append((char)0); hdr.append((char)0); hdr.append((char)0); return hdr; } GlassCoder-2.0.1/src/glasscoder/opuscodec.h000066400000000000000000000133211425562563600206300ustar00rootroot00000000000000// opuscodec.h // // Codec class for Opus [RFC 6716] // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef OPUSCODEC_H #define OPUSCODEC_H #ifdef HAVE_OPUS #include #include #endif // HAVE_OPUS #include "codec.h" #define OPUSCODEC_ENCODER_COMPLEXITY 0 class OpusCodec : public Codec { Q_OBJECT; public: OpusCodec(Ringbuffer *ring,QObject *parent=0); QByteArray streamPrologue() const; bool isAvailable() const; QString contentType() const; unsigned pcmFrames() const; QString defaultExtension() const; QString formatIdentifier() const; bool startCodec(); protected: void encodeData(Connector *conn,const float *pcm,int frames); private: QByteArray MakeInfoHeader(unsigned chans,unsigned samprate); QByteArray MakeCommentHeader(); #ifdef HAVE_OPUS void *opus_handle; void *opus_ogg_handle; OpusEncoder *opus_encoder; int (*opus_encoder_get_size)(int); OpusEncoder *(*opus_encoder_create)(opus_int32, int, int, int *); int (*opus_encoder_init)(OpusEncoder *, opus_int32, int, int); opus_int32 (*opus_encode)(OpusEncoder *, const opus_int16 *, int, unsigned char *, opus_int32); opus_int32 (*opus_encode_float)(OpusEncoder *, const float *, int, unsigned char *, opus_int32); void (*opus_encoder_destroy)(OpusEncoder *); int (*opus_encoder_ctl)(OpusEncoder *, int request,...); const char *(*opus_strerror)(int error); void (*oggpack_writeinit)(oggpack_buffer *); int (*oggpack_writecheck)(oggpack_buffer *); void (*oggpack_writetrunc)(oggpack_buffer *,long); void (*oggpack_writealign)(oggpack_buffer *); void (*oggpack_writecopy)(oggpack_buffer *,void *,long); void (*oggpack_reset)(oggpack_buffer *); void (*oggpack_writeclear)(oggpack_buffer *); void (*oggpack_readinit)(oggpack_buffer *,unsigned char *,int); void (*oggpack_write)(oggpack_buffer *,unsigned long,int); long (*oggpack_look)(oggpack_buffer *,int); long (*oggpack_look1)(oggpack_buffer *); void (*oggpack_adv)(oggpack_buffer *,int); void (*oggpack_adv1)(oggpack_buffer *); long (*oggpack_read)(oggpack_buffer *,int); long (*oggpack_read1)(oggpack_buffer *); long (*oggpack_bytes)(oggpack_buffer *); long (*oggpack_bits)(oggpack_buffer *); unsigned char *(*oggpack_get_buffer)(oggpack_buffer *); void (*oggpackB_writeinit)(oggpack_buffer *); int (*oggpackB_writecheck)(oggpack_buffer *); void (*oggpackB_writetrunc)(oggpack_buffer *,long); void (*oggpackB_writealign)(oggpack_buffer *); void (*oggpackB_writecopy)(oggpack_buffer *,void *,long); void (*oggpackB_reset)(oggpack_buffer *); void (*oggpackB_writeclear)(oggpack_buffer *); void (*oggpackB_readinit)(oggpack_buffer *,unsigned char *,int); void (*oggpackB_write)(oggpack_buffer *,unsigned long,int); long (*oggpackB_look)(oggpack_buffer *,int); long (*oggpackB_look1)(oggpack_buffer *); void (*oggpackB_adv)(oggpack_buffer *,int); void (*oggpackB_adv1)(oggpack_buffer *); long (*oggpackB_read)(oggpack_buffer *,int); long (*oggpackB_read1)(oggpack_buffer *); long (*oggpackB_bytes)(oggpack_buffer *); long (*oggpackB_bits)(oggpack_buffer *); unsigned char *(*oggpackB_get_buffer)(oggpack_buffer *); int (*ogg_stream_packetin)(ogg_stream_state *, ogg_packet *); int (*ogg_stream_iovecin)(ogg_stream_state *, ogg_iovec_t *, int,long, ogg_int64_t); int (*ogg_stream_pageout)(ogg_stream_state *, ogg_page *); int (*ogg_stream_flush)(ogg_stream_state *, ogg_page *); int (*ogg_sync_init)(ogg_sync_state *); int (*ogg_sync_clear)(ogg_sync_state *); int (*ogg_sync_reset)(ogg_sync_state *); int (*ogg_sync_destroy)(ogg_sync_state *); int (*ogg_sync_check)(ogg_sync_state *); char (*ogg_sync_buffer)(ogg_sync_state *, long); int (*ogg_sync_wrote)(ogg_sync_state *, long); long (*ogg_sync_pageseek)(ogg_sync_state *,ogg_page *); int (*ogg_sync_pageout)(ogg_sync_state *, ogg_page *); int (*ogg_stream_pagein)(ogg_stream_state *, ogg_page *); int (*ogg_stream_packetout)(ogg_stream_state *,ogg_packet *); int (*ogg_stream_packetpeek)(ogg_stream_state *,ogg_packet *); int (*ogg_stream_init)(ogg_stream_state *,int); int (*ogg_stream_clear)(ogg_stream_state *); int (*ogg_stream_reset)(ogg_stream_state *); int (*ogg_stream_reset_serialno)(ogg_stream_state *,int); int (*ogg_stream_destroy)(ogg_stream_state *); int (*ogg_stream_check)(ogg_stream_state *); int (*ogg_stream_eos)(ogg_stream_state *); void (*ogg_page_checksum_set)(ogg_page *); int (*ogg_page_version)(const ogg_page *); int (*ogg_page_continued)(const ogg_page *); int (*ogg_page_bos)(const ogg_page *); int (*ogg_page_eos)(const ogg_page *); ogg_int64_t (*ogg_page_granulepos)(const ogg_page *); int (*ogg_page_serialno)(const ogg_page *); long (*ogg_page_pageno)(const ogg_page *); int (*ogg_page_packets)(const ogg_page *); void (*ogg_packet_clear)(ogg_packet *); ogg_stream_state opus_ogg_stream; ogg_page opus_ogg_page; ogg_packet opus_ogg_packet; #endif // HAVE_OPUS uint64_t opus_packet_number; uint64_t opus_packet_granulepos; QByteArray opus_stream_prologue; bool opus_prologue_sent; }; #endif // OPUSCODEC_H GlassCoder-2.0.1/src/glasscoder/pcm16codec.cpp000066400000000000000000000032371425562563600211300ustar00rootroot00000000000000// pcm16codec.cpp // // Codec class for 16 bit PCM (little endian) // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include "pcm16codec.h" Pcm16Codec::Pcm16Codec(Ringbuffer *ring,QObject *parent) : Codec(Codec::TypePcm16,ring,parent) { } bool Pcm16Codec::isAvailable() const { return true; } QString Pcm16Codec::contentType() const { return QString("audio/x-wav"); } unsigned Pcm16Codec::pcmFrames() const { return 1024; } QString Pcm16Codec::defaultExtension() const { return QString("wav"); } QString Pcm16Codec::formatIdentifier() const { return QString("wav"); } bool Pcm16Codec::startCodec() { return true; } void Pcm16Codec::encodeData(Connector *conn,const float *pcm,int frames) { src_float_to_short_array(pcm,pcm16_buffer,frames*channels()); for(int i=0;i<(frames*(int)channels());i++) { pcm16_buffer[i]=htons(pcm16_buffer[i]); } conn->writeData(frames,(const unsigned char *)pcm16_buffer, frames*channels()*2); } GlassCoder-2.0.1/src/glasscoder/pcm16codec.h000066400000000000000000000025041425562563600205710ustar00rootroot00000000000000// pcm16codec.h // // Codec class for 16 bit PCM (little endian) // // (C) Copyright 2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef PCM16CODEC_H #define PCM16CODEC_H #include "codec.h" #define PCM16_MAX_FRAMES 1024 class Pcm16Codec : public Codec { Q_OBJECT; public: Pcm16Codec(Ringbuffer *ring,QObject *parent=0); bool isAvailable() const; QString contentType() const; unsigned pcmFrames() const; QString defaultExtension() const; QString formatIdentifier() const; bool startCodec(); protected: void encodeData(Connector *conn,const float *pcm,int frames); private: short pcm16_buffer[PCM16_MAX_FRAMES*MAX_AUDIO_CHANNELS]; }; #endif // PCM16CODEC_H GlassCoder-2.0.1/src/glasscoder/socketmessage.cpp000066400000000000000000000025031425562563600220340ustar00rootroot00000000000000// socketmessage.cpp // // Abstract a WebSockets message // // (C) Copyright 2016-2019 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "socketmessage.h" SocketMessage::SocketMessage() { sock_op_code=SocketMessage::Close; } SocketMessage::OpCode SocketMessage::opCode() const { return sock_op_code; } void SocketMessage::setOpCode(OpCode opcode) { sock_op_code=opcode; } QByteArray SocketMessage::payload() const { return sock_payload; } void SocketMessage::appendPayload(const char c) { sock_payload+=c; } void SocketMessage::clearPayload() { sock_payload.clear(); } bool SocketMessage::isControlMessage(OpCode opcode) { return opcode>=SocketMessage::Close; } GlassCoder-2.0.1/src/glasscoder/socketmessage.h000066400000000000000000000026341425562563600215060ustar00rootroot00000000000000// socketmessage.h // // Abstract a WebSockets message // // (C) Copyright 2016-2019 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef SOCKETMESSAGE_H #define SOCKETMESSAGE_H #include class SocketMessage { public: enum OpCode {Continuation=0,Text=1,Binary=2,AppReserv3=3, AppReserv4=4,AppReserv5=5,AppReserv6=6,AppReserv7=7, Close=8,Ping=9,Pong=10,CntlReserv11=11, CntlReserv12=12,CntlReserv13=13,CntlReserv14=14,CntlReserv15=15}; SocketMessage(); OpCode opCode() const; void setOpCode(OpCode opcode); QByteArray payload() const; void appendPayload(const char c); void clearPayload(); static bool isControlMessage(OpCode opcode); private: OpCode sock_op_code; QByteArray sock_payload; }; #endif // SOCKETMESSAGE_H GlassCoder-2.0.1/src/glasscoder/socketserver.cpp000066400000000000000000000076501425562563600217260ustar00rootroot00000000000000// socketserver.cpp // // Receive TCP socket connections via a UNIX socket pipe // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include "socketserver.h" SocketServer::SocketServer(QObject *parent) : QObject(parent) { server_socket=-1; server_notifier=NULL; } SocketServer::~SocketServer() { while(server_connections.size()>0) { delete server_connections.front(); server_connections.pop(); } if(server_socket>=0) { delete server_notifier; close(server_socket); } if(!server_path.isEmpty()) { unlink(server_path.toUtf8()); } } QString SocketServer::path() const { return server_path; } int SocketServer::maxPendingConnections() const { return SOCKETSERVER_MAX_PENDING_CONNECTIONS; } QTcpSocket *SocketServer::nextPendingConnection() { if(server_connections.size()==0) { return NULL; } QTcpSocket *sock=server_connections.front(); server_connections.pop(); return sock; } bool SocketServer::listen(const QString &path) { struct sockaddr_un sa; if(server_socket>=0) { return false; } if((server_socket=socket(AF_UNIX,SOCK_STREAM,0))<0) { return false; } server_notifier=new QSocketNotifier(server_socket,QSocketNotifier::Read,this); connect(server_notifier,SIGNAL(activated(int)),this,SLOT(notifiedData(int))); memset(&sa,0,sizeof(sa)); sa.sun_family=AF_UNIX; strncpy(sa.sun_path,path.toUtf8(),107); if(bind(server_socket,(const struct sockaddr *)(&sa),sizeof(sa))<0) { close(server_socket); server_socket=-1; delete server_notifier; server_notifier=NULL; return false; } server_path=path; if(::listen(server_socket,SOCKETSERVER_MAX_PENDING_CONNECTIONS)<0) { close(server_socket); server_socket=-1; delete server_notifier; server_notifier=NULL; return false; } return true; } void SocketServer::notifiedData(int sock) { int unixsock=-1; int newsock=-1; if((unixsock=accept(sock,NULL,NULL))>=0) { if((newsock=GetDescriptor(unixsock))>=0) { server_connections.push(new QTcpSocket()); server_connections.back()->setSocketDescriptor(newsock); emit newConnection(); } } } int SocketServer::GetDescriptor(int unix_sock) { char buf[1]; struct msghdr msg; struct iovec iov[1]; ssize_t n; char data[256]; union { struct cmsghdr cm; char control[CMSG_SPACE(sizeof(int))]; } control_un; struct cmsghdr *cmptr; // // Did The Operation Succeed? // if(recv(unix_sock,buf,1,MSG_NOSIGNAL)<=0) { return -1; } if(buf[0]!=1) { return -1; } // // Get Descriptor // memset(&msg,0,sizeof(msg)); memset(&iov,0,sizeof(struct iovec)); msg.msg_control=control_un.control; msg.msg_controllen=sizeof(control_un.control); iov[0].iov_base=data; iov[0].iov_len=256; msg.msg_iov=iov; msg.msg_iovlen=1; if((n=recvmsg(unix_sock,&msg,0))<=0) { close(unix_sock); return -1; } close(unix_sock); if((cmptr=CMSG_FIRSTHDR(&msg))!=NULL && cmptr->cmsg_len==CMSG_LEN(sizeof(int))) { if(cmptr->cmsg_level!=SOL_SOCKET) { return -1; } if(cmptr->cmsg_type!=SCM_RIGHTS) { return -1; } return *((int *)CMSG_DATA(cmptr)); } return -1; } GlassCoder-2.0.1/src/glasscoder/socketserver.h000066400000000000000000000027631425562563600213730ustar00rootroot00000000000000// socketserver.h // // Receive TCP socket connections via a UNIX socket pipe // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef SOCKETSERVER_H #define SOCKETSERVER_H #include #include #include #include #define SOCKETSERVER_MAX_PENDING_CONNECTIONS 5 class SocketServer : public QObject { Q_OBJECT; public: SocketServer(QObject *parent=0); ~SocketServer(); QString path() const; int maxPendingConnections() const; QTcpSocket *nextPendingConnection(); bool listen(const QString &path); signals: void newConnection(); private slots: void notifiedData(int sock); private: int GetDescriptor(int unix_sock); QString server_path; QSocketNotifier *server_notifier; int server_socket; std::queue server_connections; }; #endif // SOCKETSERVER_H GlassCoder-2.0.1/src/glasscoder/vorbiscodec.cpp000066400000000000000000000342621425562563600215100ustar00rootroot00000000000000// vorbiscodec.cpp // // Codec class for OggVorbis // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "logging.h" #include "vorbiscodec.h" VorbisCodec::VorbisCodec(Ringbuffer *ring,QObject *parent) : Codec(Codec::TypeVorbis,ring,parent) { vorbis_prologue_sent=false; vorbis_buffer=NULL; } VorbisCodec::~VorbisCodec() { if(vorbis_buffer!=NULL) { delete vorbis_buffer; } } bool VorbisCodec::isAvailable() const { #ifdef HAVE_VORBIS return (dlopen("libvorbisenc.so.2",RTLD_LAZY)!=NULL)&& (dlopen("libvorbis.so.0",RTLD_LAZY)!=NULL)&& (dlopen("libogg.so.0",RTLD_LAZY)!=NULL); #else return false; #endif // HAVE_VORBIS } QString VorbisCodec::contentType() const { return "audio/ogg"; } unsigned VorbisCodec::pcmFrames() const { return 2048; } QString VorbisCodec::defaultExtension() const { return QString("ogg"); } QString VorbisCodec::formatIdentifier() const { return QString(); } QByteArray VorbisCodec::streamPrologue() const { return vorbis_stream_prologue; } bool VorbisCodec::startCodec() { #ifdef HAVE_VORBIS ogg_packet header; ogg_packet comment; ogg_packet codebook; vorbis_comment vorbis_comment; // // Load Library // vorbis_vorbisenc_handle=dlopen("libvorbisenc.so.2",RTLD_LAZY); if(vorbis_vorbisenc_handle==NULL) { Log(LOG_ERR,"unsupported audio format (library not found)"); return false; } vorbis_vorbis_handle=dlopen("libvorbis.so.0",RTLD_LAZY); if(vorbis_vorbis_handle==NULL) { Log(LOG_ERR,"unsupported audio format (library not found)"); return false; } vorbis_ogg_handle=dlopen("libogg.so.0",RTLD_LAZY); if(vorbis_ogg_handle==NULL) { Log(LOG_ERR,"unsupported audio format (library not found)"); return false; } *(void **)(&vorbis_encode_init)= dlsym(vorbis_vorbisenc_handle,"vorbis_encode_init"); *(void **)(&vorbis_encode_setup_managed)= dlsym(vorbis_vorbisenc_handle,"vorbis_encode_setup_managed"); *(void **)(&vorbis_encode_setup_vbr)= dlsym(vorbis_vorbisenc_handle,"vorbis_encode_setup_vbr"); *(void **)(&vorbis_encode_init_vbr)= dlsym(vorbis_vorbisenc_handle,"vorbis_encode_init_vbr"); *(void **)(&vorbis_encode_setup_init)= dlsym(vorbis_vorbisenc_handle,"vorbis_encode_setup_init"); *(void **)(&vorbis_encode_ctl)= dlsym(vorbis_vorbisenc_handle,"vorbis_encode_ctl"); *(void **)(&vorbis_info_init)= dlsym(vorbis_vorbis_handle,"vorbis_info_init"); *(void **)(&vorbis_info_clear)= dlsym(vorbis_vorbis_handle,"vorbis_info_clear"); *(void **)(&vorbis_info_blocksize)= dlsym(vorbis_vorbis_handle,"vorbis_info_blocksize"); *(void **)(&vorbis_comment_init)= dlsym(vorbis_vorbis_handle,"vorbis_comment_init"); *(void **)(&vorbis_comment_add)= dlsym(vorbis_vorbis_handle,"vorbis_comment_add"); *(void **)(&vorbis_comment_add_tag)= dlsym(vorbis_vorbis_handle,"vorbis_comment_add_tag"); *(void **)(&vorbis_comment_query)= dlsym(vorbis_vorbis_handle,"vorbis_comment_query"); *(void **)(&vorbis_comment_query_count)= dlsym(vorbis_vorbis_handle,"vorbis_comment_query_count"); *(void **)(&vorbis_comment_clear)= dlsym(vorbis_vorbis_handle,"vorbis_comment_clear"); *(void **)(&vorbis_block_init)= dlsym(vorbis_vorbis_handle,"vorbis_block_init"); *(void **)(&vorbis_block_clear)= dlsym(vorbis_vorbis_handle,"vorbis_block_clear"); *(void **)(&vorbis_dsp_clear)= dlsym(vorbis_vorbis_handle,"vorbis_dsp_clear"); *(void **)(&vorbis_granule_time)= dlsym(vorbis_vorbis_handle,"vorbis_granule_time"); *(void **)(&vorbis_version_string)= dlsym(vorbis_vorbis_handle,"vorbis_version_string"); *(void **)(&vorbis_analysis_init)= dlsym(vorbis_vorbis_handle,"vorbis_analysis_init"); *(void **)(&vorbis_commentheader_out)= dlsym(vorbis_vorbis_handle,"vorbis_commentheader_out"); *(void **)(&vorbis_analysis_headerout)= dlsym(vorbis_vorbis_handle,"vorbis_analysis_headerout"); *(void **)(&vorbis_analysis_buffer)= dlsym(vorbis_vorbis_handle,"vorbis_analysis_buffer"); *(void **)(&vorbis_analysis_wrote)= dlsym(vorbis_vorbis_handle,"vorbis_analysis_wrote"); *(void **)(&vorbis_analysis_blockout)= dlsym(vorbis_vorbis_handle,"vorbis_analysis_blockout"); *(void **)(&vorbis_analysis)= dlsym(vorbis_vorbis_handle,"vorbis_analysis"); *(void **)(&vorbis_bitrate_addblock)= dlsym(vorbis_vorbis_handle,"vorbis_bitrate_addblock"); *(void **)(&vorbis_bitrate_flushpacket)= dlsym(vorbis_vorbis_handle,"vorbis_bitrate_flushpacket"); *(void **)(&vorbis_synthesis_idheader)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_idheader"); *(void **)(&vorbis_synthesis_headerin)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_headerin"); *(void **)(&vorbis_synthesis_init)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_init"); *(void **)(&vorbis_synthesis_restart)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_restart"); *(void **)(&vorbis_synthesis)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis"); *(void **)(&vorbis_synthesis_trackonly)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_trackonly"); *(void **)(&vorbis_synthesis_blockin)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_blockin"); *(void **)(&vorbis_synthesis_pcmout)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_pcmout"); *(void **)(&vorbis_synthesis_lapout)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_lapout"); *(void **)(&vorbis_synthesis_read)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_read"); *(void **)(&vorbis_packet_blocksize)= dlsym(vorbis_vorbis_handle,"vorbis_packets_blocksize"); *(void **)(&vorbis_synthesis_halfrate)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_halfrate"); *(void **)(&vorbis_synthesis_halfrate_p)= dlsym(vorbis_vorbis_handle,"vorbis_synthesis_halfrate"); *(void **)(&oggpack_writeinit)=dlsym(vorbis_ogg_handle,"oggpack_writeint"); *(void **)(&oggpack_writecheck)=dlsym(vorbis_ogg_handle,"oggpack_writecheck"); *(void **)(&oggpack_writetrunc)=dlsym(vorbis_ogg_handle,"oggpack_writetrunc"); *(void **)(&oggpack_writealign)=dlsym(vorbis_ogg_handle,"oggpack_writealign"); *(void **)(&oggpack_writecopy)=dlsym(vorbis_ogg_handle,"oggpack_writecopy"); *(void **)(&oggpack_reset)=dlsym(vorbis_ogg_handle,"oggpack_reset"); *(void **)(&oggpack_writeclear)=dlsym(vorbis_ogg_handle,"oggpack_writeclear"); *(void **)(&oggpack_readinit)=dlsym(vorbis_ogg_handle,"oggpack_readinit"); *(void **)(&oggpack_write)=dlsym(vorbis_ogg_handle,"oggpack_write"); *(void **)(&oggpack_look)=dlsym(vorbis_ogg_handle,"oggpack_look"); *(void **)(&oggpack_look1)=dlsym(vorbis_ogg_handle,"oggpack_look1"); *(void **)(&oggpack_adv)=dlsym(vorbis_ogg_handle,"oggpack_adv"); *(void **)(&oggpack_adv1)=dlsym(vorbis_ogg_handle,"oggpack_adv1"); *(void **)(&oggpack_read)=dlsym(vorbis_ogg_handle,"oggpack_read"); *(void **)(&oggpack_read1)=dlsym(vorbis_ogg_handle,"oggpack_read1"); *(void **)(&oggpack_bytes)=dlsym(vorbis_ogg_handle,"oggpack_bytes"); *(void **)(&oggpack_bits)=dlsym(vorbis_ogg_handle,"oggpack_bits"); *(void **)(&oggpack_get_buffer)= dlsym(vorbis_ogg_handle,"oggpack_get_buffer"); *(void **)(&oggpackB_writeinit)=dlsym(vorbis_ogg_handle,"oggpackB_writeinit"); *(void **)(&oggpackB_writecheck)= dlsym(vorbis_ogg_handle,"oggpackB_writecheck"); *(void **)(&oggpackB_writetrunc)= dlsym(vorbis_ogg_handle,"oggpackB_writetrunc"); *(void **)(&oggpackB_writealign)= dlsym(vorbis_ogg_handle,"oggpackB_writealign"); *(void **)(&oggpackB_writecopy)=dlsym(vorbis_ogg_handle,"oggpackB_writecopy"); *(void **)(&oggpackB_reset)=dlsym(vorbis_ogg_handle,"oggpackB_reset"); *(void **)(&oggpackB_writeclear)= dlsym(vorbis_ogg_handle,"oggpackB_writeclear"); *(void **)(&oggpackB_readinit)=dlsym(vorbis_ogg_handle,"oggpackB_readini"); *(void **)(&oggpackB_write)=dlsym(vorbis_ogg_handle,"oggpackB_write"); *(void **)(&oggpackB_look)=dlsym(vorbis_ogg_handle,"oggpackB_look"); *(void **)(&oggpackB_look1)=dlsym(vorbis_ogg_handle,"oggpackB_look1"); *(void **)(&oggpackB_adv)=dlsym(vorbis_ogg_handle,"oggpackB_adv"); *(void **)(&oggpackB_adv1)=dlsym(vorbis_ogg_handle,"oggpackB_adv1"); *(void **)(&oggpackB_read)=dlsym(vorbis_ogg_handle,"oggpackB_read"); *(void **)(&oggpackB_read1)=dlsym(vorbis_ogg_handle,"oggpackB_read1"); *(void **)(&oggpackB_bytes)=dlsym(vorbis_ogg_handle,"oggpackB_bytes"); *(void **)(&oggpackB_bits)=dlsym(vorbis_ogg_handle,"oggpackB_bits"); *(void **)(&oggpackB_get_buffer)= dlsym(vorbis_ogg_handle,"oggpackB_get_buffer"); *(void **)(&ogg_stream_packetin)= dlsym(vorbis_ogg_handle,"ogg_stream_packetin"); *(void **)(&ogg_stream_iovecin)=dlsym(vorbis_ogg_handle,"ogg_stream_iovecin"); *(void **)(&ogg_stream_pageout)=dlsym(vorbis_ogg_handle,"ogg_stream_pageout"); *(void **)(&ogg_stream_flush)=dlsym(vorbis_ogg_handle,"ogg_stream_flush"); *(void **)(&ogg_sync_init)=dlsym(vorbis_ogg_handle,"ogg_sync_init"); *(void **)(&ogg_sync_clear)=dlsym(vorbis_ogg_handle,"ogg_sync_clear"); *(void **)(&ogg_sync_reset)=dlsym(vorbis_ogg_handle,"ogg_sync_reset"); *(void **)(&ogg_sync_destroy)=dlsym(vorbis_ogg_handle,"ogg_sync_destroy"); *(void **)(&ogg_sync_check)=dlsym(vorbis_ogg_handle,"ogg_sync_check"); *(void **)(&ogg_sync_buffer)=dlsym(vorbis_ogg_handle,"ogg_sync_buffer"); *(void **)(&ogg_sync_wrote)=dlsym(vorbis_ogg_handle,"ogg_sync_wrote"); *(void **)(&ogg_sync_pageseek)=dlsym(vorbis_ogg_handle,"ogg_sync_pageseek"); *(void **)(&ogg_sync_pageout)=dlsym(vorbis_ogg_handle,"ogg_sync_pageout"); *(void **)(&ogg_stream_pagein)=dlsym(vorbis_ogg_handle,"ogg_stream_pagein"); *(void **)(&ogg_stream_packetout)= dlsym(vorbis_ogg_handle,"ogg_stream_packetout"); *(void **)(&ogg_stream_packetpeek)= dlsym(vorbis_ogg_handle,"ogg_stream_packetpeek"); *(void **)(&ogg_stream_init)=dlsym(vorbis_ogg_handle,"ogg_stream_init"); *(void **)(&ogg_stream_clear)=dlsym(vorbis_ogg_handle,"ogg_stream_clear"); *(void **)(&ogg_stream_reset)=dlsym(vorbis_ogg_handle,"ogg_stream_reset"); *(void **)(&ogg_stream_reset_serialno)= dlsym(vorbis_ogg_handle,"ogg_stream_reset_serialn"); *(void **)(&ogg_stream_destroy)=dlsym(vorbis_ogg_handle,"ogg_stream_destroy"); *(void **)(&ogg_stream_check)=dlsym(vorbis_ogg_handle,"ogg_stream_check"); *(void **)(&ogg_stream_eos)=dlsym(vorbis_ogg_handle,"ogg_stream_eos"); *(void **)(&ogg_page_checksum_set)= dlsym(vorbis_ogg_handle,"ogg_page_checksum_set"); *(void **)(&ogg_page_version)=dlsym(vorbis_ogg_handle,"ogg_page_version"); *(void **)(&ogg_page_continued)=dlsym(vorbis_ogg_handle,"ogg_page_continued"); *(void **)(&ogg_page_bos)=dlsym(vorbis_ogg_handle,"ogg_page_bos"); *(void **)(&ogg_page_eos)=dlsym(vorbis_ogg_handle,"ogg_page_eos"); *(void **)(&ogg_page_granulepos)= dlsym(vorbis_ogg_handle,"ogg_page_granulepos"); *(void **)(&ogg_page_serialno)=dlsym(vorbis_ogg_handle,"ogg_page_serialno"); *(void **)(&ogg_page_pageno)=dlsym(vorbis_ogg_handle,"ogg_page_pageno"); *(void **)(&ogg_page_packets)=dlsym(vorbis_ogg_handle,"ogg_page_packets"); *(void **)(&ogg_packet_clear)=dlsym(vorbis_ogg_handle,"ogg_packet_clear"); // // Initialize Encoder Instance // vorbis_info_init(&vorbis_vorbis_info); if(bitrate()==0) { if(vorbis_encode_init_vbr(&vorbis_vorbis_info,channels(),streamSamplerate(), quality())!=0) { Log(LOG_ERR,"unable to initialize encoder"); return false; } } else { if(vorbis_encode_init(&vorbis_vorbis_info,channels(), streamSamplerate(),1000*bitrate(), 1000*bitrate(),1000*bitrate())!=0) { Log(LOG_ERR,"unable to initialize encoder"); return false; } } vorbis_comment_init(&vorbis_comment); // Metadata stuff goes here... vorbis_analysis_init(&vorbis_vorbis_dsp,&vorbis_vorbis_info); vorbis_block_init(&vorbis_vorbis_dsp,&vorbis_vorbis_block); vorbis_analysis_headerout(&vorbis_vorbis_dsp,&vorbis_comment, &header,&comment,&codebook); ogg_stream_init(&vorbis_ogg_stream,rand()); ogg_stream_packetin(&vorbis_ogg_stream,&header); ogg_stream_packetin(&vorbis_ogg_stream,&comment); ogg_stream_packetin(&vorbis_ogg_stream,&codebook); while(ogg_stream_flush(&vorbis_ogg_stream,&vorbis_ogg_page)!=0) { vorbis_stream_prologue.append((const char *)vorbis_ogg_page.header, vorbis_ogg_page.header_len); vorbis_stream_prologue.append((const char *)vorbis_ogg_page.body, vorbis_ogg_page.body_len); } return true; #else Log(LOG_ERR,"unsupported audio format (no build support)"); return false; #endif // HAVE_VORBIS } void VorbisCodec::encodeData(Connector *conn,const float *pcm,int frames) { #ifdef HAVE_VORBIS float **vorbis; if(!vorbis_prologue_sent) { conn->writeData(0,(const unsigned char *)vorbis_stream_prologue.constData(), vorbis_stream_prologue.size()); vorbis_prologue_sent=true; } if((vorbis=vorbis_analysis_buffer(&vorbis_vorbis_dsp,frames))==NULL) { Log(LOG_ERR,"unable to allocate stream buffer"); exit(256); } for(int i=0;i0) { vorbis_analysis(&vorbis_vorbis_block,&vorbis_ogg_packet); vorbis_bitrate_addblock(&vorbis_vorbis_block); while(vorbis_bitrate_flushpacket(&vorbis_vorbis_dsp,&vorbis_ogg_packet)) { ogg_stream_packetin(&vorbis_ogg_stream,&vorbis_ogg_packet); while(ogg_stream_pageout(&vorbis_ogg_stream,&vorbis_ogg_page)!=0) { conn-> writeData(frames,vorbis_ogg_page.header,vorbis_ogg_page.header_len); conn->writeData(frames,vorbis_ogg_page.body,vorbis_ogg_page.body_len); } } } #endif // HAVE_VORBIS } GlassCoder-2.0.1/src/glasscoder/vorbiscodec.h000066400000000000000000000174461425562563600211620ustar00rootroot00000000000000// vorbiscodec.h // // Codec class for OggVorbis // // (C) Copyright 2014-2015 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef VORBISCODEC_H #define VORBISCODEC_H #ifdef HAVE_VORBIS #include #include #endif // HAVE_VORBIS #include "codec.h" class VorbisCodec : public Codec { Q_OBJECT; public: VorbisCodec(Ringbuffer *ring,QObject *parent=0); ~VorbisCodec(); bool isAvailable() const; QString contentType() const; unsigned pcmFrames() const; QString defaultExtension() const; QString formatIdentifier() const; QByteArray streamPrologue() const; bool startCodec(); protected: void encodeData(Connector *conn,const float *pcm,int frames); private: #ifdef HAVE_VORBIS void *vorbis_vorbisenc_handle; void *vorbis_vorbis_handle; void *vorbis_ogg_handle; int (*vorbis_encode_init)(vorbis_info *,long,long,long,long,long); int (*vorbis_encode_setup_managed)(vorbis_info *,long,long,long,long,long); int (*vorbis_encode_setup_vbr)(vorbis_info *,long,long,float); int (*vorbis_encode_init_vbr)(vorbis_info *,long,long,float); int (*vorbis_encode_setup_init)(vorbis_info *); int (*vorbis_encode_ctl)(vorbis_info *,int,void *); void (*vorbis_info_init)(vorbis_info *); void (*vorbis_info_clear)(vorbis_info *); int (*vorbis_info_blocksize)(vorbis_info,int); void (*vorbis_comment_init)(vorbis_comment *); void (*vorbis_comment_add)(vorbis_comment *, const char *); void (*vorbis_comment_add_tag)(vorbis_comment *,const char *,const char *); char (*vorbis_comment_query)(vorbis_comment *, const char *, int); int (*vorbis_comment_query_count)(vorbis_comment *, const char *); void (*vorbis_comment_clear)(vorbis_comment *); int (*vorbis_block_init)(vorbis_dsp_state *, vorbis_block *); int (*vorbis_block_clear)(vorbis_block *); void (*vorbis_dsp_clear)(vorbis_dsp_state *); double (*vorbis_granule_time)(vorbis_dsp_state *,ogg_int64_t); const char *(*vorbis_version_string)(void); int (*vorbis_analysis_init)(vorbis_dsp_state *,vorbis_info *); int (*vorbis_commentheader_out)(vorbis_comment *, ogg_packet *); int (*vorbis_analysis_headerout)(vorbis_dsp_state *,vorbis_comment *, ogg_packet *,ogg_packet *,ogg_packet *); float **(*vorbis_analysis_buffer)(vorbis_dsp_state *,int); int (*vorbis_analysis_wrote)(vorbis_dsp_state *,int); int (*vorbis_analysis_blockout)(vorbis_dsp_state *,vorbis_block *); int (*vorbis_analysis)(vorbis_block *,ogg_packet *); int (*vorbis_bitrate_addblock)(vorbis_block *); int (*vorbis_bitrate_flushpacket)(vorbis_dsp_state *,ogg_packet *); int (*vorbis_synthesis_idheader)(ogg_packet *); int (*vorbis_synthesis_headerin)(vorbis_info *vi,vorbis_comment *, ogg_packet *); int (*vorbis_synthesis_init)(vorbis_dsp_state *,vorbis_info *); int (*vorbis_synthesis_restart)(vorbis_dsp_state *); int (*vorbis_synthesis)(vorbis_block *,ogg_packet *); int (*vorbis_synthesis_trackonly)(vorbis_block *,ogg_packet *); int (*vorbis_synthesis_blockin)(vorbis_dsp_state *,vorbis_block *); int (*vorbis_synthesis_pcmout)(vorbis_dsp_state *,float ***); int (*vorbis_synthesis_lapout)(vorbis_dsp_state *,float ***); int (*vorbis_synthesis_read)(vorbis_dsp_state *,int); long (*vorbis_packet_blocksize)(vorbis_info *,ogg_packet *); int (*vorbis_synthesis_halfrate)(vorbis_info *,int); int (*vorbis_synthesis_halfrate_p)(vorbis_info *); void (*oggpack_writeinit)(oggpack_buffer *); int (*oggpack_writecheck)(oggpack_buffer *); void (*oggpack_writetrunc)(oggpack_buffer *,long); void (*oggpack_writealign)(oggpack_buffer *); void (*oggpack_writecopy)(oggpack_buffer *,void *,long); void (*oggpack_reset)(oggpack_buffer *); void (*oggpack_writeclear)(oggpack_buffer *); void (*oggpack_readinit)(oggpack_buffer *,unsigned char *,int); void (*oggpack_write)(oggpack_buffer *,unsigned long,int); long (*oggpack_look)(oggpack_buffer *,int); long (*oggpack_look1)(oggpack_buffer *); void (*oggpack_adv)(oggpack_buffer *,int); void (*oggpack_adv1)(oggpack_buffer *); long (*oggpack_read)(oggpack_buffer *,int); long (*oggpack_read1)(oggpack_buffer *); long (*oggpack_bytes)(oggpack_buffer *); long (*oggpack_bits)(oggpack_buffer *); unsigned char *(*oggpack_get_buffer)(oggpack_buffer *); void (*oggpackB_writeinit)(oggpack_buffer *); int (*oggpackB_writecheck)(oggpack_buffer *); void (*oggpackB_writetrunc)(oggpack_buffer *,long); void (*oggpackB_writealign)(oggpack_buffer *); void (*oggpackB_writecopy)(oggpack_buffer *,void *,long); void (*oggpackB_reset)(oggpack_buffer *); void (*oggpackB_writeclear)(oggpack_buffer *); void (*oggpackB_readinit)(oggpack_buffer *,unsigned char *,int); void (*oggpackB_write)(oggpack_buffer *,unsigned long,int); long (*oggpackB_look)(oggpack_buffer *,int); long (*oggpackB_look1)(oggpack_buffer *); void (*oggpackB_adv)(oggpack_buffer *,int); void (*oggpackB_adv1)(oggpack_buffer *); long (*oggpackB_read)(oggpack_buffer *,int); long (*oggpackB_read1)(oggpack_buffer *); long (*oggpackB_bytes)(oggpack_buffer *); long (*oggpackB_bits)(oggpack_buffer *); unsigned char *(*oggpackB_get_buffer)(oggpack_buffer *); int (*ogg_stream_packetin)(ogg_stream_state *, ogg_packet *); int (*ogg_stream_iovecin)(ogg_stream_state *, ogg_iovec_t *, int,long, ogg_int64_t); int (*ogg_stream_pageout)(ogg_stream_state *, ogg_page *); int (*ogg_stream_flush)(ogg_stream_state *, ogg_page *); int (*ogg_sync_init)(ogg_sync_state *); int (*ogg_sync_clear)(ogg_sync_state *); int (*ogg_sync_reset)(ogg_sync_state *); int (*ogg_sync_destroy)(ogg_sync_state *); int (*ogg_sync_check)(ogg_sync_state *); char (*ogg_sync_buffer)(ogg_sync_state *, long); int (*ogg_sync_wrote)(ogg_sync_state *, long); long (*ogg_sync_pageseek)(ogg_sync_state *,ogg_page *); int (*ogg_sync_pageout)(ogg_sync_state *, ogg_page *); int (*ogg_stream_pagein)(ogg_stream_state *, ogg_page *); int (*ogg_stream_packetout)(ogg_stream_state *,ogg_packet *); int (*ogg_stream_packetpeek)(ogg_stream_state *,ogg_packet *); int (*ogg_stream_init)(ogg_stream_state *,int); int (*ogg_stream_clear)(ogg_stream_state *); int (*ogg_stream_reset)(ogg_stream_state *); int (*ogg_stream_reset_serialno)(ogg_stream_state *,int); int (*ogg_stream_destroy)(ogg_stream_state *); int (*ogg_stream_check)(ogg_stream_state *); int (*ogg_stream_eos)(ogg_stream_state *); void (*ogg_page_checksum_set)(ogg_page *); int (*ogg_page_version)(const ogg_page *); int (*ogg_page_continued)(const ogg_page *); int (*ogg_page_bos)(const ogg_page *); int (*ogg_page_eos)(const ogg_page *); ogg_int64_t (*ogg_page_granulepos)(const ogg_page *); int (*ogg_page_serialno)(const ogg_page *); long (*ogg_page_pageno)(const ogg_page *); int (*ogg_page_packets)(const ogg_page *); void (*ogg_packet_clear)(ogg_packet *); vorbis_info vorbis_vorbis_info; vorbis_dsp_state vorbis_vorbis_dsp; vorbis_block vorbis_vorbis_block; ogg_stream_state vorbis_ogg_stream; ogg_page vorbis_ogg_page; ogg_packet vorbis_ogg_packet; #endif // HAVE_VORBIS unsigned long vorbis_input_samples; unsigned long vorbis_buffer_size; unsigned char *vorbis_buffer; QByteArray vorbis_stream_prologue; bool vorbis_prologue_sent; }; #endif // VORBISCODEC_H GlassCoder-2.0.1/src/glasscommander/000077500000000000000000000000001425562563600173445ustar00rootroot00000000000000GlassCoder-2.0.1/src/glasscommander/Makefile.am000066400000000000000000000131521425562563600214020ustar00rootroot00000000000000## automake.am ## ## Makefile for the glasscommander(1) Audio Encoder front-end. ## ## (C) Copyright 2016-2022 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Use automake to process this into a Makefile.in AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -Wno-strict-aliasing -std=c++11 -fPIC @QT5_GUI_CFLAGS@ @SNDFILE_CFLAGS@ @ALSA_CFLAGS@ MOC = @QT5_MOC@ # The dependency for qt's Meta Object Compiler (moc) moc_%.cpp: %.h @MOC@ $< -o $@ bin_PROGRAMS = glasscommander dist_glasscommander_SOURCES = configdialog.cpp configdialog.h\ deletedialog.cpp deletedialog.h\ filenamevalidator.cpp filenamevalidator.h\ glasscommander.cpp glasscommander.h\ glasswidget.cpp glasswidget.h\ instancedialog.cpp instancedialog.h\ playmeter.cpp playmeter.h nodist_glasscommander_SOURCES = asihpi.cpp asihpi.h\ audiodevice.cpp audiodevice.h\ cmdswitch.cpp cmdswitch.h\ codec.cpp codec.h\ codecdialog.cpp codecdialog.h\ codeviewer.cpp codeviewer.h\ combobox.cpp combobox.h\ connector.cpp connector.h\ glasslimits.h\ guiapplication.cpp guiapplication.h\ hpiinputlistview.cpp hpiinputlistview.h\ hpiwidget.cpp hpiwidget.h\ logging.cpp logging.h\ messagewidget.cpp messagewidget.h\ metaevent.cpp metaevent.h\ moc_audiodevice.cpp\ moc_codec.cpp\ moc_codecdialog.cpp\ moc_codeviewer.cpp\ moc_combobox.cpp\ moc_configdialog.cpp\ moc_connector.cpp\ moc_deletedialog.cpp\ moc_filenamevalidator.cpp\ moc_glasscommander.cpp\ moc_glasswidget.cpp\ moc_guiapplication.cpp\ moc_hpiinputlistview.cpp\ moc_hpiwidget.cpp\ moc_instancedialog.cpp\ moc_messagewidget.cpp\ moc_playmeter.cpp\ moc_segmeter.cpp\ moc_serverdialog.cpp\ moc_sourcedialog.cpp\ moc_spinbox.cpp\ moc_statuswidget.cpp\ moc_stereometer.cpp\ moc_streamdialog.cpp\ profile.cpp profile.h\ ringbuffer.cpp ringbuffer.h\ segmeter.cpp segmeter.h\ serverdialog.cpp serverdialog.h\ sourcedialog.cpp sourcedialog.h\ spinbox.cpp spinbox.h\ statuswidget.cpp statuswidget.h\ stereometer.cpp stereometer.h\ streamdialog.cpp streamdialog.h glasscommander_LDADD = @GUILIBS@ @LIBJACK@ @SNDFILE_LIBS@ @ALSA_LIBS@ @ASIHPI_LIBS@ @QT5_GUI_LIBS@ -lpthread glasscommander_LDFLAGS = @GUIFLAGS@ CLEANFILES = *~\ moc_*\ *.obj\ *.idb\ *.pdb\ *ilk DISTCLEANFILES = asihpi.cpp asihpi.h\ audiodevice.cpp audiodevice.h\ cmdswitch.cpp cmdswitch.h\ codec.cpp codec.h\ codecdialog.cpp codecdialog.h\ codeviewer.cpp codeviewer.h\ combobox.cpp combobox.h\ connector.cpp connector.h\ glasslimits.h\ guiapplication.cpp guiapplication.h\ hpiinputlistview.cpp hpiinputlistview.h\ hpiwidget.cpp hpiwidget.h\ logging.cpp logging.h\ messagewidget.cpp messagewidget.h\ metaevent.cpp metaevent.h\ paths.h\ profile.cpp profile.h\ ringbuffer.cpp ringbuffer.h\ segmeter.cpp segmeter.h\ serverdialog.cpp serverdialog.h\ sourcedialog.cpp sourcedialog.h\ spinbox.cpp spinbox.h\ statuswidget.cpp statuswidget.h\ stereometer.cpp stereometer.h\ streamdialog.cpp streamdialog.h MAINTAINERCLEANFILES = *~\ Makefile.in GlassCoder-2.0.1/src/glasscommander/configdialog.cpp000066400000000000000000000074201425562563600225000ustar00rootroot00000000000000// configdialog.cpp // // Show configration buttons for GlassCommander // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "configdialog.h" ConfigDialog::ConfigDialog(const QString &instance_name, ServerDialog *server_dialog, CodecDialog *codec_dialog, StreamDialog *stream_dialog, SourceDialog *source_dialog, CodeViewer *code_dialog,QWidget *parent) : QDialog(parent) { conf_server_dialog=server_dialog; conf_codec_dialog=codec_dialog; conf_stream_dialog=stream_dialog; conf_source_dialog=source_dialog; conf_code_dialog=code_dialog; setWindowTitle(tr("Instance")+": "+instance_name); QFont bold_font(font().family(),font().pointSize(),QFont::Bold); // // Server Settings // conf_server_button=new QPushButton(tr("Server")+"\n"+tr("Settings"),this); conf_server_button->setFont(bold_font); connect(conf_server_button,SIGNAL(clicked()),conf_server_dialog,SLOT(exec())); // // Codec Settings // conf_codec_button=new QPushButton(tr("Codec")+"\n"+tr("Settings"),this); conf_codec_button->setFont(bold_font); connect(conf_codec_button,SIGNAL(clicked()),conf_codec_dialog,SLOT(exec())); // // Stream Settings // conf_stream_button=new QPushButton(tr("Stream")+"\n"+tr("Settings"),this); conf_stream_button->setFont(bold_font); connect(conf_stream_button,SIGNAL(clicked()),conf_stream_dialog,SLOT(exec())); // // Source Settings // conf_source_button=new QPushButton(tr("Source")+"\n"+tr("Settings"),this); conf_source_button->setFont(bold_font); connect(conf_source_button,SIGNAL(clicked()),conf_source_dialog,SLOT(exec())); // // Code Viewer // conf_code_button=new QPushButton(tr("Show")+"\n"+tr("Code"),this); conf_code_button->setFont(bold_font); connect(conf_code_button,SIGNAL(clicked()),this,SLOT(showCodeDialog())); // // Autostart // conf_autostart_checkbox=new QCheckBox(this); conf_autostart_label= new QLabel(tr("Start this instance at application launch"),this); conf_autostart_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); conf_autostart_label->setFont(bold_font); } QSize ConfigDialog::sizeHint() const { return QSize(460,100); } int ConfigDialog::exec(bool *autostart) { conf_autostart=autostart; conf_autostart_checkbox->setChecked(*autostart); return QDialog::exec(); } void ConfigDialog::showCodeDialog() { QStringList args; args.push_back("glasscoder"); conf_server_dialog->makeArgs(&args,true); conf_codec_dialog->makeArgs(&args); conf_stream_dialog->makeArgs(&args,true); conf_source_dialog->makeArgs(&args,true); conf_code_dialog->exec(args); } void ConfigDialog::closeEvent(QCloseEvent *e) { *conf_autostart=conf_autostart_checkbox->isChecked(); done(0); } void ConfigDialog::resizeEvent(QResizeEvent *e) { conf_server_button->setGeometry(10,10,80,50); conf_codec_button->setGeometry(100,10,80,50); conf_stream_button->setGeometry(190,10,80,50); conf_source_button->setGeometry(280,10,80,50); conf_code_button->setGeometry(370,10,80,50); conf_autostart_checkbox->setGeometry(20,65,20,20); conf_autostart_label->setGeometry(45,65,size().width()-45,20); } GlassCoder-2.0.1/src/glasscommander/configdialog.h000066400000000000000000000037441425562563600221520ustar00rootroot00000000000000// configdialog.h // // Show configration buttons for GlassCommander // // (C) Copyright 2016-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef CONFIGDIALOG_H #define CONFIGDIALOG_H #include #include #include #include #include "codecdialog.h" #include "codeviewer.h" #include "serverdialog.h" #include "sourcedialog.h" #include "streamdialog.h" class ConfigDialog : public QDialog { Q_OBJECT; public: ConfigDialog(const QString &instance_name,ServerDialog *server_dialog, CodecDialog *codec_dialog,StreamDialog *stream_dialog, SourceDialog *source_dialog,CodeViewer *code_dialog, QWidget *parent=0); QSize sizeHint() const; public slots: int exec(bool *autostart); private slots: void showCodeDialog(); protected: void closeEvent(QCloseEvent *e); void resizeEvent(QResizeEvent *e); private: ServerDialog *conf_server_dialog; QPushButton *conf_server_button; CodecDialog *conf_codec_dialog; QPushButton *conf_codec_button; StreamDialog *conf_stream_dialog; QPushButton *conf_stream_button; SourceDialog *conf_source_dialog; QPushButton *conf_source_button; CodeViewer *conf_code_dialog; QPushButton *conf_code_button; QLabel *conf_autostart_label; QCheckBox *conf_autostart_checkbox; bool *conf_autostart; }; #endif // CONFIGDIALOG_H GlassCoder-2.0.1/src/glasscommander/deletedialog.cpp000066400000000000000000000053261425562563600225000ustar00rootroot00000000000000// deletedialog.cpp // // Confirm deletion of a GlassCoder instance. // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "deletedialog.h" DeleteDialog::DeleteDialog(QDir *inst_dir,QWidget *parent) : QDialog(parent) { setWindowTitle("GlassCommander - "+tr("Remove Instance")); // // Fonts // QFont bold_font(font().family(),font().pointSize(),QFont::Bold); dialog_delete_instance=NULL;; dialog_dir=inst_dir; dialog_name_label=new QLabel(this); dialog_name_label->setAlignment(Qt::AlignCenter|Qt::AlignVCenter); dialog_name_label->setFont(bold_font); dialog_delete_label=new QLabel(tr("Delete underlying instance data"),this); dialog_delete_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); dialog_delete_checkbox=new QCheckBox(this); dialog_yes_button=new QPushButton(tr("Yes"),this); dialog_yes_button->setFont(bold_font); connect(dialog_yes_button,SIGNAL(clicked()),this,SLOT(yesData())); dialog_no_button=new QPushButton(tr("No"),this); dialog_no_button->setFont(bold_font); connect(dialog_no_button,SIGNAL(clicked()),this,SLOT(noData())); } QSize DeleteDialog::sizeHint() const { return QSize(400,140); } int DeleteDialog::exec(const QString &inst_name,bool *delete_instance) { dialog_delete_instance=delete_instance; dialog_delete_checkbox->setChecked(*delete_instance); dialog_name_label-> setText(tr("Remove instance")+" \""+inst_name+"\" "+ tr("from the list?")); return QDialog::exec(); } void DeleteDialog::yesData() { *dialog_delete_instance=dialog_delete_checkbox->isChecked(); done(true); } void DeleteDialog::noData() { done(false); } void DeleteDialog::closeEvent(QCloseEvent *e) { noData(); } void DeleteDialog::resizeEvent(QResizeEvent *e) { dialog_name_label->setGeometry(10,10,size().width()-20,20); dialog_delete_checkbox->setGeometry(80,40,20,20); dialog_delete_label->setGeometry(105,40,size().width()-115,20); dialog_yes_button->setGeometry(size().width()-180,size().height()-60,80,50); dialog_no_button->setGeometry(size().width()-90,size().height()-60,80,50); } GlassCoder-2.0.1/src/glasscommander/deletedialog.h000066400000000000000000000030071425562563600221370ustar00rootroot00000000000000// deletedialog.h // // Confirm deletion of a GlassCoder instance. // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef DELETEDIALOG_H #define DELETEDIALOG_H #include #include #include #include #include class DeleteDialog : public QDialog { Q_OBJECT; public: DeleteDialog(QDir *inst_dir,QWidget *parent=0); QSize sizeHint() const; public slots: int exec(const QString &inst_name,bool *delete_instance); private slots: void yesData(); void noData(); protected: void closeEvent(QCloseEvent *e); void resizeEvent(QResizeEvent *e); private: bool *dialog_delete_instance; QLabel *dialog_name_label; QLabel *dialog_delete_label; QCheckBox *dialog_delete_checkbox; QPushButton *dialog_yes_button; QPushButton *dialog_no_button; QDir *dialog_dir; }; #endif // DELETEDIALOG_H GlassCoder-2.0.1/src/glasscommander/filenamevalidator.cpp000066400000000000000000000020711425562563600235360ustar00rootroot00000000000000// filenamevalidator.cpp // // Validator for legal filenames. // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include "filenamevalidator.h" FilenameValidator::FilenameValidator(QObject *parent) : QValidator(parent) { } QValidator::State FilenameValidator::validate(QString &input,int &pos) const { if(input.contains("/")) { return QValidator::Invalid; } return QValidator::Acceptable; } GlassCoder-2.0.1/src/glasscommander/filenamevalidator.h000066400000000000000000000020411425562563600232000ustar00rootroot00000000000000// filenamevalidator.h // // Validator for legal filenames. // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef FILENAMEVALIDATOR_H #define FILENAMEVALIDATOR_H #include class FilenameValidator : public QValidator { Q_OBJECT; public: FilenameValidator(QObject *parent=0); State validate(QString &input,int &pos) const; }; #endif // FILENAMEVALIDATOR_H GlassCoder-2.0.1/src/glasscommander/glasscommander.cpp000066400000000000000000000405741425562563600230610ustar00rootroot00000000000000// glasscommander.cpp // // glasscommander(1) Audio Encoder front end // // (C) Copyright 2016-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include #include #include "cmdswitch.h" #include "logging.h" #include "profile.h" #include "glasscommander.h" #include "../../icons/back.xpm" #include "../../icons/glasscoder-16x16.xpm" #include "../../icons/minussign.xpm" #include "../../icons/plussign.xpm" MainWidget::MainWidget(QWidget *parent) : GuiApplication(parent) { gui_temp_dir=NULL; gui_process=NULL; gui_starting_all=false; CmdSwitch *cmd=new CmdSwitch("glasscommander",GLASSCOMMANDER_USAGE); for(unsigned i=0;ikeys();i++) { if(cmd->key(i)=="--instance-directory") { if(!setSettingsDirectory(cmd->value(i))) { QMessageBox::critical(this,"GlassCommander - "+tr("Error"), tr("Unable to access specified instance directory")+ ":\n\""+cmd->value(i)+"\"."); ExitProgram(1); } cmd->setProcessed(i,true); } if(!cmd->processed(i)) { QMessageBox::critical(this,"GlassCommander - "+tr("Error"), tr("Unknown argument")+" \""+cmd->key(i)+"\"."); ExitProgram(256); } } setWindowIcon(QPixmap(glasscoder_16x16_xpm)); setWindowTitle(QString("GlassCommander v")+VERSION); // // Create Temp Directory // char tempdir[PATH_MAX]; strncpy(tempdir,"/tmp",PATH_MAX); if(getenv("TEMP")!=NULL) { strncpy(tempdir,getenv("TEMP"),PATH_MAX-1); } strncat(tempdir,"/glasscommander-XXXXXX",PATH_MAX-strlen(tempdir)); if(mkdtemp(tempdir)==NULL) { QMessageBox::critical(this,"GlassCommander - "+tr("Error"), tr("Unable to create temporary directory in")+ "\""+tempdir+"\". ["+strerror(errno)+"]"); ExitProgram(256); } gui_temp_dir=new QDir(tempdir); // // Temp Directory Keepalive // (To keep Systemd from "helpfully" deleting the "stale" temp dir) // QString keepalive_pathname= gui_temp_dir->absolutePath()+"/"+GLASSCOMMANDER_TEMP_KEEPALIVE_FILENAME; if((gui_temp_keepalive_fd=open(keepalive_pathname.toUtf8(), O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR))<0) { QMessageBox::critical(this,"GlassCommander - "+tr("Error"), tr("Unable to create keepalive file")+ " \""+keepalive_pathname+"\". ["+strerror(errno)+"]"); ExitProgram(256); } updateKeepaliveData(); gui_temp_keepalive_timer=new QTimer(this); connect(gui_temp_keepalive_timer,SIGNAL(timeout()), this,SLOT(updateKeepaliveData())); gui_temp_keepalive_timer->start(GLASSCOMMANDER_TEMP_KEEPALIVE_INTERVAL); // // Timers // gui_stop_timer=new QTimer(this); gui_stop_timer->setSingleShot(true); connect(gui_stop_timer,SIGNAL(timeout()),this,SLOT(stopTimeoutData())); gui_autostart_timer=new QTimer(this); gui_autostart_timer->setSingleShot(true); connect(gui_autostart_timer,SIGNAL(timeout()),this,SLOT(autostartData())); gui_autostart_index=0; // // Fonts // QFont bold_font(font().family(),font().pointSize(),QFont::Bold); // // Dialogs // gui_instance_dialog=new InstanceDialog(settingsDirectory(),this); gui_delete_dialog=new DeleteDialog(settingsDirectory(),this); // // Tool Bar // gui_toolbar=addToolBar("foo"); gui_toolbar->setAllowedAreas(Qt::TopToolBarArea); QAction *action= new QAction(QIcon(QPixmap(plussign_xpm)),tr("Add Instance"),this); action->setStatusTip(tr("Add an encoder instance")); connect(action,SIGNAL(triggered()),this,SLOT(addInstanceData())); gui_toolbar->addAction(action); gui_remove_action= new QAction(QIcon(QPixmap(minussign_xpm)),tr("Remove Instance"),this); gui_remove_action->setStatusTip(tr("Remove an encoder instance")); connect(gui_remove_action,SIGNAL(triggered()), this,SLOT(removeInstanceData())); gui_toolbar->addAction(gui_remove_action); gui_abandon_action= new QAction(QIcon(QPixmap(back_xpm)),tr("Abandon change"),this); gui_abandon_action-> setStatusTip(tr("Abandon Add or Removal of encoder instance")); gui_abandon_action->setDisabled(true); connect(gui_abandon_action,SIGNAL(triggered()), this,SLOT(abandonInstanceData())); gui_toolbar->addAction(gui_abandon_action); gui_toolbar->addSeparator(); gui_startall_button=new QPushButton(tr("Start All"),this); connect(gui_startall_button,SIGNAL(clicked()),this,SLOT(startAllData())); gui_toolbar->addWidget(gui_startall_button); gui_stopall_button=new QPushButton(tr("Stop All"),this); connect(gui_stopall_button,SIGNAL(clicked()),this,SLOT(stopAllData())); gui_toolbar->addWidget(gui_stopall_button); gui_insert_button=new QPushButton(tr("Insert"),this); gui_insert_button->setFont(bold_font); gui_insert_button->setStyleSheet("background-color: yellow"); connect(gui_insert_button,SIGNAL(clicked()), this,SLOT(topInsertClickedData())); gui_insert_button->hide(); LoadEncoders(); // // Set Size // setMinimumSize(sizeHint()); setMaximumHeight(sizeHint().height()); // // Get Codec List // gui_process=new QProcess(this); connect(gui_process,SIGNAL(error(QProcess::ProcessError)), this,SLOT(processErrorData(QProcess::ProcessError))); connect(gui_process,SIGNAL(finished(int,QProcess::ExitStatus)), this,SLOT(codecFinishedData(int,QProcess::ExitStatus))); QStringList args; args.push_back("--list-codecs"); gui_process->start("glasscoder",args); } QSize MainWidget::sizeHint() const { int encoder_height=0; if(gui_encoders.size()>0) { encoder_height=gui_encoders.at(0)->sizeHint().height()*gui_encoders.size(); } return QSize(1000,10+encoder_height+gui_toolbar->size().height()); } void MainWidget::addInstanceData() { QStringList used_names; for(int i=0;iinstanceName()); } if(gui_instance_dialog->exec(&gui_new_instance_name,used_names)) { gui_startall_button->setDisabled(true); gui_stopall_button->setDisabled(true); for(int i=0;isetMode(GlassWidget::InsertMode); } gui_abandon_action->setEnabled(true); gui_insert_button->show(); } } void MainWidget::removeInstanceData() { gui_startall_button->setDisabled(true); gui_stopall_button->setDisabled(true); for(int i=0;isetMode(GlassWidget::RemoveMode); } gui_insert_button->hide(); gui_abandon_action->setEnabled(true); } void MainWidget::abandonInstanceData() { gui_startall_button->setEnabled(true); gui_stopall_button->setEnabled(true); for(int i=0;isetMode(GlassWidget::NormalMode); } gui_insert_button->hide(); gui_abandon_action->setDisabled(true); } void MainWidget::topInsertClickedData() { insertClickedData(""); } void MainWidget::insertClickedData(const QString &instance_name) { int pos=1+GetEncoderPosition(instance_name); gui_encoders. insert(pos,new GlassWidget(gui_new_instance_name,gui_temp_dir,this)); ConnectEncoder(gui_encoders.at(pos)); gui_encoders.at(pos)->addCodecTypes(gui_codec_types); gui_encoders.at(pos)->addSourceTypes(gui_source_types); LoadEncoderConfig(gui_encoders.at(pos)); int w=size().width(); int h=size().height()+gui_encoders.at(pos)->sizeHint().height(); setMaximumHeight(h); setMinimumSize(w,h); abandonInstanceData(); gui_remove_action->setEnabled(gui_encoders.size()>0); gui_abandon_action->setDisabled(true); SaveEncoders(); } void MainWidget::removeClickedData(const QString &instance_name) { bool delete_instance=false; if(gui_delete_dialog->exec(instance_name,&delete_instance)) { int pos=GetEncoderPosition(instance_name); int w=size().width(); int h=size().height()-gui_encoders.at(pos)->sizeHint().height(); delete gui_encoders.at(pos); gui_encoders.erase(gui_encoders.begin()+pos); setMaximumHeight(h); setMinimumSize(w,h); abandonInstanceData(); SaveEncoders(); if(delete_instance) { deleteInstance(instance_name); } } gui_remove_action->setEnabled(gui_encoders.size()>0); gui_abandon_action->setDisabled(true); } void MainWidget::codecFinishedData(int exit_code, QProcess::ExitStatus exit_status) { QStringList f0; if(exit_code==0) { // // Populate Codec Types // gui_codec_types=gui_process->readAllStandardOutput(); for(int i=0;iaddCodecTypes(gui_codec_types); } gui_process->deleteLater(); // // Get Device List // gui_process=new QProcess(this); connect(gui_process,SIGNAL(error(QProcess::ProcessError)), this,SLOT(processErrorData(QProcess::ProcessError))); connect(gui_process,SIGNAL(finished(int,QProcess::ExitStatus)), this,SLOT(deviceFinishedData(int,QProcess::ExitStatus))); QStringList args; args.push_back("--list-devices"); gui_process->start("glasscoder",args); } else { ProcessError(exit_code,exit_status); } } void MainWidget::deviceFinishedData(int exit_code, QProcess::ExitStatus exit_status) { if(exit_code==0) { // // Populate Device Types // gui_source_types=gui_process->readAllStandardOutput(); for(int i=0;iaddSourceTypes(gui_source_types); } gui_process->deleteLater(); gui_process=NULL; } else { ProcessError(exit_code,exit_status); } for(int i=0;iinstanceName()); if((f=fopen((basepath+".tmp").toUtf8(),"w"))==NULL) { return; } fprintf(f,"[GlassGui]\n"); encoder->save(f); fclose(f); rename((basepath+".tmp").toUtf8(),basepath.toUtf8()); umask(mask); } SaveEncoders(); } void MainWidget::autostartData() { for(int i=gui_autostart_index;iisActive())&& (gui_starting_all||gui_encoders.at(i)->autoStart())) { gui_encoders.at(i)->start(); gui_autostart_timer->start(1000); gui_autostart_index=i+1; return; } } gui_starting_all=false; } void MainWidget::startAllData() { gui_starting_all=true; gui_autostart_index=0; autostartData(); } void MainWidget::stopAllData() { for(int i=0;iisActive()) { gui_encoders.at(i)->terminate(); } } } void MainWidget::encoderStoppedData() { if(--gui_stop_count==0) { ExitProgram(0); } } void MainWidget::stopTimeoutData() { for(int i=0;iisActive()) { gui_encoders.at(i)->kill(); } } ExitProgram(1); } void MainWidget::updateKeepaliveData() { QByteArray now_stamp= QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss").toUtf8(); ftruncate(gui_temp_keepalive_fd,0); if(write(gui_temp_keepalive_fd,now_stamp,now_stamp.size())<0) { syslog(LOG_WARNING,"unable to update keepalive file \"%s\" [%s]", gui_temp_keepalive_pathname.toUtf8().constData(), strerror(errno)); } } void MainWidget::closeEvent(QCloseEvent *e) { bool active=false; for(int i=0;iisActive(); } if(active) { if(QMessageBox::question(this,"GlassCommander - "+tr("Close"), tr("There are still streams active.")+" "+ tr("Shutting down will cause them to be stopped.")+ "\n"+tr("Shut down?"), QMessageBox::Yes,QMessageBox::No)!= QMessageBox::Yes) { e->ignore(); return; } gui_stop_count=0; for(int i=0;iisActive()) { connect(gui_encoders.at(i),SIGNAL(stopped()), this,SLOT(encoderStoppedData())); gui_encoders.at(i)->terminate(); gui_stop_count++; } } gui_stop_timer->start(10000); e->ignore(); return; } ExitProgram(0); } void MainWidget::resizeEvent(QResizeEvent *e) { gui_insert_button->setGeometry(size().width()-80,5,70,27); for(int i=0;isetGeometry(0,gui_toolbar->size().height()+i*enc->sizeHint().height(), size().width(),enc->sizeHint().height()); } } void MainWidget::ConnectEncoder(GlassWidget *encoder) { connect(encoder,SIGNAL(configurationChanged(GlassWidget *)), this,SLOT(configurationChangedData(GlassWidget *))); connect(encoder,SIGNAL(insertClicked(const QString &)), this,SLOT(insertClickedData(const QString &))); connect(encoder,SIGNAL(removeClicked(const QString &)), this,SLOT(removeClickedData(const QString &))); encoder->show(); } void MainWidget::LoadEncoders() { Profile *p=new Profile(); int count=0; QString name; QString section=QString().sprintf("Encoder%d",count+1); bool ok=false; p->setSource(settingsDirectory()->path()+"/"+GLASSCOMMANDER_SETTINGS_FILE); name=p->stringValue(section,"InstanceName","",&ok); while(ok) { gui_encoders.push_back(new GlassWidget(name,gui_temp_dir,this)); gui_encoders.back()->setAutoStart(p->boolValue(section,"AutoStart")); ConnectEncoder(gui_encoders.back()); count++; section=QString().sprintf("Encoder%d",count+1); name=p->stringValue(section,"InstanceName","",&ok); } delete p; gui_remove_action->setEnabled(gui_encoders.size()>0); } void MainWidget::SaveEncoders() { FILE *f; mode_t mask; if(checkSettingsDirectory()) { mask=umask(077); QString basepath= settingsDirectory()->path()+"/"+GLASSCOMMANDER_SETTINGS_FILE; if((f=fopen((basepath+".tmp").toUtf8(),"w"))==NULL) { umask(mask); return; } for(int i=0;iinstanceName().toUtf8()); fprintf(f,"AutoStart=%d\n",gui_encoders.at(i)->autoStart()); fprintf(f,"\n"); } fclose(f); rename((basepath+".tmp").toUtf8(),basepath.toUtf8()); umask(mask); } } void MainWidget::LoadEncoderConfig(GlassWidget *encoder) { Profile *p=new Profile(); p->setSource(settingsFilename(encoder->instanceName())); encoder->load(p); delete p; } int MainWidget::GetEncoderPosition(const QString &instance_name) const { for(int i=0;iinstanceName()==instance_name) { return i; } } return -1; } void MainWidget::ProcessError(int exit_code,QProcess::ExitStatus exit_status) { if(exit_status==QProcess::CrashExit) { QMessageBox::warning(this,"GlassCommander - "+tr("GlassCoder Error"), tr("GlassCoder crashed!")); } else { QString msg=gui_process->readAllStandardError(); QMessageBox::warning(this,"GlassCommander - "+tr("GlassCoder Error"), tr("GlassCoder returned a non-zero exit code")+ "\n\""+msg+"\"."); } ExitProgram(256); } void MainWidget::ExitProgram(int exit_code) const { // // Clean up temp directory // if(gui_temp_dir!=NULL) { QStringList files= gui_temp_dir->entryList(QDir::Files|QDir::NoDotAndDotDot); for(int i=0;ipath()+"/"+files.at(i)).toUtf8()); } rmdir(gui_temp_dir->path().toUtf8()); } exit(exit_code); } int main(int argc,char *argv[]) { QApplication a(argc,argv); MainWidget *w=new MainWidget(); w->show(); return a.exec(); } GlassCoder-2.0.1/src/glasscommander/glasscommander.h000066400000000000000000000062201425562563600225140ustar00rootroot00000000000000// glasscommander.h // // glasscommander(1) Audio Encoder front end // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef GLASSCOMMANDER_H #define GLASSCOMMANDER_H #include #include #include #include #include #include "deletedialog.h" #include "glasswidget.h" #include "guiapplication.h" #include "instancedialog.h" #define GLASSCOMMANDER_USAGE "[options]\n" #define GLASSCOMMANDER_SETTINGS_FILE QString("glasscommanderrc") #define GLASSCOMMANDER_TEMP_KEEPALIVE_FILENAME QString("keepalive") #define GLASSCOMMANDER_TEMP_KEEPALIVE_INTERVAL 60000 #define GLASSCOMMANDER_TERMINATE_TIMEOUT 5000 class MainWidget : public GuiApplication { Q_OBJECT; public: MainWidget(QWidget *parent=0); QSize sizeHint() const; private slots: void addInstanceData(); void removeInstanceData(); void abandonInstanceData(); void topInsertClickedData(); void insertClickedData(const QString &instance_name); void removeClickedData(const QString &instance_name); void codecFinishedData(int exit_code,QProcess::ExitStatus exit_status); void deviceFinishedData(int exit_code,QProcess::ExitStatus exit_status); void processErrorData(QProcess::ProcessError err); void configurationChangedData(GlassWidget *encoder); void autostartData(); void startAllData(); void stopAllData(); void encoderStoppedData(); void stopTimeoutData(); void updateKeepaliveData(); protected: void closeEvent(QCloseEvent *e); void resizeEvent(QResizeEvent *e); private: void ConnectEncoder(GlassWidget *encoder); void LoadEncoders(); void SaveEncoders(); void LoadEncoderConfig(GlassWidget *encoder); int GetEncoderPosition(const QString &instance_name) const; void ProcessError(int exit_code,QProcess::ExitStatus exit_status); void ExitProgram(int exit_code) const; InstanceDialog *gui_instance_dialog; DeleteDialog *gui_delete_dialog; QString gui_new_instance_name; QToolBar *gui_toolbar; QAction *gui_remove_action; QAction *gui_abandon_action; QList gui_encoders; QString gui_codec_types; QString gui_source_types; QProcess *gui_process; QPushButton *gui_insert_button; QPushButton *gui_startall_button; QPushButton *gui_stopall_button; QTimer *gui_stop_timer; int gui_stop_count; QTimer *gui_autostart_timer; int gui_autostart_index; bool gui_starting_all; QDir *gui_temp_dir; QTimer *gui_temp_keepalive_timer; QString gui_temp_keepalive_pathname; int gui_temp_keepalive_fd; }; #endif // GLASSCOMMANDER_H GlassCoder-2.0.1/src/glasscommander/glasswidget.cpp000066400000000000000000000271421425562563600223730ustar00rootroot00000000000000// glasswidget.cpp // // Encoder widget for GlassCommander(1) // // (C) Copyright 2016-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "glasslimits.h" #include "glasswidget.h" #include "logging.h" GlassWidget::GlassWidget(const QString &instance_name,QDir *temp_dir, QWidget *parent) : QFrame(parent) { gw_temp_dir=temp_dir; gw_process=NULL; gw_auto_start=false; setFrameStyle(QFrame::Box|QFrame::Raised); setMidLineWidth(2); // // Fonts // QFont bold_font(font().family(),font().pointSize(),QFont::Bold); // // Dialogs // gw_server_dialog=new ServerDialog(gw_temp_dir,"GlassCommander",this); gw_codec_dialog=new CodecDialog("GlassCommander",this); gw_source_dialog=new SourceDialog("GlassCommander",this); connect(gw_source_dialog,SIGNAL(updated()),this,SLOT(checkArgs())); gw_stream_dialog=new StreamDialog("GlassCommander",this); gw_code_dialog=new CodeViewer("GlassCommander",this); gw_config_dialog=new ConfigDialog(instance_name,gw_server_dialog, gw_codec_dialog,gw_stream_dialog, gw_source_dialog,gw_code_dialog,this); connect(gw_server_dialog,SIGNAL(typeChanged(Connector::ServerType)), this,SLOT(serverTypeChangedData(Connector::ServerType))); connect(gw_server_dialog,SIGNAL(settingsChanged()),this,SLOT(checkArgs())); for(int i=0;i<2;i++) { gw_meters[i]=new PlayMeter(SegMeter::Right,this); gw_meters[i]->setRange(-3000,0); gw_meters[i]->setHighThreshold(-800); gw_meters[i]->setClipThreshold(-100); gw_meters[i]->setMode(SegMeter::Peak); } gw_meters[0]->setLabel(tr("L")); gw_meters[1]->setLabel(tr("R")); gw_status_frame_widget=new QLabel(this); gw_status_frame_widget->setFrameStyle(QFrame::Box|QFrame::Raised); gw_status_widget=new StatusWidget(this); gw_name_label=new QLabel(instance_name,this); gw_name_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); gw_name_label->setFont(bold_font); gw_message_widget=new MessageWidget(this); gw_message_widget->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); gw_start_button=new QPushButton(tr("Start"),this); gw_start_button->setFont(bold_font); connect(gw_start_button,SIGNAL(clicked()),this,SLOT(startEncodingData())); gw_config_button=new QPushButton(tr("Settings"),this); gw_config_button->setFont(bold_font); connect(gw_config_button,SIGNAL(clicked()),this,SLOT(configData())); gw_insert_button=new QPushButton(tr("Insert"),this); gw_insert_button->setFont(bold_font); gw_insert_button->setStyleSheet("background-color: yellow"); connect(gw_insert_button,SIGNAL(clicked()),this,SLOT(insertData())); gw_insert_button->hide(); gw_remove_button=new QPushButton(tr("Remove"),this); gw_remove_button->setFont(bold_font); gw_remove_button->setStyleSheet("background-color: violet"); connect(gw_remove_button,SIGNAL(clicked()),this,SLOT(removeData())); gw_remove_button->hide(); // // Kill Timer // gw_kill_timer=new QTimer(this); gw_kill_timer->setSingleShot(true); connect(gw_kill_timer,SIGNAL(timeout()),this,SLOT(killData())); } QSize GlassWidget::sizeHint() const { return QSize(800,36); } bool GlassWidget::autoStart() const { return gw_auto_start; } void GlassWidget::setAutoStart(bool state) { gw_auto_start=state; } void GlassWidget::setMode(GlassWidget::Mode mode) { if(mode!=gw_mode) { switch(mode) { case GlassWidget::NormalMode: gw_insert_button->hide(); gw_remove_button->hide(); gw_start_button->show(); gw_config_button->show(); break; case GlassWidget::InsertMode: gw_insert_button->show(); gw_remove_button->hide(); gw_start_button->hide(); gw_config_button->hide(); break; case GlassWidget::RemoveMode: gw_insert_button->hide(); gw_remove_button->show(); gw_remove_button->setDisabled((gw_process!=NULL)&& (gw_process->state()==QProcess::Running)); gw_start_button->hide(); gw_config_button->hide(); break; } gw_mode=mode; } } QString GlassWidget::instanceName() const { return gw_name_label->text(); } void GlassWidget::addCodecTypes(const QString &codecs) { gw_codec_dialog->addCodecTypes(codecs); } void GlassWidget::addSourceTypes(const QString &sources) { gw_source_dialog->addSourceTypes(sources); } bool GlassWidget::isActive() { return (gw_process!=NULL)&&(gw_process->state()==QProcess::Running); } void GlassWidget::start() { startEncodingData(); } void GlassWidget::terminate() { if(isActive()) { gw_process->terminate(); } } void GlassWidget::kill() { if(isActive()) { gw_process->kill(); } } void GlassWidget::load(Profile *p) { gw_server_dialog->load(p); gw_codec_dialog->load(p); gw_source_dialog->load(p); gw_stream_dialog->load(p); } void GlassWidget::save(FILE *f) const { gw_server_dialog->save(f); gw_codec_dialog->save(f); gw_source_dialog->save(f); gw_stream_dialog->save(f); } void GlassWidget::setNormalMode() { setMode(GlassWidget::NormalMode); } void GlassWidget::setInsertMode() { setMode(GlassWidget::InsertMode); } void GlassWidget::setRemoveMode() { setMode(GlassWidget::RemoveMode); } void GlassWidget::startEncodingData() { QStringList args; gw_status_widget->setStatus(CONNECTION_PENDING); // // Generate Credentials File // if(!gw_server_dialog->writeCredentials()) { QMessageBox::warning(this,"GlassCommander - "+tr("Process Error"), tr("Unable to create credentials file!")); return; } gw_process=new QProcess(this); gw_process->setReadChannel(QProcess::StandardOutput); connect(gw_process,SIGNAL(readyRead()), this,SLOT(processReadyReadStandardOutputData())); connect(gw_process,SIGNAL(error(QProcess::ProcessError)), this,SLOT(processErrorData(QProcess::ProcessError))); connect(gw_process,SIGNAL(finished(int,QProcess::ExitStatus)), this,SLOT(processFinishedData(int,QProcess::ExitStatus))); gw_server_dialog->makeArgs(&args,false); gw_codec_dialog->makeArgs(&args); gw_stream_dialog->makeArgs(&args,false); gw_source_dialog->makeArgs(&args,false); args.push_back("--meter-data"); args.push_back("--errors-to=STDOUT"); args.push_back("--errors-string="+gw_name_label->text()); gw_process->start("glasscoder",args); gw_start_button->disconnect(); connect(gw_start_button,SIGNAL(clicked()),this,SLOT(stopEncodingData())); gw_start_button->setText(tr("Stop")); LockControls(true); } void GlassWidget::stopEncodingData() { gw_status_widget->setStatus(CONNECTION_STOPPING); gw_process->terminate(); gw_kill_timer->start(PROCESS_TERMINATION_TIMEOUT); } void GlassWidget::processReadyReadStandardOutputData() { char data[1500]; int n=0; if((n=gw_process->read(data,1500))>0) { data[n]=0; for(int i=0;isetPeakBar(-10000); gw_meters[1]->setPeakBar(-10000); gw_start_button->disconnect(); connect(gw_start_button,SIGNAL(clicked()),this,SLOT(startEncodingData())); gw_start_button->setText(tr("Start")); LockControls(false); } else { ProcessError(exit_code,exit_status); } gw_kill_timer->stop(); gw_status_widget->setStatus(CONNECTION_IDLE); gw_process->deleteLater(); gw_process=NULL; emit stopped(); } void GlassWidget::processErrorData(QProcess::ProcessError err) { printf("processErrorData(%u)\n",err); /* QMessageBox::warning(this,"GlassGui - "+tr("Process Error"), tr("Received QProcess error")+ QString().sprintf(": %d",err)); exit(256); */ } void GlassWidget::insertData() { emit insertClicked(gw_name_label->text()); } void GlassWidget::removeData() { emit removeClicked(gw_name_label->text()); } void GlassWidget::configData() { bool autostart=autoStart(); gw_config_dialog->exec(&autostart); setAutoStart(autostart); emit configurationChanged(this); } void GlassWidget::checkArgs() { QStringList args; bool state; state=gw_server_dialog->makeArgs(&args,false); state=state&&gw_source_dialog->makeArgs(&args,false); gw_start_button->setEnabled(state); } void GlassWidget::serverTypeChangedData(Connector::ServerType type) { gw_stream_dialog->setServerType(type); } void GlassWidget::killData() { gw_message_widget->addMessage(tr("Invoked process kill!")); kill(); } void GlassWidget::resizeEvent(QResizeEvent *e) { int w=size().width(); int h=size().height(); gw_meters[0]->setGeometry(4,4,300,h/2-4); gw_meters[1]->setGeometry(4,h/2,300,h/2-4); gw_status_frame_widget->setGeometry(307,4,134,h-8); gw_status_widget->setGeometry(310,7,128,h-14); gw_name_label->setGeometry(445,2,190,h-4); gw_message_widget->setGeometry(640,2,w-810,h-4); gw_start_button->setGeometry(w-160,5,70,h-9); gw_config_button->setGeometry(w-80,5,70,h-9); gw_insert_button->setGeometry(w-80,5,70,h-9); gw_remove_button->setGeometry(w-80,5,70,h-9); } void GlassWidget::ProcessFeedback(const QString &str) { QStringList f0; bool ok=false; int level; int prio; int status; QString msg; f0=str.split(" "); if((f0[0]=="CS")&&(f0.size()==2)) { // Connection Status status=f0[1].toInt(&ok); if(ok) { if(!gw_status_widget->setStatus(status)) { gw_message_widget->addMessage(tr("Unknown status code")+ QString().sprintf(" \"%d\" ",status)+ tr("received.")); } } } if((f0[0]=="ER")&&(f0.size()>=2)) { // Error Message prio=f0[1].toInt(); f0.erase(f0.begin()); f0.erase(f0.begin()); msg=f0.join(" "); switch(prio) { case LOG_EMERG: case LOG_ALERT: case LOG_CRIT: case LOG_ERR: gw_message_widget->addMessage(msg); break; case LOG_WARNING: case LOG_NOTICE: case LOG_INFO: gw_message_widget->addMessage(msg); break; } return; } if(f0[0]=="ME") { // Meter Levels if((f0.size()==2)&&(f0[1].length()==8)) { level=f0[1].left(4).toInt(&ok,16); if(ok) { gw_meters[0]->setPeakBar(-level); } level=f0[1].right(4).toInt(&ok,16); if(ok) { gw_meters[1]->setPeakBar(-level); } } } } void GlassWidget::ProcessError(int exit_code,QProcess::ExitStatus exit_status) { printf("ProcessError(%d,%d)\n",exit_code,exit_status); /* if(exit_status==QProcess::CrashExit) { QMessageBox::warning(this,"GlassGui - "+tr("GlassCoder Error"), tr("GlassCoder crashed!")); } else { QString msg=gw_process->readAllStandardError(); QMessageBox::warning(this,"GlassGui - "+tr("GlassCoder Error"), tr("GlassCoder returned a non-zero exit code")+ "\n\""+msg+"\"."); } exit(256); */ } void GlassWidget::LockControls(bool state) { gw_server_dialog->setControlsLocked(state); gw_codec_dialog->setControlsLocked(state); gw_stream_dialog->setControlsLocked(state); gw_source_dialog->setControlsLocked(state); } GlassCoder-2.0.1/src/glasscommander/glasswidget.h000066400000000000000000000063431425562563600220400ustar00rootroot00000000000000// glasswidget.h // // Encoder widget for GlassCommander(1) // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef GLASSWIDGET_H #define GLASSWIDGET_H #include #include #include #include #include #include #include #include "codecdialog.h" #include "codeviewer.h" #include "configdialog.h" #include "messagewidget.h" #include "playmeter.h" #include "profile.h" #include "serverdialog.h" #include "sourcedialog.h" #include "statuswidget.h" #include "streamdialog.h" class GlassWidget : public QFrame { Q_OBJECT; public: enum Mode {NormalMode=0,InsertMode=1,RemoveMode=2}; GlassWidget(const QString &instance_name,QDir *temp_dir,QWidget *parent=0); QSize sizeHint() const; bool autoStart() const; void setAutoStart(bool state); void setMode(Mode mode); QString instanceName() const; void addCodecTypes(const QString &codecs); void addSourceTypes(const QString &sources); bool isActive(); void start(); void terminate(); void kill(); void load(Profile *p); void save(FILE *f) const; signals: void configurationChanged(GlassWidget *encoder); void stopped(); void insertClicked(const QString &instance_name); void removeClicked(const QString &instance_name); public slots: void setNormalMode(); void setInsertMode(); void setRemoveMode(); private slots: void startEncodingData(); void stopEncodingData(); void processReadyReadStandardOutputData(); void processFinishedData(int exit_code,QProcess::ExitStatus exit_status); void processErrorData(QProcess::ProcessError err); void insertData(); void removeData(); void configData(); void checkArgs(); void serverTypeChangedData(Connector::ServerType type); void killData(); protected: void resizeEvent(QResizeEvent *e); private: void LockControls(bool state); void ProcessFeedback(const QString &str); void ProcessError(int exit_code,QProcess::ExitStatus exit_status); PlayMeter *gw_meters[2]; QLabel *gw_name_label; QLabel *gw_status_frame_widget; StatusWidget *gw_status_widget; MessageWidget *gw_message_widget; QPushButton *gw_start_button; ConfigDialog *gw_config_dialog; QPushButton *gw_config_button; ServerDialog *gw_server_dialog; CodecDialog *gw_codec_dialog; StreamDialog *gw_stream_dialog; SourceDialog *gw_source_dialog; CodeViewer *gw_code_dialog; QProcess *gw_process; QString gw_process_accum; Mode gw_mode; QPushButton *gw_insert_button; QPushButton *gw_remove_button; bool gw_auto_start; QTimer *gw_kill_timer; QDir *gw_temp_dir; }; #endif // GLASSWIDGET_H GlassCoder-2.0.1/src/glasscommander/instancedialog.cpp000066400000000000000000000112561425562563600230410ustar00rootroot00000000000000// instancedialog.cpp // // Pick an existing or new GlassGui instance. // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include "guiapplication.h" #include "instancedialog.h" InstanceDialog::InstanceDialog(QDir *inst_dir,QWidget *parent) : QDialog(parent) { instance_dir=inst_dir; setWindowTitle("GlassCommander - "+tr("Pick Instance")); // // Fonts // QFont bold_font(font().family(),font().pointSize(),QFont::Bold); // // Instance Name // instance_name_label=new QLabel(tr("Instance")+":",this); instance_name_label->setFont(bold_font); instance_name_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); instance_name_edit=new QLineEdit(this); instance_validator=new FilenameValidator(this); instance_name_edit->setValidator(instance_validator); connect(instance_name_edit,SIGNAL(textChanged(const QString &)), this,SLOT(textChangedData(const QString &))); // // Instance List // instance_list=new QListWidget(this); connect(instance_list,SIGNAL(itemClicked(QListWidgetItem *)), this,SLOT(itemClickedData(QListWidgetItem *))); connect(instance_list,SIGNAL(itemDoubleClicked(QListWidgetItem *)), this,SLOT(itemDoubleClickedData(QListWidgetItem *))); // // OK Button // instance_ok_button=new QPushButton(tr("OK"),this); instance_ok_button->setFont(bold_font); connect(instance_ok_button,SIGNAL(clicked()),this,SLOT(okData())); // // Cancel Button // instance_cancel_button=new QPushButton(tr("Cancel"),this); instance_cancel_button->setFont(bold_font); connect(instance_cancel_button,SIGNAL(clicked()),this,SLOT(cancelData())); } QSize InstanceDialog::sizeHint() const { return QSize(350,400); } int InstanceDialog::exec(QString *inst_name,const QStringList &used_names) { instance_name=inst_name; instance_used_names=used_names; instance_name_edit->setText(*inst_name); textChangedData(*inst_name); RefreshList(); return QDialog::exec(); } void InstanceDialog::textChangedData(const QString &str) { instance_ok_button->setDisabled(str.isEmpty()); QListitems=instance_list->findItems(str,Qt::MatchExactly); if(items.size()>0) { instance_list->setCurrentItem(items.at(0)); } else { instance_list->clearSelection(); } } void InstanceDialog::itemClickedData(QListWidgetItem *item) { instance_name_edit->setText(item->data(Qt::DisplayRole).toString()); instance_ok_button->setEnabled(true); } void InstanceDialog::itemDoubleClickedData(QListWidgetItem *item) { instance_name_edit->setText(item->data(Qt::DisplayRole).toString()); okData(); } void InstanceDialog::okData() { if(IsMemberOf(instance_used_names,instance_name_edit->text())) { QMessageBox::information(this,"GlassCommander - "+tr("Error"), tr("That instance is already loaded.")); return; } *instance_name=instance_name_edit->text(); done(true); } void InstanceDialog::cancelData() { done(false); } void InstanceDialog::closeEvent(QCloseEvent *e) { cancelData(); } void InstanceDialog::resizeEvent(QResizeEvent *e) { instance_name_label->setGeometry(10,10,80,20); instance_name_edit->setGeometry(95,10,size().width()-105,20); instance_list->setGeometry(10,32,size().width()-20,size().height()-102); instance_ok_button-> setGeometry(size().width()-180,size().height()-60,80,50); instance_cancel_button-> setGeometry(size().width()-90,size().height()-60,80,50); } void InstanceDialog::RefreshList() { QStringList filters; filters.push_back(QString(GUI_SETTINGS_FILE)+"-*"); QStringList instances= instance_dir->entryList(filters,QDir::Files,QDir::Name|QDir::IgnoreCase); instance_list->clear(); for(int i=0;iaddItem(name); } } } bool InstanceDialog::IsMemberOf(const QStringList &list,const QString &str) const { for(int i=0;i // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef INSTANCEDIALOG_H #define INSTANCEDIALOG_H #include #include #include #include #include #include #include #include "filenamevalidator.h" class InstanceDialog : public QDialog { Q_OBJECT; public: InstanceDialog(QDir *inst_dir,QWidget *parent=0); QSize sizeHint() const; public slots: int exec(QString *inst_name,const QStringList &used_names); private slots: void textChangedData(const QString &str); void itemClickedData(QListWidgetItem *item); void itemDoubleClickedData(QListWidgetItem *item); void okData(); void cancelData(); protected: void closeEvent(QCloseEvent *e); void resizeEvent(QResizeEvent *e); private: void RefreshList(); bool IsMemberOf(const QStringList &list,const QString &str) const; QLabel *instance_name_label; QLineEdit *instance_name_edit; QListWidget *instance_list; QPushButton *instance_ok_button; QPushButton *instance_cancel_button; QString *instance_name; QStringList instance_used_names; QDir *instance_dir; FilenameValidator *instance_validator; }; #endif // INSTANCEDIALOG_H GlassCoder-2.0.1/src/glasscommander/playmeter.cpp000066400000000000000000000126571425562563600220650ustar00rootroot00000000000000// playmeter.cpp // // This implements a widget that represents a stereo audio level meter, // complete with labels and scale. // // (C) Copyright 2002-2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License // version 2 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include PlayMeter::PlayMeter(SegMeter::Orientation orient,QWidget *parent) : QWidget(parent) { meter_label=QString(""); orientation=orient; makeFont(); meter=new SegMeter(orientation,this); meter->setSegmentSize(5); meter->setSegmentGap(1); // // Set Colors // QPalette p=palette(); p.setColor(QPalette::Window,Qt::black); setPalette(p); } QSize PlayMeter::sizeHint() const { if(meter_label==QString("")) { return QSize(335,60); } else { return QSize(335,80); } } QSizePolicy PlayMeter::sizePolicy() const { return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); } void PlayMeter::setRange(int min,int max) { meter->setRange(min,max); } void PlayMeter::setDarkLowColor(QColor color) { meter->setDarkLowColor(color); } void PlayMeter::setDarkHighColor(QColor color) { meter->setDarkHighColor(color); } void PlayMeter::setDarkClipColor(QColor color) { meter->setDarkClipColor(color); } void PlayMeter::setLowColor(QColor color) { meter->setLowColor(color); } void PlayMeter::setHighColor(QColor color) { meter->setHighColor(color); } void PlayMeter::setClipColor(QColor color) { meter->setClipColor(color); } void PlayMeter::setHighThreshold(int level) { meter->setHighThreshold(level); } void PlayMeter::setClipThreshold(int level) { meter->setClipThreshold(level); } void PlayMeter::setLabel(QString label) { meter_label=label; makeFont(); setGeometry(geometry().left(),geometry().top(), geometry().width(),geometry().height()); } void PlayMeter::setGeometry(int x,int y,int w,int h) { QWidget::setGeometry(x,y,w,h); if(meter_label.isEmpty()) { meter->setGeometry(2,2,w-4,h-4); } else { switch(orientation) { case SegMeter::Left: meter->setGeometry(2,2,w-4-h,h-4); label_font=QFont("helvetica",height()-2,QFont::Bold); label_font.setPixelSize(height()-2); break; case SegMeter::Right: meter->setGeometry(2+h,2,w-4-h,h-4); label_font=QFont("helvetica",height()-2,QFont::Bold); label_font.setPixelSize(height()-2); break; case SegMeter::Up: meter->setGeometry(2,2,w-4,h-4-w); label_font=QFont("helvetica",width()-2,QFont::Bold); label_font.setPixelSize(width()-2); break; case SegMeter::Down: meter->setGeometry(2,2+width(),w-4,h-4-w); label_font=QFont("helvetica",width()-2,QFont::Bold); label_font.setPixelSize(width()-2); break; } makeFont(); } } void PlayMeter::setGeometry(QRect &rect) { setGeometry(rect.left(),rect.top(),rect.width(),rect.height()); } void PlayMeter::setSolidBar(int level) { meter->setSolidBar(level); } void PlayMeter::setPeakBar(int level) { meter->setPeakBar(level); } void PlayMeter::setFloatingBar(int level) { meter->setFloatingBar(level); } void PlayMeter::setSegmentSize(int size) { meter->setSegmentSize(size); } void PlayMeter::setSegmentGap(int gap) { meter->setSegmentGap(gap); } SegMeter::Mode PlayMeter::mode() const { return meter->mode(); } void PlayMeter::setMode(SegMeter::Mode mode) { meter->setMode(mode); } void PlayMeter::paintEvent(QPaintEvent *paintEvent) { // // Setup // QPainter *p=new QPainter(this); p->fillRect(0,0,size().width(),size().height(),Qt::black); p->setFont(label_font); p->setPen(Qt::white); if(!meter_label.isEmpty()) { switch(orientation) { case SegMeter::Left: p->drawText(width()-height()+meter_label_x,height()-2,meter_label); break; case SegMeter::Right: p->drawText(meter_label_x,height()-2,meter_label); break; case SegMeter::Up: p->drawText(meter_label_x,height()-3,meter_label); break; case SegMeter::Down: p->drawText(meter_label_x,width()-1,meter_label); break; } } p->end(); delete p; } void PlayMeter::makeFont() { switch(orientation) { case SegMeter::Left: label_font=QFont("helvetica",height()-2,QFont::Bold); label_font.setPixelSize(height()-2); meter_label_x=(height()-QFontMetrics(label_font). width(meter_label))/2; break; case SegMeter::Right: label_font=QFont("helvetica",height()-2,QFont::Bold); label_font.setPixelSize(height()-2); meter_label_x=(height()-QFontMetrics(label_font). width(meter_label))/2; break; case SegMeter::Up: label_font=QFont("helvetica",width()-2,QFont::Bold); label_font.setPixelSize(width()-2); meter_label_x=(width()-QFontMetrics(label_font). width(meter_label))/2; break; case SegMeter::Down: label_font=QFont("helvetica",width()-2,QFont::Bold); label_font.setPixelSize(width()-2); meter_label_x=(width()-QFontMetrics(label_font). width(meter_label))/2; break; } } GlassCoder-2.0.1/src/glasscommander/playmeter.h000066400000000000000000000036461425562563600215300ustar00rootroot00000000000000// playmeter.h // // A playback audio meter widget. // // (C) Copyright 2002-2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License // version 2 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef PLAYMETER_H #define PLAYMETER_H #include #include class PlayMeter : public QWidget { Q_OBJECT public: PlayMeter(SegMeter::Orientation orient,QWidget *parent=0); QSize sizeHint() const; QSizePolicy sizePolicy() const; void setRange(int min,int max); void setDarkLowColor(QColor color); void setDarkHighColor(QColor color); void setDarkClipColor(QColor color); void setLowColor(QColor color); void setHighColor(QColor color); void setClipColor(QColor color); void setHighThreshold(int level); void setClipThreshold(int level); void setSegmentSize(int size); void setSegmentGap(int gap); SegMeter::Mode mode() const; void setMode(SegMeter::Mode mode); void setLabel(QString label); public slots: void setGeometry(int x,int y,int w,int h); void setGeometry(QRect &rect); void setSolidBar(int level); void setFloatingBar(int level); void setPeakBar(int level); protected: void paintEvent(QPaintEvent *); private: void makeFont(); SegMeter *meter; QString meter_label; QFont label_font; SegMeter::Orientation orientation; int meter_label_x; }; #endif // PLAYMETER_H GlassCoder-2.0.1/src/glassgui/000077500000000000000000000000001425562563600161635ustar00rootroot00000000000000GlassCoder-2.0.1/src/glassgui/Makefile.am000066400000000000000000000111671425562563600202250ustar00rootroot00000000000000## automake.am ## ## Makefile for the glassgui(1) Audio Encoder front-end. ## ## (C) Copyright 2015-2022 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Use automake to process this into a Makefile.in AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -Wno-strict-aliasing -std=c++11 -fPIC @QT5_GUI_CFLAGS@ @SNDFILE_CFLAGS@ @ALSA_CFLAGS@ MOC = @QT5_MOC@ # The dependency for qt's Meta Object Compiler (moc) moc_%.cpp: %.h @MOC@ $< -o $@ bin_PROGRAMS = glassgui dist_glassgui_SOURCES = glassgui.cpp glassgui.h nodist_glassgui_SOURCES = asihpi.cpp asihpi.h\ audiodevice.cpp audiodevice.h\ cmdswitch.cpp cmdswitch.h\ codec.cpp codec.h\ codecdialog.cpp codecdialog.h\ codeviewer.cpp codeviewer.h\ combobox.cpp combobox.h\ connector.cpp connector.h\ glasslimits.h\ guiapplication.cpp guiapplication.h\ hpiinputlistview.cpp hpiinputlistview.h\ hpiwidget.cpp hpiwidget.h\ logging.cpp logging.h\ messagewidget.cpp messagewidget.h\ metaevent.cpp metaevent.h\ moc_audiodevice.cpp\ moc_codec.cpp\ moc_codecdialog.cpp\ moc_codeviewer.cpp\ moc_combobox.cpp\ moc_connector.cpp\ moc_glassgui.cpp\ moc_guiapplication.cpp\ moc_hpiinputlistview.cpp\ moc_hpiwidget.cpp\ moc_messagewidget.cpp\ moc_segmeter.cpp\ moc_serverdialog.cpp\ moc_sourcedialog.cpp\ moc_spinbox.cpp\ moc_statuswidget.cpp\ moc_stereometer.cpp\ moc_streamdialog.cpp\ profile.cpp profile.h\ ringbuffer.cpp ringbuffer.h\ segmeter.cpp segmeter.h\ serverdialog.cpp serverdialog.h\ sourcedialog.cpp sourcedialog.h\ spinbox.cpp spinbox.h\ statuswidget.cpp statuswidget.h\ stereometer.cpp stereometer.h\ streamdialog.cpp streamdialog.h glassgui_LDADD = @GUILIBS@ @LIBJACK@ @SNDFILE_LIBS@ @ALSA_LIBS@ @ASIHPI_LIBS@ @QT5_GUI_LIBS@ -lpthread glassgui_LDFLAGS = @GUIFLAGS@ CLEANFILES = *~\ moc_*\ *.obj\ *.idb\ *.pdb\ *ilk DISTCLEANFILES = asihpi.cpp asihpi.h\ audiodevice.cpp audiodevice.h\ cmdswitch.cpp cmdswitch.h\ codec.cpp codec.h\ codecdialog.cpp codecdialog.h\ codeviewer.cpp codeviewer.h\ combobox.cpp combobox.h\ connector.cpp connector.h\ glasslimits.h\ guiapplication.cpp guiapplication.h\ hpiinputlistview.cpp hpiinputlistview.h\ hpiwidget.cpp hpiwidget.h\ logging.cpp logging.h\ messagewidget.cpp messagewidget.h\ metaevent.cpp metaevent.h\ paths.h\ profile.cpp profile.h\ ringbuffer.cpp ringbuffer.h\ segmeter.cpp segmeter.h\ serverdialog.cpp serverdialog.h\ sourcedialog.cpp sourcedialog.h\ spinbox.cpp spinbox.h\ statuswidget.cpp statuswidget.h\ stereometer.cpp stereometer.h\ streamdialog.cpp streamdialog.h MAINTAINERCLEANFILES = *~\ Makefile.in GlassCoder-2.0.1/src/glassgui/glassgui.cpp000066400000000000000000000446251425562563600205200ustar00rootroot00000000000000// glassgui.cpp // // glassgui(1) Audio Encoder front end // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include #include #include "audiodevice.h" #include "cmdswitch.h" #include "codec.h" #include "connector.h" #include "logging.h" #include "profile.h" #include "glassgui.h" #include "../../icons/glasscoder-16x16.xpm" MainWidget::MainWidget(QWidget *parent) : GuiApplication(parent) { gui_temp_dir=NULL; instance_name=""; QString delete_instance=""; bool list_instances=false; gui_autostart=false; CmdSwitch *cmd=new CmdSwitch("glassgui",GLASSGUI_USAGE); for(unsigned i=0;ikeys();i++) { if(cmd->key(i)=="--autostart") { gui_autostart=true; cmd->setProcessed(i,true); } if(cmd->key(i)=="--delete-instance") { delete_instance=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--instance-directory") { if(!setSettingsDirectory(cmd->value(i))) { QMessageBox::critical(this,"GlassGui - "+tr("Error"), tr("Unable to access specified instance directory")+ ":\n\""+cmd->value(i)+"\"."); ExitProgram(1); } cmd->setProcessed(i,true); } if(cmd->key(i)=="--instance-name") { instance_name=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--list-instances") { list_instances=true; cmd->setProcessed(i,true); } if(!cmd->processed(i)) { QMessageBox::critical(this,"GlassGui - "+tr("Error"), tr("Unknown argument")+" \""+cmd->key(i)+"\"."); ExitProgram(256); } } if(list_instances&&(!delete_instance.isEmpty())) { fprintf(stderr,"glassgui: --list-instances and --delete-instance are mutually exclusive\n"); ExitProgram(256); } if(list_instances) { ListInstances(); ExitProgram(0); } if(!delete_instance.isEmpty()) { deleteInstance(delete_instance); ExitProgram(0); } setWindowIcon(QPixmap(glasscoder_16x16_xpm)); if(instance_name.isEmpty()) { setWindowTitle(QString("GlassGui v")+VERSION); } else { setWindowTitle(QString("GlassGui v")+VERSION+" - "+instance_name); } // // Create Temp Directory // char tempdir[PATH_MAX]; strncpy(tempdir,"/tmp",PATH_MAX); if(getenv("TEMP")!=NULL) { strncpy(tempdir,getenv("TEMP"),PATH_MAX-1); } strncat(tempdir,"/glassgui-XXXXXX",PATH_MAX-strlen(tempdir)); if(mkdtemp(tempdir)==NULL) { QMessageBox::critical(this,"GlassGui - "+tr("Error"), tr("Unable to create temporary directory in")+ "\""+tempdir+"\". ["+strerror(errno)+"]"); ExitProgram(256); } gui_temp_dir=new QDir(tempdir); // // Fonts // QFont big_font("helvetica",20,QFont::Bold); big_font.setPixelSize(20); QFont section_font("helvetica",16,QFont::Bold); section_font.setPixelSize(16); QFont label_font("helvetica",14,QFont::Bold); label_font.setPixelSize(14); QFont message_font("helvetica",14,QFont::Normal); message_font.setPixelSize(14); QFont metadata_font("helvetica",12,QFont::Bold); metadata_font.setPixelSize(12); // // Set Size // setMinimumSize(sizeHint()); setMaximumHeight(sizeHint().height()); // // Dialogs // gui_server_dialog=new ServerDialog(gui_temp_dir,"GlassGui",this); gui_codec_dialog=new CodecDialog("GlassGui",this); gui_codeviewer_dialog=new CodeViewer("GlassGui",this); gui_source_dialog=new SourceDialog("GlassGui",this); connect(gui_source_dialog,SIGNAL(updated()),this,SLOT(checkArgs())); gui_stream_dialog=new StreamDialog("GlassGui",this); connect(gui_server_dialog,SIGNAL(typeChanged(Connector::ServerType)), this,SLOT(serverTypeChangedData(Connector::ServerType))); connect(gui_server_dialog,SIGNAL(settingsChanged()),this,SLOT(checkArgs())); // // Status Bar // gui_message_widget=new MessageWidget(this); gui_message_widget->setFont(message_font); gui_message_widget->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); gui_message_widget->setFrameStyle(QFrame::Box|QFrame::Raised); gui_status_frame_widget=new QLabel(this); gui_status_frame_widget->setFrameStyle(QFrame::Box|QFrame::Raised); gui_status_widget=new StatusWidget(this); gui_status_widget->setFont(label_font); // // Meter Section // gui_meter=new StereoMeter(this); gui_meter->setReference(800); gui_meter->setMode(SegMeter::Peak); gui_start_button=new QPushButton(tr("Start"),this); gui_start_button->setFont(big_font); connect(gui_start_button,SIGNAL(clicked()),this,SLOT(startEncodingData())); gui_start_button->setDisabled(true); gui_code_button=new QPushButton(tr("Show")+"\n"+tr("Code"),this); gui_code_button->setFont(section_font); connect(gui_code_button,SIGNAL(clicked()),this,SLOT(showCodeData())); gui_code_button->setDisabled(true); // // Metadata Sender // gui_metadata_label=new QLabel(tr("Stream Metadata")+":",this); gui_metadata_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gui_metadata_label->setFont(label_font); gui_metadata_edit=new QLineEdit(this); gui_metadata_button=new QPushButton(tr("Send"),this); gui_metadata_button->setFont(label_font); connect(gui_metadata_button,SIGNAL(clicked()),this,SLOT(metadataData())); // // Server Settings // gui_server_button=new QPushButton(tr("Server\nSettings"),this); gui_server_button->setFont(section_font); connect(gui_server_button,SIGNAL(clicked()),this,SLOT(serverData())); // // Codec Settings // gui_codec_button=new QPushButton(tr("Codec\nSettings"),this); gui_codec_button->setFont(section_font); connect(gui_codec_button,SIGNAL(clicked()),this,SLOT(codecData())); // // Stream Settings // gui_stream_button=new QPushButton(tr("Stream\nSettings"),this); gui_stream_button->setFont(section_font); connect(gui_stream_button,SIGNAL(clicked()),this,SLOT(streamData())); // // Source Settings // gui_source_button=new QPushButton(tr("Source\nSettings"),this); gui_source_button->setFont(section_font); connect(gui_source_button,SIGNAL(clicked()),this,SLOT(sourceData())); // // Process Timers // gui_process_cleanup_timer=new QTimer(this); gui_process_cleanup_timer->setSingleShot(true); connect(gui_process_cleanup_timer,SIGNAL(timeout()), this,SLOT(processCollectGarbageData())); gui_process_kill_timer=new QTimer(this); gui_process_kill_timer->setSingleShot(true); connect(gui_process_kill_timer,SIGNAL(timeout()), this,SLOT(processKillData())); // // Autostart Timer // gui_autostart_timer=new QTimer(this); gui_autostart_timer->setSingleShot(true); connect(gui_autostart_timer,SIGNAL(timeout()),this,SLOT(startEncodingData())); // // Get Codec List // gui_process=new QProcess(this); connect(gui_process,SIGNAL(error(QProcess::ProcessError)), this,SLOT(processErrorData(QProcess::ProcessError))); connect(gui_process,SIGNAL(finished(int,QProcess::ExitStatus)), this,SLOT(codecFinishedData(int,QProcess::ExitStatus))); QStringList args; args.push_back("--list-codecs"); gui_process->start("glasscoder",args); } QSize MainWidget::sizeHint() const { return QSize(500,205); } void MainWidget::closeEvent(QCloseEvent *e) { if(gui_process!=NULL) { if(QMessageBox::question(this,"GlassGui - "+tr("Exiting"), tr("The encoder is currently running.")+" "+ tr("Close will shut it down.")+"\n\n"+ tr("Proceed?"),QMessageBox::Yes,QMessageBox::No)== QMessageBox::No) { e->ignore(); return; } gui_process->terminate(); gui_process_kill_timer->start(GLASSGUI_TERMINATE_TIMEOUT); SaveSettings(); e->ignore(); return; } if(!SaveSettings()) { QMessageBox::warning(this,"GlassGui - "+tr("Save Error"), tr("Unable to save configuration!")); } ExitProgram(0); } void MainWidget::resizeEvent(QResizeEvent *e) { int ypos=10; gui_meter->setGeometry(10,ypos,gui_meter->sizeHint().width(), gui_meter->sizeHint().height()); gui_start_button->setGeometry(gui_meter->sizeHint().width()+20,ypos, size().width()-gui_meter->sizeHint().width()-30, gui_meter->sizeHint().height()); ypos+=(gui_meter->sizeHint().height()+10); int w_edge=(size().width()-60)/5; gui_metadata_label->setGeometry(10,ypos,125,20); gui_metadata_edit->setGeometry(140,ypos,size().width()-230,20); gui_metadata_button->setGeometry(size().width()-80,ypos-5,70,30); ypos+=30; gui_server_button->setGeometry(10,ypos,w_edge,55); gui_codec_button->setGeometry(20+w_edge,ypos,w_edge,55); gui_stream_button->setGeometry(30+2*w_edge,ypos,w_edge,55); gui_source_button->setGeometry(40+3*w_edge,ypos,w_edge,55); gui_code_button->setGeometry(50+4*w_edge,ypos,w_edge,55); ypos+=70; // // Status Bar // gui_message_widget->setGeometry(0,size().height()-30,size().width()-140,30); gui_status_frame_widget-> setGeometry(size().width()-140,size().height()-30,140,30); gui_status_widget->setGeometry(size().width()-137,size().height()-27,134,24); } void MainWidget::startEncodingData() { QStringList args; if(gui_process!=NULL) { QMessageBox::warning(this,"GlassGui - "+tr("Process Error"), tr("Process is not in ready state!")); return; } gui_status_widget->setStatus(CONNECTION_PENDING); // // Generate Credentials File // if(!gui_server_dialog->writeCredentials()) { QMessageBox::warning(this,"GlassGui - "+tr("Process Error"), tr("Unable to create credentials file!")); return; } gui_process=new QProcess(this); gui_process->setReadChannel(QProcess::StandardOutput); connect(gui_process,SIGNAL(readyRead()), this,SLOT(processReadyReadStandardOutputData())); connect(gui_process,SIGNAL(error(QProcess::ProcessError)), this,SLOT(processErrorData(QProcess::ProcessError))); connect(gui_process,SIGNAL(finished(int,QProcess::ExitStatus)), this,SLOT(processFinishedData(int,QProcess::ExitStatus))); gui_server_dialog->makeArgs(&args,false); gui_codec_dialog->makeArgs(&args); gui_stream_dialog->makeArgs(&args,false); gui_source_dialog->makeArgs(&args,false); args.push_back("--meter-data"); args.push_back("--errors-to=STDOUT"); if(!instance_name.isEmpty()) { args.push_back("--errors-string="+instance_name); } gui_process->start("glasscoder",args); gui_start_button->disconnect(); connect(gui_start_button,SIGNAL(clicked()),this,SLOT(stopEncodingData())); gui_start_button->setText(tr("Stop")); LockControls(true); } void MainWidget::stopEncodingData() { gui_status_widget->setStatus(CONNECTION_STOPPING); gui_process->terminate(); } void MainWidget::showCodeData() { QStringList args; args.push_back("glasscoder"); gui_server_dialog->makeArgs(&args,true); gui_codec_dialog->makeArgs(&args); gui_stream_dialog->makeArgs(&args,true); gui_source_dialog->makeArgs(&args,true); gui_codeviewer_dialog->exec(args); } void MainWidget::metadataData() { gui_process->write(("MD "+gui_metadata_edit->text()+"\n").toUtf8()); } void MainWidget::serverTypeChangedData(Connector::ServerType type) { gui_stream_dialog->setServerType(type); } void MainWidget::serverData() { if(gui_server_dialog->isVisible()) { gui_server_dialog->hide(); } else { gui_server_dialog->show(); } } void MainWidget::codecData() { if(gui_codec_dialog->isVisible()) { gui_codec_dialog->hide(); } else { gui_codec_dialog->show(); } } void MainWidget::streamData() { if(gui_stream_dialog->isVisible()) { gui_stream_dialog->hide(); } else { gui_stream_dialog->show(); } } void MainWidget::sourceData() { if(gui_source_dialog->isVisible()) { gui_source_dialog->hide(); } else { gui_source_dialog->show(); } } void MainWidget::codecFinishedData(int exit_code, QProcess::ExitStatus exit_status) { QStringList f0; if(exit_code==0) { // // Populate Codec Types // gui_codec_dialog->addCodecTypes(gui_process->readAllStandardOutput()); // // Get Device List // gui_process=new QProcess(this); connect(gui_process,SIGNAL(error(QProcess::ProcessError)), this,SLOT(processErrorData(QProcess::ProcessError))); connect(gui_process,SIGNAL(finished(int,QProcess::ExitStatus)), this,SLOT(deviceFinishedData(int,QProcess::ExitStatus))); QStringList args; args.push_back("--list-devices"); gui_process->start("glasscoder",args); } else { ProcessError(exit_code,exit_status); } } void MainWidget::checkArgs() { QStringList args; bool state; state=gui_server_dialog->makeArgs(&args,false); state=state&&gui_source_dialog->makeArgs(&args,false); gui_start_button->setEnabled(state); gui_code_button->setEnabled(state); } void MainWidget::checkArgs(const QString &str) { checkArgs(); } void MainWidget::deviceFinishedData(int exit_code, QProcess::ExitStatus exit_status) { QStringList f0; if(exit_code==0) { // // Populate Device Types // gui_source_dialog->addSourceTypes(gui_process->readAllStandardOutput()); gui_process=NULL; LoadSettings(); if(gui_autostart) { gui_autostart_timer->start(0); } } else { ProcessError(exit_code,exit_status); } } void MainWidget::processReadyReadStandardOutputData() { char data[1500]; int n=0; if((n=gui_process->read(data,1500))>0) { data[n]=0; for(int i=0;iisActive()) { ExitProgram(0); } gui_meter->setLeftPeakBar(-10000); gui_meter->setRightPeakBar(-10000); gui_start_button->disconnect(); connect(gui_start_button,SIGNAL(clicked()),this,SLOT(startEncodingData())); gui_start_button->setText(tr("Start")); LockControls(false); } else { ProcessError(exit_code,exit_status); } gui_status_widget->setStatus(CONNECTION_IDLE); gui_process_cleanup_timer->start(0); } void MainWidget::processErrorData(QProcess::ProcessError err) { QMessageBox::warning(this,"GlassGui - "+tr("Process Error"), tr("Received QProcess error")+ QString().sprintf(": %d",err)); ExitProgram(256); } void MainWidget::processCollectGarbageData() { delete gui_process; gui_process=NULL; } void MainWidget::processKillData() { gui_process->kill(); qApp->processEvents(); ExitProgram(0); } void MainWidget::LockControls(bool state) { gui_codec_dialog->setControlsLocked(state); gui_stream_dialog->setControlsLocked(state); gui_source_dialog->setControlsLocked(state); } void MainWidget::ProcessFeedback(const QString &str) { QStringList f0; bool ok=false; int level; int prio; int status; QString msg; f0=str.split(" "); if((f0[0]=="CS")&&(f0.size()==2)) { // Connection Status status=f0[1].toInt(&ok); if(ok) { if(!gui_status_widget->setStatus(status)) { gui_message_widget->addMessage(tr("Unknown status code")+ QString().sprintf(" \"%d\" ",status)+ tr("received.")); } } } if((f0[0]=="ER")&&(f0.size()>=2)) { // Error Message prio=f0[1].toInt(); f0.erase(f0.begin()); f0.erase(f0.begin()); msg=f0.join(" "); switch(prio) { case LOG_EMERG: case LOG_ALERT: case LOG_CRIT: case LOG_ERR: gui_message_widget->addMessage(msg); break; case LOG_WARNING: case LOG_NOTICE: case LOG_INFO: gui_message_widget->addMessage(msg); break; } return; } if(f0[0]=="ME") { // Meter Levels if((f0.size()==2)&&(f0[1].length()==8)) { level=f0[1].left(4).toInt(&ok,16); if(ok) { gui_meter->setLeftPeakBar(-level); } level=f0[1].right(4).toInt(&ok,16); if(ok) { gui_meter->setRightPeakBar(-level); } } } } void MainWidget::ProcessError(int exit_code,QProcess::ExitStatus exit_status) { if(exit_status==QProcess::CrashExit) { QMessageBox::warning(this,"GlassGui - "+tr("GlassCoder Error"), tr("GlassCoder crashed!")); } else { QString msg=gui_process->readAllStandardError(); QMessageBox::warning(this,"GlassGui - "+tr("GlassCoder Error"), tr("GlassCoder returned a non-zero exit code")+ "\n\""+msg+"\"."); } ExitProgram(256); } void MainWidget::LoadSettings() { if(checkSettingsDirectory()) { Profile *p=new Profile(); p->setSource(settingsFilename(instance_name)); gui_server_dialog->load(p); gui_codec_dialog->load(p); gui_source_dialog->load(p); gui_stream_dialog->load(p); delete p; } checkArgs(); } bool MainWidget::SaveSettings() { FILE *f; mode_t mask; if(checkSettingsDirectory()) { mask=umask(077); QString basepath=settingsFilename(instance_name); if((f=fopen((basepath+".tmp").toUtf8(),"w"))==NULL) { return false; } fprintf(f,"[GlassGui]\n"); gui_server_dialog->save(f); gui_codec_dialog->save(f); gui_source_dialog->save(f); gui_stream_dialog->save(f); fclose(f); rename((basepath+".tmp").toUtf8(),basepath.toUtf8()); umask(mask); } return true; } void MainWidget::ListInstances() { QStringList files=GuiApplication::listInstances(); for(int i=0;ientryList(QDir::Files|QDir::NoDotAndDotDot); for(int i=0;ipath()+"/"+files.at(i)).toUtf8()); } rmdir(gui_temp_dir->path().toUtf8()); } exit(exit_code); } int main(int argc,char *argv[]) { QApplication a(argc,argv); MainWidget *w=new MainWidget(); w->show(); return a.exec(); } GlassCoder-2.0.1/src/glassgui/glassgui.h000066400000000000000000000065451425562563600201640ustar00rootroot00000000000000// glassgui.h // // glassgui(1) Audio Encoder front end // // (C) Copyright 2015-2022 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef GLASSGUI_H #define GLASSGUI_H #include #include #include #include #include #include #include #include #include #include #include "codecdialog.h" #include "codeviewer.h" #include "combobox.h" #include "guiapplication.h" #include "hpiinputlistview.h" #include "messagewidget.h" #include "serverdialog.h" #include "sourcedialog.h" #include "statuswidget.h" #include "stereometer.h" #include "streamdialog.h" #define GLASSGUI_USAGE "[options]\n" #define GLASSGUI_TERMINATE_TIMEOUT 5000 class MainWidget : public GuiApplication { Q_OBJECT; public: MainWidget(QWidget *parent=0); QSize sizeHint() const; protected: void closeEvent(QCloseEvent *e); void resizeEvent(QResizeEvent *e); private slots: void startEncodingData(); void stopEncodingData(); void showCodeData(); void metadataData(); void serverTypeChangedData(Connector::ServerType type); void serverData(); void codecData(); void streamData(); void sourceData(); void codecFinishedData(int exit_code,QProcess::ExitStatus exit_status); void checkArgs(); void checkArgs(const QString &str); void deviceFinishedData(int exit_code,QProcess::ExitStatus exit_status); void processReadyReadStandardOutputData(); void processFinishedData(int exit_code,QProcess::ExitStatus exit_status); void processErrorData(QProcess::ProcessError err); void processCollectGarbageData(); void processKillData(); private: QString instance_name; void LockControls(bool state); void ProcessFeedback(const QString &str); void ProcessError(int exit_code,QProcess::ExitStatus exit_status); void LoadSettings(); bool SaveSettings(); void ListInstances(); void ExitProgram(int exit_code) const; StereoMeter *gui_meter; QPushButton *gui_start_button; QLabel *gui_metadata_label; QLineEdit *gui_metadata_edit; QPushButton *gui_metadata_button; QPushButton *gui_code_button; ServerDialog *gui_server_dialog; QPushButton *gui_server_button; CodecDialog *gui_codec_dialog; QPushButton *gui_codec_button; StreamDialog *gui_stream_dialog; QPushButton *gui_stream_button; SourceDialog *gui_source_dialog; QPushButton *gui_source_button; CodeViewer *gui_codeviewer_dialog; QProcess *gui_process; QTimer *gui_process_cleanup_timer; QTimer *gui_process_kill_timer; QString gui_process_accum; MessageWidget *gui_message_widget; StatusWidget *gui_status_widget; QLabel *gui_status_frame_widget; bool gui_autostart; QTimer *gui_autostart_timer; QDir *gui_temp_dir; }; #endif // GLASSGUI_H GlassCoder-2.0.1/src/tests/000077500000000000000000000000001425562563600155075ustar00rootroot00000000000000GlassCoder-2.0.1/src/tests/Makefile.am000066400000000000000000000072501425562563600175470ustar00rootroot00000000000000## automake.am ## ## Makefile for GlassCoder Audio Encoder tests. ## ## (C) Copyright 2014-2019 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Use automake to process this into a Makefile.in AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -Wno-strict-aliasing -std=c++11 -fPIC @QT5_CLI_CFLAGS@ @SNDFILE_CFLAGS@ @ALSA_CFLAGS@ MOC = @QT5_MOC@ # The dependency for qt's Meta Object Compiler (moc) moc_%.cpp: %.h @MOC@ $< -o $@ noinst_PROGRAMS = pipe_connect\ urldecode\ urlencode dist_pipe_connect_SOURCES = pipe_connect.cpp pipe_connect.h nodist_pipe_connect_SOURCES = cmdswitch.cpp cmdswitch.h\ moc_pipe_connect.cpp pipe_connect_LDADD = @SIRLIBS@ @LIBJACK@ @SNDFILE_LIBS@ @ALSA_LIBS@ @ASIHPI_LIBS@ @QT5_CLI_LIBS@ -lpthread pipe_connect_LDFLAGS = @SIRFLAGS@ dist_urldecode_SOURCES = urldecode.cpp urldecode.h nodist_urldecode_SOURCES = cmdswitch.cpp cmdswitch.h\ connector.cpp connector.h\ logging.cpp logging.h\ metaevent.cpp metaevent.h\ moc_connector.cpp\ moc_urldecode.cpp urldecode_LDADD = @SIRLIBS@ @LIBJACK@ @SNDFILE_LIBS@ @ALSA_LIBS@ @ASIHPI_LIBS@ @QT5_CLI_LIBS@ -lpthread urldecode_LDFLAGS = @SIRFLAGS@ dist_urlencode_SOURCES = urlencode.cpp urlencode.h nodist_urlencode_SOURCES = cmdswitch.cpp cmdswitch.h\ connector.cpp connector.h\ logging.cpp logging.h\ metaevent.cpp metaevent.h\ moc_connector.cpp\ moc_urlencode.cpp urlencode_LDADD = @SIRLIBS@ @LIBJACK@ @SNDFILE_LIBS@ @ALSA_LIBS@ @ASIHPI_LIBS@ @QT5_CLI_LIBS@ -lpthread urlencode_LDFLAGS = @SIRFLAGS@ CLEANFILES = *~\ moc_*\ *.obj\ *.idb\ *.pdb\ *ilk DISTCLEANFILES = asihpi.cpp asihpi.h\ audiodevice.cpp audiodevice.h\ cmdswitch.cpp cmdswitch.h\ codec.cpp codec.h\ codecdialog.cpp codecdialog.h\ codeviewer.cpp codeviewer.h\ combobox.cpp combobox.h\ connector.cpp connector.h\ glasslimits.h\ guiapplication.cpp guiapplication.h\ hpiinputlistview.cpp hpiinputlistview.h\ hpiwidget.cpp hpiwidget.h\ logging.cpp logging.h\ messagewidget.cpp messagewidget.h\ metaevent.cpp metaevent.h\ paths.h\ profile.cpp profile.h\ ringbuffer.cpp ringbuffer.h\ segmeter.cpp segmeter.h\ serverdialog.cpp serverdialog.h\ sourcedialog.cpp sourcedialog.h\ spinbox.cpp spinbox.h\ statuswidget.cpp statuswidget.h\ stereometer.cpp stereometer.h\ streamdialog.cpp streamdialog.h MAINTAINERCLEANFILES = *~\ Makefile.in GlassCoder-2.0.1/src/tests/pipe_connect.cpp000066400000000000000000000077471425562563600207000ustar00rootroot00000000000000// pipe_connect.cpp // // Test a UNIX pipe connection // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include #include #include #include #include #include "cmdswitch.h" #include "pipe_connect.h" MainObject::MainObject(QObject *parent) : QObject(parent) { pipe_server_pipe=""; unsigned port=0; bool ok=false; // // Get Arguments // CmdSwitch *cmd=new CmdSwitch("pipe_connect",PIPE_CONNECT_USAGE); for(unsigned i=0;ikeys();i++) { if(cmd->key(i)=="--server-pipe") { pipe_server_pipe=cmd->value(i); cmd->setProcessed(i,true); } if(cmd->key(i)=="--port") { port=cmd->value(i).toUInt(&ok); if((!ok)||(port>=65536)) { fprintf(stderr,"pipe_connect: invalid value for --port\n"); exit(256); } cmd->setProcessed(i,true); } } if(port==0) { fprintf(stderr,"pipe_connect: you must specify a --port\n"); exit(256); } if(pipe_server_pipe.isEmpty()) { fprintf(stderr,"pipe_connect: you must specify a --server-pipe\n"); exit(256); } // // Initialize Sending UNIX Socket // memset(&pipe_sa,0,sizeof(pipe_sa)); pipe_sa.sun_family=AF_UNIX; strncpy(pipe_sa.sun_path,pipe_server_pipe.toUtf8(),107); // // TCP Server // pipe_server=new QTcpServer(this); connect(pipe_server,SIGNAL(newConnection()),this,SLOT(newConnectionData())); if(!pipe_server->listen(QHostAddress::Any,port)) { fprintf(stderr,"pipe_connect: unable to listen to port %u\n",port); exit(256); } } void MainObject::newConnectionData() { QTcpSocket *sock=pipe_server->nextPendingConnection(); printf("processing connection from %s:%u...", (const char *)sock->peerAddress().toString().toUtf8(), 0xFFFF&sock->peerPort()); fflush(stdout); SendSocket(sock->socketDescriptor()); printf("done.\n"); delete sock; } void MainObject::SendSocket(int sock) const { char buf[1]={1}; struct msghdr msg; struct iovec iov[1]; union { struct cmsghdr cm; char control[CMSG_SPACE(sizeof(int))]; } control_un; struct cmsghdr *cmptr; int sendsock=-1; // // Open UNIX Socket Connection // if((sendsock=socket(AF_UNIX,SOCK_STREAM,0))<0) { fprintf(stderr, "pipe_connect: unable to initialize UNIX socket at \"%s\" [%s]\n", (const char *)pipe_server_pipe.toUtf8(),strerror(errno)); exit(256); } if(::connect(sendsock,(const struct sockaddr *)(&pipe_sa),sizeof(pipe_sa))<0) { fprintf(stderr,"pipe_connect: unable to connect to \"%s\" [%s]\n", (const char *)pipe_server_pipe.toUtf8(),strerror(errno)); exit(256); } // // Send Socket Descriptor // send(sendsock,buf,1,MSG_NOSIGNAL); memset(&msg,0,sizeof(msg)); memset(iov,0,sizeof(struct iovec)); msg.msg_control=control_un.control; msg.msg_controllen=sizeof(control_un.control); cmptr=CMSG_FIRSTHDR(&msg); cmptr->cmsg_len=CMSG_LEN(sizeof(int)); cmptr->cmsg_level=SOL_SOCKET; cmptr->cmsg_type=SCM_RIGHTS; *((int *) CMSG_DATA(cmptr))=sock; iov[0].iov_base=buf; iov[0].iov_len=1; msg.msg_iov=iov; msg.msg_iovlen=1; sendmsg(sendsock,&msg,MSG_NOSIGNAL); close(sock); // // Clean Up // close(sendsock); } int main(int argc,char *argv[]) { QCoreApplication a(argc,argv); new MainObject(); return a.exec(); } GlassCoder-2.0.1/src/tests/pipe_connect.h000066400000000000000000000024101425562563600203230ustar00rootroot00000000000000// pipe_connect.h // // Test a UNIX pipe connection // // (C) Copyright 2016 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef PIPE_CONNECT_H #define PIPE_CONNECT_H #include #include #include #include #define PIPE_CONNECT_USAGE "--server-pipe= --port-number=\n\n" class MainObject : public QObject { Q_OBJECT; public: MainObject(QObject *parent=0); private slots: void newConnectionData(); private: void SendSocket(int sock) const; QString pipe_server_pipe; struct sockaddr_un pipe_sa; QTcpServer *pipe_server; }; #endif // PIPE_CONNECT_H GlassCoder-2.0.1/src/tests/urldecode.cpp000066400000000000000000000024731425562563600201670ustar00rootroot00000000000000// urldecode.cpp // // Test URL encoding methods // // (C) Copyright 2020 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include "urldecode.h" MainObject::MainObject(QObject *parent) { QStringList args=QCoreApplication::arguments(); if(args.size()!=2) { fprintf(stderr, "urldecode: you must provide exactly one string to decode\n"); exit(1); } printf("%s => %s\n", (const char *)args.at(1).toUtf8(), (const char *)Connector::urlDecode(args.at(1)). toUtf8()); exit(0); } int main(int argc,char *argv[]) { QCoreApplication a(argc,argv); new MainObject(); return a.exec(); } GlassCoder-2.0.1/src/tests/urldecode.h000066400000000000000000000017641425562563600176360ustar00rootroot00000000000000// urldecode.h // // Test URL encoding methods // // (C) Copyright 2020 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef URLDECODE_H #define URLDECODE_H #include #include "connector.h" #define URLDECODE_USAGE "\n\n" class MainObject : public QObject { Q_OBJECT; public: MainObject(QObject *parent=0); }; #endif // URLDECODE_H GlassCoder-2.0.1/src/tests/urlencode.cpp000066400000000000000000000024731425562563600202010ustar00rootroot00000000000000// urlencode.cpp // // Test URL encoding methods // // (C) Copyright 2020 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #include #include #include #include "urlencode.h" MainObject::MainObject(QObject *parent) { QStringList args=QCoreApplication::arguments(); if(args.size()!=2) { fprintf(stderr, "urlencode: you must provide exactly one string to encode\n"); exit(1); } printf("%s => %s\n", (const char *)args.at(1).toUtf8(), (const char *)Connector::urlEncode(args.at(1)). toUtf8()); exit(0); } int main(int argc,char *argv[]) { QCoreApplication a(argc,argv); new MainObject(); return a.exec(); } GlassCoder-2.0.1/src/tests/urlencode.h000066400000000000000000000017641425562563600176500ustar00rootroot00000000000000// urlencode.h // // Test URL encoding methods // // (C) Copyright 2020 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // #ifndef URLENCODE_H #define URLENCODE_H #include #include "connector.h" #define URLENCODE_USAGE "\n\n" class MainObject : public QObject { Q_OBJECT; public: MainObject(QObject *parent=0); }; #endif // URLENCODE_H GlassCoder-2.0.1/xdg/000077500000000000000000000000001425562563600143405ustar00rootroot00000000000000GlassCoder-2.0.1/xdg/Makefile.am000066400000000000000000000024461425562563600164020ustar00rootroot00000000000000## automake.am ## ## xdg/automake.am for GlassCoder ## ## (C) Copyright 2015 Fred Gleason ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License version 2 as ## published by the Free Software Foundation. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public ## License along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## Use automake to process this into a Makefile.in install-exec-am: mkdir -p $(DESTDIR)@prefix@/share/applications cp glasscommander.desktop $(DESTDIR)@prefix@/share/applications/ cp glassgui.desktop $(DESTDIR)@prefix@/share/applications/ uninstall-local: rm -f $(DESTDIR)@prefix@/share/applications/glasscommander.desktop rm -f $(DESTDIR)@prefix@/share/applications/glassgui.desktop EXTRA_DIST = glasscommander.desktop\ glassgui.desktop CLEANFILES = *~ MAINTAINERCLEANFILES = *~\ Makefile.in GlassCoder-2.0.1/xdg/glasscommander.desktop000066400000000000000000000002741425562563600207350ustar00rootroot00000000000000[Desktop Entry] Categories=AudioVideo;Player; Encoding=UTF-8 Name=GlassCommander GenericName=Audio Stream Encoder Exec=glasscommander Icon=glasscoder Path= Type=Application Terminal=false GlassCoder-2.0.1/xdg/glassgui.desktop000066400000000000000000000002601425562563600175470ustar00rootroot00000000000000[Desktop Entry] Categories=AudioVideo;Player; Encoding=UTF-8 Name=GlassGui GenericName=Audio Stream Encoder Exec=glassgui Icon=glasscoder Path= Type=Application Terminal=false