├── .gitignore ├── Jenkinsfile ├── LICENSE ├── gstreamer.prf ├── pipeviz-0.10.pro ├── pipeviz.pri ├── pipeviz.pro ├── readme.md └── src ├── CustomMenuAction.cpp ├── CustomMenuAction.h ├── CustomSettings.cpp ├── CustomSettings.h ├── ElementProperties.cpp ├── ElementProperties.h ├── FavoritesList.cpp ├── FavoritesList.h ├── GraphDisplay.cpp ├── GraphDisplay.h ├── GraphManager.cpp ├── GraphManager.h ├── Logger.cpp ├── Logger.h ├── MainWindow.cpp ├── MainWindow.h ├── PadProperties.cpp ├── PadProperties.h ├── PipelineIE.cpp ├── PipelineIE.h ├── PluginsList.cpp ├── PluginsList.h ├── SeekSlider.cpp ├── SeekSlider.h ├── main.cpp ├── verinfo └── verinfo.sh └── version.in /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile* 2 | debug/* 3 | object_script* 4 | pipeviz_plugin_import.cpp 5 | pipeviz 6 | *.o 7 | moc_* 8 | src/version_info.h 9 | pipeviz.pro.user 10 | pipeviz.app 11 | .qmake.stash 12 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | stages { 4 | stage('build') { 5 | steps { 6 | pwd(tmp: true) 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /gstreamer.prf: -------------------------------------------------------------------------------- 1 | # ============================================================= 2 | # This optional feature file adds GStreamer dependencies 3 | # ============================================================= 4 | unix { 5 | CONFIG += link_pkgconfig 6 | PKGCONFIG += gstreamer-1.0 7 | } else { 8 | 9 | GSTREAMER_PATH = $$clean_path($$(GSTREAMER_1_0_ROOT_X86)) 10 | if(isEmpty(GSTREAMER_PATH)) { 11 | GSTREAMER_PATH = $$clean_path($$(GSTREAMER_1_0_ROOT_X86_64)) 12 | } 13 | 14 | if(isEmpty(GSTREAMER_PATH)|!exists($${GSTREAMER_PATH})) { 15 | 16 | text = "\"GStreamer\" not found: to be able to use the debugger, don't forget to add" 17 | text = "$${text} \"%GSTREAMER_1_0_ROOT_X86_64%\bin\" in your PATH" 18 | !build:warning("$${text}") 19 | 20 | } else { 21 | 22 | DEFINES += GST_USE_UNSTABLE_API 23 | 24 | GST_INCLUDEPATH = \ 25 | $$clean_path($$GSTREAMER_PATH/include/gstreamer-1.0) \ 26 | $$clean_path($$GSTREAMER_PATH/include/glib-2.0) \ 27 | $$clean_path($$GSTREAMER_PATH/lib/glib-2.0/include) 28 | *-g++ { 29 | # To avoid warnings due to GStreamer, use -isystem automatically for any GStreamer system header: 30 | for(somelib, $$list($$GST_INCLUDEPATH)) { 31 | QMAKE_CXXFLAGS += -isystem $${somelib} 32 | } 33 | } else { 34 | INCLUDEPATH += $${GST_INCLUDEPATH} 35 | } 36 | unset(GST_INCLUDEPATH) 37 | 38 | win32-g++ { 39 | LIBS += \ 40 | $${GSTREAMER_PATH}/lib/glib-2.0.lib \ 41 | $${GSTREAMER_PATH}/lib/gobject-2.0.lib \ 42 | $${GSTREAMER_PATH}/lib/gstreamer-1.0.lib 43 | } else { 44 | LIBS += \ 45 | -L$$GSTREAMER_PATH/lib \ 46 | -lglib-2.0 \ 47 | -lgobject-2.0 \ 48 | -lgstreamer-1.0 49 | } 50 | } # GStreamer found 51 | } 52 | -------------------------------------------------------------------------------- /pipeviz-0.10.pro: -------------------------------------------------------------------------------- 1 | include(pipeviz.pri) 2 | 3 | PKGCONFIG += gstreamer-0.10 4 | 5 | -------------------------------------------------------------------------------- /pipeviz.pri: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.0) ?? ???. 22 21:50:14 2014 3 | ###################################################################### 4 | 5 | CONFIG += qt debug 6 | TEMPLATE = app 7 | TARGET = pipeviz 8 | QT += widgets 9 | QT += xml 10 | QT += core 11 | INCLUDEPATH += $$OUT_PWD/src 12 | 13 | CONFIG += gstreamer 14 | 15 | QMAKE_CXXFLAGS += -std=c++11 16 | 17 | gitinfo.commands = $$PWD/src/verinfo/verinfo.sh $$PWD/src/version.in $$OUT_PWD/src/version_info.h 18 | gitinfo.target = $$OUT_PWD/src/version_info.h 19 | 20 | QMAKE_EXTRA_TARGETS += gitinfo 21 | PRE_TARGETDEPS += $$OUT_PWD/src/version_info.h 22 | 23 | # Input 24 | HEADERS += src/PluginsList.h \ 25 | src/MainWindow.h \ 26 | src/GraphManager.h \ 27 | src/GraphDisplay.h \ 28 | src/ElementProperties.h \ 29 | src/PadProperties.h \ 30 | src/PipelineIE.h \ 31 | src/CustomSettings.h \ 32 | src/SeekSlider.h \ 33 | src/CustomMenuAction.h \ 34 | src/FavoritesList.h \ 35 | src/Logger.h 36 | 37 | SOURCES += src/main.cpp \ 38 | src/PluginsList.cpp \ 39 | src/MainWindow.cpp \ 40 | src/GraphManager.cpp \ 41 | src/GraphDisplay.cpp \ 42 | src/ElementProperties.cpp \ 43 | src/PadProperties.cpp \ 44 | src/PipelineIE.cpp \ 45 | src/CustomSettings.cpp \ 46 | src/SeekSlider.cpp \ 47 | src/CustomMenuAction.cpp \ 48 | src/FavoritesList.cpp \ 49 | src/Logger.cpp 50 | -------------------------------------------------------------------------------- /pipeviz.pro: -------------------------------------------------------------------------------- 1 | # Location of our own features: 2 | command = $$[QT_INSTALL_BINS]/qmake -set QMAKEFEATURES $$_PRO_FILE_PWD_ 3 | system($$command)|error("Failed to run: $$command") 4 | 5 | include(pipeviz.pri) 6 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | pipeviz 2 | ========== 3 | 4 | Pipeviz is a graphedit for gstreamer-1.0. This is a gui tool for constructing and testing gstreamer pipelines. 5 | 6 | It allows you: 7 | 8 | * to construct the pipelines via the gui interface 9 | 10 | * to test different types of pipes easy 11 | 12 | * save and open your graphs 13 | 14 | Who might be interested in it? 15 | 16 | * quality assurance 17 | 18 | * technical support 19 | 20 | * software engineers 21 | 22 | 23 | ![alt tag](https://cloud.githubusercontent.com/assets/10683398/6396608/94f89e3a-be09-11e4-982c-5bf3a57bc6f4.png) 24 | 25 | Pre-requirements: 26 | ----- 27 | 28 | * qt (4.0 5.0) 29 | 30 | * gstreamer-1.0 31 | 32 | * pkgconfig 33 | 34 | 35 | 36 | Building: 37 | ----- 38 | 39 | cd pipeviz 40 | 41 | QMAKEFEATURES=. qmake pipeviz.pro 42 | 43 | make gitinfo 44 | 45 | make 46 | 47 | 48 | 49 | Prebuilt binaries 50 | ----- 51 | 52 | Prebuilt binaries for windows are available. 53 | 54 | [Latest Release](https://github.com/virinext/pipeviz/releases/latest) 55 | -------------------------------------------------------------------------------- /src/CustomMenuAction.cpp: -------------------------------------------------------------------------------- 1 | #include "CustomMenuAction.h" 2 | 3 | CustomMenuAction::CustomMenuAction (const QString& displayName, 4 | QObject * parent) 5 | : QAction (displayName, parent), 6 | m_name (displayName) 7 | { 8 | } 9 | 10 | CustomMenuAction::CustomMenuAction (const QString& displayName, 11 | const QString& name, QObject * parent) 12 | : QAction (displayName, parent), 13 | m_name (name) 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /src/CustomMenuAction.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOM_MENU_ACTION_H_ 2 | #define CUSTOM_MENU_ACTION_H_ 3 | 4 | #include 5 | 6 | class CustomMenuAction: public QAction 7 | { 8 | public: 9 | CustomMenuAction(const QString& displayName, QObject * parent); 10 | CustomMenuAction(const QString& displayName, const QString& name, QObject * parent); 11 | 12 | QString getName() {return m_name;} 13 | 14 | private: 15 | QString m_name; 16 | }; 17 | 18 | #endif //CUSTOM_MENU_ACTION_H_ 19 | -------------------------------------------------------------------------------- /src/CustomSettings.cpp: -------------------------------------------------------------------------------- 1 | #include "CustomSettings.h" 2 | 3 | #include 4 | 5 | #define COMPANY_NAME "virinext" 6 | #define APPLICATION_NAME "pipeviz" 7 | 8 | void 9 | CustomSettings::saveLastIODirectory (const QString &name) 10 | { 11 | QSettings settings (COMPANY_NAME, APPLICATION_NAME); 12 | settings.setValue ("last_directory", name); 13 | } 14 | 15 | QString 16 | CustomSettings::lastIODirectory () 17 | { 18 | QSettings settings (COMPANY_NAME, APPLICATION_NAME); 19 | QString res = settings.value ("last_directory").toString (); 20 | 21 | if (res.isEmpty ()) 22 | res = "./"; 23 | 24 | return res; 25 | } 26 | 27 | void 28 | CustomSettings::saveFavoriteList (const QStringList &favorite_list) 29 | { 30 | QSettings settings (COMPANY_NAME, APPLICATION_NAME); 31 | settings.setValue("favorite_list", QVariant::fromValue(favorite_list)); 32 | } 33 | 34 | QStringList 35 | CustomSettings::loadFavoriteList () 36 | { 37 | QSettings settings (COMPANY_NAME, APPLICATION_NAME); 38 | QStringList data = settings.value("favorite_list").value(); 39 | 40 | return data; 41 | } 42 | 43 | void 44 | CustomSettings::saveMainWindowGeometry (const QByteArray &geometry) 45 | { 46 | QSettings settings (COMPANY_NAME, APPLICATION_NAME); 47 | settings.setValue ("geometry", geometry); 48 | } 49 | 50 | QByteArray 51 | CustomSettings::mainWindowGeometry () 52 | { 53 | QSettings settings (COMPANY_NAME, APPLICATION_NAME); 54 | return settings.value ("geometry").toByteArray (); 55 | } 56 | 57 | void 58 | CustomSettings::saveGstDebugString (const QString &string) 59 | { 60 | QSettings settings (COMPANY_NAME, APPLICATION_NAME); 61 | settings.setValue ("gst_log_string", string); 62 | } 63 | 64 | QString 65 | CustomSettings::lastGstDebugString () 66 | { 67 | QSettings settings (COMPANY_NAME, APPLICATION_NAME); 68 | QString res = settings.value ("gst_log_string").toString (); 69 | 70 | if (res.isEmpty ()) 71 | res = "GST_DEBUG=*:5"; 72 | 73 | return res; 74 | } 75 | -------------------------------------------------------------------------------- /src/CustomSettings.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOM_SETTINGS_H_ 2 | #define CUSTOM_SETTINGS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace CustomSettings 9 | { 10 | void saveLastIODirectory(const QString &name); 11 | QString lastIODirectory(); 12 | void saveFavoriteList(const QStringList &name); 13 | QStringList loadFavoriteList(); 14 | 15 | void saveGstDebugString(const QString &name); 16 | QString lastGstDebugString(); 17 | 18 | void saveMainWindowGeometry(const QByteArray &geometry); 19 | QByteArray mainWindowGeometry(); 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/ElementProperties.cpp: -------------------------------------------------------------------------------- 1 | #include "ElementProperties.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | ElementProperties::ElementProperties (QSharedPointer pGraph, 14 | const char *name, QWidget *parent, 15 | Qt::WindowFlags flags) 16 | : QWidget (parent, flags), 17 | m_pGraphManager (pGraph), 18 | m_name (name) 19 | { 20 | setWindowTitle (QString (name) + " properties"); 21 | 22 | create (); 23 | } 24 | 25 | void 26 | ElementProperties::addParamEnum (GParamSpec *param, GstElement *element, 27 | QGridLayout *play) 28 | { 29 | GValue value = G_VALUE_INIT; 30 | 31 | g_value_init (&value, param->value_type); 32 | if (param->flags & G_PARAM_READABLE) 33 | g_object_get_property (G_OBJECT (element), param->name, &value); 34 | else { 35 | const GValue *valueDef = g_param_spec_get_default_value (param); 36 | g_value_copy (valueDef, &value); 37 | } 38 | 39 | QString propertyName = g_param_spec_get_name (param); 40 | int propertyValue; 41 | 42 | propertyValue = g_value_get_enum (&value); 43 | 44 | GParamSpecEnum *penumSpec = G_PARAM_SPEC_ENUM (param); 45 | 46 | if (!penumSpec) 47 | return; 48 | 49 | QComboBox *pcomBox = new QComboBox; 50 | 51 | for (std::size_t i = 0; i < penumSpec->enum_class->n_values; i++) { 52 | QVariant var (penumSpec->enum_class->values[i].value); 53 | QString valueName = penumSpec->enum_class->values[i].value_name; 54 | 55 | pcomBox->addItem (valueName, var); 56 | 57 | if (penumSpec->enum_class->values[i].value == propertyValue) 58 | pcomBox->setCurrentIndex (i); 59 | } 60 | 61 | int row = play->rowCount (); 62 | play->addWidget (new QLabel (propertyName), row, 0); 63 | play->addWidget (pcomBox, row, 1); 64 | m_values.insert (propertyName, pcomBox); 65 | } 66 | 67 | void 68 | ElementProperties::addParamFlags (GParamSpec *param, GstElement *element, 69 | QGridLayout *play) 70 | { 71 | GValue value = G_VALUE_INIT; 72 | 73 | g_value_init (&value, param->value_type); 74 | if (param->flags & G_PARAM_READABLE) 75 | g_object_get_property (G_OBJECT (element), param->name, &value); 76 | else { 77 | const GValue *valueDef = g_param_spec_get_default_value (param); 78 | g_value_copy (valueDef, &value); 79 | } 80 | 81 | QString propertyName = g_param_spec_get_name (param); 82 | size_t propertyValue; 83 | 84 | propertyValue = g_value_get_flags (&value); 85 | 86 | GParamSpecFlags *pflagsSpec = G_PARAM_SPEC_FLAGS (param); 87 | 88 | if (!pflagsSpec) 89 | return; 90 | 91 | QComboBox *pcomBox = new QComboBox; 92 | 93 | for (std::size_t i = 0; i < pflagsSpec->flags_class->n_values; i++) { 94 | QVariant var (pflagsSpec->flags_class->values[i].value); 95 | QString valueName = pflagsSpec->flags_class->values[i].value_name; 96 | 97 | pcomBox->addItem (valueName, var); 98 | 99 | if (pflagsSpec->flags_class->values[i].value == propertyValue) 100 | pcomBox->setCurrentIndex (i); 101 | } 102 | 103 | int row = play->rowCount (); 104 | 105 | play->addWidget (new QLabel (propertyName), row, 0); 106 | 107 | play->addWidget (pcomBox, row, 1); 108 | 109 | m_values.insert (propertyName, pcomBox); 110 | } 111 | 112 | void 113 | ElementProperties::addParamSimple (GParamSpec *param, GstElement *element, 114 | QGridLayout *play) 115 | { 116 | bool readOnly = true; 117 | 118 | if (param->flags & G_PARAM_WRITABLE) 119 | readOnly = false; 120 | 121 | GValue value = G_VALUE_INIT; 122 | 123 | g_value_init (&value, param->value_type); 124 | if (param->flags & G_PARAM_READABLE) 125 | g_object_get_property (G_OBJECT (element), param->name, &value); 126 | else { 127 | const GValue *valueDef = g_param_spec_get_default_value (param); 128 | g_value_copy (valueDef, &value); 129 | } 130 | 131 | QString propertyName = g_param_spec_get_name (param); 132 | QString propertyValue; 133 | GType type = G_VALUE_TYPE (&value); 134 | 135 | bool skip = false; 136 | 137 | switch (type) { 138 | case G_TYPE_STRING: { 139 | const char *string_val = g_value_get_string (&value); 140 | propertyValue = string_val; 141 | break; 142 | } 143 | case G_TYPE_BOOLEAN: { 144 | gboolean bool_val = g_value_get_boolean (&value); 145 | propertyValue = QString::number (bool_val); 146 | break; 147 | } 148 | case G_TYPE_ULONG: { 149 | propertyValue = QString::number (g_value_get_ulong (&value)); 150 | break; 151 | } 152 | case G_TYPE_LONG: { 153 | propertyValue = QString::number (g_value_get_long (&value)); 154 | break; 155 | } 156 | case G_TYPE_UINT: { 157 | propertyValue = QString::number (g_value_get_uint (&value)); 158 | break; 159 | } 160 | case G_TYPE_INT: { 161 | propertyValue = QString::number (g_value_get_int (&value)); 162 | break; 163 | } 164 | case G_TYPE_UINT64: { 165 | propertyValue = QString::number (g_value_get_uint64 (&value)); 166 | break; 167 | } 168 | case G_TYPE_INT64: { 169 | propertyValue = QString::number (g_value_get_int64 (&value)); 170 | break; 171 | } 172 | case G_TYPE_FLOAT: { 173 | propertyValue = QString::number (g_value_get_float (&value)); 174 | break; 175 | } 176 | case G_TYPE_DOUBLE: { 177 | propertyValue = QString::number (g_value_get_double (&value)); 178 | break; 179 | } 180 | case G_TYPE_CHAR: { 181 | propertyValue = QString::number (g_value_get_schar (&value)); 182 | break; 183 | } 184 | case G_TYPE_UCHAR: { 185 | propertyValue = QString::number (g_value_get_uchar (&value)); 186 | break; 187 | } 188 | 189 | default: { 190 | if (type == g_type_from_name("GstCaps")) { 191 | GstCaps *gstcaps; 192 | g_object_get (G_OBJECT (element), param->name, &gstcaps, NULL); 193 | const char *string_val = gst_caps_to_string (gstcaps); 194 | if (gstcaps == NULL) 195 | string_val = "ANY"; 196 | propertyValue = string_val; 197 | break; 198 | } 199 | 200 | skip = true; 201 | LOG_INFO("property %s not supported", propertyName.toStdString ().c_str ()); 202 | break; 203 | } 204 | }; 205 | 206 | int row = play->rowCount (); 207 | 208 | play->addWidget (new QLabel (propertyName), row, 0); 209 | 210 | QLineEdit *ple = new QLineEdit (propertyValue); 211 | ple->setReadOnly (readOnly); 212 | play->addWidget (ple, row, 1); 213 | if (!skip) 214 | m_values.insert (propertyName, ple); 215 | else 216 | ple->setReadOnly (true); 217 | } 218 | 219 | void 220 | ElementProperties::create () 221 | { 222 | GstElement *element = gst_bin_get_by_name ( 223 | GST_BIN (m_pGraphManager->m_pGraph), m_name.toStdString ().c_str ()); 224 | 225 | if (!element) 226 | return; 227 | 228 | QGridLayout *play = new QGridLayout; 229 | 230 | GParamSpec **prop_specs; 231 | guint num_props; 232 | 233 | prop_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (element), 234 | &num_props); 235 | 236 | for (std::size_t i = 0; i < num_props; i++) { 237 | GParamSpec *param = prop_specs[i]; 238 | 239 | if (G_IS_PARAM_SPEC_ENUM (param)) 240 | addParamEnum (param, element, play); 241 | else if (G_IS_PARAM_SPEC_FLAGS (param)) 242 | addParamFlags (param, element, play); 243 | else 244 | addParamSimple (param, element, play); 245 | } 246 | 247 | QVBoxLayout *pvblay = new QVBoxLayout; 248 | QWidget *pwgt = new QWidget (this); 249 | pwgt->setLayout (play); 250 | QScrollArea *pscroll = new QScrollArea (this); 251 | pscroll->setWidget (pwgt); 252 | 253 | pvblay->addWidget (pscroll); 254 | 255 | QHBoxLayout *phblay = new QHBoxLayout; 256 | 257 | QPushButton *pcmdApply = new QPushButton ("Apply"); 258 | QPushButton *pcmdOk = new QPushButton ("OK"); 259 | QPushButton *pcmdCancel = new QPushButton ("Cancel"); 260 | 261 | phblay->addStretch (1); 262 | phblay->addWidget (pcmdApply); 263 | phblay->addWidget (pcmdCancel); 264 | phblay->addWidget (pcmdOk); 265 | 266 | pvblay->addLayout (phblay); 267 | 268 | QObject::connect (pcmdApply, SIGNAL (clicked ()), this, 269 | SLOT (applyClicked ())); 270 | QObject::connect (pcmdCancel, SIGNAL (clicked ()), this, SLOT (close ())); 271 | QObject::connect (pcmdOk, SIGNAL (clicked ()), this, SLOT (okClicked ())); 272 | 273 | setLayout (pvblay); 274 | 275 | g_free (prop_specs); 276 | gst_object_unref (element); 277 | 278 | } 279 | 280 | void 281 | ElementProperties::applyClicked () 282 | { 283 | GstElement *element = gst_bin_get_by_name ( 284 | GST_BIN (m_pGraphManager->m_pGraph), m_name.toStdString ().c_str ()); 285 | 286 | if (!element) 287 | return; 288 | 289 | QMap::iterator itr = m_values.begin (); 290 | 291 | for (; itr != m_values.end (); itr++) { 292 | GParamSpec *param = g_object_class_find_property ( 293 | G_OBJECT_GET_CLASS (element), itr.key ().toStdString ().c_str ()); 294 | 295 | if (!param) { 296 | LOG_INFO("problem with setting %s property", itr.key ().toStdString ().c_str ()); 297 | continue; 298 | } 299 | 300 | if (!(param->flags & G_PARAM_WRITABLE)) 301 | continue; 302 | 303 | QString valStr; 304 | 305 | if (dynamic_cast (itr.value ())) 306 | valStr = ((QLineEdit *) itr.value ())->text (); 307 | else if (dynamic_cast (itr.value ())) { 308 | QComboBox *pcomBox = (QComboBox *) itr.value (); 309 | int val = pcomBox->itemData (pcomBox->currentIndex ()).toInt (); 310 | valStr = QString::number (val); 311 | } 312 | 313 | std::string tmpStr = itr.key ().toStdString (); 314 | const char *propName = tmpStr.c_str (); 315 | 316 | if (G_IS_PARAM_SPEC_ENUM (param) || G_IS_PARAM_SPEC_FLAGS (param)) { 317 | if (dynamic_cast (itr.value ())) { 318 | QComboBox *pcomBox = (QComboBox *) itr.value (); 319 | int val = pcomBox->itemData (pcomBox->currentIndex ()).toInt (); 320 | g_object_set (G_OBJECT (element), propName, val, NULL); 321 | } 322 | } 323 | else { 324 | GType type = param->value_type; 325 | switch (type) { 326 | case G_TYPE_STRING: { 327 | g_object_set (G_OBJECT (element), propName, 328 | valStr.toStdString ().c_str (), NULL); 329 | break; 330 | } 331 | case G_TYPE_BOOLEAN: { 332 | gboolean val = valStr.toInt (); 333 | g_object_set (G_OBJECT (element), propName, val, NULL); 334 | break; 335 | } 336 | case G_TYPE_ULONG: { 337 | gulong val = valStr.toULong (); 338 | g_object_set (G_OBJECT (element), propName, val, NULL); 339 | break; 340 | } 341 | case G_TYPE_LONG: { 342 | glong val = valStr.toLong (); 343 | g_object_set (G_OBJECT (element), propName, val, NULL); 344 | break; 345 | } 346 | case G_TYPE_UINT: { 347 | guint val = valStr.toUInt (); 348 | g_object_set (G_OBJECT (element), propName, val, NULL); 349 | break; 350 | } 351 | case G_TYPE_INT: { 352 | gint val = valStr.toInt (); 353 | g_object_set (G_OBJECT (element), propName, val, NULL); 354 | break; 355 | } 356 | case G_TYPE_UINT64: { 357 | guint64 val = valStr.toULongLong (); 358 | g_object_set (G_OBJECT (element), propName, val, NULL); 359 | break; 360 | } 361 | case G_TYPE_INT64: { 362 | gint64 val = valStr.toLongLong (); 363 | g_object_set (G_OBJECT (element), propName, val, NULL); 364 | break; 365 | } 366 | case G_TYPE_FLOAT: { 367 | gfloat val = valStr.toFloat (); 368 | g_object_set (G_OBJECT (element), propName, val, NULL); 369 | break; 370 | } 371 | case G_TYPE_DOUBLE: { 372 | gdouble val = valStr.toDouble (); 373 | g_object_set (G_OBJECT (element), propName, val, NULL); 374 | break; 375 | } 376 | case G_TYPE_CHAR: { 377 | gchar val = valStr.toInt (); 378 | g_object_set (G_OBJECT (element), propName, val, NULL); 379 | break; 380 | } 381 | case G_TYPE_UCHAR: { 382 | guchar val = valStr.toUInt (); 383 | g_object_set (G_OBJECT (element), propName, val, NULL); 384 | break; 385 | } 386 | default: { 387 | if (type == g_type_from_name("GstCaps")) { 388 | GstCaps *oldval; 389 | GstCaps *newval = gst_caps_from_string (valStr.toStdString ().c_str ()); 390 | g_object_get (G_OBJECT (element), propName, &oldval, NULL); 391 | 392 | if (oldval != newval && oldval != NULL) { 393 | /* Release old */ 394 | gst_caps_unref (oldval); 395 | } 396 | 397 | g_object_set (G_OBJECT (element), propName, newval, NULL); 398 | break; 399 | } 400 | 401 | LOG_INFO("property %s not supported", itr.key ().constData()); 402 | break; 403 | } 404 | }; 405 | } 406 | } 407 | 408 | gst_object_unref (element); 409 | 410 | delete layout (); 411 | qDeleteAll ( 412 | children ()); 413 | 414 | create (); 415 | } 416 | 417 | void 418 | ElementProperties::okClicked () 419 | { 420 | applyClicked (); 421 | close (); 422 | } 423 | -------------------------------------------------------------------------------- /src/ElementProperties.h: -------------------------------------------------------------------------------- 1 | #ifndef ELEMENT_PROPERTIES_H_ 2 | #define ELEMENT_PROPERTIES_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "GraphManager.h" 12 | 13 | class ElementProperties: public QWidget 14 | { 15 | Q_OBJECT 16 | public: 17 | ElementProperties(QSharedPointer pGraphManager, const char *name, 18 | QWidget *parent = 0, Qt::WindowFlags flags = 0); 19 | 20 | private slots: 21 | void applyClicked(); 22 | void okClicked(); 23 | 24 | private: 25 | QSharedPointer m_pGraphManager; 26 | QMap m_values; 27 | QString m_name; 28 | 29 | void create(); 30 | void addParamSimple(GParamSpec *value, GstElement *element, QGridLayout *play); 31 | void addParamEnum(GParamSpec *value, GstElement *element, QGridLayout *play); 32 | void addParamFlags(GParamSpec *value, GstElement *element, QGridLayout *play); 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/FavoritesList.cpp: -------------------------------------------------------------------------------- 1 | #include "FavoritesList.h" 2 | #include "CustomSettings.h" 3 | #include "Logger.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | 10 | FavoritesList::FavoritesList (QWidget *pwgt) 11 | : QListWidget (pwgt) 12 | { 13 | loadFavorites(); 14 | } 15 | 16 | FavoritesList::~FavoritesList () 17 | { 18 | } 19 | 20 | int FavoritesList::isFavorite(const QString& plugin_name) 21 | { 22 | for (int i = 0; i < this->count (); i++) { 23 | QListWidgetItem *pitem = this->item (i); 24 | if (pitem->text () == plugin_name) 25 | return i; 26 | } 27 | return -1; 28 | } 29 | 30 | void FavoritesList::addFavorite (const QString& plugin_name) 31 | { 32 | if (isFavorite(plugin_name) == -1) 33 | this->addItem(plugin_name); 34 | saveFavorites(); 35 | } 36 | 37 | void FavoritesList::removeFavorite (const QString& plugin_name) 38 | { 39 | int favorite = isFavorite(plugin_name); 40 | if (favorite != -1) 41 | delete this->takeItem(this->row(this->item(favorite))); 42 | saveFavorites(); 43 | } 44 | 45 | void FavoritesList::loadFavorites() 46 | { 47 | QStringList favorites = CustomSettings::loadFavoriteList(); 48 | foreach (QString plugin_name, favorites) { 49 | this->addItem(plugin_name); 50 | } 51 | LOG_INFO("Just load favorites"); 52 | } 53 | 54 | void FavoritesList::saveFavorites() 55 | { 56 | QStringList favorites; 57 | 58 | for (int i = 0; i < this->count (); i++) { 59 | QListWidgetItem *pitem = this->item (i); 60 | favorites << pitem->text(); 61 | } 62 | LOG_INFO("About to save favorites"); 63 | CustomSettings::saveFavoriteList(favorites); 64 | } 65 | -------------------------------------------------------------------------------- /src/FavoritesList.h: -------------------------------------------------------------------------------- 1 | #ifndef FAVORITES_LIST_H_ 2 | #define FAVORITES_LIST_H_ 3 | 4 | #include 5 | #include 6 | 7 | class FavoritesList: public QListWidget 8 | { 9 | Q_OBJECT 10 | public: 11 | FavoritesList(QWidget *pwgt = NULL); 12 | ~FavoritesList(); 13 | 14 | int isFavorite(const QString& plugin_name); 15 | 16 | void addFavorite(const QString& plugin_name); 17 | void removeFavorite(const QString& plugin_name); 18 | 19 | private: 20 | void loadFavorites(); 21 | void saveFavorites(); 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/GraphDisplay.cpp: -------------------------------------------------------------------------------- 1 | #include "GraphDisplay.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "ElementProperties.h" 15 | #include "PadProperties.h" 16 | #include "CustomMenuAction.h" 17 | #include "PluginsList.h" 18 | 19 | #define PAD_SIZE 8 20 | #define PAD_SIZE_ACTION 16 21 | 22 | GraphDisplay::GraphDisplay (QWidget *parent, Qt::WindowFlags f) 23 | : QWidget (parent, f) 24 | { 25 | setFocusPolicy (Qt::WheelFocus); 26 | setMouseTracking (true); 27 | } 28 | 29 | ElementInfo* 30 | GraphDisplay::getElement (std::size_t elementId) 31 | { 32 | ElementInfo* element = NULL; 33 | std::size_t i = 0; 34 | for (; i < m_info.size (); i++) { 35 | if (m_info[i].m_id == elementId) { 36 | element = &m_info[i]; 37 | break; 38 | } 39 | } 40 | return element; 41 | } 42 | 43 | PadInfo* 44 | GraphDisplay::getPad (std::size_t elementId, std::size_t padId) 45 | { 46 | PadInfo* pad = NULL; 47 | ElementInfo* element = getElement (elementId); 48 | if (!element) 49 | return NULL; 50 | std::size_t j = 0; 51 | for (; j < element->m_pads.size (); j++) 52 | if (element->m_pads[j].m_id == padId) { 53 | pad = &element->m_pads[j]; 54 | break; 55 | } 56 | return pad; 57 | } 58 | 59 | void 60 | GraphDisplay::updateDisplayInfoIds () 61 | { 62 | for (std::size_t i = 0; i < m_info.size (); i++) { 63 | for (std::size_t j = 0; j < m_displayInfo.size (); j++) { 64 | if (m_info[i].m_name == m_displayInfo[j].m_name) { 65 | m_displayInfo[j].m_id = m_info[i].m_id; 66 | for (std::size_t k = 0; k < m_displayInfo.size (); k++) { 67 | if (k != j && m_displayInfo[j].m_id == m_displayInfo[k].m_id) 68 | m_displayInfo[k].m_id = (size_t) -1; 69 | } 70 | } 71 | } 72 | } 73 | } 74 | 75 | void 76 | GraphDisplay::update (const std::vector &info) 77 | { 78 | bool needUpdate = false; 79 | 80 | if (m_info.size () != info.size ()) 81 | needUpdate = true; 82 | 83 | if (!needUpdate) { 84 | for (std::size_t i = 0; i < info.size (); i++) { 85 | std::size_t j = 0; 86 | 87 | for (; j < m_info.size (); j++) { 88 | if (info[i].m_name == m_info[j].m_name) 89 | break; 90 | } 91 | 92 | if (j == m_info.size ()) { 93 | needUpdate = true; 94 | break; 95 | } 96 | 97 | if (info[i].m_pads != m_info[j].m_pads) { 98 | needUpdate = true; 99 | break; 100 | } 101 | 102 | if (info[i].m_connections != m_info[j].m_connections) { 103 | needUpdate = true; 104 | break; 105 | } 106 | } 107 | } 108 | 109 | if (needUpdate) { 110 | m_info = info; 111 | updateDisplayInfoIds (); 112 | calculatePositions (); 113 | repaint (); 114 | } 115 | } 116 | 117 | void 118 | GraphDisplay::paintEvent (QPaintEvent *event) 119 | { 120 | Q_UNUSED(event); 121 | QPainter painter (this); 122 | QPen defaultPen = painter.pen (); 123 | for (std::size_t i = 0; i < m_displayInfo.size (); i++) { 124 | if (m_displayInfo[i].m_isSelected) 125 | painter.setPen (QPen (Qt::blue)); 126 | 127 | painter.drawRect (m_displayInfo[i].m_rect); 128 | 129 | painter.setPen (defaultPen); 130 | 131 | for (std::size_t j = 0; j < m_info[i].m_pads.size (); j++) { 132 | 133 | QPoint point = getPadPosition (m_info[i].m_id, m_info[i].m_pads[j].m_id); 134 | 135 | int xPos, yPos; 136 | xPos = point.x (); 137 | yPos = point.y (); 138 | 139 | xPos -= PAD_SIZE / 2; 140 | yPos -= PAD_SIZE / 2; 141 | 142 | painter.fillRect (xPos, yPos, PAD_SIZE, PAD_SIZE, Qt::black); 143 | 144 | QPoint textPos; 145 | 146 | QRect rect = painter.boundingRect ( 147 | 0, 0, width (), height (), Qt::AlignLeft | Qt::AlignTop, 148 | QString (m_info[i].m_pads[j].m_name.c_str ())); 149 | if (m_info[i].m_pads[j].m_type == PadInfo::Out) 150 | textPos = QPoint (point.x () - PAD_SIZE - rect.width (), 151 | point.y () + PAD_SIZE / 2); 152 | else if (m_info[i].m_pads[j].m_type == PadInfo::In) 153 | textPos = QPoint (point.x () + PAD_SIZE, point.y () + PAD_SIZE / 2); 154 | painter.drawText (textPos, QString (m_info[i].m_pads[j].m_name.c_str ())); 155 | 156 | if (m_info[i].m_connections[j].m_elementId != ((size_t) -1) 157 | && m_info[i].m_connections[j].m_padId != ((size_t) -1)) { 158 | xPos = point.x (); 159 | yPos = point.y (); 160 | 161 | point = getPadPosition (m_info[i].m_connections[j].m_elementId, 162 | m_info[i].m_connections[j].m_padId); 163 | int xPosPeer, yPosPeer; 164 | 165 | xPosPeer = point.x (); 166 | yPosPeer = point.y (); 167 | 168 | painter.drawLine (xPos, yPos, xPosPeer, yPosPeer); 169 | } 170 | 171 | } 172 | 173 | painter.drawText (m_displayInfo[i].m_rect.topLeft () + QPoint (10, 15), 174 | QString (m_displayInfo[i].m_name.c_str ())); 175 | } 176 | 177 | if (m_moveInfo.m_action == MakeConnect) { 178 | painter.drawLine (m_moveInfo.m_position, m_moveInfo.m_startPosition); 179 | } 180 | else if (m_moveInfo.m_action == Select) { 181 | if (!m_moveInfo.m_position.isNull ()) { 182 | painter.setPen (Qt::DashLine); 183 | painter.drawRect ( 184 | QRect (m_moveInfo.m_startPosition, m_moveInfo.m_position)); 185 | } 186 | } 187 | } 188 | 189 | GraphDisplay::ElementDisplayInfo 190 | GraphDisplay::calculateOnePosition (const ElementInfo &info) 191 | { 192 | ElementDisplayInfo displayInfo; 193 | displayInfo.m_id = info.m_id; 194 | displayInfo.m_name = info.m_name; 195 | displayInfo.m_isSelected = false; 196 | 197 | int width = 150; 198 | int height = 50; 199 | 200 | int numInPads, numOutPads; 201 | numInPads = numOutPads = 0; 202 | 203 | for (std::size_t j = 0; j < info.m_pads.size (); j++) { 204 | if (info.m_pads[j].m_type == PadInfo::Out) 205 | numOutPads++; 206 | else if (info.m_pads[j].m_type == PadInfo::In) 207 | numInPads++; 208 | } 209 | 210 | if (std::max (numInPads, numOutPads) >= 1) 211 | height += (std::max (numInPads, numOutPads) - 1) * 25; 212 | 213 | int curX, curY; 214 | curX = curY = 10; 215 | 216 | QRect rect (curX, curY, width, height); 217 | 218 | while (true) { 219 | rect = QRect (curX, curY, width, height); 220 | QRect rectTest (curX, curY - 15, width + 15, height + 15); 221 | bool noIntersects = true; 222 | for (std::size_t i = 0; i < m_displayInfo.size (); i++) { 223 | if (rectTest.intersects (m_displayInfo[i].m_rect)) { 224 | noIntersects = false; 225 | break; 226 | } 227 | } 228 | 229 | if (noIntersects) 230 | break; 231 | 232 | curY += 25; 233 | } 234 | 235 | displayInfo.m_rect = rect; 236 | 237 | return displayInfo; 238 | 239 | } 240 | 241 | void 242 | GraphDisplay::calculatePositions () 243 | { 244 | while (true) { 245 | std::size_t i = 0; 246 | for (; i < m_displayInfo.size (); i++) { 247 | std::size_t j = 0; 248 | for (; j < m_info.size (); j++) { 249 | if (m_displayInfo[i].m_id == m_info[j].m_id) 250 | break; 251 | } 252 | 253 | if (j == m_info.size ()) { 254 | m_displayInfo.erase (m_displayInfo.begin () + i); 255 | break; 256 | } 257 | } 258 | 259 | if (i >= m_displayInfo.size ()) 260 | break; 261 | } 262 | 263 | std::size_t i = 0; 264 | for (; i < m_info.size (); i++) { 265 | std::size_t j = 0; 266 | for (; j < m_displayInfo.size (); j++) { 267 | if (m_displayInfo[j].m_id == m_info[i].m_id) 268 | break; 269 | } 270 | 271 | if (j == m_displayInfo.size ()) 272 | m_displayInfo.push_back (calculateOnePosition (m_info[i])); 273 | } 274 | 275 | std::vector reorderedDisplayInfo (m_info.size ()); 276 | for (std::size_t i = 0; i < m_info.size (); i++) { 277 | for (std::size_t j = 0; j < m_displayInfo.size (); j++) { 278 | if (m_displayInfo[j].m_id == m_info[i].m_id) { 279 | reorderedDisplayInfo[i] = m_displayInfo[j]; 280 | break; 281 | } 282 | } 283 | } 284 | 285 | m_displayInfo = reorderedDisplayInfo; 286 | } 287 | 288 | void 289 | GraphDisplay::mousePressEvent (QMouseEvent *event) 290 | { 291 | std::size_t elementId, padId; 292 | getIdByPosition (event->pos (), elementId, padId); 293 | 294 | if (event->buttons () & Qt::RightButton) { 295 | showContextMenu (event); 296 | } 297 | else { 298 | if (padId != ((size_t) -1)) { 299 | m_moveInfo.m_padId = padId; 300 | m_moveInfo.m_elementId = elementId; 301 | m_moveInfo.m_position = event->pos (); 302 | m_moveInfo.m_action = MakeConnect; 303 | m_moveInfo.m_startPosition = event->pos (); 304 | } 305 | else if (elementId != ((size_t) -1)) { 306 | m_moveInfo.m_elementId = elementId; 307 | m_moveInfo.m_padId = -1; 308 | m_moveInfo.m_position = event->pos (); 309 | m_moveInfo.m_action = MoveComponent; 310 | m_moveInfo.m_startPosition = event->pos (); 311 | } 312 | else { 313 | m_moveInfo.m_startPosition = event->pos (); 314 | m_moveInfo.m_action = Select; 315 | m_moveInfo.m_position = QPoint (); 316 | } 317 | } 318 | 319 | for (std::size_t i = 0; i < m_displayInfo.size (); i++) 320 | m_displayInfo[i].m_isSelected = false; 321 | } 322 | 323 | void 324 | GraphDisplay::mouseReleaseEvent (QMouseEvent *event) 325 | { 326 | if (m_moveInfo.m_action == MakeConnect) { 327 | std::size_t elementId, padId; 328 | getIdByPosition (event->pos (), elementId, padId); 329 | 330 | if (elementId != ((size_t) -1) && padId != ((size_t) -1)) { 331 | ElementInfo infoSrc, infoDst; 332 | const char *srcPad, *dstPad; 333 | srcPad = NULL; 334 | dstPad = NULL; 335 | 336 | for (std::size_t i = 0; i < m_info.size (); i++) { 337 | if (m_info[i].m_id == m_moveInfo.m_elementId) { 338 | infoSrc = m_info[i]; 339 | for (std::size_t j = 0; j < m_info[i].m_pads.size (); j++) { 340 | if (m_info[i].m_pads[j].m_id == m_moveInfo.m_padId) { 341 | srcPad = m_info[i].m_pads[j].m_name.c_str (); 342 | break; 343 | } 344 | } 345 | } 346 | if (m_info[i].m_id == elementId) { 347 | infoDst = m_info[i]; 348 | for (std::size_t j = 0; j < m_info[i].m_pads.size (); j++) { 349 | if (m_info[i].m_pads[j].m_id == padId) { 350 | dstPad = m_info[i].m_pads[j].m_name.c_str (); 351 | break; 352 | } 353 | } 354 | } 355 | 356 | if (srcPad != NULL && dstPad != NULL) 357 | break; 358 | } 359 | if (!infoSrc.m_name.compare (infoDst.m_name)) { 360 | LOG_INFO("infoSrc == infoDst. No need to connect anything"); 361 | goto exit; 362 | 363 | } 364 | 365 | assert(srcPad != NULL && dstPad != NULL); 366 | 367 | LOG_INFO("Connection from %s:%s to %s:%s", infoSrc.m_name.c_str (), srcPad, infoDst.m_name.c_str (), dstPad); 368 | 369 | if (!m_pGraph->Connect (infoSrc.m_name.c_str (), srcPad, 370 | infoDst.m_name.c_str (), dstPad)) { 371 | QString msg; 372 | msg = "Connection "; 373 | msg += QString (infoSrc.m_name.c_str ()) + ":" + srcPad; 374 | msg += " => "; 375 | msg += QString (infoDst.m_name.c_str ()) + ":" + dstPad; 376 | msg += " was FAILED"; 377 | 378 | QMessageBox::warning (this, "Connection failed", msg); 379 | } 380 | 381 | m_info = m_pGraph->GetInfo (); 382 | updateDisplayInfoIds (); 383 | if (g_str_has_prefix (infoDst.m_name.c_str (), "decodebin")) { 384 | m_pGraph->Play (); 385 | LOG_INFO("Launch play to discover the new pad"); 386 | } 387 | } 388 | } 389 | else if (m_moveInfo.m_action == Select) { 390 | std::size_t width = std::abs ( 391 | m_moveInfo.m_position.x () - m_moveInfo.m_startPosition.x ()); 392 | std::size_t height = std::abs ( 393 | m_moveInfo.m_position.y () - m_moveInfo.m_startPosition.y ()); 394 | 395 | if (!m_moveInfo.m_position.isNull () && width * height > 5) { 396 | QRect selectionRect (m_moveInfo.m_startPosition, m_moveInfo.m_position); 397 | for (std::size_t i = 0; i < m_displayInfo.size (); i++) { 398 | if (selectionRect.intersects (m_displayInfo[i].m_rect)) 399 | m_displayInfo[i].m_isSelected = true; 400 | } 401 | } 402 | 403 | repaint (); 404 | } 405 | else if (m_moveInfo.m_action == MoveComponent) { 406 | int dx = event->x () - m_moveInfo.m_startPosition.x (); 407 | int dy = event->y () - m_moveInfo.m_startPosition.y (); 408 | 409 | if (dx == dy && dy == 0) { 410 | for (std::size_t i = 0; i < m_displayInfo.size (); i++) { 411 | if (m_displayInfo[i].m_id == m_moveInfo.m_elementId) { 412 | m_displayInfo[i].m_isSelected = true; 413 | repaint (); 414 | break; 415 | } 416 | } 417 | } 418 | 419 | } 420 | exit: m_moveInfo.m_action = None; 421 | m_moveInfo.m_elementId = -1; 422 | m_moveInfo.m_padId = -1; 423 | m_moveInfo.m_startPosition = QPoint (); 424 | m_moveInfo.m_position = QPoint (); 425 | repaint (); 426 | } 427 | 428 | void 429 | GraphDisplay::mouseMoveEvent (QMouseEvent *event) 430 | { 431 | if (m_moveInfo.m_action == MoveComponent) { 432 | int dx = event->x () - m_moveInfo.m_position.x (); 433 | int dy = event->y () - m_moveInfo.m_position.y (); 434 | 435 | for (std::size_t i = 0; i < m_displayInfo.size (); i++) { 436 | if (m_displayInfo[i].m_id == m_moveInfo.m_elementId) { 437 | QRect newRect = m_displayInfo[i].m_rect; 438 | newRect.adjust (dx, dy, dx, dy); 439 | if (contentsRect ().contains (newRect)) 440 | m_displayInfo[i].m_rect = newRect; 441 | break; 442 | } 443 | } 444 | } 445 | 446 | if (m_moveInfo.m_action != None) { 447 | m_moveInfo.m_position = event->pos (); 448 | repaint (); 449 | } 450 | else { 451 | std::size_t elementId, padId; 452 | getIdByPosition (event->pos (), elementId, padId); 453 | if (padId != ((size_t) -1)) { 454 | ElementInfo* element = getElement (elementId); 455 | PadInfo* pad = getPad (elementId, padId); 456 | QString caps = m_pGraph->getPadCaps (element, pad, PAD_CAPS_ALL, true); 457 | setToolTip (caps); 458 | } 459 | else 460 | setToolTip (""); 461 | } 462 | } 463 | 464 | void 465 | GraphDisplay::keyPressEvent (QKeyEvent* event) 466 | { 467 | if (event->key () == Qt::Key_Delete) 468 | removeSelected (); 469 | 470 | return QWidget::keyPressEvent (event); 471 | } 472 | 473 | void 474 | GraphDisplay::showContextMenu (QMouseEvent *event) 475 | { 476 | QMenu menu; 477 | 478 | std::size_t elementId, padId; 479 | getIdByPosition (event->pos (), elementId, padId); 480 | 481 | GstState state; 482 | GstStateChangeReturn res = gst_element_get_state (m_pGraph->m_pGraph, &state, 483 | NULL, 484 | GST_MSECOND); 485 | 486 | bool isActive = false; 487 | 488 | if (res != GST_STATE_CHANGE_SUCCESS || state == GST_STATE_PAUSED 489 | || state == GST_STATE_PLAYING) 490 | isActive = true; 491 | 492 | int selectedCount = 0; 493 | for (std::size_t i = 0; i < m_displayInfo.size (); i++) 494 | if (m_displayInfo[i].m_isSelected) 495 | selectedCount++; 496 | 497 | if (selectedCount > 1) { 498 | CustomMenuAction *pact = new CustomMenuAction ("Remove selected", &menu); 499 | menu.addAction (pact); 500 | if (isActive) 501 | pact->setDisabled (true); 502 | 503 | } 504 | else if (padId != ((size_t) -1)) { 505 | menu.addAction (new CustomMenuAction ("Render", &menu)); 506 | menu.addAction (new CustomMenuAction ("Render anyway", &menu)); 507 | menu.addAction (new CustomMenuAction ("Pad properties", &menu)); 508 | menu.addAction (new CustomMenuAction ("typefind", "ElementName", &menu)); 509 | } 510 | else if (elementId != ((size_t) -1)) { 511 | menu.addAction (new CustomMenuAction ("Element properties", &menu)); 512 | QAction *pact = new CustomMenuAction ("Remove", &menu); 513 | menu.addAction (pact); 514 | 515 | if (isActive) 516 | pact->setDisabled (true); 517 | 518 | menu.addAction (new CustomMenuAction ("Request pad...", &menu)); 519 | } 520 | else { 521 | for (std::size_t i = 0; i < m_info.size (); i++) { 522 | for (std::size_t j = 0; j < m_info[i].m_connections.size (); j++) { 523 | 524 | QPoint point = getPadPosition (m_info[i].m_id, 525 | m_info[i].m_pads[j].m_id); 526 | 527 | double x1, y1; 528 | x1 = point.x (); 529 | y1 = point.y (); 530 | 531 | if (m_info[i].m_connections[j].m_elementId != ((size_t) -1) 532 | && m_info[i].m_connections[j].m_padId != ((size_t) -1)) { 533 | point = getPadPosition (m_info[i].m_connections[j].m_elementId, 534 | m_info[i].m_connections[j].m_padId); 535 | double x2, y2; 536 | 537 | x2 = point.x (); 538 | y2 = point.y (); 539 | 540 | double dy = y2 - y1; 541 | double dx = x2 - x1; 542 | 543 | double x0 = event->pos ().x (); 544 | double y0 = event->pos ().y (); 545 | 546 | double distance = std::abs ( 547 | (int) (dy * x0 - dx * y0 + x2 * y1 - y2 * x1)); 548 | distance = distance / sqrt (dy * dy + dx * dx); 549 | 550 | if (distance < 5) { 551 | elementId = m_info[i].m_id; 552 | padId = m_info[i].m_pads[j].m_id; 553 | 554 | QAction *pact = new CustomMenuAction ("Disconnect", &menu); 555 | menu.addAction (pact); 556 | 557 | if (isActive) 558 | pact->setDisabled (true); 559 | break; 560 | } 561 | } 562 | } 563 | if (!menu.isEmpty ()) 564 | break; 565 | } 566 | if (menu.isEmpty ()) { 567 | menu.addAction (new CustomMenuAction ("Add plugin", &menu)); 568 | menu.addAction (new CustomMenuAction ("Clear graph", &menu)); 569 | } 570 | } 571 | 572 | if (!menu.isEmpty ()) { 573 | CustomMenuAction *pact = (CustomMenuAction*) menu.exec ( 574 | event->globalPos ()); 575 | if (pact) { 576 | if (pact->getName () == "Remove") 577 | removePlugin (elementId); 578 | else if (pact->getName () == "Element properties") 579 | showElementProperties (elementId); 580 | else if (pact->getName () == "Pad properties") 581 | showPadProperties (elementId, padId); 582 | else if (pact->getName () == "Render") 583 | renderPad (elementId, padId, true); 584 | else if (pact->getName () == "Render anyway") 585 | renderPad (elementId, padId, false); 586 | else if (pact->getName () == "Disconnect") 587 | disconnect (elementId, padId); 588 | else if (pact->getName () == "Request pad...") 589 | requestPad (elementId); 590 | else if (pact->getName () == "Remove selected") 591 | removeSelected (); 592 | else if (pact->getName () == "ElementName") 593 | connectPlugin (elementId, pact->text ()); 594 | else if (pact->getName () == "Add plugin") 595 | addPlugin (); 596 | else if (pact->getName () == "Clear graph") 597 | clearGraph (); 598 | } 599 | } 600 | } 601 | 602 | void 603 | GraphDisplay::removeSelected () 604 | { 605 | GstState state; 606 | GstStateChangeReturn res = gst_element_get_state (m_pGraph->m_pGraph, &state, 607 | NULL, 608 | GST_MSECOND); 609 | 610 | if (res != GST_STATE_CHANGE_SUCCESS || state == GST_STATE_PAUSED 611 | || state == GST_STATE_PLAYING) 612 | return; 613 | 614 | while (true) { 615 | std::size_t i = 0; 616 | for (; i < m_displayInfo.size (); i++) { 617 | if (m_displayInfo[i].m_isSelected) 618 | break; 619 | } 620 | 621 | if (i != m_displayInfo.size ()) 622 | removePlugin (m_displayInfo[i].m_id); 623 | else 624 | break; 625 | } 626 | } 627 | 628 | void 629 | GraphDisplay::addPlugin () 630 | { 631 | emit signalAddPlugin(); 632 | } 633 | 634 | void 635 | GraphDisplay::clearGraph () 636 | { 637 | emit signalClearGraph(); 638 | } 639 | 640 | void 641 | GraphDisplay::removePlugin (std::size_t id) 642 | { 643 | ElementInfo* element = getElement (id); 644 | if (element) { 645 | if (m_pGraph->RemovePlugin (element->m_name.c_str ())) { 646 | std::vector info = m_pGraph->GetInfo (); 647 | update (info); 648 | } 649 | else 650 | QMessageBox::warning ( 651 | this, 652 | "Element removing problem", 653 | "Element `" + QString (element->m_name.c_str ()) 654 | + "` remowing was FAILED"); 655 | } 656 | } 657 | 658 | void 659 | GraphDisplay::connectPlugin (std::size_t elementId, const QString& name) 660 | { 661 | ElementInfo* element = getElement (elementId); 662 | gchar* pluginName = m_pGraph->AddPlugin (name.toStdString ().c_str (), NULL); 663 | m_pGraph->Connect (element->m_name.c_str (), pluginName); 664 | g_free (pluginName); 665 | } 666 | 667 | void 668 | GraphDisplay::showElementProperties (std::size_t id) 669 | { 670 | ElementInfo* element = getElement (id); 671 | if (element) { 672 | ElementProperties *pprops = new ElementProperties ( 673 | m_pGraph, element->m_name.c_str ()); 674 | pprops->setAttribute (Qt::WA_QuitOnClose, false); 675 | pprops->show (); 676 | } 677 | } 678 | 679 | void 680 | GraphDisplay::showPadProperties (std::size_t elementId, std::size_t padId) 681 | { 682 | ElementInfo* element = getElement (elementId); 683 | PadInfo* pad = getPad (elementId, padId); 684 | if (pad) { 685 | PadProperties *pprops = new PadProperties (m_pGraph, 686 | element->m_name.c_str (), 687 | pad->m_name.c_str ()); 688 | pprops->setAttribute (Qt::WA_QuitOnClose, false); 689 | pprops->show (); 690 | } 691 | } 692 | 693 | void 694 | GraphDisplay::renderPad (std::size_t elementId, std::size_t padId, bool capsAny) 695 | { 696 | ElementInfo* element = getElement (elementId); 697 | PadInfo* pad = getPad (elementId, padId); 698 | 699 | if (!element || !pad) 700 | LOG_INFO("element or pad is unreachable"); 701 | 702 | PluginsList* pluginList = new PluginsList (); 703 | GList* plugins_list = pluginList->getSortedByRank (); 704 | GList* l; 705 | 706 | for (l = plugins_list; l != NULL; l = l->next) { 707 | Plugin* plugin = (Plugin*) (l->data); 708 | if (m_pGraph->CanConnect (element->m_name.c_str (), pad->m_name.c_str (), 709 | plugin->getName ().toStdString ().c_str (), 710 | capsAny)) { 711 | gchar* pluginName = m_pGraph->AddPlugin ( 712 | plugin->getName ().toStdString ().c_str (), NULL); 713 | m_pGraph->Connect (element->m_name.c_str (), pluginName); 714 | g_free (pluginName); 715 | break; 716 | } 717 | } 718 | delete pluginList; 719 | } 720 | 721 | void 722 | GraphDisplay::disconnect (size_t elementId, size_t padId) 723 | { 724 | std::string src, dst, srcPad, dstPad; 725 | 726 | for (std::size_t i = 0; i < m_info.size (); i++) { 727 | if (m_info[i].m_id == elementId) { 728 | for (std::size_t j = 0; j < m_info[i].m_pads.size (); j++) { 729 | if (m_info[i].m_pads[j].m_id == padId) { 730 | if (m_info[i].m_pads[j].m_type == PadInfo::Out) { 731 | src = m_info[i].m_name; 732 | srcPad = m_info[i].m_pads[j].m_name.c_str (); 733 | elementId = m_info[i].m_connections[j].m_elementId; 734 | padId = m_info[i].m_connections[j].m_padId; 735 | } 736 | else { 737 | dst = m_info[i].m_name; 738 | dstPad = m_info[i].m_pads[j].m_name.c_str (); 739 | elementId = m_info[i].m_connections[j].m_elementId; 740 | padId = m_info[i].m_connections[j].m_padId; 741 | } 742 | break; 743 | } 744 | } 745 | 746 | break; 747 | } 748 | } 749 | 750 | for (std::size_t i = 0; i < m_info.size (); i++) { 751 | if (m_info[i].m_id == elementId) { 752 | for (std::size_t j = 0; j < m_info[i].m_pads.size (); j++) { 753 | if (m_info[i].m_pads[j].m_id == padId) { 754 | if (m_info[i].m_pads[j].m_type == PadInfo::Out) { 755 | src = m_info[i].m_name; 756 | srcPad = m_info[i].m_pads[j].m_name.c_str (); 757 | } 758 | else { 759 | dst = m_info[i].m_name; 760 | dstPad = m_info[i].m_pads[j].m_name.c_str (); 761 | } 762 | break; 763 | } 764 | } 765 | break; 766 | } 767 | } 768 | 769 | LOG_INFO("Disconnect %s:%s <-> %s,%s", src.c_str (), srcPad.c_str(), dst.c_str (), dstPad.c_str ()); 770 | 771 | if (src.empty () || dst.empty () || srcPad.empty () || dstPad.empty ()) 772 | return; 773 | 774 | m_pGraph->Disconnect (src.c_str (), srcPad.c_str (), dst.c_str (), 775 | dstPad.c_str ()); 776 | 777 | m_info = m_pGraph->GetInfo (); 778 | updateDisplayInfoIds (); 779 | repaint (); 780 | } 781 | 782 | void 783 | GraphDisplay::requestPad (std::size_t elementId) 784 | { 785 | QStringList labels; 786 | labels.push_back ("Template name"); 787 | labels.push_back ("Caps"); 788 | labels.push_back ("Direction"); 789 | 790 | QTableWidget *ptwgt = new QTableWidget (); 791 | ptwgt->setColumnCount (3); 792 | ptwgt->setHorizontalHeaderLabels (labels); 793 | ptwgt->setSelectionBehavior (QAbstractItemView::SelectRows); 794 | ptwgt->setEditTriggers (QAbstractItemView::NoEditTriggers); 795 | 796 | ElementInfo* elementInfo = getElement (elementId); 797 | GstElement *element = NULL; 798 | if (elementInfo) 799 | element = gst_bin_get_by_name (GST_BIN (m_pGraph->m_pGraph), 800 | elementInfo->m_name.c_str ()); 801 | 802 | if (!element) { 803 | QMessageBox::warning (this, "Request pad failed", 804 | "Request pad list obtaining was failed"); 805 | return; 806 | } 807 | 808 | GstElementClass *klass; 809 | klass = GST_ELEMENT_GET_CLASS (element); 810 | 811 | GList *lst = gst_element_class_get_pad_template_list (klass); 812 | 813 | std::size_t k = 0; 814 | while (lst != NULL) { 815 | GstPadTemplate *templ; 816 | templ = (GstPadTemplate *) lst->data; 817 | 818 | if (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_REQUEST) { 819 | ptwgt->setRowCount (k + 1); 820 | ptwgt->setItem ( 821 | k, 0, new QTableWidgetItem (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ))); 822 | 823 | GstCaps *caps = gst_pad_template_get_caps (templ); 824 | gchar *capsStr = gst_caps_to_string (caps); 825 | ptwgt->setItem (k, 1, new QTableWidgetItem (capsStr)); 826 | g_free (capsStr); 827 | gst_caps_unref (caps); 828 | 829 | const char *directionSrc = "SRC"; 830 | const char *directionSink = "SINK"; 831 | const char *directionUnknown = "UNKNOWN"; 832 | 833 | QString direction; 834 | switch (GST_PAD_TEMPLATE_DIRECTION (templ)) { 835 | case GST_PAD_UNKNOWN: 836 | direction = directionUnknown; 837 | break; 838 | 839 | case GST_PAD_SRC: 840 | direction = directionSrc; 841 | break; 842 | 843 | case GST_PAD_SINK: 844 | direction = directionSink; 845 | break; 846 | }; 847 | 848 | ptwgt->setItem (k, 2, new QTableWidgetItem (direction)); 849 | k++; 850 | } 851 | 852 | lst = g_list_next (lst); 853 | } 854 | 855 | qulonglong v ((qulonglong) element); 856 | ptwgt->setProperty ("element", v); 857 | 858 | connect(ptwgt, SIGNAL(cellActivated(int, int)), SLOT(addRequestPad(int, int))); 859 | 860 | ptwgt->setAttribute (Qt::WA_QuitOnClose, false); 861 | ptwgt->show (); 862 | } 863 | 864 | void 865 | GraphDisplay::addRequestPad (int row, int collumn) 866 | { 867 | Q_UNUSED(collumn); 868 | QTableWidget *ptwgt = dynamic_cast (QObject::sender ()); 869 | 870 | qulonglong v = ptwgt->property ("element").toULongLong (); 871 | GstElement *element = (GstElement *) v; 872 | GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); 873 | 874 | GstPadTemplate *templ = gst_element_class_get_pad_template ( 875 | klass, ptwgt->itemAt (row, 0)->text ().toStdString ().c_str ()); 876 | 877 | gst_element_request_pad (element, templ, NULL, NULL); 878 | 879 | gst_object_unref (element); 880 | ptwgt->close (); 881 | 882 | std::vector info = m_pGraph->GetInfo (); 883 | update (info); 884 | 885 | } 886 | 887 | void 888 | GraphDisplay::getIdByPosition (const QPoint &pos, std::size_t &elementId, 889 | std::size_t &padId) 890 | { 891 | std::size_t i = 0; 892 | elementId = padId = -1; 893 | for (; i < m_displayInfo.size (); i++) { 894 | if (elementId != ((size_t) -1)) 895 | break; 896 | 897 | QRect rect (m_displayInfo[i].m_rect.x () - 8, 898 | m_displayInfo[i].m_rect.y () - 6, 899 | m_displayInfo[i].m_rect.width () + 16, 900 | m_displayInfo[i].m_rect.height () + 12); 901 | if (rect.contains (pos)) { 902 | std::size_t j = 0; 903 | for (; j < m_info[i].m_pads.size (); j++) { 904 | QPoint point = getPadPosition (m_displayInfo[i].m_id, 905 | m_info[i].m_pads[j].m_id); 906 | 907 | int xPos, yPos; 908 | xPos = point.x (); 909 | yPos = point.y (); 910 | 911 | xPos -= PAD_SIZE_ACTION / 2; 912 | yPos -= PAD_SIZE_ACTION / 2; 913 | 914 | QRect rect (xPos, yPos, PAD_SIZE_ACTION, PAD_SIZE_ACTION); 915 | if (rect.contains (pos)) { 916 | padId = m_info[i].m_pads[j].m_id; 917 | elementId = m_displayInfo[i].m_id; 918 | break; 919 | } 920 | } 921 | 922 | if (j == m_info[i].m_pads.size ()) { 923 | if (m_displayInfo[i].m_rect.contains (pos)) 924 | elementId = m_displayInfo[i].m_id; 925 | } 926 | } 927 | } 928 | } 929 | 930 | QPoint 931 | GraphDisplay::getPadPosition (std::size_t elementId, std::size_t padId) 932 | { 933 | QPoint res; 934 | if (elementId == ((size_t) -1) || padId == ((size_t) -1)) 935 | return res; 936 | 937 | for (std::size_t i = 0; i < m_displayInfo.size (); i++) { 938 | if (m_displayInfo[i].m_id == elementId) { 939 | int numInPads, numOutPads; 940 | numInPads = numOutPads = 0; 941 | for (std::size_t j = 0; j < m_info[i].m_pads.size (); j++) { 942 | if (m_info[i].m_pads[j].m_type == PadInfo::Out) 943 | numOutPads++; 944 | else if (m_info[i].m_pads[j].m_type == PadInfo::In) 945 | numInPads++; 946 | } 947 | 948 | int inDelta, outDelta, inPos, outPos; 949 | 950 | inDelta = m_displayInfo[i].m_rect.height () / (numInPads + 1); 951 | outDelta = m_displayInfo[i].m_rect.height () / (numOutPads + 1); 952 | 953 | inPos = inDelta; 954 | outPos = outDelta; 955 | for (std::size_t j = 0; j < m_info[i].m_pads.size (); j++) { 956 | int xPos, yPos; 957 | yPos = m_displayInfo[i].m_rect.topRight ().y (); 958 | 959 | if (m_info[i].m_pads[j].m_type == PadInfo::Out) { 960 | xPos = m_displayInfo[i].m_rect.topRight ().x (); 961 | yPos += outPos; 962 | outPos += outDelta; 963 | } 964 | else if (m_info[i].m_pads[j].m_type == PadInfo::In) { 965 | xPos = m_displayInfo[i].m_rect.topLeft ().x (); 966 | yPos += inPos; 967 | inPos += inDelta; 968 | } 969 | 970 | if (m_info[i].m_pads[j].m_id == padId) { 971 | res = QPoint (xPos, yPos); 972 | break; 973 | } 974 | } 975 | 976 | break; 977 | } 978 | } 979 | 980 | return res; 981 | } 982 | -------------------------------------------------------------------------------- /src/GraphDisplay.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPH_DISPLAY_H_ 2 | #define GRAPH_DISPLAY_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "GraphManager.h" 11 | #include 12 | 13 | class GraphDisplay: public QWidget 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | GraphDisplay(QWidget *parent=0, Qt::WindowFlags f=0); 19 | void update(const std::vector &info); 20 | void paintEvent(QPaintEvent *event); 21 | void mousePressEvent(QMouseEvent *event); 22 | void mouseReleaseEvent(QMouseEvent *event); 23 | void mouseMoveEvent(QMouseEvent *event); 24 | 25 | void keyPressEvent(QKeyEvent* event); 26 | 27 | QSharedPointer m_pGraph; 28 | 29 | private slots: 30 | void addRequestPad(int row, int collumn); 31 | 32 | signals: 33 | void signalAddPlugin(); 34 | void signalClearGraph(); 35 | 36 | private: 37 | 38 | enum MoveAction 39 | { 40 | None = 0, 41 | MoveComponent, 42 | MakeConnect, 43 | Select 44 | }; 45 | 46 | struct MoveInfo 47 | { 48 | MoveInfo(): m_action(None) 49 | { 50 | } 51 | 52 | MoveAction m_action; 53 | size_t m_elementId; 54 | size_t m_padId; 55 | QPoint m_position; 56 | QPoint m_startPosition; 57 | }; 58 | 59 | struct ElementDisplayInfo 60 | { 61 | QRect m_rect; 62 | size_t m_id; 63 | std::string m_name; 64 | bool m_isSelected; 65 | }; 66 | 67 | void calculatePositions(); 68 | void updateDisplayInfoIds(); 69 | ElementDisplayInfo calculateOnePosition(const ElementInfo &info); 70 | void showContextMenu(QMouseEvent *event); 71 | void showElementProperties(std::size_t id); 72 | void showPadProperties(std::size_t elementId, std::size_t padId); 73 | void renderPad(std::size_t elementId, std::size_t padId, bool capsAny); 74 | void removePlugin(std::size_t id); 75 | void removeSelected(); 76 | void getIdByPosition(const QPoint &pos, std::size_t &elementId, std::size_t &padId); 77 | QPoint getPadPosition(std::size_t elementId, std::size_t padId); 78 | void disconnect(std::size_t elementId, std::size_t padId); 79 | void requestPad(std::size_t elementId); 80 | void connectPlugin(std::size_t elementId, const QString& destElementName); 81 | void addPlugin(); 82 | void clearGraph(); 83 | 84 | ElementInfo* getElement(std::size_t elementId); 85 | PadInfo* getPad(std::size_t elementId, std::size_t padId); 86 | 87 | std::vector m_info; 88 | std::vector m_displayInfo; 89 | 90 | MoveInfo m_moveInfo; 91 | }; 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /src/GraphManager.cpp: -------------------------------------------------------------------------------- 1 | #include "GraphManager.h" 2 | #include "PluginsList.h" 3 | 4 | #include "MainWindow.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "CustomSettings.h" 11 | 12 | GST_DEBUG_CATEGORY_STATIC(pipeviz_debug); 13 | #define GST_CAT_DEFAULT pipeviz_debug 14 | 15 | #define MAX_STR_CAPS_SIZE 150 16 | gchar* 17 | get_str_caps_limited (gchar* str) 18 | { 19 | gchar* result; 20 | if (strlen (str) > MAX_STR_CAPS_SIZE) { 21 | result = g_strndup (str, MAX_STR_CAPS_SIZE); 22 | for (size_t i = strlen (result) - 1; i > strlen (result) - 4; i--) 23 | result[i] = '.'; 24 | } 25 | else 26 | result = g_strdup (str); 27 | return result; 28 | } 29 | 30 | static void 31 | typefind_have_type_callback (GstElement * typefind, guint probability, 32 | GstCaps * caps, GraphManager * thiz) 33 | { 34 | Q_UNUSED(typefind); 35 | gchar *caps_description = gst_caps_to_string (caps); 36 | GST_DEBUG_OBJECT(thiz, "Found caps %s with probability %d",caps_description, probability); 37 | g_free (caps_description); 38 | thiz->Pause (); 39 | } 40 | 41 | GraphManager::GraphManager () 42 | { 43 | m_pGraph = gst_pipeline_new ("pipeline"); 44 | GST_DEBUG_CATEGORY_INIT(pipeviz_debug, "pipeviz", 0, "Pipeline vizualizer"); 45 | 46 | GST_WARNING("init"); 47 | } 48 | 49 | GraphManager::~GraphManager () 50 | { 51 | } 52 | 53 | QString 54 | GraphManager::getPadCaps (ElementInfo* elementInfo, PadInfo* padInfo, 55 | ePadCapsSubset subset, bool afTruncated) 56 | { 57 | QString padCaps = ""; 58 | if (!elementInfo || !padInfo) 59 | return padCaps; 60 | 61 | GstElement *element = gst_bin_get_by_name (GST_BIN (m_pGraph), 62 | elementInfo->m_name.c_str ()); 63 | 64 | if (!element) 65 | return padCaps; 66 | GstPad *pad = gst_element_get_static_pad (GST_ELEMENT (element), 67 | padInfo->m_name.c_str ()); 68 | if (!pad) { 69 | gst_object_unref (element); 70 | return padCaps; 71 | } 72 | GstCaps *caps; 73 | switch (subset) { 74 | case PAD_CAPS_ALLOWED: 75 | caps = gst_pad_get_allowed_caps (pad); 76 | break; 77 | case PAD_CAPS_NEGOCIATED: 78 | #if GST_VERSION_MAJOR >= 1 79 | caps = gst_pad_get_current_caps(pad); 80 | #else 81 | caps = gst_pad_get_negotiated_caps (pad); 82 | #endif 83 | break; 84 | case PAD_CAPS_ALL: 85 | default: 86 | #if GST_VERSION_MAJOR >= 1 87 | caps = gst_pad_query_caps(pad, NULL); 88 | #else 89 | caps = gst_pad_get_caps (pad); 90 | #endif 91 | } 92 | #if GST_VERSION_MAJOR >= 1 93 | caps = gst_pad_query_caps(pad, NULL); 94 | #else 95 | caps = gst_pad_get_caps (pad); 96 | #endif 97 | if (caps) { 98 | gchar* str = gst_caps_to_string (caps); 99 | if (afTruncated) { 100 | gchar* str_limited = get_str_caps_limited (str); 101 | g_free (str); 102 | padCaps = str_limited; 103 | g_free (str_limited); 104 | } 105 | else { 106 | padCaps = str; 107 | g_free (str); 108 | } 109 | 110 | } 111 | 112 | gst_object_unref (element); 113 | gst_object_unref (pad); 114 | return padCaps; 115 | } 116 | 117 | gchar* 118 | GraphManager::AddPlugin (const char *plugin, const char *name) 119 | { 120 | GstElement *pel = gst_element_factory_make (plugin, name); 121 | if (!pel) 122 | return NULL; 123 | 124 | if (GST_IS_URI_HANDLER (pel)) { 125 | static const gchar * const *protocols; 126 | protocols = gst_uri_handler_get_protocols (GST_URI_HANDLER (pel)); 127 | 128 | bool isFile = false; 129 | 130 | for (std::size_t i = 0; protocols[i] != NULL; i++) { 131 | if (strcmp ("file", protocols[i]) == 0) { 132 | isFile = true; 133 | break; 134 | } 135 | } 136 | 137 | if (isFile) { 138 | QString path; 139 | QString dir = CustomSettings::lastIODirectory (); 140 | 141 | if (gst_uri_handler_get_uri_type (GST_URI_HANDLER (pel)) == GST_URI_SRC) 142 | path = QFileDialog::getOpenFileName (NULL, "Open Media Source File...", 143 | dir); 144 | else 145 | path = QFileDialog::getSaveFileName (NULL, "Open Sink File...", dir); 146 | 147 | if (!path.isEmpty ()) { 148 | gchar *uri = gst_filename_to_uri (path.toStdString ().c_str (), 149 | NULL); 150 | if (uri) { 151 | GST_DEBUG("Set uri: %s", uri); 152 | #if GST_VERSION_MAJOR >= 1 153 | gst_uri_handler_set_uri(GST_URI_HANDLER(pel), uri, NULL); 154 | #else 155 | gst_uri_handler_set_uri (GST_URI_HANDLER (pel), uri); 156 | #endif 157 | g_free (uri); 158 | 159 | QString dir = QFileInfo (path).absoluteDir ().absolutePath (); 160 | CustomSettings::saveLastIODirectory (dir); 161 | } 162 | } 163 | } 164 | else { 165 | QString uri = QInputDialog::getText (NULL, "Uri...", "Uri:"); 166 | 167 | if (!uri.isEmpty ()) { 168 | GST_DEBUG("Set uri: %s", uri.toStdString ().c_str ()); 169 | #if GST_VERSION_MAJOR >= 1 170 | gst_uri_handler_set_uri(GST_URI_HANDLER(pel), uri.toStdString().c_str(), NULL); 171 | #else 172 | gst_uri_handler_set_uri (GST_URI_HANDLER (pel), 173 | uri.toStdString ().c_str ()); 174 | #endif 175 | } 176 | } 177 | } 178 | 179 | bool res = gst_bin_add (GST_BIN (m_pGraph), pel); 180 | if (res) 181 | gst_element_sync_state_with_parent (pel); 182 | else { 183 | gst_object_unref (pel); 184 | return NULL; 185 | } 186 | 187 | return gst_element_get_name (pel); 188 | } 189 | 190 | bool 191 | GraphManager::RemovePlugin (const char *name) 192 | { 193 | GstElement *element = gst_bin_get_by_name (GST_BIN (m_pGraph), name); 194 | 195 | if (!element) 196 | return false; 197 | 198 | bool res = gst_bin_remove (GST_BIN (m_pGraph), element); 199 | gst_object_unref (element); 200 | 201 | return res; 202 | } 203 | 204 | bool 205 | GraphManager::OpenUri (const char *uri, const char *name) 206 | { 207 | #if GST_VERSION_MAJOR >= 1 208 | GstElement *element = gst_element_make_from_uri(GST_URI_SRC, uri, name, NULL); 209 | #else 210 | GstElement *element = gst_element_make_from_uri (GST_URI_SRC, uri, name); 211 | #endif 212 | if (!element) 213 | return false; 214 | 215 | bool res = gst_bin_add (GST_BIN (m_pGraph), element); 216 | if (res) 217 | gst_element_sync_state_with_parent (element); 218 | 219 | return res; 220 | } 221 | 222 | bool 223 | GraphManager::Connect (const char *srcElement, const char *srcPad, 224 | const char *dstElement, const char *dstPad) 225 | { 226 | GstElement *src = gst_bin_get_by_name (GST_BIN (m_pGraph), srcElement); 227 | GstElement *dst = gst_bin_get_by_name (GST_BIN (m_pGraph), dstElement); 228 | 229 | bool res = gst_element_link_pads (src, srcPad, dst, dstPad); 230 | 231 | gboolean seekRes = gst_element_seek_simple (m_pGraph, GST_FORMAT_TIME, 232 | GST_SEEK_FLAG_FLUSH, 0); 233 | Q_UNUSED(seekRes); 234 | gst_object_unref (src); 235 | gst_object_unref (dst); 236 | 237 | return res; 238 | } 239 | 240 | bool 241 | GraphManager::Connect (const char *srcElement, const char *dstElement) 242 | { 243 | 244 | GstElement *src = gst_bin_get_by_name (GST_BIN (m_pGraph), srcElement); 245 | GstElement *dst = gst_bin_get_by_name (GST_BIN (m_pGraph), dstElement); 246 | 247 | bool res = gst_element_link (src, dst); 248 | 249 | gboolean seekRes = gst_element_seek_simple (m_pGraph, GST_FORMAT_TIME, 250 | GST_SEEK_FLAG_FLUSH, 0); 251 | Q_UNUSED(seekRes); 252 | /* add a callback to handle have-type signal */ 253 | if (g_str_has_prefix (dstElement, "typefindelement")) { 254 | g_signal_connect (dst, "have-type", 255 | G_CALLBACK (typefind_have_type_callback), this); 256 | Play (); 257 | } 258 | 259 | gst_object_unref (src); 260 | gst_object_unref (dst); 261 | 262 | return res; 263 | } 264 | 265 | bool 266 | GraphManager::Disconnect (const char *srcElement, const char *srcPad, 267 | const char *dstElement, const char *dstPad) 268 | { 269 | GstElement *src = gst_bin_get_by_name (GST_BIN (m_pGraph), srcElement); 270 | GstElement *dst = gst_bin_get_by_name (GST_BIN (m_pGraph), dstElement); 271 | 272 | gst_element_unlink_pads (src, srcPad, dst, dstPad); 273 | 274 | gst_object_unref (src); 275 | gst_object_unref (dst); 276 | 277 | return true; 278 | } 279 | 280 | std::vector 281 | GraphManager::GetInfo () 282 | { 283 | std::vector res; 284 | 285 | GstIterator *iter; 286 | iter = gst_bin_iterate_elements (GST_BIN (m_pGraph)); 287 | GstElement* element = NULL; 288 | bool done = false; 289 | size_t id = 0; 290 | while (!done) { 291 | #if GST_VERSION_MAJOR >= 1 292 | GValue value = G_VALUE_INIT; 293 | switch (gst_iterator_next (iter, &value)) 294 | { 295 | case GST_ITERATOR_OK: 296 | { 297 | element = GST_ELEMENT(g_value_get_object(&value)); 298 | #else 299 | switch (gst_iterator_next (iter, (gpointer *) &element)) { 300 | case GST_ITERATOR_OK: { 301 | #endif 302 | ElementInfo elementInfo; 303 | elementInfo.m_id = id; 304 | id++; 305 | 306 | gchar *name = gst_element_get_name (element); 307 | elementInfo.m_name = name; 308 | g_free (name); 309 | 310 | GstElementFactory *pfactory = gst_element_get_factory (element); 311 | 312 | elementInfo.m_pluginName = gst_plugin_feature_get_name ( 313 | GST_PLUGIN_FEATURE (pfactory)); 314 | 315 | GstIterator *padItr = gst_element_iterate_pads (element); 316 | bool padDone = FALSE; 317 | std::size_t padId = 0; 318 | GstPad *pad; 319 | while (!padDone) { 320 | #if GST_VERSION_MAJOR >= 1 321 | GValue padVal = G_VALUE_INIT; 322 | switch (gst_iterator_next (padItr, &padVal)) 323 | { 324 | case GST_ITERATOR_OK: 325 | { 326 | pad = GST_PAD(g_value_get_object(&padVal)); 327 | #else 328 | switch (gst_iterator_next (padItr, (gpointer *) &pad)) { 329 | case GST_ITERATOR_OK: { 330 | #endif 331 | PadInfo padInfo; 332 | padInfo.m_id = padId; 333 | 334 | gchar *pad_name = gst_pad_get_name (pad); 335 | padInfo.m_name = pad_name; 336 | g_free (pad_name); 337 | 338 | GstPadDirection direction = gst_pad_get_direction (pad); 339 | if (direction == GST_PAD_SRC) 340 | padInfo.m_type = PadInfo::Out; 341 | else if (direction == GST_PAD_SINK) 342 | padInfo.m_type = PadInfo::In; 343 | else 344 | padInfo.m_type = PadInfo::None; 345 | 346 | elementInfo.m_pads.push_back (padInfo); 347 | #if GST_VERSION_MAJOR >= 1 348 | g_value_reset (&padVal); 349 | #endif 350 | break; 351 | } 352 | case GST_ITERATOR_RESYNC: 353 | case GST_ITERATOR_ERROR: 354 | case GST_ITERATOR_DONE: 355 | padDone = TRUE; 356 | break; 357 | }; 358 | padId++; 359 | } 360 | #if GST_VERSION_MAJOR >= 1 361 | g_value_reset (&value); 362 | #endif 363 | res.push_back (elementInfo); 364 | break; 365 | } 366 | case GST_ITERATOR_DONE: 367 | case GST_ITERATOR_RESYNC: 368 | case GST_ITERATOR_ERROR: { 369 | done = true; 370 | break; 371 | } 372 | }; 373 | } 374 | 375 | gst_iterator_free (iter); 376 | 377 | for (std::size_t i = 0; i < res.size (); i++) { 378 | res[i].m_connections.resize (res[i].m_pads.size ()); 379 | 380 | GstElement *element = gst_bin_get_by_name (GST_BIN (m_pGraph), 381 | res[i].m_name.c_str ()); 382 | 383 | for (std::size_t j = 0; j < res[i].m_pads.size (); j++) { 384 | res[i].m_connections[j].m_elementId = -1; 385 | res[i].m_connections[j].m_padId = -1; 386 | 387 | GstPad *pad = gst_element_get_static_pad ( 388 | element, res[i].m_pads[j].m_name.c_str ()); 389 | GstPad *peerPad = gst_pad_get_peer (pad); 390 | 391 | if (peerPad) { 392 | GstElement *peerElement = GST_ELEMENT (gst_pad_get_parent (peerPad)); 393 | 394 | gchar *peerName = gst_element_get_name (peerElement); 395 | gchar *peerPadName = gst_pad_get_name (peerPad); 396 | 397 | for (std::size_t k = 0; k < res.size (); k++) { 398 | if (res[k].m_name == peerName) { 399 | for (std::size_t l = 0; l < res[k].m_pads.size (); l++) { 400 | if (res[k].m_pads[l].m_name == peerPadName) { 401 | res[i].m_connections[j].m_elementId = res[k].m_id; 402 | res[i].m_connections[j].m_padId = res[k].m_pads[l].m_id; 403 | break; 404 | } 405 | } 406 | } 407 | } 408 | 409 | g_free (peerName); 410 | g_free (peerPadName); 411 | 412 | gst_object_unref (peerPad); 413 | gst_object_unref (peerElement); 414 | } 415 | 416 | gst_object_unref (pad); 417 | } 418 | gst_object_unref (element); 419 | 420 | } 421 | 422 | return res; 423 | } 424 | 425 | bool 426 | GraphManager::Play () 427 | { 428 | GstStateChangeReturn res; 429 | gst_element_set_state (m_pGraph, GST_STATE_PLAYING); 430 | 431 | GstState state; 432 | res = gst_element_get_state (m_pGraph, &state, NULL, GST_SECOND); 433 | 434 | if (res != GST_STATE_CHANGE_SUCCESS) { 435 | gst_element_abort_state (m_pGraph); 436 | GST_WARNING("state changing to Play was FAILED"); 437 | } 438 | 439 | return state == GST_STATE_PLAYING; 440 | } 441 | 442 | bool 443 | GraphManager::Pause () 444 | { 445 | GstStateChangeReturn res; 446 | 447 | GstState state; 448 | gst_element_set_state (m_pGraph, GST_STATE_PAUSED); 449 | res = gst_element_get_state (m_pGraph, &state, NULL, GST_SECOND); 450 | if (res != GST_STATE_CHANGE_SUCCESS) { 451 | gst_element_abort_state (m_pGraph); 452 | GST_WARNING("state changing to Pause was FAILED"); 453 | } 454 | 455 | return state == GST_STATE_PAUSED; 456 | } 457 | 458 | bool 459 | GraphManager::Stop () 460 | { 461 | GstStateChangeReturn res = gst_element_set_state (m_pGraph, GST_STATE_READY); 462 | return res == GST_STATE_CHANGE_SUCCESS; 463 | } 464 | 465 | double 466 | GraphManager::GetPosition () 467 | { 468 | gint64 current, duration; 469 | GstFormat fmt = GST_FORMAT_TIME; 470 | #if GST_VERSION_MAJOR >= 1 471 | if(!gst_element_query_position(m_pGraph, fmt, ¤t)) 472 | return 0; 473 | #else 474 | if (!gst_element_query_position (m_pGraph, &fmt, ¤t)) 475 | return 0; 476 | #endif 477 | 478 | #if GST_VERSION_MAJOR >= 1 479 | if(!gst_element_query_duration(m_pGraph, fmt, &duration)) 480 | return 0; 481 | #else 482 | if (!gst_element_query_duration (m_pGraph, &fmt, &duration)) 483 | return 0; 484 | #endif 485 | 486 | if (duration < 0 || current < 0) 487 | return 0; 488 | 489 | return (double) current / duration; 490 | } 491 | 492 | bool 493 | GraphManager::SetPosition (double pos) 494 | { 495 | GstQuery *query = gst_query_new_seeking (GST_FORMAT_TIME); 496 | GstFormat fmt = GST_FORMAT_TIME; 497 | if (!query) 498 | return false; 499 | 500 | if (!gst_element_query (m_pGraph, query)) 501 | return false; 502 | 503 | gboolean seekable; 504 | gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL); 505 | 506 | gst_query_unref (query); 507 | 508 | if (!seekable) 509 | return false; 510 | 511 | gint64 duration; 512 | 513 | #if GST_VERSION_MAJOR >= 1 514 | if(!gst_element_query_duration(m_pGraph, fmt, &duration)) 515 | return 0; 516 | #else 517 | if (!gst_element_query_duration (m_pGraph, &fmt, &duration)) 518 | return 0; 519 | #endif 520 | 521 | if (duration < 0) 522 | return 0; 523 | 524 | gboolean seekRes = gst_element_seek_simple (m_pGraph, GST_FORMAT_TIME, 525 | GST_SEEK_FLAG_FLUSH, 526 | duration * pos); 527 | 528 | return seekRes; 529 | } 530 | 531 | bool 532 | GraphManager::CanConnect (const char *srcName, const char *srcPadName, 533 | const char *destName, bool noANY) 534 | { 535 | bool ret = false; 536 | bool added = false; 537 | GstElement *dest = NULL; 538 | GstElement *src = NULL; 539 | GstPad* srcPad = NULL; 540 | GstCaps* srcCaps = NULL; 541 | GstElementFactory *destFactory = NULL; 542 | 543 | src = gst_bin_get_by_name (GST_BIN (m_pGraph), srcName); 544 | if (!src) { 545 | GST_DEBUG("Unable to get the src element: %s",srcName); 546 | goto done; 547 | } 548 | 549 | srcPad = gst_element_get_static_pad (src, srcPadName); 550 | if (!srcPad) { 551 | GST_DEBUG("Unable to get the src pad: %s",srcPadName); 552 | goto done; 553 | } 554 | 555 | srcCaps = gst_pad_get_current_caps (srcPad); 556 | if (!srcCaps) { 557 | GST_DEBUG("Unable to get the current caps for pad: %s",srcPadName); 558 | srcCaps = gst_pad_get_pad_template_caps (srcPad); 559 | if (!srcCaps) { 560 | GST_DEBUG("Unable to get the template caps for pad: %s",srcPadName); 561 | goto done; 562 | } 563 | } 564 | 565 | dest = gst_element_factory_make (destName, NULL); 566 | if (!dest) { 567 | GST_DEBUG("Unable to get the dest element: %s",destName); 568 | goto done; 569 | } 570 | 571 | destFactory = gst_element_get_factory (dest); 572 | if (!destFactory) { 573 | GST_DEBUG("Unable to get the factory for dest element %s",destName); 574 | goto done; 575 | } 576 | if (noANY && gst_element_factory_can_sink_any_caps (destFactory, srcCaps)) { 577 | GST_DEBUG("The dest element %s can sink any caps",destName); 578 | goto done; 579 | } 580 | 581 | if (!gst_element_factory_can_sink_all_caps (destFactory, srcCaps)) { 582 | gchar* caps_string = gst_caps_to_string (srcCaps); 583 | GST_DEBUG("The dest element %s can not sink this caps %s",destName, caps_string); 584 | g_free (caps_string); 585 | goto done; 586 | } 587 | 588 | added = gst_bin_add (GST_BIN (m_pGraph), dest); 589 | if (!added) { 590 | GST_DEBUG("Unable to add element %s to the bin", destName); 591 | goto done; 592 | } 593 | 594 | ret = gst_element_link (src, dest); 595 | if (ret) { 596 | GST_INFO("Can link elements src %s with dest %s", GST_OBJECT_NAME (src), GST_OBJECT_NAME (dest)); 597 | gst_element_unlink (src, dest); 598 | } 599 | 600 | done: if (added) { 601 | gst_bin_remove (GST_BIN (m_pGraph), dest); 602 | dest = NULL; 603 | } 604 | if (src) 605 | gst_object_unref (src); 606 | if (srcPad) 607 | gst_object_unref (srcPad); 608 | if (dest) 609 | gst_object_unref (dest); 610 | return ret; 611 | } 612 | -------------------------------------------------------------------------------- /src/GraphManager.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPH_MANAGER_H_ 2 | #define GRAPH_MANAGER_H_ 3 | 4 | #include "Logger.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | class QString; 12 | class PluginsList; 13 | 14 | enum ePadCapsSubset { 15 | PAD_CAPS_ALLOWED = 0, 16 | PAD_CAPS_NEGOCIATED, 17 | PAD_CAPS_ALL 18 | }; 19 | 20 | struct PadInfo 21 | { 22 | public: 23 | enum PadType 24 | { 25 | None, 26 | Out, 27 | In 28 | }; 29 | 30 | size_t m_id; 31 | PadType m_type; 32 | std::string m_name; 33 | 34 | 35 | bool operator == (const PadInfo &obj) const 36 | { 37 | if(this == &obj) 38 | return true; 39 | 40 | if(m_id != obj.m_id) 41 | return false; 42 | 43 | if(m_type != obj.m_type) 44 | return false; 45 | 46 | if(m_name != obj.m_name) 47 | return false; 48 | 49 | return true; 50 | } 51 | }; 52 | 53 | struct ElementInfo 54 | { 55 | struct Connection 56 | { 57 | size_t m_padId; 58 | size_t m_elementId; 59 | 60 | bool operator == (const Connection &obj) const 61 | { 62 | if(this == &obj) 63 | return true; 64 | 65 | if(m_padId != obj.m_padId) 66 | return false; 67 | 68 | if(m_elementId != obj.m_elementId) 69 | return false; 70 | 71 | return true; 72 | } 73 | }; 74 | 75 | 76 | size_t m_id; 77 | std::string m_name; 78 | std::string m_pluginName; 79 | std::vector m_pads; 80 | std::vector m_connections; 81 | }; 82 | 83 | 84 | class GraphManager 85 | { 86 | 87 | public: 88 | GraphManager(); 89 | ~GraphManager(); 90 | 91 | gchar* AddPlugin(const char *plugin, const char *name); 92 | bool RemovePlugin(const char *name); 93 | bool Connect(const char *srcElement, const char *srcPad, 94 | const char *dstElement, const char *dstPad); 95 | bool Connect(const char *srcElement, const char *dstElement); 96 | bool Disconnect(const char *srcElement, const char *srcPad, 97 | const char *dstElement, const char *dstPad); 98 | std::vector GetInfo(); 99 | 100 | bool OpenUri(const char *uri, const char *name); 101 | 102 | double GetPosition(); 103 | bool SetPosition(double); 104 | 105 | bool CanConnect(const char *srcName,const char *srcPadName, const char *destName, bool noANY = true); 106 | 107 | 108 | bool Play(); 109 | bool Pause(); 110 | bool Stop(); 111 | 112 | QString getPadCaps(ElementInfo* elementInfo, PadInfo* padInfo, ePadCapsSubset subset, bool afTruncated = false); 113 | 114 | GstElement *m_pGraph; 115 | }; 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /src/Logger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Logger.cpp 3 | * 4 | * Created on: 31 mai 2017 5 | * Author: scerveau 6 | */ 7 | #include "Logger.h" 8 | 9 | #include "CustomSettings.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | G_LOCK_DEFINE_STATIC(logger); 20 | 21 | static long int getThreadID() { 22 | return quintptr(QThread::currentThreadId()); 23 | } 24 | 25 | void 26 | Logger::configure_logger () 27 | { 28 | QString lastGstDebugString = CustomSettings::lastGstDebugString (); 29 | qputenv(lastGstDebugString.split("=").at(0).toStdString ().c_str(), 30 | lastGstDebugString.split("=").at(1).toLocal8Bit()); 31 | qputenv("GST_DEBUG_NO_COLOR", QByteArray("1")); 32 | qputenv("GST_DEBUG_FILE", QByteArray("gst_pipeviz.txt")); 33 | } 34 | 35 | Logger::Logger() 36 | : QThread(), 37 | m_fExit(false), 38 | m_level(MAX_LOG_LEVEL) 39 | { 40 | } 41 | 42 | Logger& Logger::instance() 43 | { 44 | static Logger instance; 45 | return instance; 46 | } 47 | 48 | void Logger::Quit() 49 | { 50 | m_fExit = true; 51 | wait(); 52 | } 53 | 54 | void Logger::createLog(TimeStampFlag flag, const char* format, ...) 55 | { 56 | // first check if we should add the timestamp before the string 57 | char* new_fmt = NULL; 58 | if (flag == Logger::UseTimeStamp) { 59 | QString szTimestamp(QTime::currentTime().toString("HH:mm:ss:zzz")); 60 | 61 | const char* fmt_template = "%s %d 0x%x %s"; 62 | int len = snprintf(NULL, 0, fmt_template, szTimestamp.toStdString().c_str(), getThreadID(), std::this_thread::get_id(), format); 63 | if (len < 0) { 64 | // cannot parse the string 65 | return; 66 | } 67 | len++; // add 1 byte for the additional terminating null character 68 | new_fmt = static_cast(malloc(sizeof(char) * len)); 69 | snprintf(new_fmt, len, fmt_template, szTimestamp.toStdString().c_str(), getThreadID(), std::this_thread::get_id(), format); 70 | } 71 | 72 | // create the actual string (timestamp + format...) 73 | const char* formatString = new_fmt ? new_fmt : format; 74 | assert(formatString); 75 | 76 | va_list aptr; 77 | va_start(aptr, format); 78 | va_list argcopy; // copy the va_list and use vsnprintf to get the length of the final string 79 | va_copy(argcopy, aptr); 80 | int len = vsnprintf(NULL, 0, formatString , argcopy); 81 | va_end(argcopy); 82 | if (len < 0) { 83 | // cannot parse the string 84 | g_free(new_fmt); 85 | va_end(aptr); 86 | return; 87 | } 88 | 89 | len++; // add 1 byte for the additional terminating null character. 90 | char* buffer = static_cast(malloc(sizeof(char) * len)); 91 | int actualLen = vsprintf(buffer, formatString, aptr); 92 | va_end(aptr); 93 | formatString = 0; 94 | g_free (new_fmt); 95 | if (actualLen < 0) { 96 | g_free (buffer); 97 | return; 98 | } 99 | // if lengths are different, our code is bugged 100 | assert((actualLen + 1) == len); 101 | 102 | // dump the final string 103 | G_LOCK(logger); 104 | processLog(buffer); 105 | G_UNLOCK(logger); 106 | 107 | // free the buffer and flush the logs 108 | g_free(buffer); 109 | } 110 | 111 | void Logger::incrementLogLevel() { 112 | m_level++; 113 | if (m_level > MAX_LOG_LEVEL) 114 | m_level = MAX_LOG_LEVEL; 115 | char buffer[32]; 116 | sprintf(buffer,"logger log level %d\n",m_level); 117 | processLog(buffer); 118 | } 119 | 120 | void Logger::processLog(const QString& line) 121 | { 122 | emit sendLog(line,eLOG_CATEGORY_INTERNAL); 123 | } 124 | 125 | //#define GST_TIME_FORMAT "u:%02u:%02u.%09u" 126 | #define GST_TIME_FORMAT "%s" 127 | #define PRINT_FMT " " PID_FMT " " PTR_FMT " %s " CAT_FMT " %s\n" 128 | 129 | struct GSTLog { 130 | gchar* date; 131 | gchar* pid; 132 | gchar* level; 133 | gchar* category; 134 | gchar* file; 135 | gchar* line; 136 | gchar* function; 137 | gchar* obj; 138 | gchar* message; 139 | }; 140 | 141 | void Logger::processGstLog(gchar* log) 142 | { 143 | // GSTLog gstLog; 144 | if(!g_strrstr(log,"WARN") && !g_strrstr(log,"ERROR")) 145 | return; 146 | /* GList* stringList = parseGstLine(log, ' ', 6); 147 | GList* l; 148 | 149 | gstLog.date = g_strdup((gchar*)g_list_nth_data(stringList, 0)); 150 | gstLog.pid = g_strdup((gchar*)g_list_nth_data(stringList, 1)); 151 | gstLog.level = g_strdup((gchar*)g_list_nth_data(stringList, 2)); 152 | gstLog.category = g_strdup((gchar*)g_list_nth_data(stringList, 3)); 153 | 154 | gstLog.file = g_strdup((gchar*)g_list_nth_data(stringList, 4)); 155 | gstLog.line = g_strdup((gchar*)g_list_nth_data(stringList, 5)); 156 | gstLog.function = g_strdup((gchar*)g_list_nth_data(stringList, 6)); 157 | gstLog.obj = g_strdup((gchar*)g_list_nth_data(stringList, 7)); 158 | 159 | QString message = ""; 160 | for (l = stringList; l != NULL; l = l->next) { 161 | message += (gchar*)l->data; 162 | message += " "; 163 | } 164 | */ 165 | emit sendLog(log,eLOG_CATEGORY_GST); 166 | } 167 | 168 | #define MAX_LINE_LENGTH 128 169 | 170 | GList* Logger::parseGstLine(gchar* line, gchar delimiter, int max_fields) 171 | { 172 | int count = 0; 173 | size_t pos = 0; 174 | char *lineBuffer = NULL; 175 | 176 | gboolean new_string=TRUE; 177 | GList* strList = NULL; 178 | gint n_fields = 0; 179 | 180 | int maximumLineLength = MAX_LINE_LENGTH; 181 | 182 | if (line == NULL) 183 | return NULL; 184 | 185 | do { 186 | if (new_string) { 187 | if (line[pos] != delimiter) { 188 | new_string = FALSE; 189 | lineBuffer = (gchar *)malloc(sizeof(char) * maximumLineLength); 190 | count=0; 191 | lineBuffer[count++] = line[pos]; 192 | } 193 | } 194 | else { 195 | if (count == MAX_LINE_LENGTH) { 196 | maximumLineLength += MAX_LINE_LENGTH; 197 | lineBuffer = (gchar*)realloc(lineBuffer, maximumLineLength); 198 | } 199 | if (line[pos] == delimiter || line[pos] == '\0') { 200 | lineBuffer[count] = '\0'; 201 | new_string= TRUE; 202 | strList = g_list_append(strList, g_strdup(lineBuffer)); 203 | g_free(lineBuffer); 204 | n_fields++; 205 | if (max_fields != -1 && n_fields == max_fields) { 206 | pos++; 207 | if (pos < strlen(line)) 208 | strList = g_list_append(strList, g_strdup(&line[pos])); 209 | break; 210 | } 211 | } else { 212 | lineBuffer[count++] = line[pos]; 213 | } 214 | 215 | } 216 | pos++; 217 | } while (pos < strlen(line)); 218 | 219 | 220 | return strList; 221 | } 222 | 223 | gchar *Logger::readGstLine(FILE *file) 224 | { 225 | int count = 0; 226 | char ch; 227 | char *line = NULL; 228 | gchar *lineBuffer = NULL; 229 | int maximumLineLength = MAX_LINE_LENGTH; 230 | 231 | if (file == NULL) 232 | goto beach; 233 | 234 | lineBuffer = (gchar *)malloc(sizeof(char) * maximumLineLength); 235 | if (lineBuffer == NULL) 236 | goto beach; 237 | 238 | 239 | ch = getc(file); 240 | while ((ch != '\n') && (ch != EOF)) { 241 | if (count == maximumLineLength) { 242 | maximumLineLength += MAX_LINE_LENGTH; 243 | lineBuffer = (gchar*)realloc(lineBuffer, maximumLineLength); 244 | if (lineBuffer == NULL) 245 | goto beach; 246 | } 247 | lineBuffer[count] = ch; 248 | count++; 249 | 250 | ch = getc(file); 251 | } 252 | if(count==0) 253 | goto beach; 254 | 255 | lineBuffer[count] = '\0'; 256 | lineBuffer = (gchar*)realloc(lineBuffer, count + 1); 257 | line = g_strdup(lineBuffer); 258 | //qDebug() << lineBuffer; 259 | //qDebug() << line; 260 | beach: 261 | g_free(lineBuffer); 262 | return line; 263 | } 264 | 265 | void Logger::run() 266 | { 267 | FILE* file; 268 | gchar* line; 269 | 270 | file = fopen("gst_pipeviz.txt", "r"); 271 | if(!file) 272 | return; 273 | 274 | while (!m_fExit) { 275 | line = readGstLine(file); 276 | if (line != NULL) { 277 | processGstLog(line); 278 | g_free(line); 279 | } else 280 | usleep(10000); 281 | } 282 | 283 | qDebug() << "Logger closed"; 284 | 285 | fclose(file); 286 | 287 | } 288 | -------------------------------------------------------------------------------- /src/Logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Logger.h 3 | * 4 | * Created on: 31 mai 2017 5 | * Author: scerveau 6 | */ 7 | 8 | #ifndef LOGGER_H_ 9 | #define LOGGER_H_ 10 | 11 | #include 12 | #include "glib.h" 13 | 14 | enum eLogCategory { 15 | eLOG_CATEGORY_INTERNAL, 16 | eLOG_CATEGORY_GST, 17 | eLOG_CATEGORY_EVENTS, 18 | }; 19 | 20 | #ifndef LOG_LEVEL 21 | # define LOG_LEVEL 1 22 | #endif 23 | 24 | #define MAX_LOG_LEVEL 3 25 | 26 | #ifndef CLASS_LOGGER_TAG 27 | # define CLASS_LOGGER_TAG __BASE_FILE__ 28 | #endif 29 | 30 | 31 | class Logger : public QThread { 32 | Q_OBJECT 33 | public: 34 | enum TimeStampFlag { 35 | UseTimeStamp = 0, 36 | NoTimeStamp, 37 | }; 38 | 39 | Logger(); 40 | void configure_logger(); 41 | 42 | static Logger& instance(); 43 | void Quit(); 44 | 45 | void processLog(const QString& line); 46 | void createLog(Logger::TimeStampFlag, const char* format, ...); 47 | int getLevel() const { return m_level; } 48 | void incrementLogLevel(); 49 | 50 | protected: 51 | void processGstLog(gchar* line); 52 | gchar* readGstLine(FILE *file); 53 | GList* parseGstLine(gchar* line, gchar ch = ' ', int max_field = -1); 54 | 55 | signals: 56 | void sendLog(const QString& message, int category); 57 | 58 | private: 59 | void run(); 60 | bool m_fExit; 61 | int m_level; 62 | }; 63 | 64 | #define log_debug(LEVEL,TYPE,FMT, ARGS...) do { \ 65 | if (LEVEL <= Logger::instance().getLevel()) \ 66 | Logger::instance().createLog(Logger::UseTimeStamp, "" TYPE ": " FMT "", ## ARGS); \ 67 | } while (0) 68 | #define log_debug_no_time_stderr(LEVEL,TYPE,FMT, ARGS...) do { \ 69 | Logger::instance().createLog(Logger::NoTimeStamp, "" FMT "", ## ARGS); \ 70 | } while (0) 71 | 72 | #define LOG_IMPORTANT(FMT, ARGS...) log_debug_no_time_stderr(0, "",FMT ,## ARGS) 73 | 74 | #ifndef DISABLE_LOG 75 | # define LOG_ERROR(FMT, ARGS...) log_debug(0, "ERROR\t" CLASS_LOGGER_TAG ":%d:%s", FMT,__LINE__, __func__, ## ARGS) 76 | # define LOG_WARNING(FMT, ARGS...) log_debug(1, "WARN\t" CLASS_LOGGER_TAG ":%d:%s", FMT, __LINE__, __func__, ## ARGS) 77 | # define LOG_INFO(FMT, ARGS...) log_debug(2, "INFO\t" CLASS_LOGGER_TAG ":%d:%s", FMT, __LINE__, __func__, ## ARGS) 78 | # define LOG_DEBUG(FMT, ARGS...) log_debug(3, "DEBUG\t" CLASS_LOGGER_TAG ":%d:%s", FMT, __LINE__, __func__, ## ARGS) 79 | #else 80 | # define LOG_ERROR(FMT, ARGS...) 81 | # define LOG_WARNING(FMT, ARGS...) 82 | # define LOG_INFO(FMT, ARGS...) 83 | # define LOG_DEBUG(FMT, ARGS...) 84 | #endif 85 | 86 | #endif /* LOGGER_H_ */ 87 | -------------------------------------------------------------------------------- /src/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.h" 2 | 3 | #include "PluginsList.h" 4 | #include "FavoritesList.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "CustomSettings.h" 27 | #include "GraphDisplay.h" 28 | #include "PipelineIE.h" 29 | #include "SeekSlider.h" 30 | 31 | #include "version_info.h" 32 | 33 | #include 34 | 35 | MainWindow::MainWindow (QWidget *parent, Qt::WindowFlags flags) 36 | : QMainWindow (parent, flags), 37 | m_pGraph (new GraphManager) 38 | { 39 | QToolBar *ptb = addToolBar ("Menu"); 40 | 41 | QAction *pactAdd = ptb->addAction ("Add..."); 42 | pactAdd->setShortcut (QKeySequence ("Ctrl+F")); 43 | connect (pactAdd, SIGNAL (triggered ()), SLOT (AddPlugin ())); 44 | 45 | QAction *pactOpenFile = ptb->addAction ("Open Media File..."); 46 | connect (pactOpenFile, SIGNAL (triggered ()), SLOT (OpenMediaFile ())); 47 | 48 | ptb->addSeparator (); 49 | 50 | QPixmap pxPlay (24, 24); 51 | pxPlay.fill (QColor (0, 0, 0, 0)); 52 | QPainter pntrPlay (&pxPlay); 53 | pntrPlay.setPen (Qt::darkGreen); 54 | pntrPlay.setBrush (QBrush (Qt::darkGreen)); 55 | 56 | QPolygon polygon (3); 57 | polygon.setPoint (0, 4, 4); 58 | polygon.setPoint (1, 4, 20); 59 | polygon.setPoint (2, 20, 12); 60 | 61 | pntrPlay.drawPolygon (polygon, Qt::WindingFill); 62 | 63 | QAction *pactPlay = ptb->addAction (QIcon (pxPlay), "Play"); 64 | connect (pactPlay, SIGNAL (triggered ()), SLOT (Play ())); 65 | 66 | QPixmap pxPause (24, 24); 67 | pxPause.fill (QColor (0, 0, 0, 0)); 68 | QPainter pntrPause (&pxPause); 69 | pntrPause.setPen (Qt::darkGray); 70 | pntrPause.setBrush (QBrush (Qt::darkGray)); 71 | 72 | pntrPause.drawRect (8, 4, 3, 16); 73 | pntrPause.drawRect (13, 4, 3, 16); 74 | 75 | QAction *pactPause = ptb->addAction (QIcon (pxPause), "Pause"); 76 | connect (pactPause, SIGNAL (triggered ()), SLOT (Pause ())); 77 | 78 | QPixmap pxStop (24, 24); 79 | pxStop.fill (QColor (0, 0, 0, 0)); 80 | QPainter pntrStop (&pxStop); 81 | pntrStop.setPen (Qt::darkRed); 82 | pntrStop.setBrush (QBrush (Qt::darkRed)); 83 | 84 | pntrStop.drawRect (6, 6, 12, 12); 85 | 86 | QAction *pactStop = ptb->addAction (QIcon (pxStop), "Stop"); 87 | connect (pactStop, SIGNAL (triggered ()), SLOT (Stop ())); 88 | 89 | QPixmap pxFulsh (24, 24); 90 | pxFulsh.fill (QColor (0, 0, 0, 0)); 91 | QPainter pntrFlush (&pxFulsh); 92 | pntrFlush.setPen (Qt::darkGreen); 93 | pntrFlush.setBrush (QBrush (Qt::darkGreen)); 94 | 95 | pntrFlush.drawRect (3, 4, 3, 16); 96 | 97 | polygon = QPolygon (3); 98 | polygon.setPoint (0, 9, 4); 99 | polygon.setPoint (1, 9, 20); 100 | polygon.setPoint (2, 21, 12); 101 | 102 | pntrFlush.drawPolygon (polygon, Qt::WindingFill); 103 | 104 | QAction *pactFlush = ptb->addAction (QIcon (pxFulsh), "Flush"); 105 | connect (pactFlush, SIGNAL (triggered ()), SLOT (Flush ())); 106 | 107 | QAction *pactClear = ptb->addAction ("Clear"); 108 | connect (pactClear, SIGNAL (triggered ()), SLOT (ClearGraph ())); 109 | ptb->addSeparator (); 110 | 111 | m_pslider = new SeekSlider (); 112 | m_pslider->setOrientation (Qt::Horizontal); 113 | m_pslider->setRange (0, 10000); 114 | m_pslider->setTracking (false); 115 | 116 | connect(m_pslider, SIGNAL(valueChanged(int)), SLOT(Seek(int))); 117 | ptb->addWidget (m_pslider); 118 | 119 | m_menu = menuBar ()->addMenu ("&File"); 120 | 121 | QAction *pactOpen = m_menu->addAction ("Open...", this, SLOT (Open ()), 122 | QKeySequence::Open); 123 | addAction (pactOpen); 124 | 125 | QAction *pactOpenMediaFile = m_menu->addAction ("Open Media File...", this, 126 | SLOT (OpenMediaFile ()), 127 | QKeySequence::Open); 128 | addAction (pactOpenMediaFile); 129 | 130 | QAction *pactSave = m_menu->addAction ("Save", this, SLOT (Save ()), 131 | QKeySequence::Save); 132 | addAction (pactSave); 133 | 134 | QAction *pactSaveAs = m_menu->addAction ("Save As...", this, SLOT (SaveAs ()), 135 | QKeySequence::SaveAs); 136 | addAction (pactSaveAs); 137 | 138 | m_menu->addSeparator (); 139 | m_menu->addAction ("Exit", this, SLOT (close ())); 140 | 141 | m_menu = menuBar ()->addMenu ("&Graph"); 142 | 143 | m_menu->addAction (pactAdd); 144 | m_menu->addAction (pactOpenMediaFile); 145 | m_menu->addAction ("Open Media Uri...", this, SLOT (OpenMediaUri ())); 146 | m_menu->addSeparator (); 147 | m_menu->addAction (pactPlay); 148 | m_menu->addAction (pactPause); 149 | m_menu->addAction (pactStop); 150 | m_menu->addAction (pactFlush); 151 | m_menu->addSeparator (); 152 | m_menu->addAction (pactClear); 153 | 154 | m_menu = menuBar ()->addMenu ("&Help"); 155 | 156 | m_menu->addAction ("About pipeviz...", this, SLOT (About ())); 157 | 158 | m_pGraphDisplay = new GraphDisplay(this); 159 | connect(m_pGraphDisplay, SIGNAL(signalAddPlugin()), 160 | this, SLOT(AddPlugin())); 161 | connect(m_pGraphDisplay, SIGNAL(signalClearGraph()), 162 | this, SLOT(ClearGraph())); 163 | 164 | QScrollArea *pscroll = new QScrollArea; 165 | pscroll->setWidget (m_pGraphDisplay); 166 | pscroll->setWidgetResizable (false); 167 | m_pGraphDisplay->resize (10000, 10000); 168 | m_pGraphDisplay->m_pGraph = m_pGraph; 169 | setCentralWidget (pscroll); 170 | m_pstatusBar = new QStatusBar; 171 | setStatusBar (m_pstatusBar); 172 | m_pluginListDlg = new PluginsListDialog (this); 173 | m_pluginListDlg->setModal (false); 174 | 175 | connect(m_pluginListDlg, SIGNAL(signalAddPluginToFav(const QString&)), 176 | this, SLOT(AddPluginToFavorites(const QString&))); 177 | connect(m_pluginListDlg, SIGNAL(signalRemPluginToFav(const QString&)), 178 | this, SLOT(RemovePluginToFavorites(const QString&))); 179 | 180 | 181 | restoreGeometry (CustomSettings::mainWindowGeometry ()); 182 | createDockWindows(); 183 | 184 | Logger::instance().start(); 185 | connect(&Logger::instance(), SIGNAL(sendLog(const QString &, int)), 186 | this, SLOT(InsertLogLine(const QString &, int))); 187 | 188 | LOG_INFO("Mainwindow is now initialized"); 189 | 190 | startTimer (100); 191 | } 192 | 193 | void MainWindow::createDockWindows() 194 | { 195 | /* create the log list window */ 196 | QDockWidget *dock = new QDockWidget(tr("logs"), this); 197 | dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); 198 | m_logList = new QListWidget(dock); 199 | dock->setWidget(m_logList); 200 | addDockWidget(Qt::BottomDockWidgetArea, dock); 201 | m_menu->addAction(dock->toggleViewAction()); 202 | 203 | /*create the favorite list window */ 204 | dock = new QDockWidget(tr("favorite list"), this); 205 | dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); 206 | m_favoriteList = new FavoritesList(dock); 207 | dock->setWidget(m_favoriteList); 208 | addDockWidget(Qt::RightDockWidgetArea, dock); 209 | m_menu->addAction(dock->toggleViewAction()); 210 | connect(m_favoriteList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), 211 | this, SLOT(onFavoriteListItemDoubleClicked(QListWidgetItem*))); 212 | m_favoriteList->setContextMenuPolicy(Qt::CustomContextMenu); 213 | connect(m_favoriteList,SIGNAL(customContextMenuRequested(const QPoint &)), 214 | this,SLOT(ProvideContextMenu(const QPoint &))); 215 | 216 | } 217 | FavoritesList* MainWindow::getFavoritesList() 218 | { 219 | return m_favoriteList; 220 | } 221 | 222 | void MainWindow::AddPluginToFavorites(const QString& plugin_name) 223 | { 224 | m_favoriteList->addFavorite(plugin_name); 225 | } 226 | 227 | void MainWindow::RemovePluginToFavorites(const QString& plugin_name) 228 | { 229 | m_favoriteList->removeFavorite(plugin_name); 230 | } 231 | 232 | void MainWindow::onFavoriteListItemDoubleClicked(QListWidgetItem* pitem) 233 | { 234 | LOG_INFO("onFavoriteListItemDoubleClicked: %s", pitem->text().toStdString ().c_str ()); 235 | if (!m_pGraph 236 | || !m_pGraph->AddPlugin (pitem->text ().toStdString ().c_str (), NULL)) { 237 | QMessageBox::warning ( 238 | this, "Plugin addition problem", 239 | "Plugin `" + pitem->text () + "` insertion was FAILED"); 240 | LOG_INFO("Plugin '%s' insertion FAILED", pitem->text ().toStdString ().c_str ()); 241 | return; 242 | } 243 | } 244 | 245 | void MainWindow::ProvideContextMenu(const QPoint &pos) 246 | { 247 | QPoint item = m_favoriteList->mapToGlobal(pos); 248 | QMenu submenu; 249 | submenu.addAction("Delete"); 250 | QAction* rightClickItem = submenu.exec(item); 251 | if (rightClickItem && rightClickItem->text().contains("Delete") ) { 252 | LOG_INFO("Delete item: %s", m_favoriteList->currentItem()->text().toStdString ().c_str ()); 253 | RemovePluginToFavorites(m_favoriteList->currentItem()->text()); 254 | } 255 | } 256 | 257 | void MainWindow::InsertLogLine(const QString& line, int category) 258 | { 259 | QListWidgetItem* pItem =new QListWidgetItem(line); 260 | switch(category) { 261 | case eLOG_CATEGORY_INTERNAL: 262 | pItem->setForeground(Qt::blue); 263 | break; 264 | case eLOG_CATEGORY_GST: 265 | pItem->setForeground(Qt::red); 266 | break; 267 | default: 268 | pItem->setForeground(Qt::black); 269 | } 270 | m_logList->addItem(pItem); 271 | } 272 | 273 | MainWindow& MainWindow::instance() 274 | { 275 | static MainWindow instance; 276 | return instance; 277 | } 278 | 279 | MainWindow::~MainWindow () 280 | { 281 | CustomSettings::saveMainWindowGeometry (saveGeometry ()); 282 | Logger::instance().Quit(); 283 | delete m_pluginListDlg; 284 | } 285 | 286 | void 287 | MainWindow::AddPlugin () 288 | { 289 | m_pluginListDlg->setGraph (m_pGraph.data ()); 290 | 291 | m_pluginListDlg->raise (); 292 | m_pluginListDlg->show (); 293 | std::vector info = m_pGraph->GetInfo (); 294 | m_pGraphDisplay->update (info); 295 | } 296 | 297 | void 298 | MainWindow::OpenMediaFile () 299 | { 300 | QString dir = CustomSettings::lastIODirectory (); 301 | 302 | QString path = QFileDialog::getOpenFileName (this, "Open File...", dir); 303 | if (!path.isEmpty ()) { 304 | gchar *uri = gst_filename_to_uri (path.toStdString ().c_str (), NULL); 305 | if (uri) { 306 | LOG_INFO("Open Source file: %s", path.toStdString ().c_str ()); 307 | 308 | m_pGraph->OpenUri (uri, NULL); 309 | g_free (uri); 310 | 311 | std::vector info = m_pGraph->GetInfo (); 312 | m_pGraphDisplay->update (info); 313 | 314 | QString dir = QFileInfo (path).absoluteDir ().absolutePath (); 315 | CustomSettings::saveLastIODirectory (dir); 316 | } 317 | } 318 | } 319 | 320 | void 321 | MainWindow::OpenMediaUri () 322 | { 323 | QString uri = QInputDialog::getText (this, "Open Uri...", "Uri:"); 324 | 325 | if (!uri.isEmpty ()) { 326 | LOG_INFO("Open uri: %s", uri.toStdString ().c_str ()); 327 | m_pGraph->OpenUri (uri.toStdString ().c_str (), NULL); 328 | 329 | std::vector info = m_pGraph->GetInfo (); 330 | m_pGraphDisplay->update (info); 331 | } 332 | } 333 | 334 | void 335 | MainWindow::Play () 336 | { 337 | LOG_INFO( "Play"); 338 | m_pGraph->Play (); 339 | } 340 | 341 | void 342 | MainWindow::Pause () 343 | { 344 | LOG_INFO("Pause"); 345 | m_pGraph->Pause (); 346 | } 347 | 348 | void 349 | MainWindow::Stop () 350 | { 351 | LOG_INFO("Stop"); 352 | m_pGraph->Stop (); 353 | } 354 | 355 | void 356 | MainWindow::Flush () 357 | { 358 | LOG_INFO("Flush"); 359 | 360 | if (m_pGraph->m_pGraph) { 361 | gst_element_send_event (GST_ELEMENT (m_pGraph->m_pGraph), 362 | gst_event_new_flush_start ()); 363 | #if GST_VERSION_MAJOR >= 1 364 | gst_element_send_event(GST_ELEMENT(m_pGraph -> m_pGraph), gst_event_new_flush_stop(true)); 365 | #else 366 | gst_element_send_event (GST_ELEMENT (m_pGraph->m_pGraph), 367 | gst_event_new_flush_stop ()); 368 | #endif 369 | } 370 | } 371 | 372 | void 373 | MainWindow::ClearGraph () 374 | { 375 | LOG_INFO("ClearGraph"); 376 | PipelineIE::Clear (m_pGraph); 377 | } 378 | 379 | void 380 | MainWindow::Seek (int val) 381 | { 382 | if (m_pGraph->SetPosition ((double) (val) / m_pslider->maximum ())) 383 | LOG_INFO("Seek to %d", val); 384 | else 385 | LOG_INFO("Seek FAILED"); 386 | } 387 | 388 | void 389 | MainWindow::timerEvent (QTimerEvent *) 390 | { 391 | GstState state; 392 | GstStateChangeReturn res = gst_element_get_state (m_pGraph->m_pGraph, &state, 393 | NULL, 394 | GST_MSECOND); 395 | 396 | if (res == GST_STATE_CHANGE_SUCCESS) { 397 | QString str; 398 | switch (state) { 399 | case GST_STATE_VOID_PENDING: 400 | str = "Pending"; 401 | break; 402 | case GST_STATE_NULL: 403 | str = "Null"; 404 | break; 405 | case GST_STATE_READY: 406 | str = "Ready"; 407 | break; 408 | case GST_STATE_PAUSED: 409 | str = "Paused"; 410 | break; 411 | case GST_STATE_PLAYING: 412 | str = "Playing"; 413 | break; 414 | }; 415 | 416 | m_pstatusBar->showMessage (str); 417 | } 418 | else { 419 | m_pstatusBar->showMessage ( 420 | QString (gst_element_state_change_return_get_name (res))); 421 | } 422 | 423 | double pos = m_pGraph->GetPosition (); 424 | 425 | if (m_pslider->value () != (int) (m_pslider->maximum () * pos)) 426 | m_pslider->setSliderPosition (m_pslider->maximum () * pos); 427 | 428 | m_pGraphDisplay->update (m_pGraph->GetInfo ()); 429 | } 430 | 431 | void 432 | MainWindow::Save () 433 | { 434 | if (m_fileName.isEmpty ()) 435 | SaveAs (); 436 | else { 437 | QFileInfo fileInfo (m_fileName); 438 | if (fileInfo.completeSuffix ().isEmpty () 439 | || fileInfo.completeSuffix () != "gpi") 440 | m_fileName = m_fileName + ".gpi"; 441 | 442 | PipelineIE::Export (m_pGraph, m_fileName); 443 | 444 | } 445 | } 446 | 447 | void 448 | MainWindow::SaveAs () 449 | { 450 | QString dir = CustomSettings::lastIODirectory (); 451 | 452 | QString path = QFileDialog::getSaveFileName (this, "Save As...", dir, 453 | tr ("*.gpi")); 454 | 455 | if (!path.isEmpty ()) { 456 | m_fileName = path; 457 | Save (); 458 | 459 | QString dir = QFileInfo (path).absoluteDir ().absolutePath (); 460 | CustomSettings::saveLastIODirectory (dir); 461 | } 462 | } 463 | 464 | void 465 | MainWindow::Open () 466 | { 467 | QString dir = CustomSettings::lastIODirectory (); 468 | 469 | QString path = QFileDialog::getOpenFileName ( 470 | this, "Open...", dir, tr ("GPI (*.gpi *.xpm);;All files (*.*)")); 471 | 472 | if (!path.isEmpty ()) { 473 | if (PipelineIE::Import (m_pGraph, path)) 474 | m_fileName = path; 475 | 476 | QString dir = QFileInfo (path).absoluteDir ().absolutePath (); 477 | CustomSettings::saveLastIODirectory (dir); 478 | } 479 | } 480 | 481 | void 482 | MainWindow::About () 483 | { 484 | QString message; 485 | message = "
pipeviz

"; 486 | message = "
virinext@gmail.com

"; 487 | message += QString ("
Version: ") + VERSION_STR + "

"; 488 | message += "
GUI Based on Qt
"; 489 | message += "
using "; 490 | message += gst_version_string (); 491 | message += "
"; 492 | 493 | QMessageBox::about (this, "About", message); 494 | } 495 | -------------------------------------------------------------------------------- /src/MainWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_WINDOW_H_ 2 | #define MAIN_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "GraphManager.h" 17 | #include "Logger.h" 18 | 19 | class GraphDisplay; 20 | class PluginsListDialog; 21 | class FavoritesList; 22 | 23 | class MainWindow: public QMainWindow 24 | { 25 | Q_OBJECT 26 | public: 27 | MainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0); 28 | ~MainWindow(); 29 | 30 | static MainWindow& instance(); 31 | FavoritesList* getFavoritesList(); 32 | 33 | protected: 34 | void timerEvent(QTimerEvent *); 35 | void createDockWindows(); 36 | 37 | public slots: 38 | void InsertLogLine(const QString& line, int category); 39 | void AddPlugin(); 40 | void ClearGraph(); 41 | void AddPluginToFavorites(const QString& plugin_name); 42 | void RemovePluginToFavorites(const QString& plugin_name); 43 | 44 | private slots: 45 | void OpenMediaFile(); 46 | void OpenMediaUri(); 47 | void Play(); 48 | void Pause(); 49 | void Stop(); 50 | void Flush(); 51 | void Seek(int); 52 | 53 | void Save(); 54 | void SaveAs(); 55 | void Open(); 56 | 57 | void About(); 58 | 59 | 60 | void onFavoriteListItemDoubleClicked(QListWidgetItem* item); 61 | void ProvideContextMenu(const QPoint &pos); 62 | 63 | private: 64 | QSharedPointer m_pGraph; 65 | 66 | GraphDisplay *m_pGraphDisplay; 67 | 68 | QStatusBar *m_pstatusBar; 69 | QSlider *m_pslider; 70 | 71 | QString m_fileName; 72 | PluginsListDialog *m_pluginListDlg; 73 | QMenu *m_menu; 74 | QListWidget* m_logList; 75 | FavoritesList* m_favoriteList; 76 | }; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/PadProperties.cpp: -------------------------------------------------------------------------------- 1 | #include "PadProperties.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | PadProperties::PadProperties (QSharedPointer pGraphManager, 11 | const char *elementName, const char *padName, 12 | QWidget *parent, Qt::WindowFlags flags) 13 | : QWidget (parent, flags) 14 | { 15 | setWindowTitle (QString (elementName) + "::" + padName + " properties"); 16 | GstElement *element = gst_bin_get_by_name (GST_BIN (pGraphManager->m_pGraph), 17 | elementName); 18 | 19 | if (!element) 20 | return; 21 | 22 | GstPad *pad = gst_element_get_static_pad (GST_ELEMENT (element), padName); 23 | 24 | QGridLayout *play = new QGridLayout; 25 | 26 | play->addWidget (new QLabel ("Name"), 0, 0); 27 | 28 | QLabel *plbl = new QLabel (padName); 29 | plbl->setTextInteractionFlags ( 30 | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); 31 | play->addWidget (plbl, 0, 1); 32 | 33 | play->addWidget (new QLabel ("All caps:"), 1, 0); 34 | #if GST_VERSION_MAJOR >= 1 35 | GstCaps *caps = gst_pad_query_caps(pad, NULL); 36 | #else 37 | GstCaps *caps = gst_pad_get_caps (pad); 38 | #endif 39 | gchar *str; 40 | gchar *noSpecified = (gchar *) "not specified"; 41 | if (caps) 42 | str = gst_caps_to_string (caps); 43 | else 44 | str = noSpecified; 45 | 46 | plbl = new QLabel (QString (str)); 47 | plbl->setTextInteractionFlags ( 48 | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); 49 | play->addWidget (plbl, 1, 1); 50 | if (caps) { 51 | g_free (str); 52 | gst_caps_unref (caps); 53 | } 54 | 55 | play->addWidget (new QLabel ("Allowed caps:"), 2, 0); 56 | caps = gst_pad_get_allowed_caps (pad); 57 | str = NULL; 58 | if (caps) 59 | str = gst_caps_to_string (caps); 60 | else 61 | str = noSpecified; 62 | 63 | plbl = new QLabel (QString (str)); 64 | plbl->setTextInteractionFlags ( 65 | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); 66 | play->addWidget (plbl, 2, 1); 67 | if (caps) { 68 | g_free (str); 69 | gst_caps_unref (caps); 70 | } 71 | 72 | play->addWidget (new QLabel ("Current caps"), 3, 0); 73 | #if GST_VERSION_MAJOR >= 1 74 | caps = gst_pad_get_current_caps(pad); 75 | #else 76 | caps = gst_pad_get_negotiated_caps (pad); 77 | #endif 78 | str = NULL; 79 | if (caps) 80 | str = gst_caps_to_string (caps); 81 | else 82 | str = noSpecified; 83 | 84 | plbl = new QLabel (QString (str)); 85 | plbl->setTextInteractionFlags ( 86 | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); 87 | play->addWidget (plbl, 3, 1); 88 | if (caps) { 89 | g_free (str); 90 | gst_caps_unref (caps); 91 | } 92 | 93 | gst_object_unref (element); 94 | gst_object_unref (pad); 95 | 96 | QVBoxLayout *pvblay = new QVBoxLayout; 97 | QWidget *pwgt = new QWidget (this); 98 | pwgt->setLayout (play); 99 | QScrollArea *pscroll = new QScrollArea (this); 100 | pscroll->setWidget (pwgt); 101 | 102 | pvblay->addWidget (pscroll); 103 | 104 | setLayout (pvblay); 105 | } 106 | -------------------------------------------------------------------------------- /src/PadProperties.h: -------------------------------------------------------------------------------- 1 | #ifndef PAD_PROPERTIES_H_ 2 | #define PAD_PROPERTIES_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "GraphManager.h" 8 | 9 | class PadProperties: public QWidget 10 | { 11 | public: 12 | PadProperties(QSharedPointer pGraphManager, const char *element, const char *pad 13 | , QWidget *parent = 0, Qt::WindowFlags flags = 0); 14 | }; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/PipelineIE.cpp: -------------------------------------------------------------------------------- 1 | #include "PipelineIE.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | 11 | static void 12 | clearPipeline (GstElement *pipeline) 13 | { 14 | if (!pipeline) 15 | return; 16 | 17 | GstIterator *iter; 18 | iter = gst_bin_iterate_elements (GST_BIN (pipeline)); 19 | GstElement *element = NULL; 20 | bool done = false; 21 | while (!done) { 22 | #if GST_VERSION_MAJOR >= 1 23 | GValue value = G_VALUE_INIT; 24 | switch (gst_iterator_next (iter, &value)) 25 | { 26 | case GST_ITERATOR_OK: 27 | { 28 | element = GST_ELEMENT(g_value_get_object(&value)); 29 | #else 30 | switch (gst_iterator_next (iter, (gpointer *) &element)) { 31 | case GST_ITERATOR_OK: { 32 | #endif 33 | gst_bin_remove (GST_BIN (pipeline), element); 34 | #if GST_VERSION_MAJOR >= 1 35 | g_value_reset (&value); 36 | #endif 37 | iter = gst_bin_iterate_elements (GST_BIN (pipeline)); 38 | if (!iter) 39 | done = true; 40 | 41 | break; 42 | } 43 | case GST_ITERATOR_DONE: 44 | case GST_ITERATOR_RESYNC: 45 | case GST_ITERATOR_ERROR: { 46 | done = true; 47 | break; 48 | } 49 | }; 50 | } 51 | 52 | gst_iterator_free (iter); 53 | } 54 | 55 | namespace 56 | { 57 | struct Connection 58 | { 59 | std::string element1; 60 | std::string pad1; 61 | std::string element2; 62 | std::string pad2; 63 | }; 64 | } 65 | 66 | static void 67 | writeProperties (QXmlStreamWriter &xmlWriter, const GstElement *element) 68 | { 69 | GParamSpec **prop_specs; 70 | guint num_props; 71 | 72 | prop_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (element), 73 | &num_props); 74 | 75 | if (!num_props) 76 | return; 77 | 78 | xmlWriter.writeStartElement ("properties"); 79 | 80 | for (std::size_t i = 0; i < num_props; i++) { 81 | GParamSpec *param = prop_specs[i]; 82 | 83 | if ((param->flags & G_PARAM_READABLE) 84 | && (param->flags & G_PARAM_WRITABLE)) { 85 | GValue value = G_VALUE_INIT; 86 | g_value_init (&value, param->value_type); 87 | 88 | g_object_get_property (G_OBJECT (element), param->name, &value); 89 | 90 | if (!g_param_value_defaults (param, &value)) { 91 | bool skip = false; 92 | QString propertyName = g_param_spec_get_name (param); 93 | QString propertyValue; 94 | 95 | switch (G_VALUE_TYPE (&value)) { 96 | case G_TYPE_STRING: { 97 | 98 | const char *string_val = g_value_get_string (&value); 99 | if (string_val) 100 | propertyValue = string_val; 101 | else 102 | skip = true; 103 | break; 104 | } 105 | case G_TYPE_BOOLEAN: { 106 | gboolean bool_val = g_value_get_boolean (&value); 107 | propertyValue = QString::number (bool_val); 108 | break; 109 | } 110 | case G_TYPE_ULONG: { 111 | propertyValue = QString::number (g_value_get_ulong (&value)); 112 | break; 113 | } 114 | case G_TYPE_LONG: { 115 | propertyValue = QString::number (g_value_get_long (&value)); 116 | break; 117 | } 118 | case G_TYPE_UINT: { 119 | propertyValue = QString::number (g_value_get_uint (&value)); 120 | break; 121 | } 122 | case G_TYPE_INT: { 123 | propertyValue = QString::number (g_value_get_int (&value)); 124 | break; 125 | } 126 | case G_TYPE_UINT64: { 127 | propertyValue = QString::number (g_value_get_uint64 (&value)); 128 | break; 129 | } 130 | case G_TYPE_INT64: { 131 | propertyValue = QString::number (g_value_get_int64 (&value)); 132 | break; 133 | } 134 | case G_TYPE_FLOAT: { 135 | propertyValue = QString::number (g_value_get_float (&value)); 136 | break; 137 | } 138 | case G_TYPE_DOUBLE: { 139 | propertyValue = QString::number (g_value_get_double (&value)); 140 | break; 141 | } 142 | default: { 143 | gchar *elementName = gst_element_get_name (element); 144 | LOG_INFO("property `%s` for `%s` not supported", propertyName.toStdString ().c_str (), elementName); 145 | g_free (elementName); 146 | 147 | skip = true; 148 | break; 149 | } 150 | }; 151 | 152 | if (!skip) { 153 | xmlWriter.writeStartElement ("property"); 154 | xmlWriter.writeAttribute ("name", propertyName); 155 | xmlWriter.writeAttribute ("value", propertyValue); 156 | xmlWriter.writeEndElement (); 157 | } 158 | g_value_reset (&value); 159 | 160 | } 161 | } 162 | } 163 | 164 | xmlWriter.writeEndElement (); 165 | 166 | g_free (prop_specs); 167 | } 168 | 169 | static void 170 | loadProperties (QDomElement node, GstElement *element) 171 | { 172 | QDomNode child = node.firstChild (); 173 | while (!child.isNull ()) { 174 | if (child.toElement ().tagName () == "property") { 175 | QString name = child.toElement ().attribute ("name"); 176 | QString value = child.toElement ().attribute ("value"); 177 | 178 | GParamSpec *param = g_object_class_find_property ( 179 | G_OBJECT_GET_CLASS (element), name.toStdString ().c_str ()); 180 | 181 | if (!param) { 182 | gchar *elementName = gst_element_get_name (element); 183 | LOG_INFO("problem with setting property `%s` for `%s`", name.toStdString ().c_str (), elementName); 184 | g_free (elementName); 185 | continue; 186 | } 187 | 188 | if (!(param->flags & G_PARAM_WRITABLE)) 189 | continue; 190 | 191 | std::string tmpStr = name.toStdString (); 192 | const char *propName = tmpStr.c_str (); 193 | switch (param->value_type) { 194 | case G_TYPE_STRING: { 195 | g_object_set (G_OBJECT (element), propName, 196 | value.toStdString ().c_str (), NULL); 197 | break; 198 | } 199 | case G_TYPE_BOOLEAN: { 200 | gboolean val = value.toInt (); 201 | g_object_set (G_OBJECT (element), propName, val, NULL); 202 | break; 203 | } 204 | case G_TYPE_ULONG: { 205 | gulong val = value.toULong (); 206 | g_object_set (G_OBJECT (element), propName, val, NULL); 207 | break; 208 | } 209 | case G_TYPE_LONG: { 210 | glong val = value.toLong (); 211 | g_object_set (G_OBJECT (element), propName, val, NULL); 212 | break; 213 | } 214 | case G_TYPE_UINT: { 215 | guint val = value.toUInt (); 216 | g_object_set (G_OBJECT (element), propName, val, NULL); 217 | break; 218 | } 219 | case G_TYPE_INT: { 220 | gint val = value.toInt (); 221 | g_object_set (G_OBJECT (element), propName, val, NULL); 222 | break; 223 | } 224 | case G_TYPE_UINT64: { 225 | guint64 val = value.toULongLong (); 226 | g_object_set (G_OBJECT (element), propName, val, NULL); 227 | break; 228 | } 229 | case G_TYPE_INT64: { 230 | gint64 val = value.toLongLong (); 231 | g_object_set (G_OBJECT (element), propName, val, NULL); 232 | break; 233 | } 234 | case G_TYPE_FLOAT: { 235 | gfloat val = value.toFloat (); 236 | g_object_set (G_OBJECT (element), propName, val, NULL); 237 | break; 238 | } 239 | case G_TYPE_DOUBLE: { 240 | gdouble val = value.toDouble (); 241 | g_object_set (G_OBJECT (element), propName, val, NULL); 242 | break; 243 | } 244 | default: { 245 | gchar *elementName = gst_element_get_name (element); 246 | LOG_INFO("property `%s` for `%s` not supported", propName, elementName); 247 | g_free (elementName); 248 | break; 249 | } 250 | }; 251 | } 252 | 253 | child = child.nextSibling (); 254 | } 255 | 256 | } 257 | 258 | static void 259 | create_requst_pad (GstElement *element, const QString &templateName, 260 | const QString &padName) 261 | { 262 | GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); 263 | 264 | GstPadTemplate *templ = gst_element_class_get_pad_template ( 265 | klass, templateName.toStdString ().c_str ()); 266 | 267 | gst_element_request_pad (element, templ, padName.toStdString ().c_str (), 268 | NULL); 269 | } 270 | 271 | bool 272 | PipelineIE::Export (QSharedPointer pgraph, 273 | const QString &fileName) 274 | { 275 | QFile file (fileName); 276 | 277 | if (!file.open (QIODevice::WriteOnly)) { 278 | QMessageBox::warning (0, "Read only", "The file is in read only mode"); 279 | return false; 280 | } 281 | 282 | std::vector info = pgraph->GetInfo (); 283 | 284 | QXmlStreamWriter xmlWriter; 285 | xmlWriter.setDevice (&file); 286 | xmlWriter.writeStartDocument (); 287 | xmlWriter.writeStartElement ("pipeline"); 288 | 289 | for (std::size_t i = 0; i < info.size (); i++) { 290 | xmlWriter.writeStartElement ("element"); 291 | 292 | xmlWriter.writeAttribute ("name", info[i].m_name.c_str ()); 293 | xmlWriter.writeAttribute ("plugin-name", info[i].m_pluginName.c_str ()); 294 | 295 | GstElement *element = gst_bin_get_by_name (GST_BIN (pgraph->m_pGraph), 296 | info[i].m_name.c_str ()); 297 | 298 | for (std::size_t j = 0; j < info[i].m_pads.size (); j++) { 299 | xmlWriter.writeStartElement ("pad"); 300 | 301 | xmlWriter.writeAttribute ("name", info[i].m_pads[j].m_name.c_str ()); 302 | 303 | GstPad *pad = gst_element_get_static_pad ( 304 | element, info[i].m_pads[j].m_name.c_str ()); 305 | 306 | GstPadTemplate *templ = gst_pad_get_pad_template (pad); 307 | if (templ) { 308 | QString presence; 309 | switch (GST_PAD_TEMPLATE_PRESENCE (templ)) { 310 | case GST_PAD_ALWAYS: 311 | presence = "always"; 312 | break; 313 | 314 | case GST_PAD_SOMETIMES: 315 | presence = "sometimes"; 316 | break; 317 | 318 | case GST_PAD_REQUEST: 319 | presence = "request"; 320 | break; 321 | }; 322 | 323 | xmlWriter.writeAttribute ("presence", presence); 324 | xmlWriter.writeAttribute ("template-name", 325 | GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); 326 | } 327 | else { 328 | LOG_INFO("Unable to find a template for %s", info[i].m_pads[j].m_name.c_str()); 329 | xmlWriter.writeAttribute ("presence", "always"); 330 | xmlWriter.writeAttribute ("template-name", ""); 331 | } 332 | gst_object_unref (pad); 333 | 334 | if (info[i].m_connections[j].m_elementId != (size_t) -1 335 | && info[i].m_connections[j].m_padId != (size_t) -1) { 336 | std::size_t elementPos, padPos; 337 | for (elementPos = 0; elementPos < info.size (); elementPos++) { 338 | if (info[elementPos].m_id == info[i].m_connections[j].m_elementId) { 339 | for (padPos = 0; padPos < info[elementPos].m_pads.size (); padPos++) 340 | if (info[elementPos].m_pads[padPos].m_id 341 | == info[i].m_connections[j].m_padId) 342 | break; 343 | 344 | if (padPos < info[elementPos].m_pads.size ()) 345 | break; 346 | } 347 | } 348 | if (elementPos < info.size () 349 | && padPos < info[elementPos].m_pads.size ()) { 350 | xmlWriter.writeStartElement ("connected-to"); 351 | xmlWriter.writeAttribute ("element-name", 352 | info[elementPos].m_name.c_str ()); 353 | xmlWriter.writeAttribute ( 354 | "pad-name", info[elementPos].m_pads[padPos].m_name.c_str ()); 355 | xmlWriter.writeEndElement (); 356 | } 357 | } 358 | 359 | xmlWriter.writeEndElement (); 360 | } 361 | 362 | writeProperties (xmlWriter, element); 363 | gst_object_unref (element); 364 | 365 | xmlWriter.writeEndElement (); 366 | } 367 | 368 | xmlWriter.writeEndElement (); 369 | xmlWriter.writeEndDocument (); 370 | 371 | return true; 372 | } 373 | 374 | bool 375 | PipelineIE::Import (QSharedPointer pgraph, 376 | const QString &fileName) 377 | { 378 | GstElement *pipeline = pgraph->m_pGraph; 379 | QFile file (fileName); 380 | if (!file.open (QFile::ReadOnly | QFile::Text)) { 381 | QMessageBox::warning ( 382 | 0, "Open failed", 383 | QString ("Cannot read file ") + fileName + ": " + file.errorString ()); 384 | 385 | return false; 386 | } 387 | 388 | QString errorStr; 389 | int errorLine; 390 | int errorColumn; 391 | 392 | QDomDocument doc; 393 | if (!doc.setContent (&file, false, &errorStr, &errorLine, &errorColumn)) { 394 | QMessageBox::warning ( 395 | 0, "Xml parsing failed", 396 | QString ("Parse error at line ") + QString::number (errorLine) + ", " 397 | "column " + QString::number (errorColumn) + ": " + errorStr); 398 | return false; 399 | } 400 | 401 | clearPipeline (pipeline); 402 | 403 | QDomElement root = doc.documentElement (); 404 | 405 | if (root.tagName () != "pipeline") { 406 | QMessageBox::warning (0, "Parsing failed", "Is invalid pipeline file"); 407 | return false; 408 | } 409 | 410 | QDomNode child = root.firstChild (); 411 | 412 | std::vector connections; 413 | while (!child.isNull ()) { 414 | if (child.toElement ().tagName () == "element") { 415 | QDomElement elNode = child.toElement (); 416 | 417 | GstElement *pel = gst_element_factory_make ( 418 | elNode.attribute ("plugin-name").toStdString ().c_str (), 419 | elNode.attribute ("name").toStdString ().c_str ()); 420 | 421 | if (!pel) { 422 | QMessageBox::warning ( 423 | 0, 424 | "Element creation failed", 425 | QString ("Could not create element of `") 426 | + elNode.attribute ("plugin-name") + "` with name `" 427 | + elNode.attribute ("name") + "`"); 428 | 429 | child = child.nextSibling (); 430 | continue; 431 | } 432 | 433 | bool res = gst_bin_add (GST_BIN (pipeline), pel); 434 | 435 | if (!res) { 436 | QMessageBox::warning ( 437 | 0, 438 | "Element insertion failed", 439 | QString ("Could not insert element `") + elNode.attribute ("name") 440 | + "` to pipeline"); 441 | 442 | child = child.nextSibling (); 443 | continue; 444 | } 445 | 446 | gst_element_sync_state_with_parent (pel); 447 | 448 | QDomNode elementChild = elNode.firstChild (); 449 | while (!elementChild.isNull ()) { 450 | if (elementChild.toElement ().tagName () == "pad") { 451 | QDomNode padChild = elementChild.firstChild (); 452 | QDomElement elPad = elementChild.toElement (); 453 | // GstPadPresence presence = GST_PAD_ALWAYS; 454 | 455 | QString templaneName; 456 | if (elPad.attribute ("presence") == "request") 457 | create_requst_pad (pel, elPad.attribute ("template-name"), 458 | elPad.attribute ("name")); 459 | 460 | while (!padChild.isNull ()) { 461 | if (padChild.toElement ().tagName () == "connected-to") { 462 | bool isExists = false; 463 | QDomElement elCoonnectedTo = padChild.toElement (); 464 | 465 | for (std::size_t i = 0; i < connections.size (); i++) { 466 | if ((connections[i].element1 467 | == elNode.attribute ("name").toStdString () 468 | && connections[i].element2 469 | == elCoonnectedTo.attribute ("element-name").toStdString () 470 | && connections[i].pad1 471 | == elPad.attribute ("name").toStdString () 472 | && connections[i].pad2 473 | == elCoonnectedTo.attribute ("pad-name").toStdString ()) 474 | || (connections[i].element2 475 | == elNode.attribute ("name").toStdString () 476 | && connections[i].element1 477 | == elCoonnectedTo.attribute ("element-name").toStdString () 478 | && connections[i].pad2 479 | == elPad.attribute ("name").toStdString () 480 | && connections[i].pad1 481 | == elCoonnectedTo.attribute ("pad-name").toStdString ())) { 482 | isExists = true; 483 | } 484 | } 485 | 486 | if (!isExists) { 487 | Connection newConnetion; 488 | newConnetion.element1 = 489 | elNode.attribute ("name").toStdString (); 490 | newConnetion.element2 = padChild.toElement ().attribute ( 491 | "element-name").toStdString (); 492 | newConnetion.pad1 = 493 | elementChild.toElement ().attribute ("name").toStdString (); 494 | newConnetion.pad2 = 495 | padChild.toElement ().attribute ("pad-name").toStdString (); 496 | 497 | connections.push_back (newConnetion); 498 | } 499 | } 500 | padChild = padChild.nextSibling (); 501 | } 502 | } 503 | else if (elementChild.toElement ().tagName () == "properties") { 504 | loadProperties (elementChild.toElement (), pel); 505 | } 506 | elementChild = elementChild.nextSibling (); 507 | } 508 | } 509 | child = child.nextSibling (); 510 | } 511 | 512 | std::size_t maxStarts = 5; 513 | bool setReady = true; 514 | for (std::size_t k = 0; k < maxStarts; k++) { 515 | if (connections.empty ()) 516 | break; 517 | 518 | if (k > 0) 519 | setReady = false; 520 | 521 | while (true) { 522 | std::size_t i = 0; 523 | for (; i < connections.size (); i++) { 524 | GstElement *el1 = gst_bin_get_by_name ( 525 | GST_BIN (pipeline), connections[i].element1.c_str ()); 526 | GstElement *el2 = gst_bin_get_by_name ( 527 | GST_BIN (pipeline), connections[i].element2.c_str ()); 528 | 529 | if (!el1 || !el2) { 530 | QMessageBox::warning ( 531 | 0, 532 | "Internal error", 533 | QString ("Could not find one of elements `") 534 | + QString (connections[i].element1.c_str ()) + "`, `" 535 | + QString (connections[i].element2.c_str ()) + "`"); 536 | 537 | gst_object_unref (el1); 538 | gst_object_unref (el2); 539 | 540 | return false; 541 | } 542 | 543 | GstPad *pad1 = gst_element_get_static_pad ( 544 | el1, connections[i].pad1.c_str ()); 545 | GstPad *pad2 = gst_element_get_static_pad ( 546 | el2, connections[i].pad2.c_str ()); 547 | 548 | if (pad1 && pad2) { 549 | if (GST_PAD_IS_SRC (pad1)) 550 | gst_element_link_pads (el1, connections[i].pad1.c_str (), el2, 551 | connections[i].pad2.c_str ()); 552 | else 553 | gst_element_link_pads (el2, connections[i].pad2.c_str (), el1, 554 | connections[i].pad1.c_str ()); 555 | 556 | gst_object_unref (pad1); 557 | gst_object_unref (pad2); 558 | connections.erase (connections.begin () + i); 559 | 560 | gst_object_unref (el1); 561 | gst_object_unref (el2); 562 | 563 | break; 564 | } 565 | 566 | if (pad1) 567 | gst_object_unref (pad1); 568 | 569 | if (pad2) 570 | gst_object_unref (pad2); 571 | 572 | gst_object_unref (el1); 573 | gst_object_unref (el2); 574 | } 575 | 576 | if (i == connections.size ()) 577 | break; 578 | } 579 | 580 | if (!connections.empty ()) { 581 | gst_element_set_state (pipeline, GST_STATE_PAUSED); 582 | 583 | GstState state; 584 | gst_element_get_state (pipeline, &state, NULL, GST_MSECOND * 2500); 585 | } 586 | 587 | } 588 | 589 | if (setReady) 590 | gst_element_set_state (pipeline, GST_STATE_READY); 591 | 592 | return true; 593 | } 594 | 595 | bool 596 | PipelineIE::Clear (QSharedPointer pgraph) 597 | { 598 | GstElement *pipeline = pgraph->m_pGraph; 599 | clearPipeline (pipeline); 600 | return true; 601 | } 602 | -------------------------------------------------------------------------------- /src/PipelineIE.h: -------------------------------------------------------------------------------- 1 | #ifndef PIPELINE_IMPORT_EXPORT_H_ 2 | #define PIPELINE_IMPORT_EXPORT_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "GraphManager.h" 8 | 9 | namespace PipelineIE 10 | { 11 | bool Export(QSharedPointer pgraph, const QString &fileName); 12 | bool Import(QSharedPointer pgraph, const QString &fileName); 13 | bool Clear(QSharedPointer pgraph); 14 | }; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/PluginsList.cpp: -------------------------------------------------------------------------------- 1 | #include "PluginsList.h" 2 | #include "MainWindow.h" 3 | #include "FavoritesList.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | static gint 19 | plugins_sort_cb (gconstpointer a, gconstpointer b) 20 | { 21 | Plugin* p1 = (Plugin*) a; 22 | Plugin* p2 = (Plugin*) b; 23 | LOG_INFO("Sort p1: %s and p2: ", p1->getName ().toStdString ().c_str (),p2->getName ().toStdString ().c_str ()); 24 | if (p1->getRank () > p2->getRank ()) 25 | return 1; 26 | else if (p1->getRank () == p2->getRank ()) { 27 | return 0; 28 | } 29 | else { 30 | return -1; 31 | } 32 | } 33 | 34 | PluginsList::PluginsList () 35 | :m_pluginsList(NULL) 36 | { 37 | init (); 38 | } 39 | 40 | PluginsList& PluginsList::instance() 41 | { 42 | static PluginsList instance; 43 | return instance; 44 | } 45 | 46 | PluginsList::~PluginsList () 47 | { 48 | //g_list_free(m_pluginsList); 49 | } 50 | 51 | void 52 | PluginsList::init () 53 | { 54 | std::size_t num = 0; 55 | GList *plugins; 56 | m_pluginsList = NULL; 57 | GstRegistry *registry; 58 | #if GST_VERSION_MAJOR >= 1 59 | registry = gst_registry_get(); 60 | #else 61 | registry = gst_registry_get_default (); 62 | #endif 63 | plugins = gst_registry_get_plugin_list (registry); 64 | while (plugins) { 65 | GstPlugin *plugin; 66 | plugin = (GstPlugin *) (plugins->data); 67 | plugins = g_list_next (plugins); 68 | #if GST_VERSION_MAJOR >= 1 69 | registry = gst_registry_get(); 70 | #else 71 | registry = gst_registry_get_default (); 72 | #endif 73 | GList *features = gst_registry_get_feature_list_by_plugin ( 74 | registry, gst_plugin_get_name (plugin)); 75 | 76 | while (features) { 77 | GstPluginFeature *feature; 78 | feature = GST_PLUGIN_FEATURE (features->data); 79 | if (GST_IS_ELEMENT_FACTORY (feature)) { 80 | GstElementFactory *factory; 81 | factory = GST_ELEMENT_FACTORY (feature); 82 | int rank = gst_plugin_feature_get_rank (GST_PLUGIN_FEATURE (factory)); 83 | Plugin* p = new Plugin (GST_OBJECT_NAME (factory), rank); 84 | m_pluginsList = g_list_append (m_pluginsList, p); 85 | num++; 86 | } 87 | 88 | features = g_list_next (features); 89 | } 90 | } 91 | } 92 | 93 | GList* 94 | PluginsList::getSortedByRank () 95 | { 96 | GList* sorted_list = g_list_sort (m_pluginsList, plugins_sort_cb); 97 | return sorted_list; 98 | } 99 | 100 | GList* 101 | PluginsList::getPluginListByCaps (GstPadDirection direction, GstCaps* caps) 102 | { 103 | GList * caps_plugins_list = NULL; 104 | GList *l, *p; 105 | 106 | for (l = m_pluginsList; l != NULL; l = l->next) { 107 | Plugin* plugin = (Plugin*) (l->data); 108 | GstElementFactory* factory = gst_element_factory_find ( 109 | plugin->getName ().toStdString ().c_str ()); 110 | if (factory) { 111 | const GList* pads = gst_element_factory_get_static_pad_templates ( 112 | factory); 113 | for (p = (GList*) pads; p != NULL; p = p->next) { 114 | GstStaticPadTemplate* padTemplate = (GstStaticPadTemplate*) (p->data); 115 | if (padTemplate->direction == direction 116 | && gst_caps_can_intersect (caps, padTemplate->static_caps.caps)) 117 | caps_plugins_list = g_list_append (caps_plugins_list, plugin); 118 | } 119 | } 120 | } 121 | return caps_plugins_list; 122 | } 123 | 124 | 125 | #define kBUTTON_FAVORITE_ADD "Add to favorites" 126 | #define kBUTTON_FAVORITE_REMOVE "Remove from favorites" 127 | 128 | PluginsListDialog::PluginsListDialog (QWidget *pwgt, 129 | Qt::WindowFlags f) 130 | : QDialog (pwgt, f), 131 | m_pGraph (NULL) 132 | { 133 | m_main = (MainWindow*)pwgt; 134 | 135 | m_pPlugins = new QListWidget; 136 | m_pPlugins->setSortingEnabled (true); 137 | m_plblInfo = new QLabel; 138 | 139 | m_plblInfo->setTextInteractionFlags (Qt::TextSelectableByMouse); 140 | m_plblInfo->setAlignment (Qt::AlignLeft | Qt::AlignTop); 141 | QScrollArea *pscroll = new QScrollArea; 142 | pscroll->setWidget (m_plblInfo); 143 | m_plblInfo->resize (pscroll->size ()); 144 | 145 | QHBoxLayout *phblay = new QHBoxLayout; 146 | 147 | phblay->addWidget (m_pPlugins, 1); 148 | phblay->addWidget (pscroll, 2); 149 | 150 | InitPluginsList (); 151 | 152 | QHBoxLayout *phblayFind = new QHBoxLayout; 153 | 154 | QLineEdit *ple = new QLineEdit; 155 | phblayFind->addWidget (ple); 156 | phblayFind->addStretch (1); 157 | ple->setPlaceholderText ("Search..."); 158 | 159 | m_favoriteListButton = new QPushButton(kBUTTON_FAVORITE_ADD); 160 | phblayFind->addWidget (m_favoriteListButton); 161 | //phblayFind->addStretch (1); 162 | QObject::connect (m_favoriteListButton, SIGNAL (clicked ()), this, 163 | SLOT (favoritesClicked ())); 164 | 165 | QVBoxLayout *pvblay = new QVBoxLayout; 166 | pvblay->addLayout (phblayFind); 167 | pvblay->addLayout (phblay); 168 | 169 | setLayout (pvblay); 170 | 171 | setWindowTitle ("Add plugin"); 172 | 173 | QObject::connect(m_pPlugins, SIGNAL(currentItemChanged (QListWidgetItem *, QListWidgetItem *)), 174 | this, SLOT(showInfo(QListWidgetItem *, QListWidgetItem *))); 175 | 176 | QObject::connect(m_pPlugins, SIGNAL(itemDoubleClicked (QListWidgetItem *)), 177 | this, SLOT(insert(QListWidgetItem *))); 178 | 179 | QObject::connect(m_pPlugins,SIGNAL(customContextMenuRequested(const QPoint &)), 180 | this,SLOT(ProvideContextMenu(const QPoint &))); 181 | 182 | QObject::connect(ple, SIGNAL(textChanged(const QString &)), this, SLOT(filterPlugins(const QString &))); 183 | 184 | installEventFilter (this); 185 | } 186 | 187 | PluginsListDialog::~PluginsListDialog () 188 | { 189 | } 190 | 191 | void 192 | PluginsListDialog::showInfo (QListWidgetItem *pitem, QListWidgetItem *previous) 193 | { 194 | 195 | Q_UNUSED(previous); 196 | LOG_INFO("Show Info: %s", pitem->text ().toStdString ().c_str ()); 197 | m_plblInfo->clear (); 198 | QString descr; 199 | descr += "Plugin details
"; 200 | 201 | if (m_main->getFavoritesList()->isFavorite( pitem->text ()) != -1) 202 | m_favoriteListButton->setText(kBUTTON_FAVORITE_REMOVE); 203 | else 204 | m_favoriteListButton->setText(kBUTTON_FAVORITE_ADD); 205 | 206 | GstElementFactory *factory = gst_element_factory_find ( 207 | pitem->text ().toStdString ().c_str ()); 208 | if (!factory) { 209 | LOG_INFO("warning: %s not found",pitem->text ().toStdString ().c_str ()); 210 | return; 211 | } 212 | 213 | factory = GST_ELEMENT_FACTORY ( 214 | gst_plugin_feature_load (GST_PLUGIN_FEATURE (factory))); 215 | if (!factory) { 216 | LOG_INFO("warning: %s not found",pitem->text ().toStdString ().c_str ()); 217 | return; 218 | } 219 | #if GST_VERSION_MAJOR >= 1 220 | GstPlugin *plugin = gst_plugin_feature_get_plugin (GST_PLUGIN_FEATURE (factory)); 221 | #else 222 | const gchar* plugin_name = GST_PLUGIN_FEATURE (factory)->plugin_name; 223 | if (!plugin_name) { 224 | return; 225 | } 226 | GstPlugin* plugin = gst_default_registry_find_plugin (plugin_name); 227 | #endif 228 | if (!plugin) { 229 | LOG_INFO("warning: %s not found",pitem->text ().toStdString ().c_str ()); 230 | return; 231 | } 232 | 233 | #if GST_VERSION_MAJOR >= 1 234 | const gchar *release_date = gst_plugin_get_release_date_string (plugin); 235 | #else 236 | const gchar *release_date = 237 | (plugin->desc.release_datetime) ? plugin->desc.release_datetime : ""; 238 | #endif 239 | const gchar *filename = gst_plugin_get_filename (plugin); 240 | 241 | descr += "Name: " + QString (gst_plugin_get_name (plugin)) + "
"; 242 | descr += "Description: " 243 | + QString (gst_plugin_get_description (plugin)) + "
"; 244 | descr += "Filename: " 245 | + QString ((filename != NULL) ? filename : "(null)") + "
"; 246 | descr += "Version: " + QString (gst_plugin_get_version (plugin)) 247 | + "
"; 248 | descr += "License: " + QString (gst_plugin_get_license (plugin)) 249 | + "
"; 250 | descr += "Source module: " + QString (gst_plugin_get_source (plugin)) 251 | + "
"; 252 | 253 | if (release_date != NULL) { 254 | const gchar *tz = "(UTC)"; 255 | gchar *str, *sep; 256 | 257 | str = g_strdup (release_date); 258 | sep = strstr (str, "T"); 259 | if (sep != NULL) { 260 | *sep = ' '; 261 | sep = strstr (sep + 1, "Z"); 262 | if (sep != NULL) 263 | *sep = ' '; 264 | } 265 | else { 266 | tz = ""; 267 | } 268 | descr += "Source release date: " + QString (str) + " " + QString (tz) 269 | + "
"; 270 | g_free (str); 271 | } 272 | descr += "Binary package: " + QString (gst_plugin_get_package (plugin)) 273 | + "
"; 274 | descr += "Origin URL: " + QString (gst_plugin_get_origin (plugin)) 275 | + "
"; 276 | descr += "Rank: " 277 | + QString::number ( 278 | gst_plugin_feature_get_rank (GST_PLUGIN_FEATURE (factory))); 279 | m_plblInfo->setText (descr); 280 | } 281 | 282 | void 283 | PluginsListDialog::insert (QListWidgetItem *pitem) 284 | { 285 | if (!pitem) { 286 | LOG_INFO("Do not insert null item"); 287 | return; 288 | } 289 | LOG_INFO("Insert: %s", pitem->text ().toStdString ().c_str ()); 290 | 291 | if (!m_pGraph 292 | || !m_pGraph->AddPlugin (pitem->text ().toStdString ().c_str (), NULL)) { 293 | QMessageBox::warning ( 294 | this, "Plugin addition problem", 295 | "Plugin `" + pitem->text () + "` insertion was FAILED"); 296 | LOG_INFO("Plugin `%s insertion FAILED", pitem->text ().toStdString ().c_str ()); 297 | return; 298 | } 299 | } 300 | 301 | bool 302 | PluginsListDialog::eventFilter (QObject *obj, QEvent *event) 303 | { 304 | if (event->type () == QEvent::KeyPress) { 305 | QKeyEvent *key = static_cast (event); 306 | 307 | if (((key->key () == Qt::Key_Enter) 308 | || (key->key () == Qt::Key_Return)) && m_pPlugins->currentItem ()) { 309 | insert (m_pPlugins->currentItem ()); 310 | return true; 311 | } 312 | } 313 | return QDialog::eventFilter (obj, event); 314 | } 315 | 316 | void 317 | PluginsListDialog::filterPlugins (const QString &text) 318 | { 319 | for (int i = 0; i < m_pPlugins->count (); i++) { 320 | QListWidgetItem *pitem = m_pPlugins->item (i); 321 | 322 | if (pitem->text ().contains (text)) 323 | pitem->setHidden (false); 324 | else 325 | pitem->setHidden (true); 326 | } 327 | } 328 | 329 | void 330 | PluginsListDialog::favoritesClicked () 331 | { 332 | QListWidgetItem *pitem = m_pPlugins->currentItem(); 333 | if(!pitem) 334 | return; 335 | if (m_main->getFavoritesList()->isFavorite(pitem->text ()) != -1) { 336 | emit signalRemPluginToFav (pitem->text ()); 337 | } 338 | else { 339 | emit signalAddPluginToFav (pitem->text ()); 340 | } 341 | } 342 | 343 | void 344 | PluginsListDialog::InitPluginsList () 345 | { 346 | GList* plugins_list = PluginsList::instance().getList (); 347 | GList* l; 348 | std::size_t num = 0; 349 | 350 | for (l = plugins_list; l != NULL; l = l->next) { 351 | Plugin* plugin = (Plugin*) (l->data); 352 | m_pPlugins->addItem (plugin->getName ()); 353 | num++; 354 | } 355 | } 356 | 357 | void PluginsListDialog::ProvideContextMenu(const QPoint &pos) 358 | { 359 | QPoint item = m_pPlugins->mapToGlobal(pos); 360 | QListWidgetItem* current_item = m_pPlugins->currentItem(); 361 | 362 | QMenu submenu; 363 | if (m_main->getFavoritesList()->isFavorite(current_item->text ()) != -1) { 364 | emit signalRemPluginToFav (current_item->text ()); 365 | } 366 | else { 367 | emit signalAddPluginToFav (current_item->text ()); 368 | } 369 | 370 | if (m_main->getFavoritesList()->isFavorite(current_item->text ()) != -1) 371 | submenu.addAction("Add to favorites"); 372 | else 373 | submenu.addAction("Remove from favorites"); 374 | 375 | QAction* rightClickItem = submenu.exec(item); 376 | if (rightClickItem) { 377 | if(rightClickItem->text().contains("Add to favorites") ) { 378 | LOG_INFO("Add item: %s", current_item->text().toStdString ().c_str ()); 379 | emit signalAddPluginToFav (current_item->text ()); 380 | } else if(rightClickItem->text().contains("Remove from favorites") ) { 381 | LOG_INFO("Delete item: %s", current_item->text().toStdString ().c_str ()); 382 | emit signalRemPluginToFav (current_item->text ()); 383 | } 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /src/PluginsList.h: -------------------------------------------------------------------------------- 1 | #ifndef PLUGINS_LIST_H_ 2 | #define PLUGINS_LIST_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "GraphManager.h" 10 | 11 | class Plugin 12 | { 13 | public: 14 | Plugin(const QString& name, int rank): 15 | m_name(name), 16 | m_rank(rank) 17 | { 18 | } 19 | const QString getName() {return m_name;} 20 | int getRank() {return m_rank;} 21 | 22 | private: 23 | const QString m_name; 24 | int m_rank; 25 | 26 | }; 27 | 28 | class PluginsList 29 | { 30 | public: 31 | PluginsList(); 32 | ~PluginsList(); 33 | 34 | static PluginsList& instance(); 35 | 36 | GList* getList() {return m_pluginsList;} 37 | GList* getSortedByRank(); 38 | GList* getPluginListByCaps(GstPadDirection direction, GstCaps* caps); 39 | GList* getPluginListFavorite(); 40 | 41 | private: 42 | void init(); 43 | 44 | GList* m_pluginsList; //list of plugins 45 | }; 46 | 47 | class MainWindow; 48 | 49 | class PluginsListDialog: public QDialog 50 | { 51 | Q_OBJECT 52 | public: 53 | PluginsListDialog(QWidget *pwgt = NULL, Qt::WindowFlags f = Qt::Window); 54 | ~PluginsListDialog(); 55 | 56 | void setGraph(GraphManager* graph) {m_pGraph = graph;} 57 | 58 | protected: 59 | bool eventFilter(QObject *obj, QEvent *ev); 60 | 61 | private: 62 | void InitPluginsList(); 63 | 64 | public slots: 65 | void showInfo(QListWidgetItem *current, QListWidgetItem *previous); 66 | void insert(QListWidgetItem *); 67 | 68 | private slots: 69 | void filterPlugins(const QString &text); 70 | void favoritesClicked(); 71 | void ProvideContextMenu(const QPoint &pos); 72 | 73 | signals: 74 | void signalAddPluginToFav (const QString &plugin_name); 75 | void signalRemPluginToFav (const QString &plugin_name); 76 | 77 | private: 78 | MainWindow * m_main; 79 | QLabel *m_plblInfo; 80 | QListWidget *m_pPlugins; 81 | GraphManager *m_pGraph; 82 | QPushButton*m_favoriteListButton; 83 | }; 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /src/SeekSlider.cpp: -------------------------------------------------------------------------------- 1 | #include "SeekSlider.h" 2 | 3 | void 4 | SeekSlider::mousePressEvent (QMouseEvent *event) 5 | { 6 | if (event->button () == Qt::LeftButton) { 7 | if (orientation () == Qt::Vertical) 8 | setValue (minimum () 9 | + ((maximum () - minimum ()) * (height () - event->y ())) / height ()); 10 | else 11 | setValue (minimum () 12 | + ((maximum () - minimum ()) * event->x ()) / width ()); 13 | 14 | event->accept (); 15 | } 16 | 17 | QSlider::mousePressEvent (event); 18 | } 19 | ; 20 | -------------------------------------------------------------------------------- /src/SeekSlider.h: -------------------------------------------------------------------------------- 1 | #ifndef SEEK_SLIDER_H_ 2 | #define SEEK_SLIDER_H_ 3 | 4 | #include 5 | #include 6 | 7 | class SeekSlider: public QSlider 8 | { 9 | protected: 10 | void mousePressEvent(QMouseEvent *); 11 | }; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "MainWindow.h" 3 | 4 | #include 5 | 6 | int 7 | main (int argc, char **argv) 8 | { 9 | Logger::instance().configure_logger (); 10 | gst_init (&argc, &argv); 11 | 12 | GstRegistry *registry; 13 | #if GST_VERSION_MAJOR >= 1 14 | registry = gst_registry_get(); 15 | #else 16 | registry = gst_registry_get_default (); 17 | #endif 18 | gst_registry_scan_path (registry, "./plugins"); 19 | 20 | QApplication app (argc, argv); 21 | 22 | MainWindow wgt; 23 | wgt.show (); 24 | 25 | return app.exec (); 26 | } 27 | -------------------------------------------------------------------------------- /src/verinfo/verinfo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | version_file=$1 6 | out_file=$2 7 | 8 | major=`sed -n '1p' $version_file` 9 | minor=`sed -n '2p' $version_file` 10 | 11 | commit=`git rev-list --count HEAD 2>/dev/null || echo 0` 12 | if [ x"$commit" = 0 ]; then 13 | date=0 14 | else 15 | date=`date +'%d%m'%y` 16 | fi 17 | 18 | mkdir -p -- "${out_file%/*}" 19 | t=$(mktemp "$out_file".XXXXXXXX) 20 | trap 'rm -f -- "$out_file"' EXIT 21 | 22 | echo "#ifndef VERSION_INFO_H_" > $t 23 | echo "#define VERSION_INFO_H_" >> $t 24 | echo "" >> $t 25 | echo "#define VERSION_STR \"$major.$minor.$commit.$date\"" >> $t 26 | echo "" >> $t 27 | echo "#endif" >> $t 28 | 29 | if test -e "$out_file" && cmp -s "$out_file" "$s"; then 30 | rm -- "$t" 31 | else 32 | mv -f -- "$t" "$out_file" 33 | fi 34 | trap - EXIT 35 | -------------------------------------------------------------------------------- /src/version.in: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | --------------------------------------------------------------------------------