--- /dev/null
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+
+PROJECT(capi-trackrenderer-tv)
+SET(description "new multimedia player, object-oriented model")
+SET(PC_NAME "capi-trackrenderer-tv")
+SET(PC_LDFLAGS "-ltrackrenderer")
+
+SET(INC_DIR ${PROJECT_SOURCE_DIR}/include/)
+INCLUDE_DIRECTORIES(${INC_DIR})
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+CONFIGURE_FILE(capi-trackrenderer-tv.pc.in
+ capi-trackrenderer-tv.pc
+ @ONLY
+)
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/capi-trackrenderer-tv.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+ADD_SUBDIRECTORY(src)
+
+OPTION(TRACKRENDERER_BUILD_UT "Build capi-trackrenderer-tv ut codes" OFF)
+IF(TRACKRENDERER_BUILD_UT)
+ADD_SUBDIRECTORY(ut)
+ENDIF(TRACKRENDERER_BUILD_UT)
+
+IF(UNIX)
+ADD_CUSTOM_TARGET (distclean @echo cleaning for source distribution)
+ADD_CUSTOM_COMMAND(
+ DEPENDS clean
+ COMMENT "distribution clean"
+ COMMAND find
+ ARGS .
+ -not -name config.cmake -and \(
+ -name tester.c -or
+ -name Testing -or
+ -name CMakeFiles -or
+ -name cmake.depends -or
+ -name cmake.check_depends -or
+ -name CMakeCache.txt -or
+ -name cmake.check_cache -or
+ -name *.cmake -or
+ -name Makefile -or
+ -name core -or
+ -name core.* -or
+ -name gmon.out -or
+ -name install_manifest.txt -or
+ -name *.pc -or
+ -name *~ \)
+ | grep -v TC | xargs rm -rf
+ TARGET distclean
+ VERBATIM
+)
+ENDIF(UNIX)
+
+MESSAGE( STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR} )
+MESSAGE( STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR} )
+MESSAGE( STATUS "LIB_INSTALL_DIR: " ${LIB_INSTALL_DIR} )
+MESSAGE( STATUS "INC_DIR: " ${INC_DIR} )
--- /dev/null
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.\r
+\r
+ Apache License\r
+ Version 2.0, January 2004\r
+ http://www.apache.org/licenses/\r
+\r
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r
+\r
+ 1. Definitions.\r
+\r
+ "License" shall mean the terms and conditions for use, reproduction,\r
+ and distribution as defined by Sections 1 through 9 of this document.\r
+\r
+ "Licensor" shall mean the copyright owner or entity authorized by\r
+ the copyright owner that is granting the License.\r
+\r
+ "Legal Entity" shall mean the union of the acting entity and all\r
+ other entities that control, are controlled by, or are under common\r
+ control with that entity. For the purposes of this definition,\r
+ "control" means (i) the power, direct or indirect, to cause the\r
+ direction or management of such entity, whether by contract or\r
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the\r
+ outstanding shares, or (iii) beneficial ownership of such entity.\r
+\r
+ "You" (or "Your") shall mean an individual or Legal Entity\r
+ exercising permissions granted by this License.\r
+\r
+ "Source" form shall mean the preferred form for making modifications,\r
+ including but not limited to software source code, documentation\r
+ source, and configuration files.\r
+\r
+ "Object" form shall mean any form resulting from mechanical\r
+ transformation or translation of a Source form, including but\r
+ not limited to compiled object code, generated documentation,\r
+ and conversions to other media types.\r
+\r
+ "Work" shall mean the work of authorship, whether in Source or\r
+ Object form, made available under the License, as indicated by a\r
+ copyright notice that is included in or attached to the work\r
+ (an example is provided in the Appendix below).\r
+\r
+ "Derivative Works" shall mean any work, whether in Source or Object\r
+ form, that is based on (or derived from) the Work and for which the\r
+ editorial revisions, annotations, elaborations, or other modifications\r
+ represent, as a whole, an original work of authorship. For the purposes\r
+ of this License, Derivative Works shall not include works that remain\r
+ separable from, or merely link (or bind by name) to the interfaces of,\r
+ the Work and Derivative Works thereof.\r
+\r
+ "Contribution" shall mean any work of authorship, including\r
+ the original version of the Work and any modifications or additions\r
+ to that Work or Derivative Works thereof, that is intentionally\r
+ submitted to Licensor for inclusion in the Work by the copyright owner\r
+ or by an individual or Legal Entity authorized to submit on behalf of\r
+ the copyright owner. For the purposes of this definition, "submitted"\r
+ means any form of electronic, verbal, or written communication sent\r
+ to the Licensor or its representatives, including but not limited to\r
+ communication on electronic mailing lists, source code control systems,\r
+ and issue tracking systems that are managed by, or on behalf of, the\r
+ Licensor for the purpose of discussing and improving the Work, but\r
+ excluding communication that is conspicuously marked or otherwise\r
+ designated in writing by the copyright owner as "Not a Contribution."\r
+\r
+ "Contributor" shall mean Licensor and any individual or Legal Entity\r
+ on behalf of whom a Contribution has been received by Licensor and\r
+ subsequently incorporated within the Work.\r
+\r
+ 2. Grant of Copyright License. Subject to the terms and conditions of\r
+ this License, each Contributor hereby grants to You a perpetual,\r
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+ copyright license to reproduce, prepare Derivative Works of,\r
+ publicly display, publicly perform, sublicense, and distribute the\r
+ Work and such Derivative Works in Source or Object form.\r
+\r
+ 3. Grant of Patent License. Subject to the terms and conditions of\r
+ this License, each Contributor hereby grants to You a perpetual,\r
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+ (except as stated in this section) patent license to make, have made,\r
+ use, offer to sell, sell, import, and otherwise transfer the Work,\r
+ where such license applies only to those patent claims licensable\r
+ by such Contributor that are necessarily infringed by their\r
+ Contribution(s) alone or by combination of their Contribution(s)\r
+ with the Work to which such Contribution(s) was submitted. If You\r
+ institute patent litigation against any entity (including a\r
+ cross-claim or counterclaim in a lawsuit) alleging that the Work\r
+ or a Contribution incorporated within the Work constitutes direct\r
+ or contributory patent infringement, then any patent licenses\r
+ granted to You under this License for that Work shall terminate\r
+ as of the date such litigation is filed.\r
+\r
+ 4. Redistribution. You may reproduce and distribute copies of the\r
+ Work or Derivative Works thereof in any medium, with or without\r
+ modifications, and in Source or Object form, provided that You\r
+ meet the following conditions:\r
+\r
+ (a) You must give any other recipients of the Work or\r
+ Derivative Works a copy of this License; and\r
+\r
+ (b) You must cause any modified files to carry prominent notices\r
+ stating that You changed the files; and\r
+\r
+ (c) You must retain, in the Source form of any Derivative Works\r
+ that You distribute, all copyright, patent, trademark, and\r
+ attribution notices from the Source form of the Work,\r
+ excluding those notices that do not pertain to any part of\r
+ the Derivative Works; and\r
+\r
+ (d) If the Work includes a "NOTICE" text file as part of its\r
+ distribution, then any Derivative Works that You distribute must\r
+ include a readable copy of the attribution notices contained\r
+ within such NOTICE file, excluding those notices that do not\r
+ pertain to any part of the Derivative Works, in at least one\r
+ of the following places: within a NOTICE text file distributed\r
+ as part of the Derivative Works; within the Source form or\r
+ documentation, if provided along with the Derivative Works; or,\r
+ within a display generated by the Derivative Works, if and\r
+ wherever such third-party notices normally appear. The contents\r
+ of the NOTICE file are for informational purposes only and\r
+ do not modify the License. You may add Your own attribution\r
+ notices within Derivative Works that You distribute, alongside\r
+ or as an addendum to the NOTICE text from the Work, provided\r
+ that such additional attribution notices cannot be construed\r
+ as modifying the License.\r
+\r
+ You may add Your own copyright statement to Your modifications and\r
+ may provide additional or different license terms and conditions\r
+ for use, reproduction, or distribution of Your modifications, or\r
+ for any such Derivative Works as a whole, provided Your use,\r
+ reproduction, and distribution of the Work otherwise complies with\r
+ the conditions stated in this License.\r
+\r
+ 5. Submission of Contributions. Unless You explicitly state otherwise,\r
+ any Contribution intentionally submitted for inclusion in the Work\r
+ by You to the Licensor shall be under the terms and conditions of\r
+ this License, without any additional terms or conditions.\r
+ Notwithstanding the above, nothing herein shall supersede or modify\r
+ the terms of any separate license agreement you may have executed\r
+ with Licensor regarding such Contributions.\r
+\r
+ 6. Trademarks. This License does not grant permission to use the trade\r
+ names, trademarks, service marks, or product names of the Licensor,\r
+ except as required for reasonable and customary use in describing the\r
+ origin of the Work and reproducing the content of the NOTICE file.\r
+\r
+ 7. Disclaimer of Warranty. Unless required by applicable law or\r
+ agreed to in writing, Licensor provides the Work (and each\r
+ Contributor provides its Contributions) on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r
+ implied, including, without limitation, any warranties or conditions\r
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r
+ PARTICULAR PURPOSE. You are solely responsible for determining the\r
+ appropriateness of using or redistributing the Work and assume any\r
+ risks associated with Your exercise of permissions under this License.\r
+\r
+ 8. Limitation of Liability. In no event and under no legal theory,\r
+ whether in tort (including negligence), contract, or otherwise,\r
+ unless required by applicable law (such as deliberate and grossly\r
+ negligent acts) or agreed to in writing, shall any Contributor be\r
+ liable to You for damages, including any direct, indirect, special,\r
+ incidental, or consequential damages of any character arising as a\r
+ result of this License or out of the use or inability to use the\r
+ Work (including but not limited to damages for loss of goodwill,\r
+ work stoppage, computer failure or malfunction, or any and all\r
+ other commercial damages or losses), even if such Contributor\r
+ has been advised of the possibility of such damages.\r
+\r
+ 9. Accepting Warranty or Additional Liability. While redistributing\r
+ the Work or Derivative Works thereof, You may choose to offer,\r
+ and charge a fee for, acceptance of support, warranty, indemnity,\r
+ or other liability obligations and/or rights consistent with this\r
+ License. However, in accepting such obligations, You may act only\r
+ on Your own behalf and on Your sole responsibility, not on behalf\r
+ of any other Contributor, and only if You agree to indemnify,\r
+ defend, and hold each Contributor harmless for any liability\r
+ incurred by, or claims asserted against, such Contributor by reason\r
+ of your accepting any such warranty or additional liability.\r
+\r
+ END OF TERMS AND CONDITIONS\r
+\r
+ APPENDIX: How to apply the Apache License to your work.\r
+\r
+ To apply the Apache License to your work, attach the following\r
+ boilerplate notice, with the fields enclosed by brackets "[]"\r
+ replaced with your own identifying information. (Don't include\r
+ the brackets!) The text should be enclosed in the appropriate\r
+ comment syntax for the file format. We also recommend that a\r
+ file or class name and description of purpose be included on the\r
+ same "printed page" as the copyright notice for easier\r
+ identification within third-party archives.\r
+\r
+ Copyright [yyyy] [name of copyright owner]\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
--- /dev/null
+#* * PlusPlayer * * #\r
+ PlusPlayer is a new multimedia player object-oriented designed.\r
+\r
+## Notice\r
+ * 18_Plusplayer\r
+ - Confluence\r
+ - SW High Level Design doc.\r
+\r
+## Goals ##\r
+ * Improve maintainability / extensibility / reusability\r
+ - object oriented design\r
+ - separate the modules into high-variation part and low-variation part\r
+ - decoupling between each streaming services\r
+ * Simplification\r
+ - simplified pipeline , creates static pipeline\r
+ * Easy to upgrade\r
+ - support downloadable features (planned)\r
+\r
+## Architecture Design ##\r
+ * PlusPlayer consists of TypeFinder / TrackSource / TrackRenderer / DefaultPlayer objects.\r
+ * TypeFinder\r
+ - Probing source type by inspecting probing data\r
+ - use gstreamer typefinder plugin\r
+ * TrackSource\r
+ - Fetching data from server , demuxing , controling demuxed packet buffers\r
+ - a pipeline consists of `src - typefinder - demuxer - multiqueue - inputselector - fakesink`\r
+ - plz refer to `${plusplayer_workspace}/docs/dot/plusplayer_src_start.png`\r
+ * TrackRenderer\r
+ - maintains a pipeline consists of `appsrc - omx - sink` elements\r
+ - plz refer to `${plusplayer_workspace}/docs/dot/plusplayer_renderer_start.png`\r
+ * Class diagram\r
+ * ${plusplayer_workspace}/docs/class_diagram.plantuml\r
+ * or you can directly download it from [class_diagram.plantuml]\r
+\r
+## Development ##\r
+ * GitHub Project :\r
+ * All the milestones / activities (Codes , CodeReview , Design , Issues , etc) are being uploaded here\r
+ * Codereview on Github\r
+ - Each Developers should fork master branch and work on their own forked project.\r
+ - developer should submit pull-request to apply their changes\r
+ * all developers must sync their forked project with the latest master prior to sumit pull-request. so that we can avoid merge conflicts.\r
+ - everyone can join the codereviews\r
+ * the coding style follows Google C++ coding style guide. refer to [CODING STYLE](### coding-style)\r
+ * Test Driven Development (use gtest - plusplayer_ut -for their implementation and verifications)\r
+\r
+#### GTest guide ####\r
+ * To write Unit test case for PlusPlayer\r
+ * docs/gtest_guide.md\r
+ * Check Reference section of above link for more detailed documents.\r
+ * example\r
+ ```\r
+ sh-3.2# plusplayer_ut --gtest_filter="AvPlusPlayerTest.play"\r
+ ```\r
+\r
+#### Coding Rule Check ####\r
+ * check your coding style using **cpplint.py** before uploading your codes\r
+ > e.g) $ **cpplint.py** cpp_style_test.cpp\r
+ * Or check your coding style and fix your codes automatically using **clang-format**\r
+ > e.g) $ **clang-format-3.4** cpp_style_test.cpp -style=google **-i**\r
+\r
+#### Build ####\r
+ > 1. Clone or Download plusplayer package.\r
+ > 2. $ gbs -c ~/gbsconf/gbs_3_0.conf build -A armv7l --include-all --clean -B ~/GBS_ROOT_PLUSPLAYER\r
+\r
+#### Understanding the codes ####\r
+ to understand PlusPlayer concept quickly\r
+ plz refer to\r
+ - ${plusplayer_workspace}/ut/porting/webapi-plugins-avplay/src/avplusplayer.cc/h\r
+ - ${plusplayer_workspace}/src/player/defaultplayer.cpp/h\r
+ - ${plusplayer_workspace}/docs/class_diagram.txt\r
+\r
+#### CODING STYLE ####\r
+ 'Google C++ Style Guide' based coding.\r
+ > Translated(Korean)\r
+ > Original(English)\r
+ > [[CodingRule.md]]\r
--- /dev/null
+export BMS_SERVICE_DIR=~/bms-service\r
+\r
+#rm -f ./*build.log\r
+tizen clean -- ./src/plusplayer-core\r
+tizen clean -- ./src/tracksource\r
+tizen clean -- ./src/plusplayer\r
+\r
+#Debug mode\r
+#tizen build-native -C Debug -a arm -- ./plusplayer-core -j 16 > plusplayer-core-build.log\r
+tizen build-native -C Debug -a arm -c gcc -- ./src/plusplayer-core\r
+tizen build-native -C Debug -a arm -c gcc -- ./src/tracksource\r
+tizen build-native -C Debug -a arm -c gcc -- ./src/plusplayer\r
+\r
+tizen cli-config "profiles.path=`eval echo $HOME`/tizen-studio-data/profile/profiles.xml"\r
+tizen security-profiles add -n ABS -a packaging/author_test.p12 -p author_test -d packaging/tizen-distributor-partner-manufacturer-signer.p12 -dp tizenpkcs12passfordsigner\r
+\r
+#Release mode\r
+tizen build-native -C Release -a arm -c gcc -- ./src/plusplayer-core\r
+tizen build-native -C Release -a arm -c gcc -- ./src/tracksource\r
+tizen build-native -C Release -a arm -c gcc -- ./src/plusplayer\r
+\r
+tizen cli-config "profiles.path=`eval echo $HOME`/tizen-studio-data/profile/profiles.xml"\r
+tizen security-profiles add -n ABS -a packaging/author_test.p12 -p author_test -d packaging/tizen-distributor-partner-manufacturer-signer.p12 -dp tizenpkcs12passfordsigner\r
+\r
+rm -rf ${BMS_SERVICE_DIR}/plusplayer-api/*\r
+cp -rf ./include/plusplayer ${BMS_SERVICE_DIR}/plusplayer-api\r
+cp -vf ./src/plusplayer-core/Release/libplusplayercore_tvplus.so ${BMS_SERVICE_DIR}/lib/\r
+cp -vf ./src/tracksource/Release/libtracksource_tvplus.so ${BMS_SERVICE_DIR}/lib/ \r
+cp -vf ./src/plusplayer/Release/libplusplayer_tvplus.so ${BMS_SERVICE_DIR}/lib/\r
+cp -vf ./config/tvplus/plusplayer.ini ${BMS_SERVICE_DIR}/res/\r
+\r
+tizen clean -- ./src/plusplayer-core\r
+tizen clean -- ./src/tracksource\r
+tizen clean -- ./src/plusplayer\r
--- /dev/null
+\r
+prefix = @PREFIX@\r
+exec_prefix = /usr\r
+libdir = @LIB_INSTALL_DIR@\r
+\r
+Name: @PC_NAME@\r
+Description: @PACKAGE_DESCRYPTION@\r
+Version: @VERSION@\r
+Libs: -L${libdir} @PC_LDFLAGS@\r
+Cflags : @PC_CFLAGS@\r
--- /dev/null
+@startuml\r
+\r
+note "AS-IS" as PAGE1 #99FF99\r
+\r
+class PCTask {\r
+ # virtual bool t_OnEvent();\r
+ # virtual bool t_Create(void);\r
+ # virtual void t_Destroy(void);\r
+}\r
+\r
+interface IPlayer {\r
+ virtual bool Play()=0;\r
+ virtual bool Stop()=0;\r
+ virtual bool Pause()=0;\r
+ virtual bool Resume()=0;\r
+}\r
+\r
+interface IAVPlayHelper {\r
+ + virtual bool IsShutdown() = 0;\r
+ + virtual bool HandleOnEvent(const TSEvent* pEvent) = 0; \r
+ + virtual bool GetPlayTime(unsigned long& time) = 0;\r
+ + virtual bool GetCurrentPlayProgram(std::string& outData) = 0;\r
+ + virtual void GetDuration(int& pDuration) = 0;\r
+}\r
+\r
+class TCAVPlayer {\r
+ - IAVPlayHelper* m_pAVCurrentHelper;\r
+\r
+ - IAVPlayHelper* m_pTVPlusAVPlayHelper;\r
+ - IAVPlayHelper* m_pATSC30Helper;\r
+}\r
+note top : "BmsAvPlayer.h"\r
+\r
+IPlayer <|-- TCAVPlayer\r
+PCTask <|-- TCAVPlayer\r
+\r
+TCAVPlayer o-- TvplusAVPlayHelper\r
+TCAVPlayer o-- ATSC3AVPlayHelper\r
+\r
+class TvplusAVPlayHelper {\r
+}\r
+\r
+IAVPlayHelper <|-- TvplusAVPlayHelper\r
+\r
+class ATSC3AVPlayHelper {\r
+}\r
+\r
+IAVPlayHelper <|-- ATSC3AVPlayHelper\r
+\r
+\r
+package "capi-media-player" {\r
+}\r
+\r
+package "libmmplayer" {\r
+}\r
+\r
+TvplusAVPlayHelper --> "capi-media-player"\r
+ATSC3AVPlayHelper --> "capi-media-player"\r
+\r
+"capi-media-player" --> "libmmplayer"\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+note "TO-BE" as PAGE1 #99FF99\r
+\r
+package "plusplayer" {\r
+}\r
+\r
+package "bms-service" {\r
+\r
+class PCTask {\r
+ # virtual bool t_OnEvent();\r
+ # virtual bool t_Create(void);\r
+ # virtual void t_Destroy(void);\r
+}\r
+\r
+interface IPlayer {\r
+ virtual bool Play()=0;\r
+ virtual bool Stop()=0;\r
+ virtual bool Pause()=0;\r
+ virtual bool Resume()=0;\r
+}\r
+\r
+interface IAVPlayHelper {\r
+ + virtual bool IsShutdown() = 0;\r
+ + virtual bool HandleOnEvent(const TSEvent* pEvent) = 0; \r
+ + virtual bool GetPlayTime(unsigned long& time) = 0;\r
+ + virtual bool GetCurrentPlayProgram(std::string& outData) = 0;\r
+ + virtual void GetDuration(int& pDuration) = 0;\r
+}\r
+\r
+class TCAVPlayer {\r
+ - IAVPlayHelper* m_pAVCurrentHelper;\r
+\r
+ - IAVPlayHelper* m_pTVPlusAVPlayHelper;\r
+ - IAVPlayHelper* m_pATSC30Helper;\r
+}\r
+note top of TCAVPlayer : "BmsAvPlayer.h"\r
+\r
+IPlayer <|-- TCAVPlayer\r
+PCTask <|-- TCAVPlayer\r
+\r
+TCAVPlayer o-- TvplusPlusplayerHelper\r
+TCAVPlayer o-- ATSC3PlusplayerHelper\r
+\r
+class TvplusPlusplayerHelper {\r
+}\r
+\r
+IAVPlayHelper <|-- TvplusPlusplayerHelper\r
+\r
+class ATSC3PlusplayerHelper {\r
+}\r
+\r
+IAVPlayHelper <|-- ATSC3PlusplayerHelper\r
+\r
+}\r
+\r
+TvplusPlusplayerHelper *--> "plusplayer"\r
+ATSC3PlusplayerHelper *--> "plusplayer"\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title TrackRenderer Adapter\r
+\r
+DefaultPlayer *-- TrackRendererAdapter\r
+class TrackRendererAdapter\r
+note left : RAII container of Trackrenderer instance.\r
+TrackRendererAdapter --> TrackRendererCapi\r
+TrackRendererCapi --> TrackRenderer\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title AS-IS\r
+\r
+package "capi-media-player" {\r
+}\r
+\r
+package "webapi-plugins-avplay" {\r
+ class AVPlayInstance {\r
+ }\r
+\r
+ class AVPlayerAdapter {\r
+ }\r
+}\r
+\r
+package "webapi-plugins-avplay" {\r
+ class AVPlayInstance {\r
+ }\r
+\r
+ class AVPlayerAdapter {\r
+ }\r
+\r
+ AVPlayInstance *-- AVPlayerAdapter\r
+ AVPlayerAdapter *--> "capi-media-player"\r
+}\r
+\r
+newpage\r
+\r
+title TO-BE\r
+\r
+package "capi-media-player" {\r
+}\r
+\r
+package "webapi-plugins-avplay" {\r
+ class AVPlayInstance {\r
+ }\r
+\r
+ class AVPlayerAdapter {\r
+ }\r
+\r
+ interface AvPlay {\r
+ }\r
+\r
+ AVPlayInstance *-- AvPlay\r
+ AvPlay <|.. AVPlayerAdapter\r
+ AVPlayerAdapter *-- "capi-media-player"\r
+ AvPlay <|.. AvPlusPlayer\r
+ PlusPlayerEventListener --* AvPlusPlayer\r
+}\r
+\r
+package "plusplayer" {\r
+}\r
+\r
+AvPlusPlayer *--> "plusplayer" : use\r
+\r
+@enduml@startuml\r
+\r
+package "webapi-plugins-avplay" {\r
+ class AVPlayInstance {\r
+ }\r
+\r
+ class AVPlayerAdapter {\r
+ }\r
+\r
+ interface AvPlay {\r
+ }\r
+\r
+ AVPlayInstance *-- AvPlay\r
+ AvPlay <|.. AVPlayerAdapter\r
+ AVPlayerAdapter *-- "capi-media-player"\r
+ AvPlay <|.. AvPlusPlayer\r
+ PlusPlayerEventListener --* AvPlusPlayer\r
+}\r
+\r
+package "plusplayer" {\r
+}\r
+\r
+AvPlusPlayer *--> "plusplayer" : use\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+'HTML5 VideoTag case\r
+package "webmedia" {\r
+ class OriginalMediaPort {\r
+ }\r
+ interface MediaPort {\r
+ }\r
+ class PlusPlayerMediaPort {\r
+ /' Adapter '/\r
+ }\r
+ MediaPort <|.. OriginalMediaPort\r
+ MediaPort <|. PlusPlayerMediaPort\r
+ "capi-media-player" --* OriginalMediaPort\r
+}\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+class TvplusPlusplayerHelper {\r
+\r
+}\r
+\r
+package "plusplayer" {\r
+class Plusplayer {\r
+}\r
+}\r
+\r
+class CBmsDrmHandler {\r
+\r
+}\r
+\r
+package "DrmManager" {\r
+ class DrmManager {\r
+ }\r
+}\r
+\r
+package "IEME" {\r
+\r
+}\r
+\r
+TvplusPlusplayerHelper *-- Plusplayer\r
+TvplusPlusplayerHelper *-- CBmsDrmHandler\r
+CBmsDrmHandler *-- DrmManager\r
+DrmManager --> IEME\r
+Plusplayer --> IEME\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title DefaultPlayer\r
+' DefaultPlayer\r
+ interface PlusPlayer {\r
+ }\r
+ interface TrackSource {\r
+ }\r
+ PlusPlayer <|.. DefaultPlayer\r
+ \r
+' DefaultPlayer\r
+ DefaultPlayer "1" *-- "1" StateManager\r
+ DefaultPlayer "1" *-- "1" MsgHandler\r
+ DefaultPlayer "1" *--- "1" Feeder\r
+ DefaultPlayer "1" *--- "1" TypeFinder\r
+ DefaultPlayer "1" *--- "1" TrackSourceCompositor\r
+ TrackSource <|-- TrackSourceCompositor\r
+ DefaultPlayer "1" *--- "1" TrackRenderer\r
+ DefaultPlayer "1" *- "1" TrackSourceEventListener\r
+ DefaultPlayer "1" *- "1" TrackRendererEventListener\r
+ DefaultPlayer ..> Track\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title TrackRenderer\r
+\r
+class TrackRenderer {\r
+ + bool Open()\r
+ + bool Close()\r
+ + bool Prepare()\r
+ + bool Start()\r
+ + bool Stop()\r
+ + void SetDisplayMode()\r
+ + bool SetDisplay()\r
+ + bool SetDisplayRoi()\r
+ + bool SetVisible()\r
+ + bool SetTrack()\r
+ + bool SubmitPacket(const DecoderInputBufferPtr& data)\r
+ + bool GetPlayingTime()\r
+\r
+ - std::vector<Track> trackinfo_;\r
+ - std::unique_ptr<Pipeline<T>> pipeline_;\r
+ - AvocHandle avoc_id_;\r
+\r
+}\r
+\r
+class Display {\r
+ + void SetDisplayMode()\r
+ + bool SetDisplay()\r
+ + bool SetDisplayRoi()\r
+ + bool SetVisible()\r
+}\r
+\r
+class DecoderInputBuffer {\r
+ - GstBuffer* buffer_\r
+}\r
+\r
+class ResourceManager {\r
+ + bool Alloc()\r
+ + bool Dealloc()\r
+ - static rm_cb_result ResourceConflictCallback_()\r
+}\r
+\r
+ DefaultPlayer "1" *-- "1" TrackRenderer\r
+ TrackRenderer "1" o-- "1" Display\r
+ TrackRenderer *-- ResourceManager\r
+ TrackRenderer *-- Track\r
+ TrackRenderer ..> DecoderInputBuffer\r
+ TrackRenderer *-- Pipeline\r
+\r
+ package Gstreamer {\r
+ }\r
+ Pipeline --> Gstreamer\r
+ package Avoc {\r
+ }\r
+ TrackRenderer --> Avoc\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+' TrackSource\r
+title TrackSource & Compositor\r
+\r
+ interface TrackSource {\r
+ + virtual bool AddSource()\r
+ + virtual bool StopSource()\r
+ + virtual bool DeleteSource()\r
+ + virtual bool Open()\r
+ + virtual bool Close()\r
+ + virtual bool Prepare()\r
+ + virtual bool Start()\r
+ + virtual bool Stop()\r
+ + virtual bool Seek()\r
+ + virtual bool SelectTrack()\r
+ + virtual bool Deactivate()\r
+ + virtual bool GetTrackInfo()\r
+ + virtual bool GetDuration()\r
+ + virtual bool SetBufferConfig()\r
+ + virtual bool RegisterListener(DecoderInputBuffer*)\r
+ + virtual bool RegisterEventListener(EventListener*)\r
+\r
+ }\r
+\r
+ class TrackSourceCompositor {\r
+ + bool AddSource()\r
+ + bool StopSource()\r
+ + bool DeleteSource()\r
+ }\r
+\r
+ class HlsTrackSource {\r
+ + bool Open()\r
+ + bool Close()\r
+ + bool Prepare()\r
+ + bool Start()\r
+ + bool Stop()\r
+ + bool Seek()\r
+ }\r
+\r
+ class DashTrackSource {\r
+ + bool Open()\r
+ + bool Close()\r
+ + bool Prepare()\r
+ + bool Start()\r
+ + bool Stop()\r
+ + bool Seek()\r
+ }\r
+\r
+ class HttpTrackSource {\r
+ + bool Open()\r
+ + bool Close()\r
+ + bool Prepare()\r
+ + bool Start()\r
+ + bool Stop()\r
+ + bool Seek()\r
+ - GstElement* pipeline;\r
+ }\r
+\r
+ class ExternalSubtitleSource {\r
+ + bool Open()\r
+ + bool Close()\r
+ + bool Prepare()\r
+ + bool Start()\r
+ + bool Stop()\r
+ + bool Seek()\r
+ }\r
+\r
+ TrackSource <|.. TrackSourceCompositor\r
+ TrackSource <|.. HlsTrackSource\r
+ TrackSource <|.. DashTrackSource\r
+ TrackSource <|.. HttpTrackSource\r
+ TrackSource <|.. ExternalSubtitleSource\r
+ TrackSource --o TrackSourceCompositor\r
+\r
+ package Gstreamer {\r
+ }\r
+\r
+ HlsTrackSource --> Gstreamer\r
+ DashTrackSource --> Gstreamer\r
+ HttpTrackSource --> Gstreamer\r
+ ExternalSubtitleSource --> Gstreamer\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+package "esplusplayer" {\r
+\r
+ interface EsPlusPlayer {\r
+ }\r
+\r
+ EsPlusPlayer <|.. EsPlayer\r
+\r
+ class EsEventListener {\r
+ }\r
+\r
+ class AudioStream {\r
+ }\r
+\r
+ class VideoStream {\r
+ }\r
+\r
+ class EsPacket {\r
+ }\r
+\r
+ class TrackRenderer {\r
+ }\r
+\r
+' EsPlayer\r
+ EsPlayer "1" *-- "1" StateManager\r
+ EsPlayer "1" *-- "1" TrackRenderer\r
+ EsPlayer "1" *-- "1" MsgHandler\r
+ EsPlayer "1" *-- "1" TrackRendererEventListener\r
+ EsPlayer ..> AudioStream\r
+ EsPlayer ..> VideoStream\r
+ EsPlayer ..> EsPacket\r
+ EsPlayer ..> Track\r
+ EsPlayer --- DecoderInputBuffer\r
+ \r
+ AudioStream ..> Track \r
+ VideoStream ..> Track \r
+\r
+' TrackRenderer\r
+ TrackRenderer "1" o--- "1" Display\r
+ TrackRenderer *-- ResourceManager\r
+ TrackRenderer *-- Track\r
+ TrackRenderer ...> DecoderInputBuffer\r
+ TrackRenderer *- Pipeline\r
+\r
+ Pipeline --> GstObjectGuard\r
+ \r
+ ResourceManager --> Resource\r
+ ResourceManager *-- Resource\r
+}\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+package "plusplayer" {\r
+\r
+ interface PlusPlayer {\r
+ }\r
+\r
+ PlusPlayer <|.. DefaultPlayer\r
+\r
+ class TrackRenderer {\r
+ }\r
+\r
+ ResourceManager --> Resource\r
+ ResourceManager *-- Resource\r
+\r
+ class DecoderInputBufferListener {\r
+ }\r
+\r
+ class TrackSourceCompositor {\r
+ }\r
+\r
+ class TypeFinder {\r
+ }\r
+\r
+ TypeFinder *-- SourceType\r
+ DecoderInputBufferListener <|-- Feeder\r
+\r
+' DefaultPlayer\r
+ DefaultPlayer "1" *-- "1" Feeder\r
+ DefaultPlayer "1" *- "1" StateManager\r
+ DefaultPlayer "1" *-- "1" TypeFinder\r
+ DefaultPlayer "1" *-- "1" TrackSourceCompositor\r
+ DefaultPlayer "1" *--- "1" TrackRenderer\r
+ DefaultPlayer "1" *-- "1" MsgHandler\r
+ DefaultPlayer "1" *-- "1" TrackSourceEventListener\r
+ DefaultPlayer "1" *-- "1" TrackRendererEventListener\r
+ DefaultPlayer ..> Track\r
+\r
+' TrackSource\r
+ interface TrackSource {\r
+ }\r
+\r
+ class HlsTrackSource {\r
+ }\r
+ TrackSource <|.. TrackSourceCompositor\r
+ TrackSource <|.. HlsTrackSource\r
+ TrackSource <|.. ExternalSubtitleSource\r
+ TrackSource --o TrackSourceCompositor\r
+ HlsTrackSource *-- Track\r
+ HlsTrackSource --- DecoderInputBuffer\r
+ ExternalSubtitleSource --- DecoderInputBuffer\r
+\r
+ Pipeline --> GstObjectGuard\r
+ HlsTrackSource --> GstObjectGuard\r
+ ExternalSubtitleSource --> GstObjectGuard\r
+\r
+ TrackRenderer "1" o--- "1" Display\r
+ TrackRenderer *-- ResourceManager\r
+ TrackRenderer *-- Track\r
+ TrackRenderer ...> DecoderInputBuffer\r
+ TrackRenderer *- Pipeline\r
+}\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+package "plusplayer" {\r
+\r
+ interface PlusPlayer {\r
+ }\r
+\r
+ PlusPlayer <|.. DefaultPlayer\r
+\r
+ class TrackRenderer {\r
+ }\r
+ DefaultPlayer "1" *-- "1" TrackSourceCompositor\r
+ DefaultPlayer "1" *-- "1" TrackRenderer\r
+\r
+ class TrackSourceCompositor {\r
+ }\r
+\r
+' TrackSource\r
+ interface TrackSource {\r
+ }\r
+\r
+ class HlsTrackSource {\r
+ }\r
+ TrackSource <|.. TrackSourceCompositor\r
+ TrackSource <|.. HlsTrackSource\r
+ TrackSource <|.. ExternalSubtitleSource\r
+ TrackSource --o TrackSourceCompositor\r
+}\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+## **CODING STYLE** ##\r
+ * All codes follow 'Google C++ Style Guide' \r
+ - [English](https://google.github.io/styleguide/cppguide.html)\r
+ * some major rules are below\r
+\r
+---\r
+\r
+### § Naming ###\r
+ Give as descriptive a name as possible.\r
+ \r
+#### 1. Namespace/File/Variable/Struct Data member ####\r
+ * All lower case(mandatory) + between underscore(optional)\r
+ * e.g) mediaplayer.h , trackrenderer , track_renderer\r
+\r
+#### 2. Class Data Members\r
+ * Private attributes : Variable names + Trailing underscore("_")\r
+ * e.g) tablename_ , tracksource_ , track_source_\r
+ * Public attributes - Not Allowed.\r
+\r
+#### 3. Type Names - Class,Struct,Type Aliases,Enums and type template parameters ####\r
+ * Names start with a capital letter and have a capital letter for each new word\r
+ * No Underscore\r
+ * e.g) MMPlayer(X) , MmPlayer(O)\r
+ * e.g) class MediaPlayer {} ...\r
+ * e.g) struct PlayerContext {} , enum SourceType {...}\r
+\r
+#### 4. Macro Names ####\r
+ * All capitals + Underscore\r
+\r
+#### 5. Constant / Enumerator ####\r
+ * prefix 'k' + TypeNames \r
+ * e.g \r
+ const int kDaysInAWeek = 7; \r
+ enum UrlTableErrors { \r
+ kErrorOutOfMemory \r
+ } \r
+\r
+### § Formating ###\r
+\r
+#### 1. Indentation ####\r
+ * Default indentation : 2 spaces \r
+ * We use spaces for indentation. Do not use tabs in your code.\r
+\r
+#### 2. Class ####\r
+ * Ordering\r
+ * Sections in public - protected - private order\r
+ * method(public/protected/private) -> attribute order\r
+ * public, protected and private keywords : **1** space indentation\r
+\r
+### § Header Files ###\r
+\r
+#### 1. Order of Includes ####\r
+ * Related header, C library, C++ library, other libraries' .h, your project's .h.\r
+\r
+> plz refer to "Google C++ Style Guide" for the rest of detail guide.\r
--- /dev/null
+@startuml\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+' to be updated soon\r
+' this is test\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+/'\r
+skinparam backgroundColor #EEEBDC\r
+skinparam handwritten true\r
+skinparam sequence {\r
+ ArrowColor DeepSkyBlue\r
+ ActorBorderColor DeepSkyBlue\r
+ LifeLineBorderColor blue\r
+ LifeLineBackgroundColor #A9DCDF\r
+ ParticipantBorderColor DeepSkyBlue\r
+ ParticipantBackgroundColor DodgerBlue\r
+ ParticipantFontName Impact\r
+ ParticipantFontSize 17\r
+ ParticipantFontColor #A9DCDF\r
+ ActorBackgroundColor aqua\r
+ ActorFontColor DeepSkyBlue\r
+ ActorFontSize 17\r
+ ActorFontName Aapex\r
+}\r
+'/\r
+\r
+title tvplus case(1) dynamic loading\r
+\r
+participant AppLauncher\r
+participant App\r
+participant BmsService\r
+note over BmsService : daemon\r
+participant Plusplayer\r
+participant PlusplayerLoader\r
+database version_info\r
+\r
+== initialization ==\r
+alt successful case\r
+ AppLauncher -> App : load\r
+ App -> BmsService : play()\r
+ BmsService -> Plusplayer : SetVersion("path")\r
+ Plusplayer -> PlusplayerLoader : SetVersion("path")\r
+ PlusplayerLoader -> version_info : ReadVersion()\r
+ ' 한번에 모두 로딩 or 분할하여.\r
+ PlusplayerLoader -> PlusplayerLoader : LoadProperLibSet_()\r
+ BmsService -> Plusplayer : Create()\r
+else some kind of failure (version mismatch or missing)\r
+end\r
+\r
+newpage tvplus case(2) dynamic linking\r
+\r
+actor PlayerManager\r
+participant ServiceDaemonLauncher\r
+participant App\r
+participant BmsService\r
+note over BmsService : daemon\r
+participant Plusplayer\r
+participant PlusplayerLoader\r
+database version_info\r
+\r
+\r
+== initialization ==\r
+\r
+group compile time\r
+ PlayerManager -> BmsService : update LD_PRELOAD configuration\r
+ note over BmsService : adjust bms.service configuration file\r
+end\r
+\r
+alt successful case\r
+ ServiceDaemonLauncher -> BmsService : load\r
+ note over BmsService : updated libs are dynamically linked\r
+ App -> BmsService : play()\r
+ BmsService -> Plusplayer : SetVersion("path")\r
+ Plusplayer -> PlusplayerLoader : GetVersion("path")\r
+ PlusplayerLoader -> version_info : ReadVersion()\r
+ PlusplayerLoader -> PlusplayerLoader : CheckLibValidation_()\r
+ Plusplayer <-- PlusplayerLoader : success\r
+ BmsService -> Plusplayer : Create()\r
+else some kind of failure (version mismatch or missing)\r
+end\r
+\r
+\r
+\r
+@enduml\r
--- /dev/null
+**GTest guide** \r
+===============\r
+ For unit test for plusplayer\r
+\r
+---\r
+### Reference \r
+- <https://github.com/google/googletest>\r
+\r
+## Assertion \r
+* ASSERT_* : 실패시 해당 테스트를 바로 종료 <br>\r
+* EXPECT_* : 실패하여도 테스트 계속 진행 <br>\r
+\r
+#### Basic Assertions ###\r
+\r
+These assertions do basic true/false condition testing.\r
+\r
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |\r
+|:--------------------|:-----------------------|:-------------|\r
+| `ASSERT_TRUE(`_condition_`)`; | `EXPECT_TRUE(`_condition_`)`; | _condition_ is true |\r
+| `ASSERT_FALSE(`_condition_`)`; | `EXPECT_FALSE(`_condition_`)`; | _condition_ is false |\r
+\r
+#### Binary Comparison ###\r
+\r
+This section describes assertions that compare two values.\r
+\r
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |\r
+|:--------------------|:-----------------------|:-------------|\r
+|`ASSERT_EQ(`_val1_`, `_val2_`);`|`EXPECT_EQ(`_val1_`, `_val2_`);`| _val1_ `==` _val2_ |\r
+|`ASSERT_NE(`_val1_`, `_val2_`);`|`EXPECT_NE(`_val1_`, `_val2_`);`| _val1_ `!=` _val2_ |\r
+|`ASSERT_LT(`_val1_`, `_val2_`);`|`EXPECT_LT(`_val1_`, `_val2_`);`| _val1_ `<` _val2_ |\r
+|`ASSERT_LE(`_val1_`, `_val2_`);`|`EXPECT_LE(`_val1_`, `_val2_`);`| _val1_ `<=` _val2_ |\r
+|`ASSERT_GT(`_val1_`, `_val2_`);`|`EXPECT_GT(`_val1_`, `_val2_`);`| _val1_ `>` _val2_ |\r
+|`ASSERT_GE(`_val1_`, `_val2_`);`|`EXPECT_GE(`_val1_`, `_val2_`);`| _val1_ `>=` _val2_ |\r
+\r
+\r
+#### String Comparison ###\r
+\r
+The assertions in this group compare two **C strings**.<br>\r
+If you want to compare two `string` objects, use `EXPECT_EQ`, `EXPECT_NE`, and etc instead.\r
+\r
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |\r
+|:--------------------|:-----------------------|:-------------|\r
+| `ASSERT_STREQ(`_str1_`, `_str2_`);` | `EXPECT_STREQ(`_str1_`, `_str_2`);` | the two C strings have the same content |\r
+| `ASSERT_STRNE(`_str1_`, `_str2_`);` | `EXPECT_STRNE(`_str1_`, `_str2_`);` | the two C strings have different content |\r
+| `ASSERT_STRCASEEQ(`_str1_`, `_str2_`);`| `EXPECT_STRCASEEQ(`_str1_`, `_str2_`);` | the two C strings have the same content, ignoring case |\r
+| `ASSERT_STRCASENE(`_str1_`, `_str2_`);`| `EXPECT_STRCASENE(`_str1_`, `_str2_`);` | the two C strings have different content, ignoring case |\r
+\r
+## Text Fixtures : Using the Same Data Configuration for Multiple Tests ##\r
+\r
+사용자가 유사한 data를 사용해서 하나 이상의 test를 작성한다면, test fixture를 사용할 수 있다. 이 test fixture를 사용한다는 것은 여러개의 다양한 test를 작성하는 과정에서 같은 object의 configuration을 재사용한다는 것을 의미한다.\r
+\r
+Fixture를 작성할 때에는 아래의 내용대로 수행하면 된다.\r
+\r
+1. ::testing::Test 로부터 class를 derive한다. Sub-class 에서 fixture member에 접근해야 하기 때문에 protected 혹은 public 으로 작성해야 한다. \r
+2. Class 내부에서 사용자가 원하는대로 object들을 선언해 사용한다. \r
+3. 필요하다면, 생성자나 SetUp() function을 작성해둔다. \r
+4. 생성자나 SetUp() function을 정의해서 사용하고 있다면, 해당 function에서 사용했던 resource를 반환하기 위해 소멸자나 TearDown() function을 작성한다. \r
+5. Subroutine 들을 작성한다. \r
+\r
+Fixture를 사용하기 위해서는 TEST() 대신에 TEST_F()를 사용해야만 한다.\r
+TEST()에서는 첫번째 argument가 testcase의 이름이었지만 TEST_F()를 사용할 때는 첫번째 argument로 test fixture class의 이름을 사용해야만 한다.\r
+\r
+**Fixture class 기본 구현 Form**\r
+* 관례에 따라 테스트할 클래스가 Foo라면 이름을 FooTest라고 하는게 좋다.\r
+~~~\r
+class PlusPlayerTest : public ::testing::Test {\r
+public:\r
+ PlusPlayerTest(std::string url)\r
+ : plusplayer_(nullptr), url_(url)\r
+ {\r
+ }\r
+\r
+ void SetUp() override\r
+ {\r
+ plusplayer_ = new PlusPlayer();\r
+ create(url_);\r
+ }\r
+\r
+ void TearDown() override\r
+ {\r
+ destory(plusplayer_);\r
+ }\r
+\r
+private:\r
+ std::string url_;\r
+ PlusPlayer* plusplayer_;\r
+\r
+}\r
+~~~\r
+\r
+**실행 순서** \r
+1. 모든 구글 테스트 플래그 상태를 저장한다. \r
+2. 첫 번째 테스트에 대해 테스트 fixture 객체를 생성한다. \r
+3. 만든 객체를 SetUp()에서 초기화한다. \r
+4. 픽스처 객체에 대해 테스트를 실행한다. \r
+5. TearDown()에서 해당 픽스처를 정리한다. \r
+6. 해당 픽스처를 삭제한다. \r
+7. 모든 구글 테스트 플래그 상태를 복원한다. \r
+8. 모든 테스트를 마칠 때까지 다음 테스트에 대해 위 과정을 반복한다. \r
+\r
+---\r
+\r
+## Arguments \r
+reference\r
+\r
+1. test selection\r
+ * --gtest_list_tests <br>\r
+ > 테스트할 항목을 보여준다. (테스트 실시 X)\r
+ * --gtest_also_run_disabled_tests <br>\r
+ > DISABLED_ 로 막아둔 test case 를 일시적으로 실행\r
+ * --gtest_filter <br>\r
+ > 특정 case 들만 실행 가능<br>\r
+ Ex) --gtest_filter="*.create*" : 모든 TC중에서 create 로 시작하는 모든 TC 실행. <br>\r
+\r
+2. test Execution\r
+ * --gtest_repeat <br>\r
+ > test 반복 가능. -1일 경우 무한히 반복<br>\r
+ --gtest_break_on_failure와 함께 사용하면 실패할 경우 멈춤.\r
+ * --gtest_shuffle <br>\r
+ > 무작위로 실행 가능 (test case 간 dependency 가 없어야 하기 때문이다)<br>\r
+ gtest는 기본적으로 현재시간을 랜덤함수의 시드값으로 사용하나, --gtest_random_seed로 조절가능하다\r
+ * --gtest_random_seed \r
+ > 1 ~ 99999까지의 값을 --gtest_shuffle에서 사용할 랜덤함수의 시드로 사용.\r
+\r
+---\r
+## Reference\r
+ * Gtest Primer(EN)<br> <https://github.com/google/googletest/blob/master/googletest/docs/Primer.md>\r
--- /dev/null
+@startuml\r
+\r
+title reduced downloadable module size by libav-common\n dash/hls/http streaming module 9M to 4.5M 50% decreased\r
+\r
+component "dash gst-plugin" as dash\r
+component "hls gst-plugin" as hls\r
+component "httpdemux gst-plugin" as http\r
+\r
+component "libavformat-dash.so" as dashformat\r
+component "libavformat-hls.so" as hlsformat\r
+component "libavformat-httpdemux.so" as httpformat\r
+\r
+package "libav-common" as libavcommon {\r
+ [libavcodec.so]\r
+ [libavutils.so]\r
+}\r
+\r
+dash --> dashformat : use\r
+http --> httpformat : use\r
+hls --> hlsformat : use\r
+\r
+dashformat --> libavcommon : use\r
+httpformat --> libavcommon : use\r
+hlsformat --> libavcommon : use\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title : module view : uses view\r
+\r
+component [Downloadable module] #Aquamarine\r
+\r
+package "Security" {\r
+ IEME -- [EME]\r
+}\r
+package "System" {\r
+ package "Subsystem" {\r
+ RM -- [ResourceManager]\r
+ }\r
+\r
+ package "Subsystem" {\r
+ AVOC -- [libavoc]\r
+ }\r
+}\r
+\r
+package "EFL" {\r
+ ecore_wayland_api -- [Ecore_Wayland]\r
+}\r
+\r
+package "Gstreamer" {\r
+ [gstreamer] --> [gst-openmax]\r
+ package "gst-plugins-streamingengine" {\r
+ [gst-plugin-dash] #Aquamarine\r
+ [gst-plugin-hls] #Aquamarine\r
+ [gst-plugin-http] #Aquamarine\r
+ [gstreamer] --> [gst-plugin-dash]\r
+ [gstreamer] --> [gst-plugin-hls]\r
+ [gstreamer] --> [gst-plugin-http]\r
+ }\r
+}\r
+\r
+package "Plusplayer" {\r
+ [plusplayer] #Aquamarine\r
+ [tracksource] #Aquamarine\r
+ [plusplayer-core] #Aquamarine\r
+ [plusplayer] --> [tracksource] : use\r
+ [plusplayer] --> [trackrenderer] : use\r
+ [plusplayer] --> [plusplayer-core] : use\r
+ [tracksource] --> [plusplayer-core] : use\r
+ '[tracksource] --> [gstreamer] : use\r
+ [trackrenderer] --> [plusplayer-core] : use\r
+ [trackrenderer] --> RM : use\r
+ '[trackrenderer] --> [gstreamer] : use\r
+ [trackrenderer] --> [ecore_wayland_api] : use\r
+ [trackrenderer] --> [AVOC] : use\r
+ [trackrenderer] --> [IEME] : use\r
+ [plusplayer-core] --> [gstreamer] : use\r
+}\r
+\r
+\r
+package "drmmanager" {\r
+ drmapi -- [DrmManager]\r
+ [DrmManager] --> [IEME] : use\r
+}\r
+\r
+package "webapi-avplay" {\r
+ [avplay] --> [plusplayer]\r
+ [avplay] --> [drmapi]\r
+}\r
+\r
+package "bms-service" {\r
+ [BmsAVPlayer] --> [plusplayer]\r
+ [BmsAVPlayer] --> [drmapi]\r
+}\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+# **PlusPlayer** #\r
+ PlusPlayer is a new multimedia player object-oriented designed.\r
+\r
+## Goals ##\r
+ * Improve maintainability / extensibility / reusability\r
+ - object oriented design\r
+ - separate the modules into high-variation part and low-variation part\r
+ - decoupling between each streaming services\r
+ * Simplification\r
+ - simplified pipeline , creates static pipeline\r
+ * Easy to upgrade\r
+ - support downloadable features (planned)\r
+\r
+## Architecture Design ##\r
+ * PlusPlayer consists of TypeFinder / TrackSource / TrackRenderer / DefaultPlayer objects.\r
+ * TypeFinder\r
+ - Probing source type by inspecting probing data\r
+ - use gstreamer typefinder plugin\r
+ * TrackSource\r
+ - Fetching data from server , demuxing , controling demuxed packet buffers\r
+ - a pipeline consists of `src - typefinder - demuxer - multiqueue - inputselector - fakesink`\r
+ - plz refer to `${plusplayer_workspace}/docs/dot/plusplayer_src_start.png`\r
+ * TrackRenderer\r
+ - maintains a pipeline consists of `appsrc - omx - sink` elements\r
+ - plz refer to `${plusplayer_workspace}/docs/dot/plusplayer_renderer_start.png`\r
+ * Class diagram\r
+ * ${plusplayer_workspace}/docs/class_diagram.plantuml\r
+ * or you can directly download it from [class_diagram.plantuml]\r
+\r
+## Development ##\r
+ * GitHub Project :\r
+ * All the milestones / activities (Codes , CodeReview , Design , Issues , etc) are being uploaded here\r
+ * Codereview on Github\r
+ - Each Developers should fork master branch and work on their own forked project.\r
+ - developer should submit pull-request to apply their changes\r
+ * all developers must sync their forked project with the latest master prior to sumit pull-request. so that we can avoid merge conflicts.\r
+ - everyone can join the codereviews\r
+ * the coding style follows Google C++ coding style guide. refer to [CODING STYLE](### coding-style)\r
+ * Test Driven Development (use gtest - plusplayer_ut -for their implementation and verifications)\r
+\r
+#### GTest guide ####\r
+ * To write Unit test case for PlusPlayer\r
+ * docs/gtest_guide.md\r
+ * Check Reference section of above link for more detailed documents.\r
+ * example\r
+ ```\r
+ sh-3.2# plusplayer_ut --gtest_filter="AvPlusPlayerTest.play"\r
+ ```\r
+#### Coding Rule Check ####\r
+ * check your coding style using **cpplint.py** before uploading your codes\r
+ > e.g) $ **cpplint.py** cpp_style_test.cpp\r
+ * Or check your coding style and fix your codes automatically using **clang-format**\r
+ > e.g) $ **clang-format-3.4** cpp_style_test.cpp -style=google **-i** \r
+\r
+#### Build ####\r
+ > 1. Clone or Download plusplayer package.\r
+ > 2. $ gbs -c ~/gbsconf/gbs_3_0.conf build -A armv7l --include-all --clean -B ~/GBS_ROOT_PLUSPLAYER \r
+\r
+#### Understanding the codes ####\r
+ to understand PlusPlayer concept quickly\r
+ plz refer to \r
+ - ${plusplayer_workspace}/ut/porting/webapi-plugins-avplay/src/avplusplayer.cc/h \r
+ - ${plusplayer_workspace}/src/player/defaultplayer.cpp/h\r
+ - ${plusplayer_workspace}/docs/class_diagram.txt\r
+\r
+#### CODING STYLE ####\r
+ 'Google C++ Style Guide' based coding. \r
+ > Translated(Korean)\r
+ > Original(English): [https://google.github.io/styleguide/cppguide.html](https://google.github.io/styleguide/cppguide.html)\r
--- /dev/null
+@startuml\r
+\r
+actor App\r
+boundary AvPlayInstance\r
+\r
+App -> AvPlayInstance : AVPlayOpen(url)\r
+note left : request playback\r
+AvPlayInstance -> AvPlusPlayer : Open(url)\r
+AvPlusPlayer -> Plusplayer : Open(url)\r
+AvPlusPlayer -> Plusplayer : RegisterEventListener()\r
+\r
+App -> AvPlayInstance : AVPlaySetBufferingParam()\r
+AvPlayInstance -> AvPlusPlayer : setBufferingParam()\r
+AvPlusPlayer -> Plusplayer : SetBufferConfig()\r
+App -> AvPlayInstance : AVPlaySetDisplayRect()\r
+AvPlayInstance -> AvPlusPlayer : SetOutputArea()\r
+AvPlusPlayer -> Plusplayer : SetDisplayMode()\r
+AvPlusPlayer -> Plusplayer : SetDisplayRoi()\r
+AvPlusPlayer -> Plusplayer : SetDisplay(Evas_Object*)\r
+App -> AvPlayInstance : AVPlaySetStreamingProperty()\r
+AvPlayInstance -> AvPlusPlayer : SetStreamingProperty()\r
+AvPlusPlayer -> Plusplayer : SetStreamingProperty()\r
+\r
+App -> AvPlayInstance : AVPlayPrepare()\r
+AvPlayInstance -> AvPlusPlayer : prepareAsync()\r
+AvPlusPlayer -> Plusplayer : PrepareAsync()\r
+Plusplayer --> AvPlusPlayer : OnPrepareDone(ret)\r
+AvPlusPlayer --> AvPlayInstance : onAsyncEventComplete()\r
+App -> AvPlayInstance : AVPlayPlay()\r
+AvPlayInstance -> AvPlusPlayer : play()\r
+AvPlusPlayer -> Plusplayer : Start()\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title dash_drm_playback_base_flow_1\r
+\r
+BmsAvPlayer -> TvplusPlusplayerAdapter : m_Play()\r
+TvplusPlusplayerAdapter -> CBmsDrmHandler : StoreDrmInfo()\r
+activate CBmsDrmHandler\r
+CBmsDrmHandler -> DrmManager : DMGRCreateDRMSession(DRM_TYPE_EME)\r
+CBmsDrmHandler -> DrmManager : DMGRSetDRMLocalMode()\r
+DrmManager -> IEME : create_session()\r
+IEME --> DrmManager : drm_handle*\r
+DrmManager --> CBmsDrmHandler : return\r
+CBmsDrmHandler --> TvplusPlusplayerAdapter : return\r
+deactivate CBmsDrmHandler\r
+TvplusPlusplayerAdapter -> CBmsDrmHandler : GetDrmHandle()\r
+CBmsDrmHandler --> TvplusPlusplayerAdapter : drm_handle*\r
+TvplusPlusplayerAdapter -> Plusplayer : SetDrm(DrmInfo)\r
+Plusplayer -> TrackRenderer : SetDrmInfo(DrmInfo)\r
+TvplusPlusplayerAdapter -> Plusplayer : PrepareAsync()\r
+Plusplayer -> TrackRenderer : Prepare()\r
+activate TrackRenderer\r
+group CreatePipeline\r
+ TrackRenderer -> Gstreamer : factory_make(appsrc)\r
+ TrackRenderer -> Gstreamer : factory_make(drm_eme)\r
+ TrackRenderer --> DrmEme : g_object_set(DrmInitData_Callback)\r
+ TrackRenderer -> Gstreamer : factory_make(omx_decoder)\r
+ TrackRenderer -> Gstreamer : factory_make(sink)\r
+ TrackRenderer -> Gstreamer : set_state(GST_STATE_PAUSE)(async)\r
+ TrackRenderer -> TrackRenderer : wait state_changed\r
+ activate TrackRenderer #DarkSalmon\r
+ DrmEme --> TrackRenderer : DrmInitData_Callback(init_data)\r
+ TrackRenderer --> Plusplayer : OnDrmInitData(init_data, tracktype)\r
+ Plusplayer --> TvplusPlusplayerAdapter : OnDrmInitData(init_data, tracktype)\r
+ activate TvplusPlusplayerAdapter\r
+ TvplusPlusplayerAdapter -> CBmsDrmHandler : SetDrmInitData(init_data)\r
+ activate CBmsDrmHandler\r
+ CBmsDrmHandler -> DrmManager : DMGRSetDrmInitData(init_data)\r
+ DrmManager --> CBmsDrmHandler : return\r
+ CBmsDrmHandler --> TvplusPlusplayerAdapter : return\r
+ deactivate CBmsDrmHandler\r
+ TvplusPlusplayerAdapter -> Plusplayer : DrmLicenseAcquiredDone(tracktype)\r
+ deactivate TvplusPlusplayerAdapter\r
+ Plusplayer -> TrackRenderer : DrmLicenseAcquiredDone(tractype)\r
+ TrackRenderer -> DrmEme : g_object_set(getrights-complete-return)\r
+ DrmEme --> Gstreamer : state_changed\r
+ Gstreamer --> TrackRenderer : state_changed\r
+ deactivate TrackRenderer\r
+end\r
+TrackRenderer -> Plusplayer : return\r
+deactivate TrackRenderer\r
+Plusplayer --> TvplusPlusplayerAdapter : OnPrepareDone(ret)\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+ref over BmsAVPlayer, TvplusPlusplayerHelper, Plusplayer\r
+playing previous channel\r
+end ref\r
+\r
+BmsAVPlayer -> TvplusPlusplayerHelper : m_PlayPosition()\r
+note left : request channel change\r
+TvplusPlusplayerHelper -> TvplusPlusplayerHelper : compare codec info\r
+alt if codec info is same\r
+TvplusPlusplayerHelper -> Plusplayer : ChangeSource(url,resume_time)\r
+Plusplayer -> StateManager : ProcessEvent(StopSource)\r
+StateManager -> TrackSourceCompositor : StopSource(type)\r
+TrackSourceCompositor -> HlSTrackSource : Stop()\r
+TrackSourceCompositor --> StateManager : ret\r
+StateManager -> MetaStateMachine : request state to SourceStopped\r
+MetaStateMachine -> StateManager : state changed\r
+StateManager -> TrackSourceCompositor : DeleteSource(type)\r
+TrackSourceCompositor -> HlSTrackSource : <<destroy>>\r
+destroy HlSTrackSource\r
+TrackSourceCompositor --> StateManager : ret\r
+StateManager --> Plusplayer : done\r
+Plusplayer -> ChangeSourceTask : create task\r
+ChangeSourceTask -> StateManager : ProcessEvent(InitializeSource)\r
+StateManager -> TrackSourceCompositor : AddSource(type)\r
+create DashTrackSource\r
+TrackSourceCompositor -> DashTrackSource : <<create>>\r
+StateManager --> ChangeSourceTask : ret\r
+ChangeSourceTask -> StateManager : ProcessEvent(PrepareSource)\r
+StateManager -> TrackSourceCompositor : Prepare()\r
+TrackSourceCompositor -> DashTrackSource : Prepare()\r
+StateManager --> ChangeSourceTask : ret\r
+ChangeSourceTask -> StateManager : ProcessEvent(Start)\r
+StateManager -> TrackSourceCompositor : Start()\r
+TrackSourceCompositor -> DashTrackSource : Start()\r
+StateManager --> ChangeSourceTask : ret\r
+ChangeSourceTask --> Plusplayer : ret\r
+Plusplayer --> TvplusPlusplayerHelper : OnChangeSourceDone(ret)\r
+else if codec is not same\r
+TvplusPlusplayerHelper -> Plusplayer : Stop and Destroy\r
+TvplusPlusplayerHelper -> Plusplayer : PlusPlayer::Create()\r
+TvplusPlusplayerHelper -> Plusplayer : Open(url)\r
+TvplusPlusplayerHelper -> Plusplayer : PrepareAsync(url)\r
+...\r
+Plusplayer --> TvplusPlusplayerHelper : OnPrepareDone(ret)\r
+TvplusPlusplayerHelper -> Plusplayer : Start()\r
+end\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title TrackRendererAdapter Sequence Diagram\r
+participant DefaultPlayer\r
+\r
+create TrackRendererAdapter\r
+DefaultPlayer -> TrackRendererAdapter : TrackRendererAdapter::Create()\r
+TrackRendererAdapter -> TrackRendererCapi : trackrenderer_create()\r
+create TrackRenderer\r
+TrackRendererCapi -> TrackRenderer : std::unique_ptr<T>(new TrackRenderer)\r
+TrackRendererCapi --> TrackRendererAdapter : handle\r
+\r
+@enduml\r
--- /dev/null
+@startuml\r
+\r
+title tracksource_alternative_flow_1\r
+\r
+actor App\r
+boundary BmsService\r
+\r
+App -> BmsService : request playback\r
+BmsService -> Plusplayer : Open(url)\r
+Plusplayer -> TrackSource : TrackSource::CreateCompositor()\r
+Plusplayer -> TrackSourceCompositor : RegisterEventListener(this)\r
+BmsService -> Plusplayer : RegisterEventListener()\r
+BmsService -> Plusplayer : SetSourceType(sourcetype)\r
+BmsService -> Plusplayer : PrepareAsync()\r
+Plusplayer -> TrackSourceCompositor : AddSource(sourcetype)\r
+activate TrackSourceCompositor\r
+TrackSourceCompositor -> HttpTrackSource : new\r
+HttpTrackSource --> TrackSourceCompositor : object\r
+Plusplayer -> TrackSourceCompositor : Prepare()\r
+TrackSourceCompositor -> HttpTrackSource : Prepare()\r
+group CreatePipeline\r
+HttpTrackSource -> gstreamer : pipeline_new()\r
+HttpTrackSource -> gstreamer : factory_make(src)\r
+HttpTrackSource -> gstreamer : factory_make(http_demuxer)\r
+HttpTrackSource -> gstreamer : factory_make(...)\r
+HttpTrackSource -> gstreamer : set_state(PAUSE)\r
+gstreamer --> HttpTrackSource : state_changed\r
+end\r
+HttpTrackSource --> TrackSourceCompositor : success\r
+TrackSourceCompositor --> Plusplayer : success\r
+deactivate TrackSourceCompositor\r
+Plusplayer --> BmsService : Listener::PrepareDone(success)\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title tracksource_base_flow_1\r
+\r
+actor App\r
+boundary BmsService\r
+\r
+App -> BmsService : request playback\r
+BmsService -> Plusplayer : Open(url)\r
+Plusplayer -> TrackSource : TrackSource::CreateCompositor()\r
+Plusplayer -> TrackSourceCompositor : RegisterEventListener(this)\r
+BmsService -> Plusplayer : RegisterEventListener()\r
+\r
+BmsService -> Plusplayer : PrepareAsync()\r
+Plusplayer -> TrackSourceCompositor : AddSource(url)\r
+activate TrackSourceCompositor\r
+TrackSourceCompositor -> Typefinder : probe()\r
+group createpipeline\r
+Typefinder -> gstreamer : pipeline_new()\r
+Typefinder -> gstreamer : factory_make(src)\r
+Typefinder -> gstreamer : factory_make(typefinder)\r
+gstreamer --> Typefinder : OnHaveType_(sourcetype)\r
+end\r
+Typefinder --> TrackSourceCompositor : sourcetype\r
+TrackSourceCompositor -> HttpTrackSource : new\r
+\r
+HttpTrackSource --> TrackSourceCompositor : object\r
+\r
+Plusplayer -> TrackSourceCompositor : Prepare()\r
+TrackSourceCompositor -> HttpTrackSource : Prepare()\r
+group CreatePipeline\r
+HttpTrackSource -> gstreamer : factory_make(http_demuxer)\r
+HttpTrackSource -> gstreamer : factory_make(...)\r
+HttpTrackSource -> gstreamer : set_state(PAUSE)\r
+gstreamer --> HttpTrackSource : state_changed\r
+end\r
+HttpTrackSource --> TrackSourceCompositor : success\r
+TrackSourceCompositor --> Plusplayer : success\r
+deactivate TrackSourceCompositor\r
+Plusplayer --> BmsService : Listener::PrepareDone(success)\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title tracksource_exception_flow_1\r
+\r
+actor App\r
+boundary BmsService\r
+\r
+App -> BmsService : request playback\r
+BmsService -> Plusplayer : Open(url)\r
+Plusplayer -> TrackSource : TrackSource::CreateCompositor()\r
+Plusplayer -> TrackSourceCompositor : RegisterEventListener(this)\r
+BmsService -> Plusplayer : RegisterEventListener()\r
+\r
+BmsService -> Plusplayer : PrepareAsync()\r
+Plusplayer -> TrackSourceCompositor : AddSource(url)\r
+activate TrackSourceCompositor\r
+TrackSourceCompositor -> Typefinder : probe()\r
+group createpipeline\r
+Typefinder -> gstreamer : pipeline_new()\r
+Typefinder -> gstreamer : factory_make(src)\r
+Typefinder -> gstreamer : factory_make(typefinder)\r
+gstreamer --> Typefinder : Error()\r
+end\r
+Typefinder --> TrackSourceCompositor : fail\r
+TrackSourceCompositor --> Plusplayer : fail\r
+deactivate TrackSourceCompositor\r
+Plusplayer --> BmsService : Listener::PrepareDone(fail)\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title tracksource_exception_flow_2\r
+\r
+actor App\r
+boundary BmsService\r
+\r
+App -> BmsService : request playback\r
+BmsService -> Plusplayer : Open(url)\r
+Plusplayer -> TrackSource : TrackSource::CreateCompositor()\r
+Plusplayer -> TrackSourceCompositor : RegisterEventListener(this)\r
+BmsService -> Plusplayer : RegisterEventListener()\r
+\r
+BmsService -> Plusplayer : PrepareAsync()\r
+Plusplayer -> TrackSourceCompositor : AddSource(url)\r
+activate TrackSourceCompositor\r
+TrackSourceCompositor -> Typefinder : probe()\r
+group createpipeline\r
+Typefinder -> gstreamer : pipeline_new()\r
+Typefinder -> gstreamer : factory_make(src)\r
+Typefinder -> gstreamer : factory_make(typefinder)\r
+gstreamer --> Typefinder : Error()\r
+end\r
+Typefinder --> TrackSourceCompositor : fail\r
+TrackSourceCompositor --> Plusplayer : fail\r
+deactivate TrackSourceCompositor\r
+Plusplayer --> BmsService : Listener::PrepareDone(fail)\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title tracksource_exception_flow_3\r
+\r
+actor App\r
+boundary BmsService\r
+\r
+App -> BmsService : request playback\r
+BmsService -> Plusplayer : Open(url)\r
+Plusplayer -> TrackSource : TrackSource::CreateCompositor()\r
+Plusplayer -> TrackSourceCompositor : RegisterEventListener(this)\r
+BmsService -> Plusplayer : RegisterEventListener()\r
+\r
+BmsService -> Plusplayer : PrepareAsync()\r
+Plusplayer -> TrackSourceCompositor : AddSource(url)\r
+activate TrackSourceCompositor\r
+TrackSourceCompositor -> Typefinder : probe()\r
+group createpipeline\r
+Typefinder -> gstreamer : pipeline_new()\r
+Typefinder -> gstreamer : factory_make(src)\r
+Typefinder -> gstreamer : factory_make(typefinder)\r
+gstreamer --> Typefinder : OnHaveType_(sourcetype)\r
+end\r
+Typefinder --> TrackSourceCompositor : sourcetype\r
+TrackSourceCompositor -> HttpTrackSource : new\r
+\r
+HttpTrackSource --> TrackSourceCompositor : object\r
+\r
+Plusplayer -> TrackSourceCompositor : Prepare()\r
+TrackSourceCompositor -> HttpTrackSource : Prepare()\r
+group CreatePipeline\r
+HttpTrackSource -> gstreamer : factory_make(http_demuxer)\r
+HttpTrackSource -> gstreamer : factory_make(...)\r
+HttpTrackSource -> gstreamer : set_state(PAUSE)\r
+gstreamer --> HttpTrackSource : SyncMessageHandler(Error)\r
+end\r
+HttpTrackSource --> TrackSourceCompositor : fail\r
+TrackSourceCompositor --> Plusplayer : fail\r
+deactivate TrackSourceCompositor\r
+Plusplayer --> BmsService : Listener::OnPrepareDone(fail)\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+database VersionInfo\r
+participant BmsService\r
+participant APIWrapper\r
+database Plusplayer.so\r
+\r
+ref over VersionInfo : VersionInfo contains libVersion and apiVersion\r
+ref over BmsService , VersionInfo : Compiled in SDK\r
+ref over BmsService , APIWrapper : downloaded and installed\r
+BmsService -> APIWrapper : Init()\r
+APIWrapper -> Plusplayer.so : GetVersion()\r
+Plusplayer.so --> APIWrapper : VersionInfo\r
+ref over APIWrapper : update current-apiVersion\r
+...\r
+BmsService -> APIWrapper : NewFunction()\r
+APIWrapper -> APIWrapper : check apiVersion\r
+alt current-apiVersion supports NewFuntion\r
+ref over Plusplayer.so : do something\r
+else current-apiVersion not support NewFuntion\r
+APIWrapper --> BmsService : return false\r
+end\r
+' 수정사항의 일부만 이전 모델에 반영 하는 경우....\r
+\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+title TVPlus Player update process\r
+\r
+(*) --> "Developing/Fixing Issues"\r
+--> "Make TC"\r
+--> "Devoloper Functional Verification"\r
+if "is there no issues ?" then\r
+--> [fail] "Developing/Fixing Issues"\r
+else\r
+--> [pass] "API Review w/ API reviewer board"\r
+--> "Binary Compatibility Test"\r
+if "is there no issues ?" then\r
+--> [fail] "Developing/Fixing Issues"\r
+else\r
+--> [pass] "Library distribution to Bms Team"\r
+--> "bms-daemon tpk packaing w/ player libraries"\r
+--> Installation & Verification\r
+if "is there no issues ?" then\r
+--> [fail] "Developing/Fixing Issues"\r
+else\r
+--> [pass] "Minor/Patch Version Update"\r
+--> "Request Release to Release Team"\r
+--> "App server registration"\r
+--> [Ending Process] (*)\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+BmsAVPlayer -> TvplusPlusplayerHelper : m_CreatePlayer()\r
+TvplusPlusplayerHelper -> Plusplayer : PlusPlayer::Create()\r
+Plusplayer --> TvplusPlusplayerHelper : object*\r
+BmsAVPlayer -> TvplusPlusplayerHelper : m_OpenStream()\r
+TvplusPlusplayerHelper -> Plusplayer : Open(url)\r
+TvplusPlusplayerHelper -> Plusplayer : SetBufferConfig()\r
+TvplusPlusplayerHelper -> Plusplayer : SetDisplayMode()\r
+TvplusPlusplayerHelper -> Plusplayer : SetDisplayRoi()\r
+TvplusPlusplayerHelper -> Plusplayer : SetDisplay(Evas_Object*)\r
+TvplusPlusplayerHelper -> Plusplayer : SetStreamingProperty()\r
+BmsAVPlayer -> TvplusPlusplayerHelper : m_Play()\r
+TvplusPlusplayerHelper -> Plusplayer : PrepareAsync()\r
+Plusplayer -> TvplusPlusplayerHelper : OnPrepareDone(ret)\r
+TvplusPlusplayerHelper -> Plusplayer : Start()\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+'skinparam backgroundColor LightYellow\r
+\r
+title plusplayer_internal_state_diagram\r
+\r
+state None {\r
+}\r
+state Idle {\r
+}\r
+state IdleInactive {\r
+}\r
+state TypeFinderReady {\r
+}\r
+state TrackSourceReady {\r
+}\r
+state Ready {\r
+}\r
+state ResourceConflicted {\r
+}\r
+\r
+\r
+[*] --> None\r
+None --> Idle : open\r
+None --> None : close\r
+\r
+Idle --> None : close\r
+Idle --> TypeFinderReady : probe\r
+Idle --> TypeFinderReady : pause_or_start_defered\r
+Idle --> IdleInactive : stop\r
+\r
+TypeFinderReady --> TrackSourceReady : prepare_source\r
+TypeFinderReady --> TrackSourceReady : pause_or_start_defered\r
+TypeFinderReady --> IdleInactive : stop\r
+\r
+TrackSourceReady --> Ready : prepare_renderer\r
+TrackSourceReady --> Ready : pause_or_start_defered\r
+TrackSourceReady --> IdleInactive : stop\r
+\r
+Ready --> Playing : start\r
+Ready --> Paused : pause\r
+Ready --> ResourceConflicted : resource_conflict\r
+Ready --> IdleInactive : stop\r
+Ready --> SourceStopped : stop_source\r
+\r
+Playing --> Paused : pause\r
+Playing --> Playing : resume\r
+Playing --> Paused : internal_pause\r
+Playing --> ResourceConflicted : resource_conflict\r
+Playing --> IdleInactive : stop\r
+Playing --> SourceStopped : stop_source\r
+\r
+Paused --> Playing : resume\r
+Paused --> Paused: pause\r
+Paused --> Playing : internal_resume\r
+Paused --> ResourceConflicted : resource_conflict\r
+Paused --> IdleInactive : stop\r
+Paused --> SourceStopped : stop_source\r
+\r
+ResourceConflicted --> Ready : restore\r
+ResourceConflicted --> IdleInactive : stop\r
+\r
+SourceChanged --> Playing : start\r
+SourceChanged --> SourceStopped : stop_source\r
+SourceChanged --> IdleInactive : stop\r
+SourceInitialized --> SourceChanged : prepare_source\r
+SourceInitialized --> SourceStopped : stop_source\r
+SourceInitialized --> IdleInactive : stop\r
+\r
+SourceStopped --> SourceInitialized : initialize_source\r
+SourceStopped --> IdleInactive : stop\r
+\r
+IdleInactive --> None : close\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+@startuml\r
+\r
+'skinparam backgroundColor LightYellow\r
+\r
+title plusplayer_internal_state_diagram\r
+\r
+state Statemanager {\r
+\r
+note "Active state" as N1\r
+\r
+state Idle {\r
+ [*] --> IdleZero\r
+ state TypeFinderReady {\r
+ }\r
+ state TrackSourceReady {\r
+ }\r
+ Idle: defer InternalPause\r
+ Idle: defer InternalResume\r
+\r
+ IdleZero --> PseudoExit3 : close\r
+ IdleZero --> TypeFinderReady : probe\r
+ TypeFinderReady --> TrackSourceReady : prepare_source\r
+ TrackSourceReady --> PseudoExit : prepare_renderer\r
+\r
+ IdleZero : defer SelectTrack()\r
+ IdleZero : defer Pause()\r
+ IdleZero : defer Start()\r
+ TypeFinderReady : defer SelectTrack()\r
+ TypeFinderReady : defer Pause()\r
+ TypeFinderReady : defer Start()\r
+ TrackSourceReady : defer Pause()\r
+ TrackSourceReady : defer Start()\r
+ TrackSourceReady --> TrackSourceReady : SelectTrack()\r
+}\r
+\r
+state ChangingSource {\r
+ state SourceStopped {\r
+ }\r
+ state SourceInitialized {\r
+ }\r
+ ChangingSource: defer InternalPause\r
+ ChangingSource: defer InternalResume\r
+\r
+ [*] --> SourceStopped\r
+\r
+ SourceStopped --> SourceInitialized : initialize_source\r
+ SourceStopped --> SourceStopped : stop_source\r
+\r
+ SourceInitialized --> SourceStopped : stop_source\r
+ SourceInitialized --> PseudoExit1 : prepare_source\r
+}\r
+\r
+[*] --> None\r
+None --> Idle : open\r
+None --> None : close\r
+PseudoExit3 --> None : close\r
+PseudoExit --> Ready : prepare_renderer\r
+\r
+Ready --> Playing : start\r
+Ready --> Paused : pause\r
+Ready --> Ready : internal_pause\r
+Ready --> Playing : internal_resume\r
+Paused --> Playing : resume\r
+Paused --> Playing : internal_resume\r
+Paused --> Paused : internal_pause\r
+Paused --> Playing : start\r
+Playing --> Paused : pause\r
+Playing --> Paused : internal_pause\r
+\r
+Paused --> Paused: pause\r
+Playing --> Playing : resume\r
+\r
+ResourceConflicted --> Ready : restore\r
+Ready --> ResourceConflicted : resource_conflict\r
+Playing --> ResourceConflicted : resource_conflict\r
+Paused --> ResourceConflicted : resource_conflict\r
+\r
+Ready --> ChangingSource : stop_source\r
+Paused --> ChangingSource : stop_source\r
+Playing --> ChangingSource : stop_source\r
+\r
+PseudoExit1 --> Ready : prepare_source\r
+\r
+Playing --> Playing : seek\r
+Paused --> Paused : seek\r
+\r
+Ready --> Ready : select_track()\r
+Playing --> Playing : select_track()\r
+Paused --> Paused : select_track()\r
+\r
+--\r
+\r
+note "Inactive state" as N2\r
+[*] --> Active\r
+Active --> Inactive : stop\r
+Inactive --> Inactive : stop\r
+note right of Inactive\r
+Inactive is interrupt state only allowing {stop , close} events.\r
+others will be ignored.\r
+end note\r
+Inactive --> Close : close\r
+\r
+}\r
+\r
+@enduml
\ No newline at end of file
--- /dev/null
+/**
+ * @file
+ * @brief the buffer for playback.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style buffer related enum.
+ * @see N/A
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+#ifndef __PLUSPLAYER_TRACKRENDERER_CAPI_BUFFER_H__
+#define __PLUSPLAYER_TRACKRENDERER_CAPI_BUFFER_H__
+
+#include <cstdint>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the buffer status
+ */
+enum TrackRendererBufferStatus {
+ kTrackRendererBufferStatusUnderrun,
+ kTrackRendererBufferStatusOverrun
+};
+
+/**
+ * @brief Enumerations for video decoded buffer type
+ */
+enum TrackRendererDecodedVideoFrameBufferType {
+ kTrackRendererDecodedVideoFrameBufferNone,
+ kTrackRendererDecodedVideoFrameBufferCopy,
+ kTrackRendererDecodedVideoFrameBufferReference,
+ kTrackRendererDecodedVideoFrameBufferScale
+};
+
+/**
+ * @brief video decoded buffer struct
+ */
+typedef struct {
+ /**
+ * @description buffer pts, in millisecond
+ */
+ uint64_t pts;
+ /**
+ *@description buffer duration, in millisecond
+ */
+ uint64_t duration;
+ /**
+ * @description surface data
+ */
+ void* surface_data;
+ /**
+ * @description the scaler index,0 1 ...
+ */
+ void* scaler_index;
+} TrackRendererDecodedVideoPacket;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CAPI_BUFFER_H__
--- /dev/null
+/**
+ * @file decoderinputbuffer.h
+ * @brief Managed decoderinputbuffer related data structures and enums
+ * used by trackrenderer apis.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style decoderinputbuffer releted data
+ * structures and enums.
+ * @see The decoderinputbuffer related enum values and data
+ * structures will be converted by this managed C version types
+ * to avoid binary compatibility.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_DECODERINPUTBUFFER_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_DECODERINPUTBUFFER_H__
+
+#include "gst/gst.h"
+
+#include "trackrenderer_capi/track.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Trackrenderer must not have the gstreamer dependency since Tizen 5.5.
+ * TRACKRENDERER_GST_DEPENDENCY_REMOVAL must be defined since Tizen 5.5
+ * The type of buffer memeber variable has been defined as GstBuffer* in rootstrap of Tizen 5.0.
+ * The variable type should be defined differently because of tvplus-services tpk's compiling issue.
+ */
+#define TRACKRENDERER_GST_DEPENDENCY_REMOVAL
+
+/**
+ * @brief Enumerations for the decoder input buffer
+ */
+typedef struct _TrackRendererDecoderInputBuffer {
+ /**
+ * @description track type(audio, video, subtitle)
+ * @remark default value is kTrackRendererTrackTypeMax
+ */
+ TrackRendererTrackType type;
+
+ /**
+ * @description buffer index
+ */
+ int index;
+
+ /**
+ * @description gst buffer
+ * @remark default value is nullptr
+ */
+#ifdef TRACKRENDERER_GST_DEPENDENCY_REMOVAL
+ void* buffer;
+#else
+ GstBuffer* buffer;
+#endif
+} TrackRendererDecoderInputBuffer;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_DECODERINPUTBUFFER_H__
--- /dev/null
+/**
+ * @file
+ * @brief Managed display related data structures and enums used by
+ * trackrenderer apis.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style display releted data structures
+ * and enums.
+ * @see The display related enum values and data structures will be
+ * converted by this managed C version types to avoid binary
+ * compatibility.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_DISPLAY_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_DISPLAY_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ * @brief Region of interest area of display
+ */
+typedef struct _TrackRendererGeometry {
+ /**
+ * @description x value of display window
+ */
+ int x;
+
+ /**
+ * @description y value of display window
+ */
+ int y;
+
+ /**
+ * @description width of display window
+ */
+ int w;
+
+ /**
+ * @description height of display window
+ */
+ int h;
+} TrackRendererGeometry;
+
+/**
+ * @brief Region of crop area of display
+ */
+typedef struct _TrackRendererCropArea {
+ /**
+ * @description scaled x label ratio
+ */
+ double scale_x;
+
+ /**
+ * @description scaled y label ratio
+ */
+ double scale_y;
+
+ /**
+ * @description scaled width ratio
+ */
+ double scale_w;
+
+ /**
+ * @description scaled heigh ratio
+ */
+ double scale_h;
+
+} TrackRendererCropArea;
+
+/**
+ * @brief Region of render rectangle
+ */
+typedef struct _TrackRendererRenderRect {
+ /**
+ * @description x value of render rectangle
+ */
+ int x;
+
+ /**
+ * @description y value of render rectangle
+ */
+ int y;
+
+ /**
+ * @description width of render rectangle
+ */
+ int w;
+
+ /**
+ * @description height of render rectangle
+ */
+ int h;
+} TrackRendererRenderRect;
+
+/**
+ * @brief Enumerations for the display type
+ */
+enum TrackRendererDisplayType {
+ kTrackRendererDisplayTypeNone,
+ kTrackRendererDisplayTypeOverlay,
+ kTrackRendererDisplayTypeEvas
+};
+
+/**
+ * @brief Enumerations for the display mode
+ */
+enum TrackRendererDisplayMode {
+ kTrackRendererDisplayModeLetterBox,
+ kTrackRendererDisplayModeOriginSize,
+ kTrackRendererDisplayModeFullScreen,
+ kTrackRendererDisplayModeCroppedFull,
+ kTrackRendererDisplayModeOriginOrLetter,
+ kTrackRendererDisplayModeDstRoi,
+ kTrackRendererDisplayModeAutoAspectRatio,
+ kTrackRendererDisplayModeDisplayMax
+};
+
+/**
+ * @brief Enumerations for the display rotate angle
+ */
+enum TrackRendererDisplayRotate {
+ kTrackRendererDisplayRotateNone,
+ kTrackRendererDisplayRotate90,
+ kTrackRendererDisplayRotate180,
+ kTrackRendererDisplayRotate270
+};
+
+/**
+ * @brief Enumerations for the still mode
+ */
+enum TrackRendererStillMode {
+ kTrackRendererStillModeNone,
+ kTrackRendererStillModeOff,
+ kTrackRendererStillModeOn
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_DISPLAY_H__
--- /dev/null
+/**
+ * @file
+ * @brief Managed drm related data structures and enums used by
+ * trackrenderer apis.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style drm releted data structures and
+ * enums.
+ * @see Drm releated event listeners, enum classes, etc.. are
+ * converted to this.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_DRM_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_DRM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the drm type
+ */
+enum TrackRendererDrmType {
+ kTrackRendererDrmTypeNone,
+ kTrackRendererDrmTypePlayready,
+ kTrackRendererDrmTypeMarlin,
+ kTrackRendererDrmTypeVerimatrix,
+ kTrackRendererDrmTypeWidevineClassic,
+ kTrackRendererDrmTypeSecuremedia,
+ kTrackRendererDrmTypeSdrm,
+ kTrackRendererDrmTypeWidevineCdm,
+ kTrackRendererDrmTypeDrmMax
+};
+
+/**
+ * @brief Drm properties
+ */
+typedef struct _TrackRendererDrmProperty {
+ /**
+ * @description Drm type
+ */
+ TrackRendererDrmType type;
+
+ /**
+ * @description Drm handle
+ */
+ int handle;
+
+ /**
+ * @description External Decryption Mode
+ */
+ int external_decryption;
+
+ /**
+ * @description The cb will be invoked when license was acquired.
+ */
+ void* license_acquired_cb;
+
+ /**
+ * @description The userdata will be sent by license_acquired_cb
+ */
+ void* license_acquired_userdata;
+} TrackRendererDrmProperty;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_DRM_H__
--- /dev/null
+/**
+ * @file
+ * @brief managed error related enum used by trackrenderer apis.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style error releted enum.
+ * @see All error enum values will be converted to this managed error
+ * types.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_ERROR_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_ERROR_H__
+
+#include "tizen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PLUSPLAYER_ERROR_CLASS TIZEN_ERROR_PLAYER | 0x20
+
+/* This is for custom defined player error. */
+#define PLUSPLAYER_CUSTOM_ERROR_CLASS TIZEN_ERROR_PLAYER | 0x1000
+
+/**
+ * @brief Enumerations for the error type
+ */
+enum TrackRendererErrorType {
+ kTrackRendererErrorTypeErrorNone,
+ kTrackRendererErrorTypeOutOfMemory,
+ kTrackRendererErrorTypeInvalidParameter,
+ kTrackRendererErrorTypeNoSuchFile,
+ kTrackRendererErrorTypeInvalidOperation,
+ kTrackRendererErrorTypeFileNoSpaceOnDevice,
+ kTrackRendererErrorTypeFeatureNotSupportedOnDevice,
+ kTrackRendererErrorTypeSeekFailed,
+ kTrackRendererErrorTypeInvalidState,
+ kTrackRendererErrorTypeNotSupportedFile,
+ kTrackRendererErrorTypeInvalidUri,
+ kTrackRendererErrorTypeSoundPolicy,
+ kTrackRendererErrorTypeConnectionFailed,
+ kTrackRendererErrorTypeVideoCaptureFailed,
+ kTrackRendererErrorTypeDrmExpired,
+ kTrackRendererErrorTypeDrmNoLicense,
+ kTrackRendererErrorTypeDrmFutureUse,
+ kTrackRendererErrorTypeDrmNotPermitted,
+ kTrackRendererErrorTypeResourceLimit,
+ kTrackRendererErrorTypePermissionDenied,
+ kTrackRendererErrorTypeServiceDisconnected,
+ kTrackRendererErrorTypeBufferSpace,
+ kTrackRendererErrorTypeNotSupportedAudioCodec,
+ kTrackRendererErrorTypeNotSupportedVideoCodec,
+ kTrackRendererErrorTypeNotSupportedSubtitle,
+ kTrackRendererErrorTypeDrmInfo,
+ kTrackRendererErrorTypeNotSupportedFormat,
+ kTrackRendererErrorTypeStreamingPlayer,
+ kTrackRendererErrorTypeDtcpFsk,
+ kTrackRendererErrorTypePreLoadingTimeOut,
+ kTrackRendererErrorTypeNetworkError,
+ kTrackRendererErrorTypeChannelSurfingFailed,
+ kTrackRendererErrorTypeUnknown
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_API_DRM_H__
--- /dev/null
+/**
+ * @file
+ * @brief The event for playback.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.0
+ * @SDK_Support N
+ * @remark This is a group of C style event related enum and structure.
+ * @see N/A
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CAPI_EVENT_H__
+#define __PLUSPLAYER_TRACKRENDERER_CAPI_EVENT_H__
+
+#include <cstdint>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CAPI_EVENT_H__
--- /dev/null
+/**
+ * @file iniproperty.h
+ * @brief structure for iniproperty map converting.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style iniproperty releted enum.
+ * @see Iniproperty map converting structure
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_INIPROPERTY_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_INIPROPERTY_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Plusplayer ini property map pair.
+ */
+typedef struct _TrackRendererIniProperty {
+ /**
+ * @description ini property.
+ */
+ const char* key;
+
+ /**
+ * @description ini property value.
+ */
+ int value;
+} TrackRendererIniProperty;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_INIPROPERTY_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file
+ * @brief managed error related enum used by trackrenderer apis.
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 3.0
+ * @SDK_Support N
+ * @remark This is a group of C style error releted enum.
+ * @see All error enum values will be converted to this managed error
+ * types.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_LATENCY_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_LATENCY_H__
+
+#include "tizen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ * @brief Enumerations for the error type
+ */
+enum TrackRendererCatchUpSpeed {
+ kTrackRendererCatchUpSpeedNone,
+ kTrackRendererCatchUpSpeedSlow,
+ kTrackRendererCatchUpSpeedNormal,
+ kTrackRendererCatchUpSpeedFast
+};
+
+enum TrackRendererLatencyStatus {
+ kTrackRendererLatencyStatusLow,
+ kTrackRendererLatencyStatusMid,
+ kTrackRendererLatencyStatusHigh
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_LATENCY_H__
--- /dev/null
+/**
+ * @file
+ * @brief structure for converting state enum.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style state related enum.
+ * @see State enum convertion.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_STATE_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_STATE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the trackrenderer state
+ */
+enum TrackRendererState {
+ kTrackRendererStateInit, // initial state
+ kTrackRendererStateWorking, // normal working state
+ kTrackRendererStateResourceConflicted, // resource conflicted state
+ kTrackRendererStateStopped, // stopped state
+ kTrackRendererStateUnknown
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_STATE_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file
+ * @brief structure for submitstatus enum converting.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style submitstatus related enum.
+ * @see Submitstatus enum convertion.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_SUBMITSTATUS_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_SUBMITSTATUS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the submit status
+ */
+enum TrackRendererSubmitStatus {
+ kTrackRendererSubmitStatusNotPrepared, // not prepared to get data
+ kTrackRendererSubmitStatusHold, // valid data, hold this packet
+ kTrackRendererSubmitStatusFull, // buffer already full
+ kTrackRendererSubmitStatusSuccess, // submit succeeded
+ kTrackRendererSubmitStatusDrop, // invalid data , drop this packet
+ kTrackRendererSubmitStatusFailed
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_SUBMITSTATUS_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file
+ * @brief Managed track related data structures and enums used by
+ * trackrenderer apis.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style track releted data structures and
+ * enums.
+ * @see All track releated types shared_ptr, boost::any, enum
+ * classes, std::string, etc.. are converted to this.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACK_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACK_H__
+
+#include <cstdint>
+#include <memory>
+
+#include "display.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Default value of track index.
+ */
+const int kTrackRendererInvalidTrackIndex = -1;
+
+/**
+ * @brief Enumerations for the track type
+ */
+enum TrackRendererTrackType {
+ kTrackRendererTrackTypeAudio = 0,
+ kTrackRendererTrackTypeVideo,
+ kTrackRendererTrackTypeSubtitle,
+ kTrackRendererTrackTypeMax
+};
+
+/**
+ * @brief Track info.
+ */
+typedef struct _TrackRendererTrack {
+ /**
+ * @description Index.
+ */
+ int index;
+
+ /**
+ * @description Id.
+ */
+ int id;
+
+ /**
+ * @description Mime type.
+ * @remark mimetype is declared as char pointer but it is actually
+ * string type. It must have null at the end of array
+ * to terminate string.
+ */
+ const char* mimetype;
+
+ /**
+ * @description Stream type.
+ * @remark streamtype is declared as char pointer but it is actually
+ * string type. It must have null at the end of array
+ * to terminate string.
+ */
+ const char* streamtype;
+
+ /**
+ * @description Track type.
+ */
+ TrackRendererTrackType type;
+
+ /**
+ * @description Codec data.
+ */
+ char* codec_data;
+
+ /**
+ * @description Codec data lenth.
+ */
+ int codec_data_len;
+
+ /**
+ * @description Width.
+ */
+ int width;
+
+ /**
+ * @description Height.
+ */
+ int height;
+
+ /**
+ * @description Max width.
+ */
+ int maxwidth;
+
+ /**
+ * @description Max height.
+ */
+ int maxheight;
+
+ /**
+ * @description Framerate numerator.
+ */
+ int framerate_num;
+
+ /**
+ * @description Framerate denominator.
+ */
+ int framerate_den;
+
+ /**
+ * @description Sample rate.
+ */
+ int sample_rate;
+
+ /**
+ * @description Sample format.
+ */
+ int sample_format;
+
+ /**
+ * @description Channel.
+ */
+ int channels;
+
+ /**
+ * @description Version.
+ */
+ int version;
+
+ /**
+ * @description Layer.
+ */
+ int layer;
+
+ /**
+ * @description Bit per sample.
+ */
+ int bits_per_sample;
+
+ /**
+ * @description Block alignment.
+ */
+ int block_align;
+
+ /**
+ * @description Bit rate.
+ */
+ int bitrate;
+
+ /**
+ * @description Endianness.
+ * @remark little endian : 1234 \n
+ * others big endian
+ */
+ int endianness;
+
+ /**
+ * @description Signed or unsigned.
+ */
+ int is_signed;
+
+ /**
+ * @description Active track.
+ */
+ int active;
+
+ /**
+ * @description Use software decoder.
+ */
+ int use_swdecoder;
+
+ /**
+ * @description Language code.
+ */
+ const char* language_code;
+
+ /**
+ * @description Subtitle format.
+ */
+ const char* subtitle_format;
+} TrackRendererTrack;
+
+/**
+ * @brief Enumerations for the subtitle attribute type
+ */
+enum TrackRendererSubtitleAttrType {
+ kTrackRendererSubtitleAttrTypeRegionXPos = 0, // float type
+ kTrackRendererSubtitleAttrTypeRegionYPos, // float type
+ kTrackRendererSubtitleAttrTypeRegionWidth, // float type
+ kTrackRendererSubtitleAttrTypeRegionHeight, // float type
+ kTrackRendererSubtitleAttrTypeWindowXPadding, // float type
+ kTrackRendererSubtitleAttrTypeWindowYPadding, // float type
+ kTrackRendererSubtitleAttrTypeWindowLeftMargin, // int type
+ kTrackRendererSubtitleAttrTypeWindowRightMargin, // int type
+ kTrackRendererSubtitleAttrTypeWindowTopMargin, // int type
+ kTrackRendererSubtitleAttrTypeWindowBottomMargin, // int type
+ kTrackRendererSubtitleAttrTypeWindowBgColor, // int type
+ kTrackRendererSubtitleAttrTypeWindowOpacity, // float type
+ kTrackRendererSubtitleAttrTypeWindowShowBg, // how to show window background,
+ // uint type
+ kTrackRendererSubtitleAttrTypeFontFamily, // char* type
+ kTrackRendererSubtitleAttrTypeFontSize, // float type
+ kTrackRendererSubtitleAttrTypeFontWeight, // int type
+ kTrackRendererSubtitleAttrTypeFontStyle, // int type
+ kTrackRendererSubtitleAttrTypeFontColor, // int type
+ kTrackRendererSubtitleAttrTypeFontBgColor, // int type
+ kTrackRendererSubtitleAttrTypeFontOpacity, // float type
+ kTrackRendererSubtitleAttrTypeFontBgOpacity, // float type
+ kTrackRendererSubtitleAttrTypeFontTextOutlineColor, // int type
+ kTrackRendererSubtitleAttrTypeFontTextOutlineThickness, // int type
+ kTrackRendererSubtitleAttrTypeFontTextOutlineBlurRadius, // int type
+ kTrackRendererSubtitleAttrTypeFontVerticalAlign, // int type
+ kTrackRendererSubtitleAttrTypeFontHorizontalAlign, // int type
+ kTrackRendererSubtitleAttrTypeRawSubtitle, // char* type
+ kTrackRendererSubtitleAttrTypeWebvttCueLine, // float type
+ kTrackRendererSubtitleAttrTypeWebvttCueLineNum, // int type
+ kTrackRendererSubtitleAttrTypeWebvttCueLineAlign, // int type
+ kTrackRendererSubtitleAttrTypeWebvttCueAlign, // int type
+ kTrackRendererSubtitleAttrTypeWebvttCueSize, // float type
+ kTrackRendererSubtitleAttrTypeWebvttCuePosition, // float type
+ kTrackRendererSubtitleAttrTypeWebvttCuePositionAlign, // int type
+ kTrackRendererSubtitleAttrTypeWebvttCueVertical, // int type
+ // TODO(sy0207.ju) : check later.
+ kTrackRendererSubtitleAttrTypeTimestamp,
+ kTrackRendererSubtitleAttrTypeExtsubIndex, // File index of external subtitle
+ kTrackRendererSubtitleAttrTypeTypeNone
+};
+
+/**
+ * @brief Enumerations for the subtitle type
+ */
+enum TrackRendererSubtitleType {
+ kTrackRendererSubtitleTypeText,
+ kTrackRendererSubtitleTypePicture,
+ kTrackRendererSubtitleTypeInvalid
+};
+
+/**
+ * @brief Subtitle attribute data structure
+ */
+typedef struct _TrackRendererSubtitleAttr {
+ /**
+ * @description Subtitle attribute type.
+ */
+ TrackRendererSubtitleAttrType type;
+
+ /**
+ * @description Start time.
+ */
+ uint32_t start_time;
+
+ /**
+ * @description Stop time.
+ */
+ uint32_t stop_time;
+
+ /**
+ * @description Subtitle attribute value. It can be float, int32, char*,
+ * unsigned int.
+ */
+ union {
+ /**
+ * @description Floating type subtitle attribute value.
+ * @remark kSubAttrRegionXPos \n
+ * kSubAttrRegionYPos \n
+ * kSubAttrRegionWidth \n
+ * kSubAttrRegionHeight \n
+ * kSubAttrWindowXPadding \n
+ * kSubAttrWindowYPadding \n
+ * kSubAttrWindowOpacity \n
+ * kSubAttrWindowShowBg \n
+ * kSubAttrFontSize \n
+ * kSubAttrFontOpacity \n
+ * kSubAttrFontBgOpacity \n
+ * kSubAttrWebvttCueLine \n
+ * kSubAttrWebvttCueSize \n
+ * kSubAttrWebvttCuePosition \n
+ * kSubAttrWebvttCueVertical
+ */
+ float f;
+
+ /**
+ * @description 32bit integer type subtitle atribute value.
+ * @remark kSubAttrWindowLeftMargin \n
+ * kSubAttrWindowRightMargin \n
+ * kSubAttrWindowTopMargin \n
+ * kSubAttrWindowBottomMargin \n
+ * kSubAttrWindowBgColor \n
+ * kSubAttrFontWeight \n
+ * kSubAttrFontStyle \n
+ * kSubAttrFontColor \n
+ * kSubAttrFontBgColor \n
+ * kSubAttrFontTextOutlineColor \n
+ * kSubAttrFontTextOutlineThickness \n
+ * kSubAttrFontTextOutlineBlurRadius \n
+ * kSubAttrFontVerticalAlign \n
+ * kSubAttrFontHorizontalAlign \n
+ * kSubAttrWebvttCueLineNum \n
+ * kSubAttrWebvttCueLineAlign \n
+ * kSubAttrWebvttCueAlign \n
+ * kSubAttrWebvttC
+ */
+ int32_t i32;
+
+ /**
+ * @description String type subtitle atribute value.
+ * @remark kSubAttrFontFamily \n
+ * kSubAttrRawSubtitle
+ */
+ const char* str;
+
+ /**
+ * @description Unsigned 32bit integer type subtitle attribute value.
+ */
+ uint32_t ui32;
+
+ /**
+ * @description Unsigned 64bit integer type subtitle attribute value.
+ */
+ uint64_t ui64;
+
+ /**
+ * @description 64bit integer type subtitle attribute value.
+ */
+ int64_t i64;
+
+ /**
+ * @description Double type subtitle attribute value.
+ */
+ double d64;
+ } value;
+
+ /**
+ * @description Extra subtitle attribute index.
+ */
+ int extsub_index;
+} TrackRendererSubtitleAttr;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACK_H__
--- /dev/null
+/**
+ * @file track_capi.h
+ * @brief trackrenderer internally used api c version
+ * @interfacetype Module
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 3.0
+ * @SDK_Support N
+ * @remark This is track api set for trackrenderer implemented as C
+ * style to avoid binary compatibility issues.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACK_CAPI_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACK_CAPI_H__
+
+#include "trackrenderer_capi/track.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for types of track handle
+ */
+typedef void* TrackRendererTrackHandle;
+
+/**
+ * @brief Enumerations for types of endianness
+ */
+enum TrackRendererTrackEndianness {
+ kTrackRendererTrackEndiannessLittle, /**< Little Endian */
+ kTrackRendererTrackEndiannessBig, /**< Big Endian */
+};
+
+/**
+ * @brief Enumerations for types of signedness
+ */
+enum TrackRendererTrackSignedness {
+ kTrackRendererTrackSignednessSigned, /**< Signed */
+ kTrackRendererTrackSignednessUnsigned, /**< Unsigned */
+};
+
+/**
+ * @brief Enumerations for types of activation
+ */
+enum TrackRendererTrackActivation {
+ kTrackRendererTrackActivationActive, /**< Active */
+ kTrackRendererTrackActivationDeactive, /**< Deactive */
+};
+
+/**
+ * @brief Enumerations for types of decoder
+ */
+enum TrackRendererTrackDecoderType {
+ kTrackRendererTrackDecoderTypeHwOnly, /**< H/W Decoder */
+ kTrackRendererTrackDecoderTypeSwOnly, /**< S/W Decoder */
+};
+
+/**
+ * @brief [Video/Audio/Subtitle] Create empty track handle.
+ * @param [out] handle : trackrenderer track handle ptr
+ * @param [in] type : one of track type for track handle
+ * @param [in] index : track index
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if track handle is created successfully.
+ Otherwise -1 if handle is nullptr or memory allocation failure.
+ * @retval 0 on success
+ * @retval -1 on failure
+ * @see TrackRendererTrackType
+ */
+int trackrenderer_track_create(TrackRendererTrackHandle* handle,
+ const TrackRendererTrackType type,
+ const int index);
+
+/**
+ * @brief [Video/Audio/Subtitle] Destroy track handle.
+ * @param [in] handle : trackrenderer track handle ptr to destroy
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if track handle is destroyed successfully.
+ Otherwise -1 if handle is nullptr.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_destroy(TrackRendererTrackHandle handle);
+
+/**
+ * @brief [Video/Audio/Subtitle] Set track activated. If application
+ * doesn't call this api, the track will be deactivated by
+ * default.
+ * @param [in] handle : trackrenderer track handle ptr to set activation
+ * @param [in] activation : activation value
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the activation of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr.
+ * @retval 0 on success
+ * @retval -1 on failure
+ * @see TrackRendererTrackActivation
+ */
+int trackrenderer_track_set_activation(
+ TrackRendererTrackHandle handle,
+ const TrackRendererTrackActivation activation);
+
+/**
+ * @brief [Video/Audio/Subtitle] Set mimetype for track
+ * @param [in] handle : trackrenderer track handle ptr to set mimetype
+ * @param [in] mimetype : mimetype with null terminated string
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the mimetype of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_set_mimetype(TrackRendererTrackHandle handle,
+ const char* mimetype);
+
+/**
+ * @brief [Video/Audio/Subtitle] Set streamtype for track
+ * @param [in] handle : trackrenderer track handle ptr to set streamtype
+ * @param [in] streamtype : streamtype with null terminated string
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the streamtype of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_set_streamtype(TrackRendererTrackHandle handle,
+ const char* streamtype);
+
+/**
+ * @brief [Video/Audio] Set codec data for track
+ * @param [in] handle : trackrenderer track handle ptr to set codec data
+ * @param [in] codec_data : bytes array of codec_data
+ * @param [in] codec_data_len : size of the codec_data
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the codec data of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr,
+ * codec_data_len is over 1MB or the type of the track handle is
+ * different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_set_codec_data(TrackRendererTrackHandle handle,
+ const char* codec_data,
+ const int codec_data_len);
+
+/**
+ * @brief [Video/Audio] Set decoder type. default value is
+ * kTrackRendererTrackDecoderTypeHwOnly
+ * @param [in] handle : trackrenderer track handle ptr to set decoder
+ * type
+ * @param [in] type : type of decoder (h/w or s/w)
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the decoder type of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ * @see TrackRendererTrackDecoderType
+ */
+int trackrenderer_track_set_decoder_type(
+ TrackRendererTrackHandle handle, const TrackRendererTrackDecoderType type);
+
+/**
+ * @brief [Video] Set resolution for video track
+ * @param [in] handle : trackrenderer track handle ptr to set resolution
+ * @param [in] width : width of video frame
+ * @param [in] height : height of video frame
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the resolution of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_video_set_resolution(TrackRendererTrackHandle handle,
+ const int width, const int height);
+
+/**
+ * @brief [Video] Set max resolution for video track
+ * @param [in] handle : trackrenderer track handle ptr to set max
+ * resolution
+ * @param [in] width : maximum width of video frame
+ * @param [in] height : maximum height of video frame
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the max resolution of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_video_set_max_resolution(
+ TrackRendererTrackHandle handle, const int width, const int height);
+
+/**
+ * @brief [Video] Set framerate for video track
+ * @param [in] handle : trackrenderer track handle ptr to set framerate
+ * @param [in] num : numerator of video sequence
+ * @param [in] den : denominator of video sequence
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the framerate of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_video_set_framerate(TrackRendererTrackHandle handle,
+ const int num, const int den);
+
+/**
+ * @brief [Video] Set video codec tag for video track
+ * @param [in] handle : trackrenderer track handle ptr to set video codec
+ * tag
+ * @param [in] codec_tag : codec tag with null terminated string
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the codec tag of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_video_set_codec_tag(TrackRendererTrackHandle handle,
+ const char* codec_tag);
+
+/**
+ * @brief [Video] Set version for video track
+ * @param [in] handle : trackrenderer track handle ptr to set version
+ * @param [in] version : version of video codec
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the version of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_video_set_version(TrackRendererTrackHandle handle,
+ const int version);
+
+/**
+ * @brief [Audio] Set sample rate for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set sample rate
+ * @param [in] sample_rate : sample rate of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the sample rate of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_sample_rate(TrackRendererTrackHandle handle,
+ const int sample_rate);
+
+/**
+ * @brief [Audio] Set sample format for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set sample
+ * format
+ * @param [in] sample_format : sample format of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the sample format of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_sample_format(TrackRendererTrackHandle handle,
+ const int sample_format);
+
+/**
+ * @brief [Audio] Set channels for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set channels
+ * @param [in] channels : channels of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the channels of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_channels(TrackRendererTrackHandle handle,
+ const int channels);
+
+/**
+ * @brief [Audio] Set version for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set version
+ * @param [in] version : version of audio codec
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the version of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_version(TrackRendererTrackHandle handle,
+ const int version);
+
+/**
+ * @brief [Audio] Set layer for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set layer
+ * @param [in] layer : layer of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the layer of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_layer(TrackRendererTrackHandle handle,
+ const int layer);
+
+/**
+ * @brief [Audio] Set bits per sample (bps) for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set bps
+ * @param [in] bits_per_sample : bits per sample of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the bits per sample of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_bits_per_sample(
+ TrackRendererTrackHandle handle, const int bits_per_sample);
+
+/**
+ * @brief [Audio] Set block align for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set block align
+ * @param [in] block_align : block align of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the block align of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_block_align(TrackRendererTrackHandle handle,
+ const int block_align);
+
+/**
+ * @brief [Audio] Set bitrate for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set bitrate
+ * @param [in] bitrate : bitrate of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the bitrate of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_bitrate(TrackRendererTrackHandle handle,
+ const int bitrate);
+
+/**
+ * @brief [Audio] Set layout for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set layout
+ * @param [in] layout : layout of audio stream with null terminated
+ * string
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the layout of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_layout(TrackRendererTrackHandle handle,
+ const char* layout);
+
+/**
+ * @brief [Audio] Set flavor for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set flavor
+ * @param [in] flavor : flavor of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the flavor of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_flavor(TrackRendererTrackHandle handle,
+ const int flavor);
+
+/**
+ * @brief [Audio] Set endianness for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set endianness
+ * @param [in] endianness : endianness of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the endianness of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ * @see TrackRendererTrackEndianness
+ */
+int trackrenderer_track_audio_set_endianness(
+ TrackRendererTrackHandle handle,
+ const TrackRendererTrackEndianness endianness);
+
+/**
+ * @brief [Audio] Set signedness for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set signedness
+ * @param [in] signedness : signedness of audio stream
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the signedness of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_signedness(
+ TrackRendererTrackHandle handle,
+ const TrackRendererTrackSignedness signedness);
+
+/**
+ * @brief [Audio] Set codec tag for audio track
+ * @param [in] handle : trackrenderer track handle ptr to set codec tag
+ * @param [in] codec_tag : codec tag with null terminated string
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the codec tag of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_audio_set_codec_tag(TrackRendererTrackHandle handle,
+ const char* codec_tag);
+
+/**
+ * @brief [Subtitle] Set language code for subtitle track
+ * @param [in] handle : trackrenderer track handle ptr to set language
+ * code
+ * @param [in] language_code : language code with null terminated string
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the language code of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_subtitle_set_language_code(
+ TrackRendererTrackHandle handle, const char* language_code);
+
+/**
+ * @brief [Subtitle] Set subtitle format for subtitle track
+ * @param [in] handle : trackrenderer track handle ptr to set subtitle
+ * format
+ * @param [in] subtitle_format : subtitle format with null terminated
+ * string
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Returns 0 if the subtitle format of the track handle is set
+ * successfully. Otherwise -1 if handle is nullptr or the type of
+ * the track handle is different.
+ * @retval 0 on success
+ * @retval -1 on failure
+ */
+int trackrenderer_track_subtitle_set_subtitle_format(
+ TrackRendererTrackHandle handle, const char* subtitle_format);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACKRENDERER_TRACK_CAPI_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file trackrenderer_capi.h
+ * @brief Plusplayer trackrenderer api c version.
+ * @interfacetype Platform
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is trackrenderer api header implemented as C style to
+ * avoid binary compatibility issues.
+ * @see trackrenderer api header will be exposed to rootstrap and the
+ * apis are managed.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACKRENDERER_CAPI_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACKRENDERER_CAPI_H__
+
+#include "trackrenderer_capi/buffer.h"
+#include "trackrenderer_capi/decoderinputbuffer.h"
+#include "trackrenderer_capi/display.h"
+#include "trackrenderer_capi/drm.h"
+#include "trackrenderer_capi/error.h"
+//#include "trackrenderer_capi/event.h"
+#include "trackrenderer_capi/iniproperty.h"
+#include "trackrenderer_capi/latency.h"
+#include "trackrenderer_capi/state.h"
+#include "trackrenderer_capi/submitstatus.h"
+#include "trackrenderer_capi/track.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Player app id, version, type information
+ */
+typedef struct {
+ /**
+ * @description App id for controlling resource.
+ */
+ char* id;
+ /**
+ * @description When there is playback market issue, KPI logger will
+ * send the version.
+ */
+ char* version;
+ /**
+ * @description RunningApps.InformationTicker will use this type to show
+ * stream information. ex) "MSE", "HTML5", etc..
+ */
+ char* type;
+} TrackRendererAppInfo;
+
+/**
+ * @brief Event message
+ */
+typedef struct {
+ /**
+ * @description event message data
+ * eg) kTrackRendererEventTypeResolutionChanged : "1920x1080"
+ */
+ char* data;
+ /**
+ * @description the length of event message data
+ */
+ uint64_t len;
+} TrackrendererEventMsg;
+
+/**
+ * @brief Enumerations for the audio easing type
+ * @version 3.0
+ */
+enum TrackRendererAudioEasingType {
+ /**
+ * @description audio easing function type is linear
+ */
+ kTrackRendererAudioEasingLinear = 0,
+ /**
+ * @description audio easing function type is incubic
+ */
+ kTrackRendererAudioEasingIncubic,
+ /**
+ * @description audio easing function type is outcubic
+ */
+ kTrackRendererAudioEasingOutcubic,
+ /**
+ * @description audio easing function type is none
+ */
+ kTrackRendererAudioEasingNone
+};
+
+/**
+ * @brief audio easing information struct
+ * @version 3.0
+ */
+typedef struct _TrackRendererAudioEasingInfo {
+ /**
+ * @description audio easing target volume
+ */
+ uint32_t target_volume;
+ /**
+ * @description audio easing duration, in millisecond
+ */
+ uint32_t duration;
+ /**
+ * @description audio easing type
+ */
+ TrackRendererAudioEasingType type;
+} TrackRendererAudioEasingInfo;
+
+/**
+ * @brief Enumerations for the resource types
+ * @version 3.0
+ */
+enum TrackRendererRscType {
+ kTrackRendererRscTypeVideoRenderer,
+};
+
+/**
+ * @brief Enumerations for event message types
+ */
+enum TrackRendererEventType {
+ kTrackRendererEventTypeNone,
+ kTrackRendererEventTypeResolutionChanged,
+};
+
+/**
+ * @brief Enumerations for advanced picture quality types
+ */
+enum TrackRendererAdvPictureQualityType {
+ kTrackRendererAdvPictureQualityTypeVideoCall,
+ kTrackRendererAdvPictureQualityTypeUsbCamera,
+};
+
+/**
+ * @brief Enumerations for resource allocate policy
+ */
+enum TrackRendererRscAllocPolicy {
+ kTrackRendererRscAllocExclusive,
+ kTrackRendererRscAllocConditional,
+};
+
+/**
+ * @brief the numerator/denominator value
+ * @version 3.2
+ */
+typedef struct _TrackRendererRational {
+ int num;
+ int den;
+} TrackRendererRational;
+
+typedef void* TrackRendererHandle;
+/**
+ * @description If there is an error, this function will post
+ * error message with error code.
+ * @param [in] error_code : enum TrackRendererErrorType\n
+ * @param [in] userdata : userdata of trackrenderer_error_cb
+ * callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_error_cb in advance.
+ * @post User will handle the posted error according to error code.
+ * @see trackrenderer_set_error_cb()
+ * @see trackrenderer_error_msg_cb() if a detailed error message is
+ * required
+ */
+typedef void (*trackrenderer_error_cb)(const TrackRendererErrorType error_code,
+ void* userdata);
+
+/**
+ * @description If there is an error, this function will post
+ * error message with error code and detailed infomation.
+ * @param [in] error_code : enum TrackRendererErrorType\n
+ * @param [in] error_msg : detailed error message including info related
+ * to codec,demuxer,network status, etc.
+ * @param [in] userdata : userdata of trackrenderer_error_cb
+ * callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_error_msg_cb in advance.
+ * @post User will handle the posted error according to error code.
+ * @see trackrenderer_set_error_msg_cb()
+ * @see trackrenderer_error_cb() if only simple error code is required
+ */
+typedef void (*trackrenderer_error_msg_cb)(
+ const TrackRendererErrorType error_code, char* error_msg, void* userdata);
+
+/**
+ * @description If there is a resource conflict, this function will notice
+ * that there is resource conflict. Then trackrenderer can
+ * release the current TV decoder and display resources. Data
+ * feeding also need to be stoped as well.
+ * @param [in] userdata : userdata of
+ * trackrenderer_resource_conflicted_cb callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_resourceconflict_cb in advance.
+ * @post User will control trackrenderer resources.
+ * @see trackrenderer_set_resourceconflict_cb()
+ */
+typedef void (*trackrenderer_resource_conflicted_cb)(void* userdata);
+
+/**
+ * @description When seek operation is done, this function will be called.
+ * @param [in] userdata : userdata of trackrenderer_seekdone_cb callback
+ * function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_seekdone_cb, in advance.
+ * @post User can decide the next operation after seek is done.
+ * @see trackrenderer_set_seekdone_cb()
+ */
+typedef void (*trackrenderer_seekdone_cb)(void* userdata);
+
+/**
+ * @description When flush operation is done, this function will be called.
+ * @param [in] userdata : userdata of trackrenderer_flushdone_cb callback
+ * function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_flushdone_cb, in advance.
+ * @post User can decide the next operation after flush is done.
+ * @see trackrenderer_set_flushdone_cb()
+ */
+typedef void (*trackrenderer_flushdone_cb)(void* userdata);
+
+/**
+ * @description When data stream meet the eos signal, this function will be
+ * called to notify that the contents end.
+ * @param [in] userdata : userdata of trackrenderer_eos_cb callback
+ * function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_eos_cb, in advance.
+ * @post User have to stop playback and release the TV resources.
+ * @see trackrenderer_set_eos_cb()
+ */
+typedef void (*trackrenderer_eos_cb)(void* userdata);
+
+/**
+ * @description When a specific event occurs, this function will be called to
+ * notify it.
+ * @param [in] event_type : event type that occured
+ * @param [in] msg_data : event information
+ * @param [in] userdata : userdata of trackrenderer_event_cb callback
+ * function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_event_cb, in advance.
+ * @post User can get the information of the event that occured
+ * @see trackrenderer_set_event_cb()
+ */
+typedef void (*trackrenderer_event_cb)(const TrackRendererEventType event_type,
+ const TrackrendererEventMsg msg_data,
+ void* userdata);
+/**
+ * @description When there is subtitle to display, this function is called to
+ * give subtitle data. If user want to display the subtitle,
+ * should get subtitle informations from raw data.
+ * @param [in] buf : buffer including subtitle information \n
+ * @param [in] type : subtitle type (text or picture) \n
+ * @param [in] userdata : userdata of
+ * trackrenderer_subtitle_rawdata_cb callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_subtitle_rawdata_cb, in advance.
+ * @post User have to get subtitle informations from data to display
+ * subtitle.
+ * @see trackrenderer_set_subtitle_rawdata_cb()
+ */
+typedef void (*trackrenderer_subtitle_rawdata_cb)(
+ TrackRendererDecoderInputBuffer* buf, const TrackRendererSubtitleType type,
+ void* userdata);
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+/**
+ * @description (deprecated) When there is subtitle to display, this function
+ * is called to give subtitle data. User can use the shared
+ * informations to display subtitle.
+ * @param [in] data : subtitle data \n
+ * @param [in] size : subtitle data length \n
+ * @param [in] type : subtitle type (text or picture) \n
+ * @param [in] duration : subtitle duration (ms) \n
+ * @param [in] attr_list : list of subtitle attribute \n
+ * @param [in] attr_list_size : length of subtite attribute \n
+ * @param [in] userdata : userdata of
+ * trackrenderer_subtitledata_cb callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_subtitledata_cb, in advance.
+ * @post User can display the subtitle using shared subtitle data.
+ * @see trackrenderer_set_subtitledata_cb()
+ */
+typedef void (*trackrenderer_subtitledata_cb)(
+ const char* data, const int size, const TrackRendererSubtitleType type,
+ const unsigned long long duration, TrackRendererSubtitleAttr* attr_list,
+ int attr_list_size, void* userdata);
+#endif
+
+/**
+ * @description When there is closed caption to display, this function
+ * is called to give closed caption data.
+ * @param [in] data : closedcaption data \n
+ * @param [in] size : length of closedcaption data \n
+ * @param [in] userdata : userdata of trackrenderer_closedcaption_cb
+ * callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_closedcaption_cb, in advance.
+ * @post User can display the closed caption using shared data.
+ * @see trackrenderer_set_closedcaption_cb()
+ */
+typedef void (*trackrenderer_closedcaption_cb)(const char* data, const int size,
+ void* userdata);
+
+/**
+ * @description When trackrenderer detected any informations which need to
+ * initiate drm, this function is called to share the drm
+ * informations for decryption.
+ * @param [in] drmhandle : drm handle \n
+ * @param [in] len : pssh data length \n
+ * @param [in] psshdata : pssh data \n
+ * @param [in] type : track type \n
+ * @param [in] userdata : userdata of trackrenderer_drminitdata_cb
+ * callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_drminitdata_cb, in advance.
+ * @post User can use the shared drm informations for initializing drm.
+ * @see trackrenderer_set_drminitdata_cb()
+ */
+typedef void (*trackrenderer_drminitdata_cb)(int* drmhandle, unsigned int len,
+ unsigned char* psshdata,
+ TrackRendererTrackType type,
+ void* userdata);
+
+/**
+ * @description When trackrenderer detected that there is not enough data or
+ * full at trackrenderer buffer, call this function to let user
+ * know current buffer status.
+ * @param [in] type : track type \n
+ * @param [in] status : trackrenderer buffer status \n
+ * @pre The callback function should be set by
+ * trackrenderer_set_bufferstatus_cb, in advance.
+ * @post User can control data feeding speed with this change
+ * callback.
+ * @see trackrenderer_set_bufferstatus_cb()
+ */
+typedef void (*trackrenderer_bufferstatus_cb)(
+ const TrackRendererTrackType type, const TrackRendererBufferStatus status,
+ void* userdata);
+
+/**
+ * @description When trackrenderer finish to flush the currently buffered data
+ * and trackrenderer is ready to get new seeked position segment,
+ * this is called. User can know the time to push the new segment
+ * data.
+ * @param [in] type : track type \n
+ * [in] offset : the new seeked position \n
+ * @pre The callback function should be set by
+ * trackrenderer_set_seekdata_cb, in advance.
+ * @post User can control feeding time during seek operation.
+ * @see trackrenderer_set_seekdata_cb()
+ */
+typedef void (*trackrenderer_seekdata_cb)(const TrackRendererTrackType type,
+ const uint64_t offset,
+ void* userdata);
+
+/**
+ * @description When trackrenderer finish to decode video buffer,
+ * this is called want to get an useful tbm point form the tbm
+ * list.
+ * @param [in] ptr : pointer set to get tbm address \n
+ * [in] is_scale_change : param indicate whether scale resolution
+ * changed \n
+ * @pre The callback function should be set by
+ * trackrenderer_set_media_packet_video_tbmptr_cb, in advance.
+ * @post User can get an useful tbm surface ptr
+ * @version 3.2
+ * @see trackrenderer_set_media_packet_video_tbmptr_cb()
+ */
+typedef void (*trackrenderer_media_packet_video_tbmptr_cb)(void** ptr,
+ bool is_scale_change,
+ void* userdata);
+
+/**
+ * @description When trackrenderer finish to decode video buffer,
+ * this is called, then user can get the decoded video buffer
+ * @param [in] packet : decoded video packet \n
+ * @pre The callback function should be set by
+ * trackrenderer_set_media_packet_video_decoded_cb, in advance.
+ * @post User can use the decoded buffer to render.
+ * @see trackrenderer_set_media_packet_video_decoded_cb()
+ */
+typedef void (*trackrenderer_media_packet_video_decoded_cb)(
+ const TrackRendererDecodedVideoPacket* packet, void* userdata);
+
+/**
+ * @description If the latency status of the video stream changes, this
+ * function will be called.
+ * @param [in] latency_status : the video latency status \n
+ * [in] userdata : userdata of trackrenderer_latency_status_cb
+ * function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_video_latency_status_cb, in advance.
+ * @post User can decide the next operation after the latency status
+ * changes.
+ * @see trackrenderer_set_video_latency_status_cb()
+ */
+typedef void (*trackrenderer_video_latency_status_cb)(
+ const TrackRendererLatencyStatus video_latency_status, void* userdata);
+
+/**
+ * @description If the latency status of the audio stream changes, this
+ * function will be called.
+ * @param [in] latency_status : the audio latency status \n
+ * [in] userdata : userdata of trackrenderer_latency_status_cb
+ * function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_audio_latency_status_cb, in advance.
+ * @post User can decide the next operation after the latency status
+ * changes.
+ * @see trackrenderer_set_audio_latency_status_cb()
+ */
+typedef void (*trackrenderer_audio_latency_status_cb)(
+ const TrackRendererLatencyStatus audio_latency_status, void* userdata);
+
+/**
+ * @description If high latency occurs, this function will be called.
+ * @param [in] userdata : userdata of trackrenderer_video_high_latency_cb
+ * function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_video_high_latency_cb, in advance.
+ * @post User can decide the next operation after video high latency
+ * occurs.
+ * @see trackrenderer_set_video_high_latency_cb()
+ */
+typedef void (*trackrenderer_video_high_latency_cb)(void* userdata);
+
+/**
+ * @description If high latency occurs, this function will be called.
+ * @param [in] userdata : userdata of trackrenderer_audio_high_latency_cb
+ * function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_audio_high_latency_cb, in advance.
+ * @post User can decide the next operation after audio high latency
+ * occurs.
+ * @see trackrenderer_set_audio_high_latency_cb()
+ */
+typedef void (*trackrenderer_audio_high_latency_cb)(void* userdata);
+
+/**
+ * @description If ResourceCenter sends video start request to a player when
+ * it's required, this function will be called.
+ * @param [in] userdata : userdata of
+ * trackrenderer_multiview_start_video_cb callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_multiview_start_video_cb in advance.
+ * @post User will activate video stream.
+ * @see trackrenderer_set_multiview_start_video_cb()
+ */
+typedef void (*trackrenderer_multiview_start_video_cb)(void* userdata);
+
+/**
+ * @description If ResourceCenter sends video stop request to a player when
+ * it's required, this function will be called.
+ * @param [in] userdata : userdata of
+ * trackrenderer_multiview_stop_video_cb callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_multiview_stop_video_cb in advance.
+ * @post User will deactivate video stream.
+ * @see trackrenderer_set_multiview_stop_video_cb()
+ */
+typedef void (*trackrenderer_multiview_stop_video_cb)(void* userdata);
+
+/**
+ * @brief return libtrackrenderer.so lib version as string
+ * @param None
+ * @return version info - major.minor.micro - as string
+ * @code
+ * //your logic
+ * const char* version;
+ * version = trackrenderer_get_version();
+ * //your logic
+ * @endcode
+ * @pre None
+ * @post None
+ * @exception None
+ */
+const char* trackrenderer_get_version(void);
+
+/**
+ * @brief return libtrackrenderer.so lib version as constant int.
+ * @param None
+ * @return version info - major.minor.micro - as unsigned int
+ * @code
+ * //your logic
+ * uint32_t version = 0;
+ * version = trackrenderer_get_version_int();
+ * //your logic
+ * @endcode
+ * @pre None
+ * @post None
+ * @exception None
+ */
+uint32_t trackrenderer_get_version_int(void);
+
+/**
+ * @brief Create trackrenderer instance.
+ * @description Create trackrenderer object and handle for accessing
+ * trackrenderer object. Trackrenderer is in charge of rendering
+ * related job ex) display setting, decoding, rendering, etc..
+ * @param [out] handle : Get trackrenderer handle ptr.
+ * @return 0 on success, otherwise -1 failed.
+ * @code
+ * TrackRendererHandle handle = nullptr;
+ * trackrenderer_create(&handle);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre None
+ * @post None
+ * @exception None
+ */
+int trackrenderer_create(TrackRendererHandle* handle);
+
+/**
+ * @brief Remove trackrenderer object
+ * @description Remove and dealloc trackrenderer object and handle.
+ * @param [in] handle : trackrenderer handle
+ * @return 0 on success, otherwise -1 failed.
+ * @code
+ * refer to the sample code of trackrenderer_create()
+ * @endcode
+ * @pre None
+ * @post None
+ * @exception None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_destroy(TrackRendererHandle handle);
+
+/**
+ * @brief Trackrenderer start to play media contents.
+ * @description In this function, trackrenderer start to display the delivered
+ * media contents.
+ * @param [in] handle : trackrenderer handle
+ * @return Return 0 if no problem.
+ * Otherewise -1 if the trackrenderer is not even set up.
+ * @pre The trackrenderer must be at least created and set up.
+ * @post The contents is playing.
+ * @exception None
+ * @code
+ * //prepare trackrenderer hanle
+ * trackrenderer_start(handle);
+ * //your logic
+ * trackrenderer_pause(handle);
+ * //your logic
+ * trackrenderer_resume(handle);
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+int trackrenderer_start(TrackRendererHandle handle);
+
+/**
+ * @brief Trackrenderer stop to play the media contents.
+ * @description In this function trackrenderer release the resources. When user
+ * command stop to play or got callback such as
+ * trackrenderer_error_cb or trackrenderer_resource_conflicted_cb,
+ * this is called.
+ * @param [in] handle : trackrenderer handle
+ * @return Return 0 if the trackrenderer is not even setup or release the
+ * resource successfully.
+ * Otherwise -1.
+ * @code
+ * refer to the sample code of trackrenderer_start()
+ * @endcode
+ * @pre After trackrenderer object is created, this can be called.
+ * @post Trackrenderer release the decoder and display resource.
+ * @exception None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_start()
+ */
+int trackrenderer_stop(TrackRendererHandle handle);
+
+/**
+ * @brief Trackrenderer set up properties and prepare playback.
+ * @description In this function trackrenderer prepare video/audio decoder,
+ * sink, drm info, etc.. and set up several properties for
+ * video/audio decoding and rendering.
+ * @param [in] handle : trackrenderer handle
+ * @return Return 0 if all valid tracks playback is successfully prepared,
+ * succeed to update display 1 decoded frame media contents.
+ * Otherwise -1.
+ * @code
+ * trackrenderer_create(&handle);
+ * //set tracks if needed
+ * trackrenderer_prepare(handle);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre Active tracks informations for playing need to be set up.
+ * The media contents data must be delivered as mush as
+ * trackrenderer can parse data type.
+ * @post 1 decoded frame media contents is displayed. The necessary TV
+ * resource is prepared to use.
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_set_track()
+ */
+int trackrenderer_prepare(TrackRendererHandle handle);
+
+/**
+ * @brief Trackrenderer pause displaying media contents
+ * @param [in] handle : trackrenderer handle ptr.
+ * @return Return 0 if trakrenderer is successfully paused.
+ * Oterwise return -1 if trackrenderer already release the
+ * resource.
+ * @code
+ * refer to the sample code of trackrenderer_start()
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post The trackrenderer pause playback and displaying 1 decoded frame
+ * data.
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_start()
+ */
+int trackrenderer_pause(TrackRendererHandle handle);
+
+/**
+ * @brief Trackrenderer resume displaying media contents
+ * @description In this function trackrenderer resume media contents from
+ * paused position.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @return Returen 0 if trackrenderer is successfully resume media
+ * contents.
+ * Otherwise -1.
+ * @code
+ * refer to the sample code of trackrenderer_start()
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post The contents is playing.
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_start() \n
+ * trackrenderer_pause()
+ */
+int trackrenderer_resume(TrackRendererHandle handle);
+
+/**
+ * @brief Set active track info to set up trackrenderer properties.
+ * @description In this fuction, trackrenderer set track info which is
+ * necessary for media contents playback.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] track : Active tracks for playing media contents.
+ * @param [in] size : Number of tracks. The maximum number of tracks
+ * is 3.(audio, video, subtitle)
+ * @return Return 0 if there are any active tracks among audio, video,
+ * subtitle and successcully set the track info to trackrenderer.
+ * Otherwise -1 if there is not any active tracks or already set
+ * tracks info.
+ * @code
+ * TrackRendererHandle handle = nullptr;
+ * trackrenderer_create(&handle)
+ * TrackRendererTrack tracks[] = {videotrack, audiotrack};
+ * int tracksize = 2;
+ * trackrenderer_set_track(handle, tracks, tracksize);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre Get active tracks list for playing.
+ * @post Set up left of other properties to trackrenderer with using
+ * trackrenderer_prepare().
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_track(TrackRendererHandle handle,
+ const TrackRendererTrack* track, const int size);
+
+/**
+ * @brief Set properties to trackrenderer.
+ * @description Parsing informations from loaded property file and set values
+ * to trackrenderer.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] properties : Setting properties got from property file.
+ * @param [in] property_size : Number of properties for parsing.
+ * @code
+ * trackrenderer_create(&handle)
+ * TrackRendererIniProperty properties[2];
+ * properties[0].key = "use_new_hls_mpegts_demuxer";
+ * properties[0].value = true;
+ * properties[1].key = "use_new_dash_tiny_demuxer";
+ * properties[1].value = false;
+ * trackrenderer_set_ini_property(handle, properties, 2)
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre Load property file and get informations.
+ * @post Trackrenderer set the parsed properties.
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create()
+ */
+void trackrenderer_set_ini_property(TrackRendererHandle handle,
+ TrackRendererIniProperty* properties,
+ int property_size);
+
+/**
+ * @brief Seek trackrenderer playback position, asynchronously.
+ * @description In this function, trackrenderer seek playback position to
+ * specific position and can set playback rate.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] time_millisecond : the absolute position(playingtime) of
+ * the stream in milliseconds
+ * @param [in] playback_rate : For trick play playback rate.
+ * @return Return 0 if he trackrenderer can seek the position or set
+ * playback rate successfully. Playback rate 0 is not accecptable
+ * eventhough return value is 0.
+ * Otherwise -1 if the trackrenderer is not even setup,
+ * trackrenderer fail to seek playback position or fail to set
+ * playback rate.
+ * @code
+ * static void SeekDoneCb(UserData userdata) {
+ * //do something you want when seek done
+ * }
+ * static void SeekDataCb(const TrackRendererTrackType type,
+ * const unsigned long long offset, UserData userdata) {
+ * //do something you want when trackrenderer is ready
+ * to get new seeked position segment data
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_seekdone_cb(handle, SeekDoneCb, nullptr);
+ * trackrenderer_set_seekdata_cb(handle, SeekDataCb, nullptr);
+ * trackrenderer_prepare(handle);
+ * trackrenderer_start(handle);
+ * //your logic
+ * trackrenderer_seek(handle,10,1.0);
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post Release the buffered data and start buffering seeked position
+ * segment data.
+ * @exception None
+ * @remark Seek is asynchronous operation. trackrenderer_seekdone_cb() is
+ * called when seek operation is finished.
+ * The accecptable playback rate is different for each streaming
+ * source type.
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_start() \n
+ * trackrenderer_set_seekdone_cb()
+ */
+int trackrenderer_seek(TrackRendererHandle handle,
+ unsigned long long time_millisecond,
+ double playback_rate);
+/**
+ * @brief Seek trackrenderer playback position, asynchronously.
+ * @description In this function, trackrenderer seek playback position to
+ * specific position and can set playback rate with audio mute
+ * on/off.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] time_millisecond : the absolute position(playingtime) of
+ * the stream in milliseconds
+ * @param [in] playback_rate : For trick play playback rate.
+ * @param [in] audio_mute : audio is mute on/off when seek to target
+ * position.
+ * @return Return 0 if he trackrenderer can seek the position or set
+ * playback rate successfully. Playback rate 0 is not accecptable
+ * eventhough return value is 0.
+ * Otherwise -1 if the trackrenderer is not even setup,
+ * trackrenderer fail to seek playback position or fail to set
+ * playback rate.
+ * @code
+ * //prepare trackrenderer hanle
+ * //your logic
+ * trackrenderer_start(handle);
+ * //your logic
+ * trackrenderer_seek2(handle,10,1.0,true);
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post Release the buffered data and start buffering seeked position
+ * segment data.
+ * @exception None
+ * @version 2.0
+ * @remark Seek is asynchronous operation. trackrenderer_seekdone_cb() is
+ * called when seek operation is finished.
+ * The accecptable playback rate is different for each streaming
+ * source type.
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_start()
+ */
+int trackrenderer_seek2(TrackRendererHandle handle,
+ unsigned long long time_millisecond,
+ double playback_rate, bool audio_mute);
+
+/**
+ * @brief Set trackrenderer playback rate, asynchronously.
+ * @description In this function, trackrenderer set playback rate and audio
+ * mute on/off.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] playback_rate : For trick play playback rate.
+ * @param [in] audio_mute : For trick play audio mute on/off, true: mute
+ * on, false: mute off.
+ * @return Return 0 if he trackrenderer can set playback rate
+ * successfully. Playback rate 0 is not accecptable
+ * eventhough return value is 0.
+ * Otherwise -1 if the trackrenderer is not even setup,
+ * trackrenderer fail to set playback rate.
+ * @code
+ * //prepare trackrenderer hanle
+ * //your logic
+ * trackrenderer_set_playback_rate(handle, 2.0, true);
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post Release the buffered data and start buffering current playing
+ * position
+ * @exception None
+ * @version 2.0
+ * @remark Setting playback rate is asynchronous operation.
+ * The accecptable playback rate is different for each streaming
+ * source type.
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+int trackrenderer_set_playback_rate(TrackRendererHandle handle,
+ double playback_rate, bool audio_mute);
+
+/**
+ * @brief Get current trackrenderer playing time
+ * @description In this function trackrenderer get currently rendering
+ * contents position. This is used when change playback rate from
+ * current position.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [out] time_millisecond : current playing time in milliseconds
+ * @return Return 0 if trackrenderer successfully get currently rendering
+ * contents position.
+ * Otherwise -1.
+ * @code
+ * //prepare trackrenderer hanle
+ * //your logic
+ * uint64_t time_millisecond = 0;
+ * trackrenderer_get_playing_time(handle, &time_millisecond));
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None.
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+int trackrenderer_get_playing_time(TrackRendererHandle handle,
+ unsigned long long* time_millisecond);
+/**
+ * @brief Get dropped frame counts in videosink
+ * @description In this function trackrenderer get currently dropped frames in
+ * sink.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [out] counts : dropped counts
+ * @return Return 0 if trackrenderer successfully get frame counts.
+ * Otherwise -1.
+ * @code
+ * //prepare trackrenderer hanle
+ * //your logic
+ * uint64_t count = 0;
+ * trackrenderer_get_dropped_frames(handle,static_cast<void*>(&count)));
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None.
+ * @version 2.0
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+int trackrenderer_get_dropped_frames(TrackRendererHandle handle, void* counts);
+
+/**
+ * @brief Get dropped frame counts for catchup in video/audio sink
+ * @description In this function trackrenderer get dropped frames for catchup
+ * in sink.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : the type of the track
+ * @param [out] counts : dropped counts
+ * @return Return 0 if trackrenderer successfully get frame counts.
+ * Otherwise -1.
+ * @code
+ * //prepare trackrenderer hanle
+ * //your logic
+ * uint64_t dropped_video_frames = 0;
+ * trackrenderer_get_dropped_frames_for_catchup(handle,
+ * TrackRendererTrackType::kTrackRendererTrackTypeVideo,
+ * static_cast<void*>(&dropped_video_frames)));
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None.
+ * @version 3.0
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+int trackrenderer_get_dropped_frames_for_catchup(TrackRendererHandle handle,
+ TrackRendererTrackType type,
+ void* counts);
+
+/**
+ * @brief Flush buffers for a trackrenderer.
+ * @description In this function trackrenderer will flush data in
+ * appsrc,decoder,sink.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : type of track which need to be flush.
+ * @return Return 0 if flushing buffer for a track successfully.
+ * Otherwise -1.
+ * @code
+ * static void FlushDoneCb(UserData userdata) {
+ * //do something you want to do when flush done
+ * }
+
+ * trackrenderer_create(&handle);
+ * trackrenderer_flushdone_cb(handle, FlushDoneCb, nullptr);
+ * trackrenderer_prepare(handle);
+ * //your logic
+ * trackrenderer_flush(handle, kTrackRendererTrackTypeAudio);
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None.
+ * @version 2.0
+ * @remark trackrenderer_flushdone_cb() is called when flush operation is
+ * fininshed
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_flushdone_cb()
+ */
+int trackrenderer_flush(TrackRendererHandle handle,
+ TrackRendererTrackType type);
+
+/**
+ * @brief Trackrenderer flush the buffered data and release the TV
+ * rendering resources.
+ * @description In this function flush the currently inserted data for
+ * rendering to get new data stream. Release current audio decoder
+ * and display resource for getting new data and decoder as well.
+ * The deactivatable track is audio and subtitle only.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : track type.
+ * @return Return 0 if successfully flush the inserted data and release
+ * the TV resources.
+ * Otherwise -1 if the trackrenderer is not even set up or fail to
+ * release the resources.
+ * @code
+ * //prepare trackrenderer hanle
+ * //your logic
+ * trackrenderer_deactivate(handle, kTrackRendererTrackTypeVideo)
+ * //your logic
+ * trackrenderer_activate(handle,
+ * kTrackRendererTrackTypeVideo,nullptr));
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post trackrenderer_activate() should be called if new data stream
+ * need to be inserted and get new TV resource for decoding and
+ * rendering.
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+int trackrenderer_deactivate(TrackRendererHandle handle,
+ TrackRendererTrackType type);
+
+/**
+ * @brief Trackrenderer set new tracks and set up the new data stream
+ * properties.
+ * @description In this function rebuild the trackrenderer properties for the
+ * newly changed data stream and get TV resources for playback.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : track type(audio, video, subtitle)
+ * @param [in] track : a track that need to be rebuilded.
+ * @return Return 0 if trackrenderer is successfully rebuilt with using
+ * new data stream. Otherwise -1 if the trackrenderer is not even
+ * set up or fail to get resources.
+ * @code
+ * refer to the sample code of trackrenderer_deactivate()
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_deactivate()
+ */
+int trackrenderer_activate(TrackRendererHandle handle,
+ TrackRendererTrackType type,
+ TrackRendererTrack* track);
+
+/**
+ * @brief The shared data will be pushed to the queue.
+ * @description In this function push the buffer to trackrenderer queue of
+ * buffer. When the buffer is full, the queue is blocked until
+ * free space becomes available in queue. With parameter, user can
+ * know the submition result and can handle for it. This should be
+ * called repeatedly during playback.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] data : the data user want to add to the trackrenderer
+ buffer
+ * @param [out] retval : the result of submition.
+ * @return Return 0 if trackrenderer successfully add the buffer to queue
+ * or the EOS data.
+ * Otherwise -1 if the trackrenderer is not even set up, queue is
+ * already full or the data is unusable.
+ * @code
+ * static void BufferStatusCb(const TrackRendererTrackType type,
+ * const TrackRendererBufferStatus status,UserData userdata) {
+ * if (type == kTrackRendererTrackTypeAudio) {
+ * if (status == kTrackRendererBufferStatusUnderrun)
+ * // do something when feed Audio is possible
+ * else
+ * //do something when feed Audio is impossible
+ * } else if(type == kTrackRendererTrackTypeVideo) {
+ * if (status == kTrackRendererBufferStatusUnderrun)
+ * //do something when feed Video is possible
+ * else
+ * //do something when feed Video is impossible
+ * } else if(type == kTrackRendererTrackTypeSubtitle) {
+ * if (status == kTrackRendererBufferStatusUnderrun)
+ * //do something when feed Subtitle is possible
+ * else
+ * //do something when feed Subtitle is impossible
+ * }
+ * }
+ * void FeedPacket(const TrackRendererHandle& handle,
+ * const TrackRendererDecoderInputBufferPtr& buffer,...) {
+ * //your logic
+ * SubmitStatus status=kTrackRendererSubmitStatusNotPrepared;
+ * trackrenderer_submit_packet(handle, buffer.get(),&status);
+ * //your logic
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_bufferstatus_cb(handle,BufferStatusCb,nullptr);
+ * trackrenderer_prepare(handle);
+ * //your logic
+ * //execute FeedPacket() when feed is possible.
+ * (call a new thread to do this)
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post If EOS is submitted to buffer, trackrenderer_eos_cb will be
+ * called.
+ * @exception None
+ * @remark The buffered tracksource data is submitted to trackrenderer by
+ * feeder.
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_set_bufferstatus_cb()
+ */
+int trackrenderer_submit_packet(TrackRendererHandle handle,
+ TrackRendererDecoderInputBuffer* data,
+ TrackRendererSubmitStatus* retval);
+
+/**
+ * @brief The shared data will be pushed to the queue.
+ * @description In this function push the buffer to trackrenderer queue of
+ * buffer. When the buffer is full, the queue is blocked until
+ * free space becomes available in queue. With parameter, user can
+ * know the submition result and can handle for it. This should be
+ * called repeatedly during playback.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] data : the data user want to add to the trackrenderer
+ * buffer
+ * @param [out] retval : the result of submition.
+ * @code
+ refer to the sample code of trackrenderer_submit_packet()
+ * @endcode
+ * @return Return 0 if trackrenderer successfully add the buffer to queue
+ * or the EOS data.
+ * Otherwise -1 if the trackrenderer is not even set up, queue is
+ * already full or the data is unusable.
+ * @pre The trackrenderer must be at least created and set up.
+ * @post If EOS is submitted to buffer, trackrenderer_eos_cb will be
+ * called.
+ * @exception None
+ * @remark The buffered tracksource data is submitted to trackrenderer by
+ * feeder.
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_set_bufferstatus_cb()
+ */
+int trackrenderer_submit_packet2(TrackRendererHandle handle,
+ TrackRendererDecoderInputBuffer* data,
+ TrackRendererSubmitStatus* retval);
+
+/**
+ * @brief Set drm property.
+ * @description Set drm property for decription. The properties will be used
+ * for drm licensing just in case trackrender does security
+ * process itself.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] drm_property : drm property
+ * @code
+ * trackrenderer_create(&handle);
+ * TrackRendererDrmProperty trackrenderer_drm_property{
+ * kTrackRendererDrmTypePlayready,
+ * 0, 1, nullptr, nullptr};
+ * trackrenderer_set_drm(handle,&trackrenderer_drm_property);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @remark Set drm property directly
+ * @see trackrenderer_create()
+ */
+void trackrenderer_set_drm(TrackRendererHandle handle,
+ TrackRendererDrmProperty* drm_property);
+
+/**
+ * @brief Notify drm licensing is done.
+ * @description In this function let user know that trackrenderer getright is
+ * finished successfully. This is used when trackrenderer does drm
+ * process itself.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : which type of track drm license was acquired
+ * @code
+ * //prepare trackrenderer hanle
+ * //your logic
+ * trackrenderer_drm_license_acquired_done(handle,
+ * kTrackRendererTrackTypeMax);
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+void trackrenderer_drm_license_acquired_done(TrackRendererHandle handle,
+ TrackRendererTrackType type);
+
+/**
+ * @brief Set the video display mode.
+ * @description In this function set how display shows. If the
+ * trackrenderer is not prepared to update the display yet, the
+ * trackrenderer stored the mode and update display after
+ * trackrenderer is ready to update display.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] mode : display mode
+ * @return Return 0 if rendering mode is successfully applied or stored
+ * for future displaying.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_display_mode(
+ * handle, kTrackRendererDisplayModeOriginSize));
+ * //your logic
+ * TrackRendererDisplayMode display_mode =
+ * static_cast<TrackRendererDisplayMode>(-1);
+ * trackrenderer_get_display_mode(handle, &display_mode);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_display_mode(TrackRendererHandle handle,
+ TrackRendererDisplayMode mode);
+
+/**
+ * @brief Set the video display rotate angle.
+ * @description In this function will set the video display rotate. If the
+ * trackrenderer is not prepared to update the display rotate yet,
+ * the trackrenderer stored the rotate and update it after
+ * trackrenderer is ready to update display.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] rotate : display rotate angle
+ * @return Return 0 if rendering mode is successfully applied or stored
+ * for future displaying.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_display_rotate(handle,kTrackRendererDisplayRotate90);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @version 2.0
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_display_rotate(TrackRendererHandle handle,
+ TrackRendererDisplayRotate rotate);
+
+/**
+ * @brief Get the video display rotate angle.
+ * @description In this function will get the video display rotate.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [out] rotate : store the display rotate angle
+ * @return Return 0 if get rotate value success.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @code
+ * trackrenderer_create(&handle);
+ * //your logic
+ * TrackRendererDisplayRotate gtype =
+ * kTrackRendererDisplayRotateNone;
+ * rackrenderer_get_display_rotate(handle, >ype);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @version 2.0
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_get_display_rotate(TrackRendererHandle handle,
+ TrackRendererDisplayRotate* rotate);
+
+/**
+ * @brief Set the video display properties.
+ * @description In this function set rendering properties and rendering
+ * locations. If the trackrenderer is not prepared to update the
+ * display yet, the trackrenderer store the mode and update
+ * display after trackrenderer is ready to update display.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : display type
+ * @param [in] surface_id : resource id of window.
+ * @param [in] x : x param of display window
+ * @param [in] y : y param of display window
+ * @param [in] w : width of display window
+ * @param [in] h : height of display window
+ * @return Return 0 if rendering properties are successfully applied for
+ * displaying contents or stored for future displaying.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_display_surface(handle,
+ * kTrackRendererDisplayTypeOverlay, 3, 100, 100, 100, 100);
+ * //your logic
+ * TrackRendererDisplayType type = kTrackRendererDisplayTypeNone;
+ * TrackRendererGeometry area = {-1, -1, -1, -1};
+ * trackrenderer_get_display(handle, &type, &area);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_display_surface(TrackRendererHandle handle,
+ TrackRendererDisplayType type,
+ unsigned int surface_id, long x, long y,
+ long w, long h);
+
+/**
+ * @brief Set the video rendering object.
+ * @description In this function set rendering object. If the trackrenderer is
+ * not prepared to update the display yet, the trackrenderer
+ * stored the rendering object and update display after
+ * trackrenderer is ready to update display.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : trackrenderer display type.
+ * @param [in] obj : the handle to display window.
+ * @return Return 0 if rendering object is successfully applied for
+ * displaying contents or stored for future displaying.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @code
+ * trackrenderer_create(&handle);
+ * Environment* env = new Environment();
+ * TrackRendererDisplayType type =
+ * kTrackRendererDisplayTypeOverlay;
+ * trackrenderer_set_display(handle,type,env->Window());
+ * //your logic
+ * delete env;
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_display(TrackRendererHandle handle,
+ TrackRendererDisplayType type, void* obj);
+
+/**
+ * @brief Set the video display properties.
+ * @description In this function set rendering object and rendering
+ * locations. If the trackrenderer is not prepared to update the
+ * display yet, the trackrenderer stored the rendering object
+ * properties and update display after trackrenderer is ready to
+ * update display.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : display type
+ * @param [in] ecore_wl2_window : ecore wayland handle to display window.
+ * @param [in] x : x param of display window
+ * @param [in] y : y param of display window
+ * @param [in] w : width of display window
+ * @param [in] h : height of display window
+ * @return Return 0 if rendering oejct and properties are successfully
+ * applied for displaying contents or stored for future
+ * displaying.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_display_ecore_wl2_window(
+ * handle,kTrackRendererDisplayTypeOverlay,NULL,
+ * 0,0,1920,1080));
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_display_ecore_wl2_window(TrackRendererHandle handle,
+ TrackRendererDisplayType type,
+ void* ecore_wl2_window, int x,
+ int y, int w, int h);
+/**
+ * @brief Set the video display properties.
+ * @description In this function set rendering object and rendering
+ * locations. If the trackrenderer is not prepared to update the
+ * display yet, the trackrenderer stored the rendering object
+ * properties and update display after trackrenderer is ready to
+ * update display.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : display type
+ * @param [in] ecore_wl2_subsurface : ecore wl subsurface handle to
+ * display window.
+ * @param [in] x : x param of display window
+ * @param [in] y : y param of display window
+ * @param [in] w : width of display window
+ * @param [in] h : height of display window
+ * @return Return 0 if rendering oejct and properties are successfully
+ * applied for displaying contents or stored for future
+ * displaying.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @version 3.1
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_display_ecore_wl2_subsurface(
+ TrackRendererHandle handle, TrackRendererDisplayType type,
+ void* ecore_wl2_subsurface, int x, int y, int w, int h);
+
+/**
+ * @brief Set the ROI(Region Of Interest) area of display
+ * @description In this function set contents rendering range.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] roi : Roi Geometry
+ * @return Return 0 if rendering range is successfully set or
+ * rendering range is stored for future displaying.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_display_mode(
+ * handle, kTrackRendererDisplayModeDstRoi));
+ * TrackRendererGeometry roi = {0, 0, 1920, 1080};
+ * trackrenderer_set_display_roi(handle, &roi);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * The display mode should be set to
+ * kTrackRendererDisplayModeDstRoi.
+ * @post None
+ * @exception None
+ * @remark The minimum value of width and height are 1.
+ * @see trackrenderer_create() \n
+ * trackrenderer_set_display_mode()
+ */
+int trackrenderer_set_display_roi(TrackRendererHandle handle,
+ TrackRendererGeometry* roi);
+
+/**
+ * @brief Set the Crop Area(Region Of Src ratio) area of display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] crop: x y label , height, width crop ratio in src video area.
+ * @return @c true on success, otherwise @c false.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_display_mode(
+ * handle, kTrackRendererDisplayModeDstRoi));
+ * TrackRendererCropArea roi = {0.2, 0.2, 0.5, 0.5};
+ * trackrenderer_set_video_roi(handle, &roi));
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and
+ * The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @version 2.0
+ * @remark The minimum value of input are 0,maximun value is 1.
+ * @see trackrenderer_create() \n
+ * esplusplayer_set_display() \n
+ * esplusplayer_set_display_visible()
+ */
+int trackrenderer_set_video_roi(TrackRendererHandle handle,
+ TrackRendererCropArea* crop);
+
+/**
+ * @brief Resize the render rectangle(the max region that video can be
+ * displayed).
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] rect: render rectangle.
+ * @return @c true on success, otherwise @c false.
+ * @pre This should be called after trackrenderer_set_display() \n
+ * trackrenderer_set_display_surface() \n
+ * trackrenderer_set_display_ecore_wl2_window() \n
+ * trackrenderer_set_display_ecore_wl2_subsurface
+ * @post None
+ * @exception None
+ * @version 3.2
+ * @remark The minimum value of width and height are 1.
+ * @see esplusplayer_set_display() \n
+ * trackrenderer_set_display_surface() \n
+ * trackrenderer_set_display_ecore_wl2_window() \n
+ * trackrenderer_set_display_ecore_wl2_subsurface
+ */
+int trackrenderer_resize_render_rect(TrackRendererHandle handle,
+ TrackRendererRenderRect* rect);
+
+/**
+ * @brief Set on off the video display.
+ * @description In this function trackrenderer can set contents display
+ * visibility for some reasons.
+ * trackrenderer_set_display_visible() toggle works.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] is_visible : The visibility of the display \n
+ * (@c true = visible, @c false = non-visible)
+ * @return Return 0 if visibility is updated or stored for future
+ * displaying.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_display_visible(handle, true);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @remark None
+ * @see This is similar to trackrenderer_set_audio_mute() for audio
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_display_visible(TrackRendererHandle handle,
+ bool is_visible);
+
+/**
+ * @brief Get the ROI(Region Of Interest) area of display and display
+ * type
+ * @description In this function get contents rendering range and display
+ * type.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [out] type : display type
+ * @param [out] area : Roi Geometry
+ * @code
+ * please refer to the sample code of
+ * trackrenderer_set_display_surface
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @see trackrenderer_create() \n
+ * trackrenderer_set_display_surface()
+ */
+void trackrenderer_get_display(TrackRendererHandle handle,
+ TrackRendererDisplayType* type,
+ TrackRendererGeometry* area);
+
+/**
+ * @brief Get trackrenderer video display mode.
+ * @description In this function get what mode trackrenderer displaying
+ * contents.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [out] mode : display mode
+ * @code
+ * please refer to the sample code of
+ * trackrenderer_set_display_mode
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @see trackrenderer_create() \n
+ * trackrenderer_set_display_mode()
+ */
+void trackrenderer_get_display_mode(TrackRendererHandle handle,
+ TrackRendererDisplayMode* mode);
+
+/**
+ * @brief Set app id to resource manager
+ * @description In this fuction trackrenderer set app id to resource
+ * manager. In future resource manager will control the
+ * rendering resources using this app id.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] app_id : application id
+ * @code
+ * trackrenderer_create(&handle);
+ * std::string app_id = "plusplayer-mapi";
+ * trackrenderer_set_app_id(handle, app_id.c_str());
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @see trackrenderer_create()
+ */
+void trackrenderer_set_app_id(TrackRendererHandle handle, const char* app_id);
+
+/**
+ * @brief Set App id to player for resource manager
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] app_info : application id, version, type.
+ * @return @c true on success, otherwise @c false.
+ * @code
+ * trackrenderer_create(&handle);
+ * TrackRendererAppInfo app_info;
+ * app_info.id = "plusplayer-mapi";
+ * app_info.version = "";
+ * app_info.type = "";
+ * trackrenderer_set_app_info(handle, &app_info);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @version 2.0
+ * @see trackrenderer_create()
+ */
+void trackrenderer_set_app_info(TrackRendererHandle handle,
+ const TrackRendererAppInfo* app_info);
+
+/**
+ * @brief Set mute on off the audio sound.
+ * @description In this function trackrenderer can set audio sound on off
+ * for some reasons.
+ * trackrenderer_set_audio_mute() toggle works.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] is_mute : On mute of the sound \n
+ * (@c true = mute, @c false = non-mute)
+ * @return Return 0 if audio mute successfully turn into or stored for
+ * future playing.
+ * Otherwise -1 failed if the trackrenderer not even set up.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_audio_mute(handle, true);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_audio_mute(TrackRendererHandle handle, bool is_mute);
+
+/**
+ * @brief Set trackrenderer video still mode
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : trackrenderer still mode on, off or non
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_video_still_mode(handle,
+ * kTrackRendererStillModeOn);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @see trackrenderer_create()
+ */
+void trackrenderer_set_video_still_mode(TrackRendererHandle handle,
+ TrackRendererStillMode type);
+
+/**
+ * @brief Get trackrenderer current state
+ * @param [in] handle : trackrenderer handle ptr.
+ * @return Returns TrackRendererState.
+ * @code
+ * trackrenderer_create(&handle);
+ * //your logic
+ * TrackRendererState state = trackrenderer_get_state(handle);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @see trackrenderer_create()
+ */
+TrackRendererState trackrenderer_get_state(TrackRendererHandle handle);
+
+/**
+ * @brief Set attribute and value for trackrenderer.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] attr_name : name of first property to set.
+ * @param [in] ... : value for the first property, followed optionally by
+ * more name/value pairs, followed by NULL.
+ * @code
+ * trackrenderer_create(&handle);
+ * unsigned long long video_queue_size = 2 * 1024 * 1024; // 2MB
+ * unsigned long long audio_queue_size = 100 * 1024; // 100KB
+ * trackrenderer_set_attribute(handle,
+ * "video-queue-max-byte", video_queue_byte,
+ * "audio-queue-max-byte", audio_queue_byte,
+ * NULL);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @remark a name of attribute and a type of value must be paired. \n
+ * if you set wrong type of attribute, unknown error can be
+ * occured.\n if trackrenderer can't parse attr_name, assertion
+ * will be failed.
+ * @see trackrenderer_create() \n
+ * If you want to know what kinds of attribute are there, please
+ * refer this link: \n
+ */
+void trackrenderer_set_attribute(TrackRendererHandle handle,
+ const char* attr_name, ...);
+
+/**
+ * @brief Get attribute and value for trackrenderer.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] attr_name : name of first property to set.
+ * @param [in] ... : value for the first property, followed optionally by
+ * more name/value pairs, followed by NULL.
+ * @code
+ * trackrenderer_create(&handle);
+ * unsigned long long video_current_byte;
+ * unsigned long long audio_current_byte;
+ * trackrenderer_get_attribute(handle,
+ * "video-current-level-byte", &video_current_byte,
+ * "audio-current-level-byte", &audio_current_byte,
+ * NULL);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre None
+ * @post None
+ * @exception None
+ * @version 2.0
+ * @remark a name of attribute and a type of value must be paired. \n
+ * if you set wrong type of attribute, unknown error can be
+ * occured.\n if trackrenderer can't parse attr_name, assertion
+ * will be failed.
+ * @see trackrenderer_create() \n
+ * If you want to know what kinds of attribute are there, please
+ * refer this link: \n
+ */
+void trackrenderer_get_attribute(TrackRendererHandle handle,
+ const char* attr_name, ...);
+
+/**
+ * @brief Set matroska color info to trackrenderer as string
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] color_info : matroska color info
+ * @return Return 0 if no problem.
+ * Otherewise -1 if the trackrenderer is not even set up.
+ * @code
+ * //prepare trackrenderer hanle
+ * //your logic
+ * std::string color = "red";
+ * trackrenderer_set_matroska_color_info(handle, color.c_str());
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+int trackrenderer_set_matroska_color_info(TrackRendererHandle handle,
+ const char* color_info);
+
+/**
+ * @brief Set decoded video frame buffer type.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : one of the video decoded buffer type to set .
+ * @pre The trackrenderer must be at least created, but before prepare
+ * if type is not equal to be
+ * kTrackRendererDecodedVideoFrameBufferScale.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_video_frame_buffer_type(handle,
+ * kTrackRendererDecodedVideoFrameBufferReference);
+ * //your logic
+ * trackrenderer_prepare(handle);
+ * //your logic
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None
+ * @version 2.0
+ * @see trackrenderer_create()
+ */
+void trackrenderer_set_video_frame_buffer_type(
+ TrackRendererHandle handle, TrackRendererDecodedVideoFrameBufferType type);
+
+/**
+ * @brief Set the target scale resolution when decoded video frame buffer
+ * type is scale
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] target_width : scale target width of video frame buffer.
+ * @param [in] target_width : scale target height of video frame buffer.
+ * @return Return 0 if no problem.
+ * Otherewise -1 if the trackrenderer is not created.
+ * @pre The trackrenderer must be at least created but before prepare.
+ * trackrenderer_create()
+ * @post None
+ * @version 3.1
+ * @exception None
+ */
+int trackrenderer_set_video_frame_buffer_scale_resolution(
+ TrackRendererHandle handle, uint32_t target_width, uint32_t target_height);
+
+/**
+ * @brief Set the request frame rate of decoded video
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] request_framerate : the request frame rate of returned decoded video frame
+ * The value of track_framerate and request_framerate should be one of the following sets:
+ * track_framerate indicate the frame rate of input video stream
+ * 1.A/(A-B) = X ,X means drop 1 frame every X frame
+ * 2.Special cases,such as 24000/1000 -> 15000/1000
+ * when request_framerate.num = 0, return none decoded video frame.
+ * when request_framerate.num/request_framerate.den =
+ * track_framerate.num/track_framerate.den, return all decoded
+ * video frame.
+ * when request_framerate.num/request_framerate.den <
+ * track_framerate.num/track_framerate.den, drop some decoded
+ * video frame.
+ * @return Return 0 if no problem.
+ * Otherewise -1 if the trackrenderer is not created or the value
+ * of request_framerate is invalid .
+ * @pre The trackrenderer must be at least created.
+ * trackrenderer_create()
+ * @post None
+ * @version 3.3
+ * @exception None
+ * @remark Only works when decoded video frame buffer type is scale
+ */
+int trackrenderer_set_decoded_video_frame_rate(
+ TrackRendererHandle handle, TrackRendererRational request_framerate);
+
+/**
+ * @brief Set volume to trackrenderer
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] volume : volume level(0 ~ 100)
+ * @return Return 0 if no problem.
+ * Otherewise -1 if the trackrenderer is not created.
+ * @code
+ * trackrenderer_create(&handle);
+ * int volume = 65;
+ * trackrenderer_set_volume(handle, volume));
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @version 2.0
+ * @exception None
+ * @see trackrenderer_create()
+ */
+
+int trackrenderer_set_volume(TrackRendererHandle handle, const int volume);
+
+/**
+ * @brief Get volume from trackrenderer
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [out] volume : volume ptr
+ * @return Return 0 if no problem.
+ * Otherewise -1 if the trackrenderer is not created.
+ * @code
+ * trackrenderer_create(&handle);
+ * //your logic
+ * int volume = 0;
+ * trackrenderer_get_volume(handle, &volume));
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @version 2.0
+ * @exception None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_get_volume(TrackRendererHandle handle, int* volume);
+
+/**
+ * @brief Provided api for rendering a video frame which is holded by
+ * video frame peek mode.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @return Return 0 if no problem.
+ * @code
+ * //prepare trackrenderer hanle
+ * //your logic
+ * trackrenderer_render_video_frame(handle);
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * trackrenderer_create()
+ * trackrenderer_prepare() or after trackrenderer_seekdone_cb() is
+ * called.
+ * @post None
+ * @version 2.0
+ * @exception None
+ * @remark esplusplayer_set_video_frame_peek_mode().
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+int trackrenderer_render_video_frame(TrackRendererHandle handle);
+/**
+ * @brief Set catch up speed.
+ * @description Set catch up speed for catch up. The properties will
+ * be used to catch up with latency
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] level : catch up speed level.
+ * @return Return 0 if there is no problem.
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_catch_up_speed(handle,
+ * kTrackRendererCatchUpSpeedSlow);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_catch_up_speed(TrackRendererHandle handle,
+ const TrackRendererCatchUpSpeed level);
+/**
+ * @brief Get current video latency status
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [out] status : current latency status.
+ * @return Return 0 if there is no problem.
+ * @code
+ * trackrenderer_create(&handle);
+ * //your logic
+ * TrackRendererLatencyStatus status;
+ * trackrenderer_get_video_latency_status(handle, &status)
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+
+ */
+int trackrenderer_get_video_latency_status(TrackRendererHandle handle,
+ TrackRendererLatencyStatus* status);
+/**
+ * @brief Get current audio latency status
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [out] status : current latency status.
+ * @return Return 0 if there is no problem.
+ * @code
+ * refer to the sample code of
+ * trackrenderer_get_video_latency_status()
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_get_video_latency_status()
+ */
+int trackrenderer_get_audio_latency_status(TrackRendererHandle handle,
+ TrackRendererLatencyStatus* status);
+
+/**
+ * @brief Provided api for add an aifiter to the video pipeline.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] aifilter : aifilter plugin.
+ * @return Return 0 if no problem.
+ * @code
+ * trackrenderer_create(&handle);
+ * GstElement* aifilter =
+ * gst_element_factory_make("aifilter_autozoom","auto_zoom");
+ * if (aifilter != nullptr) {
+ * g_object_set(G_OBJECT(aifilter),"detection-type",2,NULL);
+ * g_object_set(G_OBJECT(aifilter),"is-enabled",TRUE,NULL);
+ * }
+ * trackrenderer_set_aifilter(handle,aifilter);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @version 3.0
+ * @exception None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_aifilter(TrackRendererHandle handle, void* aifilter);
+
+/**
+ * @brief Set mid latency threshold to trackrenderer
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] threshold : the threshold(number) of the video frames for
+ * mid latency
+ * @code
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_audio_mid_latency_threshold(handle, 2);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @return Return 0 if there is no problem.
+ * @pre The trackrenderer must be at least created
+ * TrackRendererState should be kTrackRendererStateInit or
+ * kTrackRendererStateWorking.
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @remark None
+ * @see trackrenderer_create()
+ */
+int trackrenderer_set_video_mid_latency_threshold(TrackRendererHandle handle,
+ const unsigned int threshold);
+
+/**
+ * @brief Set mid latency threshold to trackrenderer
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] threshold : the threshold(number) of the audio frames for
+ * mid latency
+ * @code
+ * refer the sample code of
+ * trackrenderer_set_video_mid_latency_threshold()
+ * @endcode
+ * @return Return 0 if there is no problem.
+ * @pre The trackrenderer must be at least created
+ * trackrenderer_create()
+ * TrackRendererState should be kTrackRendererStateInit or
+ * kTrackRendererStateWorking.
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_set_video_mid_latency_threshold()
+ */
+int trackrenderer_set_audio_mid_latency_threshold(TrackRendererHandle handle,
+ const unsigned int threshold);
+
+/**
+ * @brief Set high latency threshold to trackrenderer
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] threshold : the threshold(number) of the video frames for
+ * high latency
+ * @return Return 0 if there is no problem.
+ * @code
+ * refer the sample code of
+ * trackrenderer_set_video_mid_latency_threshold()
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * trackrenderer_create()
+ * TrackRendererState should be kTrackRendererStateInit or
+ * kTrackRendererStateWorking.
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_set_video_mid_latency_threshold()
+ */
+int trackrenderer_set_video_high_latency_threshold(
+ TrackRendererHandle handle, const unsigned int threshold);
+
+/**
+ * @brief Set high latency threshold to trackrenderer
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] threshold : the threshold(number) of the audio frames for
+ * high latency
+ * @return Return 0 if there is no problem.
+ * @code
+ * refer the sample code of
+ * trackrenderer_set_video_mid_latency_threshold()
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * TrackRendererState should be kTrackRendererStateInit or
+ * kTrackRendererStateWorking.
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @remark None
+ * @see trackrenderer_create() \n
+ * trackrenderer_set_video_mid_latency_threshold()
+ */
+int trackrenderer_set_audio_high_latency_threshold(
+ TrackRendererHandle handle, const unsigned int threshold);
+
+/* CALL back*/
+
+/**
+ * @brief Set error callback function.
+ * @description If there is an error, the trackrenderer_error_cb will post
+ * error message with error code.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_error_cb function
+ * @param [in] userdata : userdata of trackrenderer_error_cb callback
+ * function.
+ * @code
+ * static void ErrorCb(const TrackRendererErrorType error_code,
+ * UserData userdata) {
+ * //Something you want to do when Receive error
+ * printf("Receive error\n");
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_error_cb(handle, ErrorCb, nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When there is error event, trackrenderer_error_cb() is called.
+ * @exception None
+ * @remark trackrenderer_error_cb \n
+ * [in] error_code : enum TrackRendererErrorType\n
+ * [in] userdata : userdata of trackrenderer_error_cb
+ * callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_error_cb
+ */
+void trackrenderer_set_error_cb(TrackRendererHandle handle,
+ trackrenderer_error_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set error msg callback function.
+ * @description If there is an error, the trackrenderer_error_cb will post
+ * error message with error code and detailed infomation.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_error_msg_cb function
+ * @param [in] userdata : userdata of trackrenderer_error_msg_cb callback
+ * function.
+ * @code
+ * static void ErrorMsgCb(const TrackRendererErrorType error_code,
+ * char* error_msg,UserData userdata) {
+ * //Something you want to do when Receive error
+ * printf("Receive error message: %s\n", error_msg);
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_error_msg_cb(handle, ErrorMsgCb, nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When there is error event, trackrenderer_error_msg_cb() is
+ * called.
+ * @exception None
+ * @version 3.0
+ * @remark trackrenderer_error_msg_cb \n
+ * [in] error_code : enum TrackRendererErrorType\n
+ * [in] error_msg : detailed error message including info related
+ * to codec,demuxer,network status, etc.
+ * [in] userdata : userdata of trackrenderer_error_msg_cb
+ * callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_error_msg_cb
+ */
+void trackrenderer_set_error_msg_cb(TrackRendererHandle handle,
+ trackrenderer_error_msg_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set resource conflict callback funciton
+ * @description If there is resource conflict error, trackrender must release
+ * the current TV decoder and display resource and data feeding
+ * need to be stop as well.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_resource_conflicted_cb ptr.
+ * @param [in] userdata : userdata of
+ * trackrenderer_resource_conflicted_cb callback function.
+ * @code
+ * static void ResourceConflictCb(UserData userdata) {
+ * //Something you want to do when Resource Conflict
+ * printf("Receive Resource Conflict message\n");
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_resourceconflict_cb(handle,
+ * ResourceConflictCb, nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When there is resource conflict, the
+ * trackrenderer_resource_conflicted_cb will notify the resource
+ * conflict message to manage the resource.
+ * @exception None
+ * @remark trackrenderer_resource_conflicted_cb \n
+ * [in] userdata : userdata of
+ * trackrenderer_resource_conflicted_cb callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_resource_conflicted_cb
+ */
+void trackrenderer_set_resourceconflict_cb(
+ TrackRendererHandle handle, trackrenderer_resource_conflicted_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set video latency status callback function
+ * @description If the latency status of the video stream changes, track
+ * renderer must send the callback
+ * to notify the application of the latency status.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_video_latency_status_cb ptr.
+ * @param [in] userdata : userdata of
+ * trackrenderer_video_latency_status_cb callback function.
+ * @code
+ * static void static void VideoLatencyStatusCb(
+ * const TrackRendererLatencyStatus latency_status,
+ * UserData userdata) {
+ * //Something you want to do when VideoLatency Status change
+ * printf("Receive VideoLatency Status change message\n");
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_video_latency_status_cb(handle,
+ * VideoLatencyStatusCb, nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When the latency status of the video stream changes,
+ * trackrenderer_video_latency_status_cb will be sent.
+ * @exception None
+ * @version 3.0
+ * @remark trackrenderer_video_latency_stuats_cb \n
+ * [in] userdata : userdata of
+ * trackrenderer_video_latency_status_cb callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_video_latency_stuats_cb
+ */
+void trackrenderer_set_video_latency_status_cb(
+ TrackRendererHandle handle, trackrenderer_video_latency_status_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set audio latency status callback function
+ * @description If the latency status of the audio stream changes, track
+ * renderer must send the callback
+ * to notify the application of the latency status.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_audio_latency_status_cb ptr.
+ * @param [in] userdata : userdata of
+ * trackrenderer_audio_latency_status_cb callback function.
+ * @code
+ * refer to the sample code of
+ * trackrenderer_set_video_latency_status_cb()
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When the latency status of the audio stream
+ * changes,trackrenderer_audio_latency_status_cb will besent.
+ * @exception None
+ * @version 3.0
+ * @remark trackrenderer_audio_latency_stuats_cb \n
+ * [in] userdata : userdata of
+ * trackrenderer_audio_latency_status_cb
+ * callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_audio_latency_stuats_cb
+ */
+void trackrenderer_set_audio_latency_status_cb(
+ TrackRendererHandle handle, trackrenderer_audio_latency_status_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set video high latency callback function
+ * @description If video high latency occurs, track renderer must send the
+ * callback
+ * to notify the application of the player buffer status.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_video_high_latency_cb ptr.
+ * @param [in] userdata : userdata of
+ * trackrenderer_high_latency_cb callback function.
+ * @code
+ * static void VideoHighLatencyCb(UserData userdata) {
+ * //Something you want to do when high latency occurs *
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_video_high_latency_cb(handle,
+ * VideoHighLatencyCb, nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When video high latency occurs,
+ * trackrenderer_video_high_latency_cb will be sent.
+ * @exception None
+ * @version 3.0
+ * @remark trackrenderer_video_high_latency_cb \n
+ * [in] userdata : userdata of
+ * trackrenderer_video_high_latency_cb callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_video_high_latency_cb
+ */
+void trackrenderer_set_video_high_latency_cb(
+ TrackRendererHandle handle, trackrenderer_video_high_latency_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set audio high latency callback function
+ * @description If audio high latency occurs, track renderer must send the
+ * callback
+ * to notify the application of the player buffer status.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_audio_high_latency_cb ptr.
+ * @param [in] userdata : userdata of
+ * trackrenderer_audio_high_latency_cb callback function.
+ * @code
+ * refer to the sample code of
+ * trackrenderer_set_video_high_latency_cb()
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When audio high latency occurs,
+ * trackrenderer_audio_high_latency_cb will be
+ * sent.
+ * @exception None
+ * @version 3.0
+ * @remark trackrenderer_audio_high_latency_cb \n
+ * [in] userdata : userdata of
+ * trackrenderer_audio_high_latency_cb callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_set_audio_high_latency_cb
+ */
+void trackrenderer_set_audio_high_latency_cb(
+ TrackRendererHandle handle, trackrenderer_audio_high_latency_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set seek done callback funciton.
+ * @description If seek operation is finished, the trackrenderer_seekdone_cb
+ * will be called to notify seek status to help to decide next
+ * operation during or after seek.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_seekdone_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_seekdone_cb callback
+ * function.
+ * @code
+ * refer to the sample code of trackrenderer_seek()
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When seek done, app will handle the trackrenderer_seekdone_cb
+ * to decide next operation.
+ * @exception None
+ * @remark trackrenderer_seekdone_cb \n
+ * [in] userdata : userdata of trackrenderer_seekdone_cb callback
+ * function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_seekdone_cb
+ */
+void trackrenderer_set_seekdone_cb(TrackRendererHandle handle,
+ trackrenderer_seekdone_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set flush done callback funciton.
+ * @description If flush operation is finished, the trackrenderer_flushdone_cb
+ * will be called to notify flush status to help to decide next
+ * operation after flush.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_flushdone_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_flushdone_cb callback
+ * function.
+ * @code
+ * refer to the sample code of trackrenderer_flush()
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When flush done, app will handle the trackrenderer_flushdone_cb
+ * to decide next operation.
+ * @exception None
+ * @version 2.0
+ * @remark trackrenderer_flushdone_cb \n
+ * [in] userdata : userdata of trackrenderer_flushdone_cb callback
+ * function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_flushdone_cb
+ */
+void trackrenderer_set_flushdone_cb(TrackRendererHandle handle,
+ trackrenderer_flushdone_cb callback,
+ void* userdata);
+/**
+ * @brief Set event callback function
+ * @description In this function set event callback to get event information.
+ * If a specific event occurs, trackrenderer_event_cb will be
+ * called to notify it with event information.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_event_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_event_cb
+ * callback function.
+ * @code
+ * static void OnEventCb(const TrackRendererEventType event_type,
+ * const TrackrendererEventMsg msg_data,UserData userdata) {
+ * //Something you want to do when the event occurs
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_event_cb(handle,OnEventCb, nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post App can get the information of the event that occured.
+ * @version 2.0
+ * @exception None
+ * @remark trackrenderer_event_cb
+ * [in] event_type : event type that occured.
+ * [in] msg_data : event message data.
+ * [in] userdata : userdata of trackrenderer_event_cb callback
+ * function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_event_cb
+ */
+void trackrenderer_set_event_cb(TrackRendererHandle handle,
+ trackrenderer_event_cb callback,
+ void* userdata);
+/**
+ * @brief Set subtitle callback function
+ * @description In this function set subtitle callback to handle the subtitle.
+ * If there is subtitle to display trackrenderer_subtitle_rawdata_cb
+ * will be called to notify there is subtitle to display.
+ * The subtitle whole raw data will be shared. If user get this
+ * callback and want to display the subtitle, user should get
+ * subtitle informations from shared raw data.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_subtitle_rawdata_cb ptr.
+ * @param [in] userdata : userdata of
+ * trackrenderer_subtitle_rawdata_cb callback function.
+ * @code
+ * static void SubtitleRawDataCb(TrackRendererDecoderInputBuffer* buf,
+ * const TrackRendererSubtitleType type,
+ * UserData userdata) {
+ * //Something you want to do when there is subtitle data
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_subtitle_rawdata_cb(handle,SubtitleRawDataCb,nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When there is subtitledata, call
+ * trackrenderer_subtitle_rawdata_cb to notice that there is
+ * subtitle needed to be displayed.
+ * @exception None
+ * @remark trackrenderer_subtitle_rawdata_cb \n
+ * [in] buf : buffer including subtitle information \n
+ * [in] type : subtitle type (text or picture) \n
+ * [in] userdata : userdata of
+ * trackrenderer_subtitle_rawdata_cb callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_subtitle_rawdata_cb
+ */
+void trackrenderer_set_subtitle_rawdata_cb(
+ TrackRendererHandle handle, trackrenderer_subtitle_rawdata_cb callback,
+ void* userdata);
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+/**
+ * @brief Set subtitle callback function (deprecated)
+ * @description (deprecated) In this function set subtitle callback to handle
+ * the subtitle. If there is subtitle to display
+ * trackrenderer_subtitledata_cb will be called to notify there
+ * is subtitle to display. If user get this callback and want to display the
+ * subtitle, user can use the shared subtitle informations.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_subtitledata_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_subtitledata_cb
+ * callback function.
+ * @code
+ * static void SubtitleDataCb(const char* data, const int size,
+ * const TrackRendererSubtitleType type,
+ * const unsigned long long duration,
+ * TrackRendererSubtitleAttr* attr_list,
+ * int attr_list_size, UserData userdata) {
+ * //Something you want to do when there is subtitle data
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_subtitledata_cb(handle,SubtitleDataCb,nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When there is subtitledata, call
+ * trackrenderer_subtitledata_cb to notice that there is subtitle needed to be
+ * displayed.
+ * @exception None
+ * @remark trackrenderer_subtitledata_cb \n
+ * [in] data : subtitle data \n
+ * [in] size : subtitle data length \n
+ * [in] type : subtitle type (text or picture) \n
+ * [in] duration : subtitle duration (ms) \n
+ * [in] attr_list : list of subtitle attribute \n
+ * [in] attr_list_size : length of subtite attribute \n
+ * [in] userdata : userdata of trackrenderer_subtitledata_cb
+ * callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_subtitledata_cb
+ */
+void trackrenderer_set_subtitledata_cb(TrackRendererHandle handle,
+ trackrenderer_subtitledata_cb callback,
+ void* userdata);
+#endif
+
+/**
+ * @brief Set EOS callback function
+ * @description In this function set EOS callback to handle EOS. If the
+ * playing contents meet EOS, trackrenderer_eos_cb will be called
+ * to notify the contents end.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_eos_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_eos_cb callback
+ * function.
+ * @code
+ * static void EosCb(UserData userdata) {
+ * //Something you want to do when contents meet EOS
+ * printf("EOS received\n");
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_eos_cb(handle,EosCb,nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When contents meet EOS, the trackrenderer_eos_cb will be
+ * called
+ * @exception None
+ * @remark trackrenderer_eos_cb \n
+ * [in] userdata : userdata of trackrenderer_eos_cb callback
+ * function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_eos_cb
+ */
+void trackrenderer_set_eos_cb(TrackRendererHandle handle,
+ trackrenderer_eos_cb callback, void* userdata);
+
+/**
+ * @brief Set closed caption callback function.
+ * @description In this function set closed caption callback to handle the
+ * closed caption. If there is close caption to display,
+ * trackrenderer_closedcaption_cb will be called to notify there
+ * is closed caption to display.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_closedcaption_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_closedcaption_cb
+ * callback function.
+ * @code
+ * static void ClosedCaptionDataCb(const char* data,
+ * const int size, UserData userdata) {
+ * //Something you want to do when there is closed
+ * caption needed to be displayed
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_closedcaption_cb(handle,ClosedCaptionDataCb,nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When there is closed caption data, call
+ * trackrenderer_closedcaption_cb to nofity that there is closed
+ * caption needed to be displayed.
+ * @exception None
+ * @remark trackrenderer_closedcaption_cb \n
+ * [in] data : closedcaption data \n
+ * [in] size : length of closedcaption data \n
+ * [in] userdata : userdata of trackrenderer_closedcaption_cb
+ * callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_closedcaption_cb
+ */
+void trackrenderer_set_closedcaption_cb(TrackRendererHandle handle,
+ trackrenderer_closedcaption_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set drm initialize data callback function.
+ * @description In this function set drm initialize data callback. If
+ * trackrenderer detected any informations which need to
+ * initiate drm, the trackrenderer_drminitdata_cb is called to
+ * share the drm informations for dectyption.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_drminitdata_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_drminitdata_cb
+ * callback function.
+ * @code
+ * static void DrmInitDataCb(int* drmhandle,unsigned int len,
+ * unsigned char* psshdata, TrackRendererTrackType type,
+ * UserData userdata) {
+ * //Something you want to do when detect any drm data
+ * printf("drm data received\n");
+ * }
+ * trackrenderer_create(&handle);
+ * trackrenderer_set_drminitdata_cb(handle,DrmInitDataCb,nullptr);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When the trackrenderer detect any drm data, call
+ * trackrenderer_drminitdata_cb function to share the init data.
+ * @exception The trackrenderer must be at least created
+ * @remark trackrenderer_drminitdata_cb \n
+ * [in] drmhandle : drm handle \n
+ * [in] len : pssh data length \n
+ * [in] psshdata : pssh data \n
+ * [in] type : track type \n
+ * [in] userdata : userdata of trackrenderer_drminitdata_cb
+ * callback function.
+ * @see trackrenderer_create() \n
+ * trackrenderer_set_drminitdata_cb
+ */
+void trackrenderer_set_drminitdata_cb(TrackRendererHandle handle,
+ trackrenderer_drminitdata_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set current buffer status call back function
+ * @description In this function set buffer status callback function. If
+ * trackrenderer detected that there is not enough data or full
+ * at trackrenderer buffer, call trackrenderer_bufferstatus_cb
+ * to let user know what current buffer status is.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_bufferstatus_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_bufferstatus_cb
+ * callback function.
+ * @code
+ * refer to the sample code of trackrenderer_submit_packet()
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When trackrenderer buffer status has changed to not enough or
+ * full, call trackrenderer_bufferstatus_cb()
+ * @exception None
+ * @remark trackrenderer_bufferstatus_cb \n
+ * [in] type : track type \n
+ * [in] status : trackrenderer buffer status \n
+ * @see trackrenderer_create() \n
+ * trackrenderer_bufferstatus_cb
+ */
+void trackrenderer_set_bufferstatus_cb(TrackRendererHandle handle,
+ trackrenderer_bufferstatus_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set seek data callback function
+ * @description In this function set seek data callback function. After
+ * flushing currently buffered data and trackrenderer is ready
+ * to get new seeked position segment data, the
+ * trackrenderer_seekdata_cb is called. If get this callback,
+ * user can know the time to push new segment data.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_seekdata_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_seekdata_cb
+ * callback function.
+ * @code
+ * please refer to the sample code of trackrenderer_seek()
+ * @endcode
+ * @pre The trackrenderer must be at least created
+ * @post When trackrenderer finish to flush the buffered data for seek
+ * operation, call trackrenderer_seekdata_cb .
+ * @exception None
+ * @remark trackrenderer_seekdata_cb \n
+ * [in] type : track type \n
+ * [in] offset : the new seeked position \n
+ * @see trackrenderer_create() \n
+ * trackrenderer_seekdata_cb
+ */
+void trackrenderer_set_seekdata_cb(TrackRendererHandle handle,
+ trackrenderer_seekdata_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set tbm surface get callback function
+ * @description In this function set bm surface get callback function.
+ * After setting, user can get the useful tbm surface by
+ * callback.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback :
+ * trackrenderer_media_packet_video_tbmptr_cb ptr.
+ * @param [in] userdata : userdata of callback function.
+ * @pre None
+ * @version 3.2
+ * @post When trackrenderer would send decoded video buffer, call
+ * trackrenderer_media_packet_video_tbmptr_cb .
+ * @exception None
+ * @remark trackrenderer_media_packet_video_tbmptr_cb \n
+ * [in] ptr : pointer set to get the tbm surface data \n
+ */
+void trackrenderer_set_media_packet_video_tbmptr_cb(
+ TrackRendererHandle handle,
+ trackrenderer_media_packet_video_tbmptr_cb callback, void* userdata);
+
+/**
+ * @brief Set video decoded buffer callback function
+ * @description In this function set video decoded buffer callback function.
+ * After setting, user can get the video decoded buffers by
+ * callback.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] media_packet_video_decoded_cb :
+ * trackrenderer_media_packet_video_decoded_cb ptr.
+ * @param [in] userdata : userdata of media_packet_video_decoded_cb
+ * callback function.
+ * @pre The trackrenderer must be at least created
+ * @version 2.0
+ * @post When trackrenderer decoded video buffer, call
+ * trackrenderer_media_packet_video_decoded_cb .
+ * @exception None
+ * @remark trackrenderer_media_packet_video_decoded_cb \n
+ * [in] packet : packet including decoded buffer \n
+ * @see trackrenderer_create() \n
+ * trackrenderer_media_packet_video_decoded_cb
+ */
+void trackrenderer_set_media_packet_video_decoded_cb(
+ TrackRendererHandle handle,
+ trackrenderer_media_packet_video_decoded_cb media_packet_video_decoded_cb,
+ void* userdata);
+
+/**
+ * @brief Set multiview start video callback function
+ * @description Entering mulview mode, if ResourceCenter sends
+ * video start request to a player when it's required, the callback function
+ * will be invoked. User must activate new stream by trackrenderer_activate()
+ * before callback returns, and seek to current playing position.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_multiview_start_video_cb ptr.
+ * @param [in] userdata : userdata of
+ * trackrenderer_multiview_start_video_cb callback function.
+ * @pre The trackrenderer must be created but before
+ * trackrenderer_prepare()
+ * @post When ResourceCenter sends video start request to a player,
+ * trackrenderer_multiview_start_video_cb will be sent.
+ * @exception None
+ * @version 3.1
+ * @remark trackrenderer_multiview_start_video_cb \n
+ * [in] userdata : userdata of
+ * trackrenderer_multiview_start_video_cb callback function.
+ * @see trackrenderer_activate() \n
+ * trackrenderer_seek() \n
+ * trackrenderer_seek2() \n
+ */
+void trackrenderer_set_multiview_start_video_cb(
+ TrackRendererHandle handle, trackrenderer_multiview_start_video_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set multiview stop video callback function
+ * @description Entering mulview mode, if ResourceCenter sends
+ * video stop request to a player when it's required, the callback function will
+ * be invoked. User must stop video packet feeding, and deactivate video stream
+ * by trackrenderer_deactivate() before callback returns.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_multiview_stop_video_cb ptr.
+ * @param [in] userdata : userdata of
+ * trackrenderer_multiview_stop_video_cb callback function.
+ * @pre The trackrenderer must be created but before
+ * trackrenderer_prepare()
+ * @post When ResourceCenter sends video start request to a player,
+ * trackrenderer_multiview_stop_video_cb will be sent.
+ * @exception None
+ * @version 3.1
+ * @remark trackrenderer_multiview_stop_video_cb \n
+ * [in] userdata : userdata of
+ * trackrenderer_multiview_stop_video_cb callback function.
+ * @see trackrenderer_deactivate() \n
+ */
+void trackrenderer_set_multiview_stop_video_cb(
+ TrackRendererHandle handle, trackrenderer_multiview_stop_video_cb callback,
+ void* userdata);
+
+/**
+ * @brief Initialize easing info to trackrenderer.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] init_volume : initial easing volume (0 ~ 100).
+ * @param [in] init_elapsed_time : initial elapsed time (millisecond).
+ * @param [in] easing_info : target volume, duration, type.
+ * @return 0 on success, otherwise -1 failed.
+ * @code
+ * trackrenderer_create(&handle);
+ * const TrackRendererAudioEasingInfo init_info =
+ * {0,1000,kTrackRendererAudioEasingLinear};
+ * trackrenderer_init_audio_easing_info(handle, 100, 0,
+ * &init_info);
+ * //your logic
+ * const TrackRendererAudioEasingInfo update_info =
+ * {100,1000,kTrackRendererAudioEasingLinear};
+ * trackrenderer_update_audio_easing_info(handle, &update_info));
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * @post None
+ * @exception None
+ * @remark None
+ * @version 3.0
+ * @see trackrenderer_create()
+ */
+int trackrenderer_init_audio_easing_info(
+ TrackRendererHandle handle, const uint32_t init_volume,
+ const uint32_t init_elapsed_time,
+ const TrackRendererAudioEasingInfo* easing_info);
+
+/**
+ * @brief Update easing info to trackrenderer.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] easing_info : target volume, duration, type.
+ * @return 0 on success, otherwise -1 failed.
+ * @code
+ * refer to the sample code of
+ * trackrenderer_init_audio_easing_info()
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * This api should be called after
+ * trackrenderer_init_audio_easing_info() is called
+ * @post None
+ * @exception None
+ * @remark None
+ * @version 3.0
+ * @see trackrenderer_create() \n
+ * trackrenderer_init_audio_easing_info()
+ */
+int trackrenderer_update_audio_easing_info(
+ TrackRendererHandle handle,
+ const TrackRendererAudioEasingInfo* easing_info);
+
+/**
+ * @brief Get easing info currently in easing operation from
+ * trackrenderer
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [out] current_volume : current volume (0 ~ 100).
+ * @param [out] elapsed_time : elapsed time (millisecond).
+ * @param [out] easing_info : target volume, duration, type.
+ * @return 0 on success, otherwise -1 failed.
+ * @code
+ * trackrenderer_create(&handle);
+ * const TrackRendererAudioEasingInfo init_info =
+ * {0,1000,kTrackRendererAudioEasingLinear};
+ * trackrenderer_init_audio_easing_info(handle, 100, 0,
+ * &init_info);
+ * //your logic
+ * uint32_t cur_volume = 0;
+ * uint32_t elapsed_time = 0;
+ * TrackRendererAudioEasingInfo get_info;
+ * trackrenderer_get_audio_easing_info(handle, &cur_volume,
+ * &elapsed_time, &get_info);
+ * //your logic
+ * trackrenderer_destroy(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * This api should be called after
+ * trackrenderer_init_audio_easing_info() is called
+ * @post None
+ * @exception None
+ * @remark None
+ * @version 3.0
+ * @see trackrenderer_create() \n
+ * trackrenderer_init_audio_easing_info()
+ */
+int trackrenderer_get_audio_easing_info(
+ TrackRendererHandle handle, uint32_t* current_volume,
+ uint32_t* elapsed_time, TrackRendererAudioEasingInfo* easing_info);
+
+/**
+ * @brief Start audio easing using a registered audio easing info
+ * @param [in] handle : trackrenderer handle ptr.
+ * @return 0 on success, otherwise -1 failed.
+ * @code
+ * trackrenderer_create(&handle);
+ * const TrackRendererAudioEasingInfo init_info =
+ * {0,1000,kTrackRendererAudioEasingLinear};
+ * trackrenderer_init_audio_easing_info(handle, 100, 0,
+ * &init_info);
+ * trackrenderer_prepare(handle);
+ * trackrenderer_start_audio_easing(handle);
+ * //your logic
+ * trackrenderer_stop_audio_easing(handle);
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * This api should be called after
+ * trackrenderer_init_audio_easing_info() is called
+ * @post None
+ * @exception None
+ * @remark None
+ * @version 3.0
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_init_audio_easing_info()
+ */
+int trackrenderer_start_audio_easing(TrackRendererHandle handle);
+
+/**
+ * @brief Stop audio easing
+ * @param [in] handle : trackrenderer handle ptr.
+ * @return 0 on success, otherwise -1 failed.
+ * @code
+ * refer to the sample code of trackrenderer_start_audio_easing()
+ * @endcode
+ * @pre The trackrenderer must be at least created.
+ * This api should be called after
+ * trackrenderer_init_audio_easing_info() is called
+ * @post None
+ * @exception None
+ * @remark None
+ * @version 3.0
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare() \n
+ * trackrenderer_start_audio_easing() \n
+ * trackrenderer_init_audio_easing_info()
+ */
+int trackrenderer_stop_audio_easing(TrackRendererHandle handle);
+
+/**
+ * @brief Get virtual resource id
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : the resource type of virtual id.
+ * @param [out] virtual_id : Stored virtual resource id value.
+ * @return 0 on success, otherwise -1 failed.
+ * @code
+ * //prepare trackrenderer hanle *
+ * //your logic
+ * int virtual_id = -1;
+ * trackrenderer_get_virtual_rsc_id(
+ * handle, kTrackRendererRscTypeVideoRenderer, &virtual_id);
+ * //your logic
+ * trackrenderer_stop(handle);
+ * @endcode
+ * @pre The trackrenderer must be at least created and set up.
+ * @post None
+ * @exception None
+ * @remark None
+ * @version 3.0
+ * @see trackrenderer_create() \n
+ * trackrenderer_prepare()
+ */
+int trackrenderer_get_virtual_rsc_id(TrackRendererHandle handle,
+ TrackRendererRscType type,
+ int* virtual_id);
+
+/**
+ * @brief Set advanced picture quality type
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : the advanced picture quality type.
+ * @return 0 on success, otherwise -1 failed.
+ * @pre The trackrenderer must be at least created.
+ * trackrenderer_create()
+ * @post None
+ * @exception None
+ * @remark None
+ * @version 3.1
+ */
+int trackrenderer_set_advanced_picture_quality_type(
+ TrackRendererHandle handle, TrackRendererAdvPictureQualityType type);
+
+/**
+ * @brief Set resource allocate policy
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] policy : the resource allocate policy.
+ * @return 0 on success, otherwise -1 failed.
+ * @pre The trackrenderer must be at least created.
+ * trackrenderer_create()
+ * @post None
+ * @exception None
+ * @remark None
+ * @version 3.2
+ */
+int trackrenderer_set_resource_allocate_policy(
+ TrackRendererHandle handle, TrackRendererRscAllocPolicy policy);
+
+/**
+ * @brief Set video's par(pixel aspect ratio) and dar(display aspect
+ * ratio)
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] time_millisecond : timestamp when par and dar will
+ * changed.
+ * @param [in] par_num : par_num : numerator of par.
+ * @param [in] par_den : par_den : denominator of par.
+ * @param [in] dar_num : dar_num : numerator of dar.
+ * @param [in] dar_den : dar_den : denominator of dar.
+ * @return 0 on success, otherwise -1 failed.
+ * @pre The trackrenderer must be at least created.
+ * trackrenderer_create()
+ * @post None
+ * @exception None
+ * @remark None
+ * @version 3.3
+ */
+int trackrenderer_set_video_par_dar(TrackRendererHandle handle,
+ uint64_t time_millisecond, uint32_t par_num,
+ uint32_t par_den, uint32_t dar_num,
+ uint32_t dar_den);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACKRENDERER_CAPI_H__
--- /dev/null
+/**
+ * @file trackrenderer_internal.h
+ * @brief trackrenderer internally used api c version
+ * @interfacetype Module
+ * @platform_porting_interface
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is trackrenderer api header implemented as C style to
+ * avoid binary compatibility issues.
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACKRENDERER_INTERNAL_H__
+#define __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACKRENDERER_INTERNAL_H__
+
+#include "trackrenderer_capi/display.h"
+#include "trackrenderer_capi/track_capi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief video decoded buffer struct (raw mode)
+ */
+typedef struct {
+ /**
+ * @description buffer pts, in millisecond
+ */
+ uint64_t pts;
+ /**
+ * @description width
+ */
+ uint32_t width;
+ /**
+ * @description height
+ */
+ uint32_t height;
+ /**
+ * @description internal data
+ */
+ void* internal_data;
+} TrackRendererDecodedVideoRawModePacket;
+
+/**
+ * @brief Enumerations for types of video decoded buffer (raw mode)
+ */
+enum TrackRendererDecodedVideoType {
+ kTrackRendererDecodedVideoPhysicalAddressType,
+ kTrackRendererDecodedVideoTizenBufferType,
+};
+
+/**
+ * @brief Enumerations for video decoded buffer type
+ */
+enum TrackRendererDecodedVideoFrameBufferTypeExt {
+ kTrackRendererDecodedVideoFrameBufferExtNone,
+ kTrackRendererDecodedVideoFrameBufferExtRaw
+};
+
+/**
+ * @brief Enumerations for video renderer type
+ */
+enum TrackRendererVideoRendererType {
+ TrackRendererVideoRendererTypeMain,
+ TrackRendererVideoRendererTypeSub,
+ TrackRendererVideoRendererTypeSub2,
+ TrackRendererVideoRendererTypeSub3,
+};
+
+typedef void* TrackRendererHandle;
+typedef void (*trackrenderer_first_decoding_done_cb)(void* userdata);
+
+/**
+ * @description When trackrenderer detected that there is not enough data at
+ * decoder output buffer, call this function to let user know
+ * current buffer status.
+ * @param [in] userdata : userdata of
+ * trackrenderer_decoder_underrun_cb callback function.
+ * @pre The callback function should be set by
+ * trackrenderer_set_decoder_underrun_cb, in advance.
+ * @post User can control data feeding speed with this change callback.
+ * @see trackrenderer_set_decoder_underrun_cb()
+ */
+typedef void (*trackrenderer_decoder_underrun_cb)(void* userdata);
+
+/**
+ * @description When trackrenderer finish to decode video buffer,
+ * this is called, then user can get the decoded video buffer
+ * @param [in] packet : decoded video packet \n
+ * [in] type : the type of decoded video packet.
+ * (tbm_key or h/w decoded) \n
+ * @pre The callback function should be set by
+ * trackrenderer_set_media_packet_video_raw_decoded_cb,
+ * in advance.
+ * @post User can use the decoded buffer to render.
+ * @see trackrenderer_set_media_packet_video_raw_decoded_cb()
+ */
+typedef void (*trackrenderer_media_packet_video_raw_decoded_cb)(
+ const TrackRendererDecodedVideoRawModePacket* packet,
+ TrackRendererDecodedVideoType type, void* userdata);
+
+/**
+ * @brief Set the video display properties.
+ * @description In this function set rendering object and rendering
+ * locations. If the trackrenderer is not prepared to update the
+ * display yet, the trackrenderer stored the rendering object
+ * properties and update display after trackrenderer is ready to
+ * update display.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : display type
+ * @param [in] ecore_wl2_window : ecore wayland handle to display window.
+ * @param [in] x : x param of display window
+ * @param [in] y : y param of display window
+ * @param [in] w : width of display window
+ * @param [in] h : height of display window
+ * @return Return 0 if rendering oejct and properties are successfully
+ * applied for displaying contents or stored for future
+ * displaying.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @pre The trackrenderer must be at least created.
+ * trackrenderer_create()
+ * @post None
+ * @exception None
+ * @remark None
+ */
+int trackrenderer_set_display_ecore_wl2_window(TrackRendererHandle handle,
+ TrackRendererDisplayType type,
+ void* ecore_wl2_window, int x,
+ int y, int w, int h);
+/**
+ * @brief Set first decoding done call back function
+ * @description in this function set first decoding done callback function.
+ * It wil be invoked when the first video frame is decoded.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_first_decoding_done_cb ptr.
+ * @param [in] userdata : userdata of
+ * trackrenderer_first_decoding_done_cb callback function.
+ * @pre "video-frame-peek-mode" attribute of
+ * trackrenderer_set_attribute() have to be set.
+ * @post When the first video frame is decoded, call
+ * trackrenderer_first_decoding_done_cb()
+ * @exception None
+ * @remark trackrenderer_first_decoding_done_cb \n
+ */
+void trackrenderer_set_first_decoding_done_cb(
+ TrackRendererHandle handle, trackrenderer_first_decoding_done_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set video decoder underrun status call back function
+ * @description In this function set buffer status callback function. If
+ * trackrenderer detected that not enough data at decoder output
+ * buffer, call trackrenderer_decoder_underrun_cb to let user
+ * know current buffer status.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] callback : trackrenderer_decoder_underrun_cb ptr.
+ * @param [in] userdata : userdata of trackrenderer_decoder_underrun_cb
+ * callback function.
+ * @pre None
+ * @post When video decoder buffer status has changed to not enough,
+ * call trackrenderer_decoder_underrun_cb()
+ * @exception None
+ * @remark trackrenderer_decoder_underrun_cb \n
+ */
+void trackrenderer_set_video_decoder_underrun_cb(
+ TrackRendererHandle handle, trackrenderer_decoder_underrun_cb callback,
+ void* userdata);
+
+/**
+ * @brief Set active track info to set up trackrenderer properties.
+ * @description In this fuction, trackrenderer set track info which is
+ * necessary for media contents playback.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] handles_array : Active track handles for playing media
+ * contents.
+ * @param [in] array_size : Number of tracks. The maximum number of
+ * tracks is 3.(audio, video, subtitle)
+ * @return Return 0 if there are any active tracks among audio, video,
+ * subtitle and successcully set the track info to trackrenderer.
+ * Otherwise -1 if there is not any active tracks or already set
+ * tracks info.
+ * @pre This api should never be called before.
+ * @post TrackRenderer sets factories to create pipeline using tracks
+ * passed.
+ * @exception None
+ * @remark None
+ * @see trackrenderer_track_create()\n
+ * trackrenderer_track_destroy()
+ */
+int trackrenderer_set_track_handle(
+ TrackRendererHandle handle, const TrackRendererTrackHandle* handles_array,
+ const int array_size);
+
+/**
+ * @brief Provided api for setting alternative audio resource(sub decoder
+ * and sub out)
+ * @description In this fuction, trackrenderer select the main or sub among the
+ * HW resources.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] rsc_type : set alternative audio resource
+ * (@c 0 = default audio resource(main), @c 1 = audio sub
+ * resource)
+ * @return Return 0 if set the audio resource type.
+ * Otherwise -1 if the trackrenderer is not even setup.
+ * @pre The trackrenderer must be at least created.
+ * trackrenderer_create()
+ * @post None
+ * @exception None
+ * @remark None
+ */
+
+int trackrenderer_set_alternative_audio_resource(TrackRendererHandle handle,
+ unsigned int rsc_type);
+
+/**
+ * @brief Set decoded video frame buffer extended type.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : one of the video decoded buffer type to set .
+ * @pre The trackrenderer must be at least created but before prepare.
+ * trackrenderer_create()
+ * @post None
+ * @exception None
+ */
+void trackrenderer_set_video_frame_buffer_type_ext(
+ TrackRendererHandle handle,
+ TrackRendererDecodedVideoFrameBufferTypeExt type);
+
+/**
+ * @brief Set video decoded raw buffer callback function
+ * @description In this function set video decoded buffer callback function.
+ * After setting, user can get the video decoded buffers by
+ * callback.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] media_packet_video_decoded_cb :
+ * trackrenderer_media_packet_video_raw_decoded_cb ptr.
+ * @param [in] userdata : userdata of media_packet_video_decoded_cb
+ * callback function.
+ * @pre None
+ * @post When trackrenderer decoded video buffer, call
+ * trackrenderer_media_packet_video_raw_decoded_cb.
+ * @exception None
+ * @remark trackrenderer_media_packet_video_decoded_cb \n
+ * [in] packet : packet including decoded buffer \n
+ * [in] type : the type of decoded video packet.
+ * (tbm_key or h/w decoded) \n
+ */
+void trackrenderer_set_media_packet_video_raw_decoded_cb(
+ TrackRendererHandle handle,
+ trackrenderer_media_packet_video_raw_decoded_cb
+ media_packet_video_decoded_cb,
+ void* userdata);
+
+/**
+ * @brief Set video renderer type. It overrides the scaler type from
+ * trackrenderer_set_attribute(handle,
+ * "alternative-video-resource", type, nullptr)
+ * It also depends on target which support multi video renderer.
+ * @param [in] handle : trackrenderer handle ptr.
+ * @param [in] type : video renderer type
+ * @return Return 0 if set the video renderer type.
+ * Otherwise -1 if the video renderer type is not even setup.
+ * @pre None
+ * @post None
+ * @see TrackRendererVideoRendererType
+ */
+int trackrenderer_set_video_renderer_type(
+ TrackRendererHandle handle, TrackRendererVideoRendererType renderer_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __CAPI_TRACKRENDERER_TV_TRACKRENDERER_CAPI_TRACKRENDERER_INTERNAL_H__
--- /dev/null
+<manifest>\r
+ <request>\r
+ <domain name="_" />\r
+ </request>\r
+</manifest>
\ No newline at end of file
--- /dev/null
+# %bcond_with : disable TRACKRENDERER_CAPI_UT by default, %bcond_without : enable TRACKRENDERER_CAPI_UT
+%bcond_without TRACKRENDERER_CAPI_UT
+Name: capi-trackrenderer-tv
+Summary: new multimedia streaming player capi-trackrenderer-tv
+Version: 0.0.1
+Release: 0
+Group: Multimedia/Libraries
+License: Apache-2.0
+Source0: %{name}-%{version}.tar.gz
+Source1001: capi-trackrenderer-tv.manifest
+BuildRequires: cmake
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(capi-base-common)
+BuildRequires: pkgconfig(gstreamer-1.0)
+BuildRequires: pkgconfig(gstreamer-plugins-base-1.0)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(boost)
+BuildRequires: pkgconfig(elementary)
+BuildRequires: pkgconfig(ecore)
+BuildRequires: pkgconfig(evas)
+BuildRequires: pkgconfig(ecore-wl2)
+BuildRequires: pkgconfig(wayland-client)
+BuildRequires: pkgconfig(tizen-extension-client)
+BuildRequires: pkgconfig(tv-resource-manager)
+BuildRequires: pkgconfig(tv-resource-information)
+BuildRequires: pkgconfig(resource-center-api)
+BuildRequires: pkgconfig(audio-control)
+BuildRequires: pkgconfig(libtzplatform-config)
+BuildRequires: pkgconfig(jsoncpp)
+BuildRequires: pkgconfig(gstreamer-ffsubtitle-1.0)
+BuildRequires: pkgconfig(icu-i18n)
+BuildRequires: pkgconfig(drmdecrypt)
+BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(capi-system-info)
+BuildRequires: pkgconfig(logger)
+BuildRequires: pkgconfig(gio-2.0)
+BuildRequires: pkgconfig(factory-api)
+BuildRequires: pkgconfig(libtbm)
+BuildRequires: pkgconfig(lwipc)
+BuildRequires: pkgconfig(capi-screensaver)
+BuildRequires: pkgconfig(context-aware-api)
+BuildRequires: pkgconfig(vd-win-util)
+BuildRequires: pkgconfig(iniparser)
+
+%if ("%{_vd_cfg_product_type}" == "AUDIO")
+BuildRequires: pkgconfig(libavoc-av)
+%else
+BuildRequires: pkgconfig(libavoc)
+BuildRequires: pkgconfig(graphics-control)
+%endif
+
+%if ("%{_vd_cfg_licensing}" == "n")
+# for ut
+BuildRequires: pkgconfig(capi-media-player)
+BuildRequires: pkgconfig(gtest_gmock)
+BuildRequires: pkgconfig(appcore-efl)
+BuildRequires: pkgconfig(libresourced)
+%endif
+
+%define _packagedir /usr
+%define _bindir %{_packagedir}/bin
+%define _libdir %{_packagedir}/lib
+%define _includedir %{_packagedir}/include
+%define _pkgconfigdir %{_libdir}/pkgconfig
+%define _unpackaged_files_terminate_build 0
+%define _missing_doc_files_terminate_build 0
+
+%description
+new multimedia player, object-oriented model
+
+%package devel
+Summary: Developement for multimedia player
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%package config
+Summary: Configuration for multimedia player
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+%devel_desc
+
+%description config
+
+#################################################
+# gcov
+#################################################
+%if 0%{?vd_gcov:1}
+%package gcov
+Summary: gcov enabled package
+Group: gcov package
+
+%description gcov
+This package is gcov package for coverage measurement.
+%endif
+
+%prep
+%setup -q
+cp %{SOURCE1001} .
+
+%if ("%{_vd_cfg_licensing}" == "n")
+
+%{?!TOMATO: %define TOMATO n}
+
+%define _tomatoname trackrenderer_capi
+%define _tomatodir /opt/usr/apps/tomato/testcase/%{name}
+%define _tomatobin /opt/usr/apps/tomato/testcase/%{name}/bin
+
+%package ut-component-tomato
+Summary: Test package with TOMATO
+BuildRequires: pkgconfig(tomato)
+BuildRequires: pkgconfig(gtest_gmock)
+Requires: %{name} = %{version}-%{release}
+
+%description ut-component-tomato
+This package is for test
+
+%files ut-component-tomato
+%defattr(-,root,root,-)
+%{_bindir}/capi-trackrenderer-tv_ut
+%{_tomatodir}/*
+
+%endif
+
+%build
+export CFLAGS+=" -Wno-deprecated-declarations"
+export CXXFLAGS+=" -Wno-deprecated-declarations"
+
+%if ("%{_vd_cfg_product_type}" == "AV")
+export CFLAGS+=" -DIS_AV_PRODUCT"
+export CXXFLAGS+=" -DIS_AV_PRODUCT"
+%endif
+
+export CFLAGS+=" -DPLUS_PLAYER_AI_DATA_COLLECTION"
+export CXXFLAGS+=" -DPLUS_PLAYER_AI_DATA_COLLECTION"
+
+%if ("%{_vd_cfg_product_type}" == "AUDIO")
+export CFLAGS+=" -DSOUNDBAR_PRODUCT"
+export CXXFLAGS+=" -DSOUNDBAR_PRODUCT"
+%define _support_soundbar -DSUPPORT_SOUNDBAR=ON
+%endif
+
+%if ("%{_vd_cfg_licensing}" == "n")
+%if %{with TRACKRENDERER_CAPI_UT}
+%define _trackrenderer_capi_ut -DTRACKRENDERER_BUILD_UT=ON
+%endif
+%endif
+
+%if 0%{?vd_gcov:1}
+export CFLAGS+=" -fprofile-arcs -ftest-coverage"
+export CXXFLAGS+=" -fprofile-arcs -ftest-coverage"
+export FFLAGS+=" -fprofile-arcs -ftest-coverage"
+export LDFLAGS+=" -lgcov"
+%endif
+
+%cmake \
+ %{?_trackrenderer_capi_ut:%_trackrenderer_capi_ut} \
+ %{?_support_soundbar:%_support_soundbar}
+
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+
+%if ("%{_vd_cfg_licensing}" == "n")
+mkdir -p %{buildroot}%{_tomatodir}
+mkdir -p %{buildroot}%{_tomatodir}/log
+mkdir -p %{buildroot}%{_tomatodir}/result
+mkdir -p %{buildroot}%{_tomatodir}/tc
+cp -rf tomato/tc/* %{buildroot}%{_tomatodir}/tc
+%endif
+
+%make_install
+
+%if 0%{?vd_gcov:1}
+mkdir -p %{buildroot}%{_datadir}/gcov/obj
+find . \( -name '*.gcno' -o -name '*.cpp' -o -name '*.c' -o -name '*.hpp' -o -name '*.h' \) ! -path "./ut/*" ! -path "./test/*" ! -path "*/CompilerIdCXX/*" -exec cp --parents -r '{}' %{buildroot}%{_datadir}/gcov/obj ';'
+%endif
+
+%files
+%defattr(-,root,root,-)
+%manifest capi-trackrenderer-tv.manifest
+%license LICENSE.APLv2
+%{_libdir}/libtrackrenderer.so
+
+%if ("%{_vd_cfg_licensing}" == "n")
+%if %{with TRACKRENDERER_CAPI_UT}
+%{_bindir}/capi-trackrenderer-tv_ut
+%defattr(-,root,root,-)
+%{_tomatodir}/*
+%endif
+%endif
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/trackrenderer_capi/*.h
+%{_pkgconfigdir}/capi-trackrenderer-tv.pc
+
+%files config
+%defattr(-,root,root,-)
+%manifest capi-trackrenderer-tv.manifest
+%license LICENSE.APLv2
+
+%if 0%{?vd_gcov:1}
+%files gcov
+%{_datadir}/gcov/*
+%endif
--- /dev/null
+PROJECT(trackrenderer)
+
+SET(fw_name "${PROJECT_NAME}")
+SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
+SET(${fw_name}_LDFLAGS)
+
+SET(ADD_LIBS
+ "gstvideo-1.0"
+ "gstapp-1.0"
+)
+
+SET(${fw_name}_CXXFLAGS "-Wall -Werror -std=c++11 -fPIC -Wl,-z,relro -fstack-protector -DEFL_BETA_API_SUPPORT")
+
+SET(dependents "gstreamer-1.0 gstreamer-ffsubtitle-1.0"
+ "boost"
+ "vconf"
+ "tv-resource-manager"
+ "elementary ecore ecore-wl2"
+ "audio-control"
+ "libtbm"
+ "capi-screensaver"
+ "lwipc"
+ "vd-win-util"
+ "jsoncpp"
+ "capi-system-info"
+ "iniparser"
+ "drmdecrypt"
+ "resource-center-api")
+
+IF(SUPPORT_SOUNDBAR)
+SET(dependents ${dependents} "libavoc-av")
+ELSE(SUPPORT_SOUNDBAR)
+SET(dependents ${dependents} "libavoc" "graphics-control")
+ENDIF(SUPPORT_SOUNDBAR)
+
+INCLUDE(FindPkgConfig)
+
+IF(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l)
+pkg_check_modules(${fw_name} REQUIRED ${dependents})
+ELSE(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l)
+pkg_check_modules(${fw_name} REQUIRED ${dependents})
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSDK_ENABLED_FEATURE")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSDK_ENABLED_FEATURE")
+ENDIF(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l)
+
+FOREACH(flag ${${fw_name}_CFLAGS})
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}")
+ENDFOREACH(flag)
+
+FOREACH(flag ${${fw_name}_CXXFLAGS})
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} ${flag}")
+ENDFOREACH(flag)
+
+GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_SOURCE_DIR} DIRECTORY)
+INCLUDE_DIRECTORIES(
+ ${PROJECT_SOURCE_DIR}/include_internal
+)
+
+SET(CC_SRCS
+ ${PROJECT_SOURCE_DIR}/caps_recipes.cpp
+ ${PROJECT_SOURCE_DIR}/display.cpp
+ ${PROJECT_SOURCE_DIR}/error.cpp
+ ${PROJECT_SOURCE_DIR}/latency_manager.cpp
+ ${PROJECT_SOURCE_DIR}/trackrenderer.cpp
+ ${PROJECT_SOURCE_DIR}/trackrenderer_attr.cpp
+ ${PROJECT_SOURCE_DIR}/trackrenderer_capi.cpp
+ ${PROJECT_SOURCE_DIR}/resourcemanager.cpp
+ ${PROJECT_SOURCE_DIR}/trackrenderer_capi_utils.cpp
+ ${PROJECT_SOURCE_DIR}/trackrenderer_debug.cpp
+ ${PROJECT_SOURCE_DIR}/gstcaps_builder.cpp
+ ${PROJECT_SOURCE_DIR}/pipeline.cpp
+ ${PROJECT_SOURCE_DIR}/decoderinputbuffer.cpp
+ ${PROJECT_SOURCE_DIR}/gst_utils.cpp
+ ${PROJECT_SOURCE_DIR}/gstobject_guard.cpp
+ ${PROJECT_SOURCE_DIR}/gstsignal_holder.cpp
+ ${PROJECT_SOURCE_DIR}/track_util.cpp
+ ${PROJECT_SOURCE_DIR}/screen_saver.cpp
+ ${PROJECT_SOURCE_DIR}/track_capi.cpp
+ ${PROJECT_SOURCE_DIR}/trackrenderer_vconf.cpp
+ # temporary, this will be remove when trackrenderer/subtitle_attr_parser deprecation.
+ ${PROJECT_SOURCE_DIR}/subtitle_attr_parser.cpp
+ ${PROJECT_SOURCE_DIR}/audio_easing_controller.cpp
+ ${PROJECT_SOURCE_DIR}/vr360.cpp
+)
+
+ADD_LIBRARY(${fw_name} SHARED ${CC_SRCS})
+
+SET_TARGET_PROPERTIES(${fw_name} PROPERTIES LINKER_LANGUAGE CXX)
+TARGET_LINK_LIBRARIES(${fw_name} ${CMAKE_THREAD_LIBS_INIT} ${${fw_name}_LDFLAGS} ${ADD_LIBS})
+
+INSTALL(TARGETS ${fw_name} DESTINATION ${LIB_INSTALL_DIR})
+INSTALL(
+ DIRECTORY ${INC_DIR}/ DESTINATION include/
+)
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/audio_controller/audio_easing_controller.h"
+
+#include <algorithm>
+#include <chrono>
+#include <unordered_map>
+
+#include "trackrenderer/core/gstobject_guard.h"
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+namespace internal {
+
+bool IsValidVolume(const uint32_t volume) {
+ constexpr uint32_t kVolumeMax = 100;
+ if (volume > kVolumeMax) {
+ TRACKRENDERER_ERROR("volume: %d", volume);
+ return false;
+ }
+ return true;
+}
+
+void UpdateInfo(AudioEasingInfo* dest, const AudioEasingInfo* src) {
+ TRACKRENDERER_ERROR(
+ "target vol [%u - %u] / duration [%u - %u] / type [%d - %d]",
+ dest->target_volume, src->target_volume, dest->duration, src->duration,
+ static_cast<int>(dest->type), static_cast<int>(src->type));
+
+ *dest = *src;
+}
+
+uint32_t GetMicroSecStepInterval(uint32_t duration_us) {
+ constexpr uint32_t kDefaultEaseSteps = 400;
+ constexpr uint32_t kDefaultInterval = 10000;
+
+ uint32_t interval = duration_us / kDefaultEaseSteps;
+ return std::max(interval, kDefaultInterval);
+}
+
+using SystemClockType = std::chrono::system_clock::time_point;
+uint32_t GetMicroSecElapsedEasingTime(SystemClockType begin) {
+ auto end = std::chrono::system_clock::now();
+ auto real_elapsed_time =
+ std::chrono::duration_cast<std::chrono::microseconds>(end - begin);
+
+ return real_elapsed_time.count();
+}
+
+void WaitGap(uint32_t end, uint32_t begin) {
+ if (begin > end) return;
+
+ auto wait = end - begin;
+ if (wait > 0) {
+ std::this_thread::sleep_for(std::chrono::microseconds(wait));
+ }
+}
+
+constexpr int kTimeFactor = 1000;
+uint32_t ConvertMsToMicroSec(uint32_t ms) {
+ uint32_t us = ms * kTimeFactor;
+ return us;
+}
+uint32_t ConvertMicroSecToMs(uint32_t us) {
+ uint32_t ms = us / kTimeFactor;
+ return ms;
+}
+
+} // namespace internal
+
+constexpr int kMaxVolumeIndex = 101;
+const int kVolumeToGainIndexTable[kMaxVolumeIndex] = {
+ 0, 7, 12, 17, 20, 24, 28, 32, 34, 37, // 9
+ 39, 42, 43, 46, 47, 48, 49, 51, 52, 53, // 19
+ 54, 56, 57, 57, 58, 59, 61, 61, 62, 62, // 29
+ 63, 64, 64, 66, 66, 67, 67, 68, 68, 69, // 39
+ 69, 71, 71, 71, 72, 72, 73, 73, 73, 74, // 49
+ 74, 74, 76, 76, 76, 77, 77, 77, 78, 78, // 59
+ 78, 78, 79, 79, 79, 81, 81, 81, 81, 82, // 69
+ 82, 82, 82, 83, 83, 83, 83, 83, 84, 84, // 79
+ 84, 84, 86, 86, 86, 86, 86, 87, 87, 87, // 89
+ 87, 87, 88, 88, 88, 88, 88, 88, 89, 89, // 99
+ 89};
+
+AudioEasingController::AudioEasingController(uint32_t init_volume,
+ uint32_t init_elapsed_time,
+ const AudioEasingInfo& init_info) {
+ TRACKRENDERER_ENTER;
+ is_stopped_.store(true);
+ AudioEasingController::SetInfo(init_info);
+ volume_.store(init_volume);
+ initial_volume_.store(init_volume);
+ elapsed_time_ms_.store(init_elapsed_time); // have the dependency of sequence
+ TRACKRENDERER_LEAVE;
+}
+
+AudioEasingController::~AudioEasingController() {
+ TRACKRENDERER_ENTER;
+ if (easing_task_.joinable()) {
+ TRACKRENDERER_ERROR("join before");
+ easing_task_.join();
+ TRACKRENDERER_ERROR("join after");
+ }
+}
+
+int AudioEasingController::GetGainFromVolume(uint32_t volume) {
+ TRACKRENDERER_ENTER;
+ if (volume > kMaxVolumeIndex) return -1;
+ return kVolumeToGainIndexTable[volume];
+}
+
+bool AudioEasingController::GetInfo(uint32_t* current_volume,
+ uint32_t* elapsed_time,
+ AudioEasingInfo* info) {
+ TRACKRENDERER_ENTER;
+ if (current_volume == nullptr || elapsed_time == nullptr || info == nullptr) {
+ TRACKRENDERER_ERROR("input param is nullptr");
+ return false;
+ }
+
+ *current_volume = volume_.load();
+ *elapsed_time = elapsed_time_ms_.load();
+ TRACKRENDERER_ERROR("%u - %u / %u - %u", *current_volume, volume_.load(),
+ *elapsed_time, elapsed_time_ms_.load());
+
+ std::lock_guard<std::mutex> info_lock(current_info_m_);
+ internal::UpdateInfo(info, &easing_info_);
+ TRACKRENDERER_LEAVE;
+ return true;
+} // namespace trackrenderer
+
+bool AudioEasingController::SetInfo(const AudioEasingInfo& info) {
+ TRACKRENDERER_ENTER;
+ if (!internal::IsValidVolume(info.target_volume)) return false;
+
+ std::lock_guard<std::mutex> pending_lock(pending_info_m_);
+ internal::UpdateInfo(&pending_info_, &info);
+ need_reset_ = true;
+
+ if (pending_info_.duration == 0) {
+ InterruptEasingThread_();
+ volume_.store(pending_info_.target_volume);
+ }
+
+ if (is_stopped_.load() == true) {
+ std::lock_guard<std::mutex> info_lock(current_info_m_);
+ internal::UpdateInfo(&easing_info_, &pending_info_);
+ elapsed_time_ms_.store(0);
+ initial_volume_.store(volume_.load());
+ need_reset_ = false;
+ TRACKRENDERER_ERROR("elapsed time %u, initial volume %u",
+ elapsed_time_ms_.load(), initial_volume_.load());
+ }
+ TRACKRENDERER_LEAVE;
+ return true;
+}
+
+void AudioEasingController::AudioEasingStop() {
+ TRACKRENDERER_ENTER;
+ if (is_stopped_.load() == true) {
+ TRACKRENDERER_ERROR("Stop : already paused");
+ }
+
+ InterruptEasingThread_();
+ std::lock_guard<std::mutex> pending_lock(pending_info_m_);
+ TRACKRENDERER_ERROR("before info lock");
+ std::lock_guard<std::mutex> info_lock(current_info_m_);
+ TRACKRENDERER_ERROR("after info lock");
+ internal::UpdateInfo(&pending_info_, &easing_info_);
+
+ TRACKRENDERER_LEAVE;
+}
+
+bool AudioEasingController::AudioEasingStart(GstElement* audiosink) {
+ TRACKRENDERER_ENTER;
+ if (audiosink == nullptr) {
+ TRACKRENDERER_ERROR("audiosink is nullptr");
+ return false;
+ }
+
+ bool need_lock_delay = NeedToWaitLockDelay_();
+ InterruptEasingThread_();
+
+ SetAudioGain_(audiosink, volume_.load());
+
+ std::lock_guard<std::mutex> pending_lock(pending_info_m_);
+ TRACKRENDERER_ERROR("before info lock");
+ std::lock_guard<std::mutex> info_lock(current_info_m_);
+ TRACKRENDERER_ERROR("after info lock");
+ internal::UpdateInfo(&easing_info_, &pending_info_);
+ auto volume = volume_.load();
+ if (need_reset_) {
+ elapsed_time_ms_.store(0);
+ initial_volume_.store(volume);
+ need_reset_ = false;
+ TRACKRENDERER_ERROR("elapsed time %u, initial volume %u",
+ elapsed_time_ms_.load(), initial_volume_.load());
+ }
+
+ if (easing_info_.target_volume == volume) return true;
+
+ if (easing_info_.duration == 0) {
+ SetAudioGain_(audiosink, easing_info_.target_volume);
+ } else {
+ easing_task_ = std::thread(&AudioEasingController::SetVolumeWithFade_, this,
+ audiosink, need_lock_delay);
+ }
+ TRACKRENDERER_LEAVE;
+ return true;
+}
+
+void AudioEasingController::SetVolumeWithFade_(GstElement* audiosink,
+ bool need_lock_delay) {
+ TRACKRENDERER_ENTER;
+ if (audiosink == nullptr) return;
+ is_stopped_.store(false);
+
+ if (need_lock_delay) {
+ TRACKRENDERER_ERROR("wait lock delay : %d", lock_delay_ms_);
+ std::this_thread::sleep_for(std::chrono::microseconds(lock_delay_ms_));
+ }
+
+ TRACKRENDERER_ERROR("before info lock");
+ std::unique_lock<std::mutex> info_lock(current_info_m_);
+ TRACKRENDERER_ERROR("after info lock");
+ const uint32_t target_volume = easing_info_.target_volume;
+ const uint32_t duration_ms = easing_info_.duration;
+ const AudioEasingType type = easing_info_.type;
+ info_lock.unlock();
+
+ float start_volume = static_cast<float>(initial_volume_.load());
+ float delta = static_cast<float>(target_volume) - start_volume;
+ uint32_t duration_us = internal::ConvertMsToMicroSec(duration_ms);
+ const uint32_t interval = internal::GetMicroSecStepInterval(duration_us);
+
+ if (type == AudioEasingType::kAudioEasingLinear) {
+ delta = AdjustLinearTypeVolumeDelta_(delta, start_volume, target_volume);
+ duration_us -= interval;
+ }
+
+ float set_volume = start_volume;
+ auto elapsed_time_us = internal::ConvertMsToMicroSec(elapsed_time_ms_.load());
+ const uint32_t init_elapsed_time_us = elapsed_time_us;
+ TRACKRENDERER_ERROR("start_volume %f / cur volume %u, elapsed us time %u",
+ set_volume, volume_.load(), elapsed_time_us);
+
+ TRACKRENDERER_ERROR(
+ "Ease Duration(%u us), Ease Volume Delta(%f), Ease Step Interval(%u us), "
+ "Ease Type(%d)",
+ duration_us, delta, interval, static_cast<int>(type));
+ auto begin = std::chrono::system_clock::now();
+
+ const AudioEasingInfo setup_info = {target_volume, duration_ms, type};
+ SetEasingStartProperty_(audiosink, setup_info);
+
+ while (duration_us) {
+ if (is_ease_interrupt_) {
+ TRACKRENDERER_ERROR("interrupt break");
+ SetEasingStopProperty_(audiosink);
+ is_stopped_.store(true);
+ return;
+ }
+
+ set_volume =
+ CalculateVolume_(static_cast<float>(elapsed_time_us), start_volume,
+ delta, static_cast<float>(duration_us), type);
+
+ SetAudioGain_(audiosink, static_cast<uint32_t>(set_volume));
+ auto real_elapsed_time =
+ internal::GetMicroSecElapsedEasingTime(begin) + init_elapsed_time_us;
+
+ if (elapsed_time_us >= duration_us) {
+ break;
+ }
+ elapsed_time_us += interval;
+ elapsed_time_ms_.store(internal::ConvertMicroSecToMs(elapsed_time_us));
+
+ internal::WaitGap(elapsed_time_us, real_elapsed_time);
+ }
+
+ if (static_cast<uint32_t>(set_volume) != target_volume) {
+ TRACKRENDERER_ERROR("diff set volume & target volume");
+ auto real_elapsed_time =
+ internal::GetMicroSecElapsedEasingTime(begin) + init_elapsed_time_us;
+
+ elapsed_time_us += interval;
+ elapsed_time_ms_.store(internal::ConvertMicroSecToMs(elapsed_time_us));
+
+ internal::WaitGap(internal::ConvertMsToMicroSec(duration_ms),
+ real_elapsed_time);
+
+ SetAudioGain_(audiosink, target_volume);
+ }
+ SetEasingStopProperty_(audiosink);
+ is_stopped_.store(true);
+
+ auto final_elapsed_time =
+ internal::GetMicroSecElapsedEasingTime(begin) + init_elapsed_time_us;
+
+ TRACKRENDERER_ERROR(
+ "Final target volume [%d], start volume [%f], elapsedTime[%d ms /%d ms], "
+ "final_elapsed_time [%d us]",
+ target_volume, start_volume, elapsed_time_ms_.load(), duration_ms,
+ final_elapsed_time);
+
+ TRACKRENDERER_LEAVE;
+ return;
+}
+
+bool AudioEasingController::NeedToWaitLockDelay_() {
+ return easing_task_.joinable() ? FALSE : TRUE;
+}
+
+void AudioEasingController::InterruptEasingThread_() {
+ TRACKRENDERER_ENTER;
+
+ std::lock_guard<std::mutex> lock(easing_m_);
+
+ is_ease_interrupt_ = true;
+ if (easing_task_.joinable()) {
+ TRACKRENDERER_ERROR("join before");
+ easing_task_.join();
+ TRACKRENDERER_ERROR("join after");
+ }
+
+ is_ease_interrupt_ = false;
+ TRACKRENDERER_LEAVE;
+}
+
+float AudioEasingController::AdjustLinearTypeVolumeDelta_(float delta,
+ float start,
+ int target) {
+ int i;
+ int volume = target;
+ int last_gain = kVolumeToGainIndexTable[volume];
+
+ if (delta >= 0) {
+ for (i = volume - 1; i >= 0; i--) {
+ if (kVolumeToGainIndexTable[i] != last_gain) {
+ break;
+ }
+ }
+ } else {
+ for (i = volume + 1; i < kMaxVolumeIndex; i++) {
+ if (kVolumeToGainIndexTable[i] != last_gain) {
+ break;
+ }
+ }
+ }
+
+ volume = i;
+ delta = static_cast<float>(volume) - start;
+
+ return delta;
+}
+
+float AudioEasingController::CalculateVolume_(float t, float b, float delta,
+ float d, AudioEasingType type) {
+ float volume;
+ switch (type) {
+ case AudioEasingType::kAudioEasingIncubic: {
+ TRACKRENDERER_INFO("INCUBIC Ease");
+ t /= d;
+ volume = delta * t * t * t + b;
+ } break;
+ case AudioEasingType::kAudioEasingOutcubic: {
+ TRACKRENDERER_INFO("OUTCUBIC Ease");
+ t = t / d - 1.0f;
+ volume = delta * (t * t * t + 1.0f) + b;
+ } break;
+ case AudioEasingType::kAudioEasingLinear:
+ default: {
+ TRACKRENDERER_INFO("LINEAR Ease");
+ volume = (delta * t) / d + b;
+ } break;
+ }
+
+ if (delta > 0) {
+ volume += 0.5;
+ }
+
+ return volume;
+}
+
+bool AudioEasingController::SetAudioGain_(GstElement* audiosink,
+ uint32_t volume) {
+ TRACKRENDERER_ENTER;
+ if (audiosink == nullptr) return false;
+ if (volume > kMaxVolumeIndex) return false;
+
+ int gain = kVolumeToGainIndexTable[volume];
+ g_object_set(G_OBJECT(audiosink), "device-volume", gain, NULL);
+
+ volume_.store(volume);
+ TRACKRENDERER_INFO("gain : %d / volume : %u", gain, volume);
+ TRACKRENDERER_LEAVE;
+ return true;
+}
+
+void AudioEasingController::SetEasingStartProperty_(
+ GstElement* audiosink, const AudioEasingInfo& setup_info) {
+ TRACKRENDERER_ENTER;
+ if (audiosink == nullptr) return;
+
+ const uint32_t target_volume =
+ kVolumeToGainIndexTable[setup_info.target_volume];
+ const uint32_t duration = setup_info.duration - elapsed_time_ms_.load();
+
+ const AudioEasingInfo start_info = {target_volume, duration, setup_info.type};
+ SetEasingInfoProperty_(audiosink, start_info);
+
+ TRACKRENDERER_LEAVE;
+ return;
+}
+
+void AudioEasingController::SetEasingStopProperty_(GstElement* audiosink) {
+ TRACKRENDERER_ENTER;
+ if (audiosink == nullptr) return;
+
+ const uint32_t target_volume = kVolumeToGainIndexTable[volume_.load()];
+ const AudioEasingInfo stop_info = {target_volume, static_cast<uint32_t>(-1),
+ AudioEasingType::kAudioEasingLinear};
+ SetEasingInfoProperty_(audiosink, stop_info);
+
+ TRACKRENDERER_LEAVE;
+ return;
+}
+
+void AudioEasingController::SetEasingInfoProperty_(
+ GstElement* audiosink, const AudioEasingInfo& info) {
+ if (audiosink == nullptr) return;
+
+ auto easing_info = gstguard::make_guard(gst_structure_new(
+ "easing-info", "volume", G_TYPE_UINT, info.target_volume, "duration",
+ G_TYPE_INT, info.duration, "type", G_TYPE_UINT,
+ static_cast<uint32_t>(info.type), NULL));
+ g_object_set(G_OBJECT(audiosink), "audio-easing-info", easing_info.get(),
+ NULL);
+ return;
+}
+
+} // namespace trackrenderer
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/core/caps_recipes.h"
+
+#include <cassert>
+
+#include "trackrenderer/trackrenderer.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace track {
+const int kLittleEndian = 1234;
+
+namespace audio {
+void FillDefault(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("channels", G_TYPE_INT, track.channels);
+ caps.SetValue("rate", G_TYPE_INT, track.sample_rate);
+}
+
+/* =========================================
+ * audio/mpeg
+ * =========================================
+ */
+bool CheckMpeg1(const Track& track) { return track.version == 1; }
+void FillMpeg1(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("mpegversion", G_TYPE_INT, track.version);
+ caps.SetValue("layer", G_TYPE_INT, track.layer);
+ caps.SetValue("parsed", G_TYPE_BOOLEAN, true);
+}
+
+bool CheckAac(const Track& track) {
+ return (track.version == 2 || track.version == 4) && track.layer == 0;
+}
+void FillAac(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("mpegversion", G_TYPE_INT, track.version);
+ caps.SetValue("framed", G_TYPE_BOOLEAN, true);
+ caps.SetValue("bitrate", G_TYPE_INT, track.bitrate);
+ caps.SetValue("stream-format", G_TYPE_STRING, "raw");
+}
+
+bool CheckAacWithAdtsHeader(const Track& track) {
+ return (track.version == 2 || track.version == 4) && track.layer == 1;
+}
+void FillAacWithAdtsHeader(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("mpegversion", G_TYPE_INT, track.version);
+ caps.SetValue("framed", G_TYPE_BOOLEAN, true);
+ caps.SetValue("bitrate", G_TYPE_INT, track.bitrate);
+ caps.SetValue("stream-format", G_TYPE_STRING, "adts");
+}
+
+bool CheckAacWithAdifHeader(const Track& track) {
+ return (track.version == 2 || track.version == 4) && track.layer == 2;
+}
+void FillAacWithAdifHeader(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("mpegversion", G_TYPE_INT, track.version);
+ caps.SetValue("framed", G_TYPE_BOOLEAN, true);
+ caps.SetValue("bitrate", G_TYPE_INT, track.bitrate);
+ caps.SetValue("stream-format", G_TYPE_STRING, "adif");
+}
+
+/* =========================================
+ * audio/x-raw
+ * =========================================
+ */
+bool CheckPcmS32Le(const Track& track) {
+ return track.is_signed && track.sample_format == 32 &&
+ track.endianness == kLittleEndian;
+}
+void FillPcmS32Le(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "S32LE");
+ caps.SetValue("bpp", G_TYPE_INT, 32);
+ caps.SetValue("endianness", G_TYPE_INT, 1234);
+ caps.SetValue("signed", G_TYPE_BOOLEAN, true);
+}
+
+bool CheckPcmS32Be(const Track& track) {
+ return track.is_signed && track.sample_format == 32 &&
+ track.endianness != kLittleEndian;
+}
+void FillPcmS32Be(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "S32BE");
+ caps.SetValue("bpp", G_TYPE_INT, 32);
+ caps.SetValue("signed", G_TYPE_BOOLEAN, true);
+}
+
+bool CheckPcmU32Le(const Track& track) {
+ return !track.is_signed && track.sample_format == 32 &&
+ track.endianness == kLittleEndian;
+}
+void FillPcmU32Le(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "U32LE");
+ caps.SetValue("bpp", G_TYPE_INT, 32);
+ caps.SetValue("endianness", G_TYPE_INT, 1234);
+}
+
+bool CheckPcmU32Be(const Track& track) {
+ return !track.is_signed && track.sample_format == 32 &&
+ track.endianness != kLittleEndian;
+}
+void FillPcmU32Be(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "U32BE");
+ caps.SetValue("bpp", G_TYPE_INT, 32);
+}
+
+bool CheckPcmS24Le(const Track& track) {
+ return track.is_signed && track.sample_format == 24 &&
+ track.endianness == kLittleEndian;
+}
+void FillPcmS24Le(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "S24LE");
+ caps.SetValue("bpp", G_TYPE_INT, 24);
+ caps.SetValue("endianness", G_TYPE_INT, 1234);
+ caps.SetValue("signed", G_TYPE_BOOLEAN, true);
+}
+
+bool CheckPcmS24Be(const Track& track) {
+ return track.is_signed && track.sample_format == 24 &&
+ track.endianness != kLittleEndian;
+}
+void FillPcmS24Be(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "S24BE");
+ caps.SetValue("bpp", G_TYPE_INT, 24);
+ caps.SetValue("signed", G_TYPE_BOOLEAN, true);
+}
+
+bool CheckPcmU24Le(const Track& track) {
+ return !track.is_signed && track.sample_format == 24 &&
+ track.endianness == kLittleEndian;
+}
+void FillPcmU24Le(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "U24LE");
+ caps.SetValue("bpp", G_TYPE_INT, 24);
+ caps.SetValue("endianness", G_TYPE_INT, 1234);
+}
+
+bool CheckPcmU24Be(const Track& track) {
+ return !track.is_signed && track.sample_format == 24 &&
+ track.endianness != kLittleEndian;
+}
+void FillPcmU24Be(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "U24BE");
+ caps.SetValue("bpp", G_TYPE_INT, 24);
+}
+
+bool CheckPcmS16Le(const Track& track) {
+ return ((track.is_signed && track.sample_format == 16 &&
+ track.endianness == kLittleEndian) ||
+ (track.codec_tag == "S16LE"));
+}
+void FillPcmS16Le(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "S16LE");
+ caps.SetValue("bpp", G_TYPE_INT, 16);
+ caps.SetValue("endianness", G_TYPE_INT, 1234);
+ caps.SetValue("signed", G_TYPE_BOOLEAN, true);
+ caps.SetValue("block_align", G_TYPE_INT, track.block_align);
+}
+
+bool CheckPcmS16Be(const Track& track) {
+ return track.is_signed && track.sample_format == 16 &&
+ track.endianness != kLittleEndian;
+}
+void FillPcmS16Be(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "S16BE");
+ caps.SetValue("bpp", G_TYPE_INT, 16);
+ caps.SetValue("signed", G_TYPE_BOOLEAN, true);
+}
+
+bool CheckPcmU16Le(const Track& track) {
+ return !track.is_signed && track.sample_format == 16 &&
+ track.endianness == kLittleEndian;
+}
+void FillPcmU16Le(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "U16LE");
+ caps.SetValue("bpp", G_TYPE_INT, 16);
+ caps.SetValue("endianness", G_TYPE_INT, 1234);
+}
+
+bool CheckPcmU16Be(const Track& track) {
+ return !track.is_signed && track.sample_format == 16 &&
+ track.endianness != kLittleEndian;
+}
+void FillPcmU16Be(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "U16BE");
+ caps.SetValue("bpp", G_TYPE_INT, 16);
+}
+
+bool CheckPcmS8(const Track& track) {
+ return track.is_signed && track.sample_format == 8;
+}
+void FillPcmS8(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "S8");
+ caps.SetValue("bpp", G_TYPE_INT, 8);
+ caps.SetValue("signed", G_TYPE_BOOLEAN, true);
+}
+
+bool CheckPcmU8(const Track& track) {
+ return !track.is_signed && track.sample_format == 8;
+}
+void FillPcmU8(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("layout", G_TYPE_STRING, "interleaved");
+ caps.SetValue("format", G_TYPE_STRING, "U8");
+ caps.SetValue("bpp", G_TYPE_INT, 8);
+}
+
+/* =========================================
+ * audio/x-eac3
+ * audio/x-dts
+ * audio/x-ac3
+ * =========================================
+ */
+void FillDolby(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("framed", G_TYPE_BOOLEAN, true);
+ caps.SetValue("bitrate", G_TYPE_INT, track.bitrate);
+ caps.SetValue("bpp", G_TYPE_INT, track.bits_per_sample);
+}
+
+/* =========================================
+ * audio/x-adpcm
+ * =========================================
+ */
+void FillAdpcm(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("block_align", G_TYPE_INT, track.block_align);
+ caps.SetValue("layout", G_TYPE_STRING, track.layout.c_str());
+}
+/* =========================================
+ * audio/x-wma
+ * =========================================
+ */
+void FillWma(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("block_align", G_TYPE_INT, track.block_align);
+ caps.SetValue("wmaversion", G_TYPE_INT, track.version);
+ caps.SetValue("bpp", G_TYPE_INT, track.bits_per_sample);
+ if (!track.codec_tag.empty())
+ caps.SetValue("format", G_TYPE_STRING, track.codec_tag.c_str());
+ else
+ caps.SetValue("format", G_TYPE_STRING,
+ (track.version == 1) ? "0160" : "0161");
+
+ caps.SetValue("bitrate", G_TYPE_INT, track.bitrate);
+}
+/* =========================================
+ * audio/x-pn-realaudio
+ * =========================================
+ */
+void FillRealAudio(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("flavor", G_TYPE_INT, track.flavor);
+ caps.SetValue("leaf_size", G_TYPE_INT, track.block_align);
+ caps.SetValue("bitrate", G_TYPE_INT, track.bitrate);
+}
+
+/* =========================================
+ * audio/x-vorbis
+ * =========================================
+ */
+void FillVorbis(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("block_align", G_TYPE_INT, track.block_align);
+ caps.SetValue("bpp", G_TYPE_INT, track.bits_per_sample);
+ caps.SetValue("format", G_TYPE_STRING, track.codec_tag.c_str());
+ caps.SetValue("bitrate", G_TYPE_INT, track.bitrate);
+}
+
+} // namespace audio
+namespace video {
+void FillDefault(const GstCapsWrapper& caps, const Track& track) {
+ int max_width = track.maxwidth ? track.maxwidth : track.width;
+ int max_height = track.maxheight ? track.maxheight : track.height;
+ caps.SetValue("width", G_TYPE_INT, track.width);
+ caps.SetValue("height", G_TYPE_INT, track.height);
+ caps.SetValue("maxwidth", G_TYPE_INT, max_width);
+ caps.SetValue("maxheight", G_TYPE_INT, max_height);
+ caps.SetValue("framerate", GST_TYPE_FRACTION, track.framerate_num,
+ track.framerate_den);
+}
+
+// LCOV_EXCL_START
+/* =========================================
+ * video/mpeg
+ * =========================================
+ */
+void FillMpeg(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("mpegversion", G_TYPE_INT, track.version);
+ caps.SetValue("systemstream", G_TYPE_BOOLEAN, false);
+ if (!track.codec_tag.empty())
+ caps.SetValue("format", G_TYPE_STRING, track.codec_tag.c_str());
+}
+
+/* =========================================
+ * video/x-h263
+ * =========================================
+ */
+void FillH263(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("variant", G_TYPE_STRING, "itu");
+}
+
+/* =========================================
+ * video/x-wmv
+ * =========================================
+ */
+void FillWmv(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("wmvversion", G_TYPE_INT, track.version);
+ if (!track.codec_tag.empty())
+ caps.SetValue("format", G_TYPE_STRING, track.codec_tag.c_str());
+ else
+ switch (track.version) {
+ case 1:
+ caps.SetValue("format", G_TYPE_STRING, "WMV1");
+ break;
+ case 2:
+ caps.SetValue("format", G_TYPE_STRING, "WMV2");
+ break;
+ case 3:
+ caps.SetValue("format", G_TYPE_STRING, "WMV3");
+ break;
+ }
+}
+
+/* =========================================
+ * video/x-pn-realvideo
+ * =========================================
+ */
+bool CheckRealVideo3(const Track& track) { return track.version == 3; }
+void FillRealVideo3(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("rmversion", G_TYPE_INT, track.version);
+ caps.SetValue("format", G_TYPE_STRING, "RV30");
+}
+
+bool CheckRealVideo4(const Track& track) { return track.version == 4; }
+void FillRealVideo4(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("rmversion", G_TYPE_INT, track.version);
+ caps.SetValue("format", G_TYPE_STRING, "RV40");
+}
+
+/* =========================================
+ * video/x-msmpeg
+ * =========================================
+ */
+void FillMsMpeg(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("format", G_TYPE_STRING, "DIV3");
+}
+
+/* =========================================
+ * video/x-vp9
+ * =========================================
+ */
+void FillVP9(const GstCapsWrapper& caps, const Track& track) {
+ // WHY?
+ // gst_wayland_sink_set_matroska_color_info()
+ // mdcv_structure->max_fall = sink->isVP9;
+ // SW Qualty didn't use the maxFALL parameter, and they need the new parameter
+ // to check current is VP9 or not.
+ caps.SetValue("VP9", G_TYPE_INT, 1);
+}
+/* =========================================
+ * video/x-raw
+ * =========================================
+ */
+void FillVideoRaw(const GstCapsWrapper& caps, const Track& track) {
+ caps.SetValue("format", G_TYPE_STRING, "STV1");
+}
+/* =========================================
+ * video/x-vp6
+ * =========================================
+ */
+void FillVP6(const GstCapsWrapper& caps, const Track& track) {
+ if (!track.codec_tag.empty())
+ caps.SetValue("format", G_TYPE_STRING, track.codec_tag.c_str());
+}
+/* =========================================
+ * video/x-jpeg
+ * =========================================
+ */
+void FillJpeg(const GstCapsWrapper& caps, const Track& track) {
+ if (!track.codec_tag.empty())
+ caps.SetValue("format", G_TYPE_STRING, track.codec_tag.c_str());
+}
+// LCOV_EXCL_STOP
+} // namespace video
+
+bool AlwaysPass(const Track& track) { return true; }
+
+void FillDefaultValue(const GstCapsWrapper& caps, const Track& track) {
+ switch (track.type) {
+ case kTrackTypeAudio:
+ track::audio::FillDefault(caps, track);
+ break;
+ case kTrackTypeVideo:
+ track::video::FillDefault(caps, track);
+ break;
+ default:
+ assert(false && "unsupport TrackType");
+ break;
+ }
+}
+} // namespace track
+
+const GstCapsBuilder::Recipes TrackRenderer::kCapsRecipes_ = {
+ {kDefaultRecipe, {{track::AlwaysPass, track::FillDefaultValue}}},
+ {"audio/mpeg",
+ {
+ {track::audio::CheckMpeg1, track::audio::FillMpeg1},
+ {track::audio::CheckAac, track::audio::FillAac},
+ {track::audio::CheckAacWithAdtsHeader,
+ track::audio::FillAacWithAdtsHeader},
+ {track::audio::CheckAacWithAdifHeader,
+ track::audio::FillAacWithAdifHeader},
+ }},
+ {"audio/x-raw",
+ {
+ {track::audio::CheckPcmS32Le, track::audio::FillPcmS32Le},
+ {track::audio::CheckPcmS32Be, track::audio::FillPcmS32Be},
+ {track::audio::CheckPcmU32Le, track::audio::FillPcmU32Le},
+ {track::audio::CheckPcmU32Be, track::audio::FillPcmU32Be},
+ {track::audio::CheckPcmS24Le, track::audio::FillPcmS24Le},
+ {track::audio::CheckPcmS24Be, track::audio::FillPcmS24Be},
+ {track::audio::CheckPcmU24Le, track::audio::FillPcmU24Le},
+ {track::audio::CheckPcmU24Be, track::audio::FillPcmU24Be},
+ {track::audio::CheckPcmS16Le, track::audio::FillPcmS16Le},
+ {track::audio::CheckPcmS16Be, track::audio::FillPcmS16Be},
+ {track::audio::CheckPcmU16Le, track::audio::FillPcmU16Le},
+ {track::audio::CheckPcmU16Be, track::audio::FillPcmU16Be},
+ {track::audio::CheckPcmS8, track::audio::FillPcmS8},
+ {track::audio::CheckPcmU8, track::audio::FillPcmU8},
+ }},
+ {"audio/x-eac3",
+ {
+ {track::AlwaysPass, track::audio::FillDolby},
+ }},
+ {"audio/x-dts",
+ {
+ {track::AlwaysPass, track::audio::FillDolby},
+ }},
+ {"audio/x-ac3",
+ {
+ {track::AlwaysPass, track::audio::FillDolby},
+ }},
+ {"audio/x-ac4",
+ {
+ {track::AlwaysPass, track::audio::FillDolby},
+ }},
+ {"audio/x-adpcm",
+ {
+ {track::AlwaysPass, track::audio::FillAdpcm},
+ }},
+ {"audio/x-wma",
+ {
+ {track::AlwaysPass, track::audio::FillWma},
+ }},
+ {"audio/x-pn-realaudio",
+ {
+ {track::AlwaysPass, track::audio::FillRealAudio},
+ }},
+ {"audio/x-vorbis",
+ {
+ {track::AlwaysPass, track::audio::FillVorbis},
+ }},
+ {"video/mpeg",
+ {
+ {track::AlwaysPass, track::video::FillMpeg},
+ }},
+ {"video/x-h263",
+ {
+ {track::AlwaysPass, track::video::FillH263},
+ }},
+ {"video/x-wmv",
+ {
+ {track::AlwaysPass, track::video::FillWmv},
+ }},
+ {"video/x-pn-realvideo",
+ {
+ {track::video::CheckRealVideo3, track::video::FillRealVideo3},
+ {track::video::CheckRealVideo4, track::video::FillRealVideo4},
+ }},
+ {"video/x-msmpeg",
+ {
+ {track::AlwaysPass, track::video::FillMsMpeg},
+ }},
+ {"video/x-vp9",
+ {
+ {track::AlwaysPass, track::video::FillVP9},
+ }},
+ {"video/x-raw",
+ {
+ {track::AlwaysPass, track::video::FillVideoRaw},
+ }},
+ {"video/x-vp6",
+ {
+ {track::AlwaysPass, track::video::FillVP6},
+ }},
+ {"video/x-jpeg",
+ {
+ {track::AlwaysPass, track::video::FillJpeg},
+ }},
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/core/decoderinputbuffer.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace decoderinputbuffer_util {
+
+bool FlushQueue(std::queue<DecoderInputBufferPtr>& queue) {
+ while (!queue.empty()) {
+ queue.pop();
+ }
+ return true;
+}
+
+} // namespace decoderinputbuffer_util
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/display.h"
+
+#include <boost/scope_exit.hpp>
+#include <cassert>
+#include <string>
+#include <utility>
+
+#include "Ecore.h"
+#include "Elementary.h"
+#include "glib-object.h"
+#include "tizen-extension-client-protocol.h"
+#include "trackrenderer/core/gstobject_guard.h"
+#include "trackrenderer/core/utils/log.h"
+#include "wayland-client.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+constexpr static uint32_t kInvalidSurfaceId = 0;
+
+namespace internal {
+
+struct WaylandClient {
+ wl_display* display = nullptr;
+ wl_registry* registry = nullptr;
+ tizen_surface* surface = nullptr;
+ tizen_resource* resource = nullptr;
+};
+
+void HandleResourceId(void* userdata, tizen_resource* resource,
+ unsigned int id) {
+ unsigned int* wl_surface_id = static_cast<unsigned int*>(userdata);
+ *wl_surface_id = id;
+ TRACKRENDERER_INFO("[CLIENT] got wl_surface_id(%d) from server\n", id);
+}
+
+static const tizen_resource_listener tizen_resource_listener = {
+ HandleResourceId,
+};
+
+static void GlobalHandle(void* userdata, wl_registry* registry,
+ unsigned int name, const char* interface,
+ unsigned int version) {
+ if (!userdata) {
+ TRACKRENDERER_ERROR("userdata is null!");
+ return;
+ }
+ WaylandClient* wlclient = static_cast<WaylandClient*>(userdata);
+
+ if (strcmp(interface, "tizen_surface") == 0) {
+ TRACKRENDERER_DEBUG("binding tizen_surface");
+ wlclient->surface = static_cast<tizen_surface*>(
+ wl_registry_bind(registry, name, &tizen_surface_interface, version));
+ if (!wlclient->surface) {
+ TRACKRENDERER_ERROR("wlclient->tizen_surface is null!");
+ return;
+ }
+ }
+}
+
+static const wl_registry_listener registry_listener = {
+ GlobalHandle, NULL
+};
+
+bool GetWindowGeometry(Evas_Object* obj, int* x, int* y, int* width,
+ int* height) {
+ evas_object_geometry_get(obj, x, y, width, height);
+ Evas* evas = evas_object_evas_get(obj);
+ if (!evas) {
+ TRACKRENDERER_ERROR("fail to get evas");
+ return false;
+ }
+ Ecore_Evas* ecore_evas = ecore_evas_ecore_evas_get(evas);
+ if (!ecore_evas) {
+ return false;
+ }
+
+ int rotation = ecore_evas_rotation_get(ecore_evas);
+ if (rotation == 270 || rotation == 90) {
+ std::swap(*width, *height);
+ }
+
+ TRACKRENDERER_DEBUG(
+ "window geometroy : x(%d) y(%d) width(%d) height(%d) rotation(%d)", *x,
+ *y, *width, *height, rotation);
+
+ return true;
+}
+
+uint32_t GetSurfaceId(wl_surface* surface, wl_display* display) {
+ if (!surface || !display) {
+ TRACKRENDERER_ERROR("input is nullptr!!");
+ return kInvalidSurfaceId;
+ }
+
+ WaylandClient wlclient;
+ wl_display* display_wrapper = nullptr;
+ wl_event_queue* queue = nullptr;
+ BOOST_SCOPE_EXIT(&queue, &display_wrapper, &wlclient) {
+ if (queue) wl_event_queue_destroy(queue);
+ if (display_wrapper) wl_proxy_wrapper_destroy(display_wrapper);
+ if (wlclient.registry) wl_registry_destroy(wlclient.registry);
+ if (wlclient.surface) tizen_surface_destroy(wlclient.surface);
+ if (wlclient.resource) tizen_resource_destroy(wlclient.resource);
+ }
+ BOOST_SCOPE_EXIT_END
+
+ wlclient.display = display;
+ queue = wl_display_create_queue(wlclient.display);
+ if (!queue) {
+ return kInvalidSurfaceId;
+ }
+
+ display_wrapper =
+ reinterpret_cast<wl_display*>(wl_proxy_create_wrapper(wlclient.display));
+ if (!display_wrapper) {
+ return kInvalidSurfaceId;
+ }
+
+ wl_proxy_set_queue(reinterpret_cast<wl_proxy*>(display_wrapper), queue);
+ wlclient.registry = wl_display_get_registry(display_wrapper);
+ if (!wlclient.registry) {
+ return kInvalidSurfaceId;
+ }
+
+ wl_registry_add_listener(wlclient.registry, ®istry_listener, &wlclient);
+ TRACKRENDERER_INFO("wl_display_roundtrip_queue()");
+ wl_display_roundtrip_queue(wlclient.display, queue);
+ if (!wlclient.surface) {
+ return kInvalidSurfaceId;
+ }
+
+ TRACKRENDERER_INFO("tizen_surface_get_tizen_resource()");
+ wlclient.resource =
+ tizen_surface_get_tizen_resource(wlclient.surface, surface);
+ if (!wlclient.resource) {
+ return kInvalidSurfaceId;
+ }
+
+ TRACKRENDERER_INFO("tizen_resource_add_listener()");
+ unsigned int surface_id = kInvalidSurfaceId;
+ tizen_resource_add_listener(wlclient.resource, &tizen_resource_listener,
+ &surface_id);
+ TRACKRENDERER_INFO("wl_display_roundtrip_queue()");
+ wl_display_roundtrip_queue(wlclient.display, queue);
+ if (surface_id <= 0) {
+ return kInvalidSurfaceId;
+ }
+
+ TRACKRENDERER_INFO("Surfaceid = %d", surface_id);
+ return surface_id;
+}
+
+// TODO(sy0207.ju) :
+// this is temparary solution.
+// defined in player.h , player_display_mode_e
+typedef enum {
+ PLAYER_DISPLAY_MODE_LETTER_BOX = 0, /**< Letter box */
+ PLAYER_DISPLAY_MODE_ORIGIN_SIZE, /**< Origin size */
+ PLAYER_DISPLAY_MODE_FULL_SCREEN, /**< Full-screen */
+ PLAYER_DISPLAY_MODE_CROPPED_FULL, /**< Cropped full-screen */
+ PLAYER_DISPLAY_MODE_ORIGIN_OR_LETTER, /**< Origin size (if surface size is
+ larger than video size(width/height))
+ or Letter box (if video
+ size(width/height) is larger than
+ surface size) */
+ PLAYER_DISPLAY_MODE_DST_ROI, /**< Dst ROI mode */
+ // #ifdef USE_PRODUCT_FEATURE
+ PLAYER_DISPLAY_MODE_ZOOM_HALF, /*zoom half */
+ PLAYER_DISPLAY_MODE_ZOOM_THREE_QUARTERS, /*zoom three quarters */
+ PLAYER_DISPLAY_MODE_ZOOM_16_9, /**16*9*/
+ PLAYER_DISPLAY_MODE_ZOOM, /**< WZOOM*/
+ PLAYER_DISPLAY_MODE_ZOOM_CUSTOM, /**< ZOOM CUSTOM*/
+ PLAYER_DISPLAY_MODE_ZOOM_NETFLIX_16X9, /**< zoom to 16x9, only for netflix*/
+ PLAYER_DISPLAY_MODE_ZOOM_NETFLIX_4X3, /**< zoom to 4x3, only for netflix*/
+ PLAYER_DISPLAY_MODE_DPS,
+ PLAYER_DISPLAY_MODE_ZOOM__9,
+ PLAYER_DISPLAY_MODE_AUTO,
+ PLAYER_DISPLAY_MODE_CAPTION,
+ PLAYER_DISPLAY_MODE_AUTO_ASPECT_RATIO, /**< use the dar/par from video info to
+ show video*/
+ // #endif
+ PLAYER_DISPLAY_MODE_NUM
+} player_display_mode_e;
+
+int ConvertDisplayModeValue(const DisplayMode& mode) {
+ switch (mode) {
+ case DisplayMode::kLetterBox: {
+ return PLAYER_DISPLAY_MODE_LETTER_BOX;
+ }
+ case DisplayMode::kOriginSize: {
+ return PLAYER_DISPLAY_MODE_ORIGIN_SIZE;
+ }
+ case DisplayMode::kFullScreen: {
+ return PLAYER_DISPLAY_MODE_FULL_SCREEN;
+ }
+ case DisplayMode::kCroppedFull: {
+ return PLAYER_DISPLAY_MODE_CROPPED_FULL;
+ }
+ case DisplayMode::kOriginOrLetter: {
+ return PLAYER_DISPLAY_MODE_ORIGIN_OR_LETTER;
+ }
+ case DisplayMode::kDstRoi: {
+ return PLAYER_DISPLAY_MODE_DST_ROI;
+ }
+ case DisplayMode::kAutoAspectRatio: {
+ return PLAYER_DISPLAY_MODE_AUTO_ASPECT_RATIO;
+ }
+ default:
+ assert(0 && "unknown displaymode");
+ return PLAYER_DISPLAY_MODE_FULL_SCREEN;
+ }
+}
+
+} // namespace internal
+
+Display::~Display() {}
+
+bool Display::SetDisplay(const DisplayType& type, Evas_Object* obj) {
+ assert(obj && "obj should not be null");
+
+ TRACKRENDERER_ENTER_P(this);
+ if (type == DisplayType::kNone) {
+ unsigned int surface_id = 0;
+ long x = 0, y = 0, w = 0, h = 0;
+ SetDisplay(type, surface_id, x, y, w, h);
+ return false;
+ }
+
+ ecore_thread_main_loop_begin();
+
+ BOOST_SCOPE_EXIT(&obj) { ecore_thread_main_loop_end(); }
+ BOOST_SCOPE_EXIT_END
+
+ const std::string obj_type(evas_object_type_get(obj));
+ if (!obj_type.c_str()) {
+ assert(0 && "object type is null");
+ TRACKRENDERER_ERROR_P(this, "object type is null");
+ return false;
+ }
+
+ if (type == DisplayType::kOverlay && obj_type == "elm_win") {
+ int x, y, w, h;
+ bool ret = internal::GetWindowGeometry(obj, &x, &y, &w, &h);
+ if (!ret) {
+ TRACKRENDERER_ERROR_P(this, "Fail GetWindowGeometry");
+ return false;
+ }
+ Ecore_Evas* ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj));
+
+ Ecore_Wl2_Window* wl_window = ecore_evas_wayland2_window_get(ee);
+ if (!wl_window) {
+ TRACKRENDERER_ERROR_P(this, "elm_win_wl_window_get() failed");
+ return false;
+ }
+ wl_surface* surface = GetWlSurface_(wl_window);
+ return SetDisplay_(type, surface, x, y, w, h);
+ } else {
+ // TODO(js4716.chun) :
+ // else if (type == PLAYER_DISPLAY_TYPE_EVAS &&
+ // !strcmp(object_type, "image"))
+ assert(obj && "not support yet!");
+ }
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+wl_surface* Display::GetWlSurface_(Ecore_Wl2_Window* ecore_wl2_window) {
+ /*
+ app have to call ecore_wl2_window_video_surface_create() for sync issue
+ between video and graphic. then player can get surface id from
+ ecore_wl2_window_video_surface_get().
+ */
+ wl_surface* surface =
+ (wl_surface*)ecore_wl2_window_video_surface_get(ecore_wl2_window);
+ if (surface) {
+ TRACKRENDERER_INFO_P(this,
+ "sync support: get surface id from "
+ "ecore_wl2_windoe_video_surface_get()");
+ has_parent_surface_ = true;
+ } else {
+ // EAPI struct wl_surface *
+ // ecore_wl2_window_surface_get(Ecore_Wl2_Window * win)
+ surface = ecore_wl2_window_surface_get(ecore_wl2_window);
+ }
+ return surface;
+}
+
+// LCOV_EXCL_START
+bool Display::SetDisplay(const DisplayType& type,
+ Ecore_Wl2_Window* ecore_wl2_window, const int x,
+ const int y, const int w, const int h) {
+ assert(ecore_wl2_window && "ecore_wl2_window should not be null");
+ assert((type == DisplayType::kOverlay) && "not support yet!");
+
+ TRACKRENDERER_ENTER_P(this);
+ if (type == DisplayType::kNone) {
+ unsigned int surface_id = 0;
+ int x_ = 0, y_ = 0, w_ = 0, h_ = 0;
+ SetDisplay(type, surface_id, x_, y_, w_, h_);
+ return false;
+ }
+
+ ecore_thread_main_loop_begin();
+
+ BOOST_SCOPE_EXIT(void) { ecore_thread_main_loop_end(); }
+ BOOST_SCOPE_EXIT_END
+
+ wl_surface* surface = GetWlSurface_(ecore_wl2_window);
+ return SetDisplay_(type, surface, x, y, w, h);
+}
+// LCOV_EXCL_STOP
+
+bool Display::SetDisplaySubsurface(const DisplayType& type,
+ Ecore_Wl2_Subsurface* ecore_wl2_subsurface,
+ const int x, const int y, const int w,
+ const int h) {
+ assert(ecore_wl2_subsurface && "ecore_wl2_subsurface should not be null");
+ assert((type == DisplayType::kOverlay) && "not support yet!");
+
+ TRACKRENDERER_ENTER_P(this);
+ if (type == DisplayType::kNone) {
+ unsigned int surface_id = 0;
+ int x_ = 0, y_ = 0, w_ = 0, h_ = 0;
+ SetDisplay(type, surface_id, x_, y_, w_, h_);
+ return false;
+ }
+
+ ecore_thread_main_loop_begin();
+
+ BOOST_SCOPE_EXIT(void) { ecore_thread_main_loop_end(); }
+ BOOST_SCOPE_EXIT_END
+
+ wl_surface* surface = (wl_surface*)ecore_wl2_subsurface_native_surface_get(
+ ecore_wl2_subsurface);
+ has_parent_surface_ = true;
+ return SetDisplay_(type, surface, x, y, w, h);
+}
+
+bool Display::SetDisplay_(const DisplayType& type, wl_surface* surface,
+ const int x, const int y, const int w, const int h) {
+ if (!surface) {
+ TRACKRENDERER_ERROR_P(this, "ecore_wl_window_surface_get() failed");
+ return false;
+ }
+
+ Ecore_Wl2_Display* wl2_display = ecore_wl2_connected_display_get(NULL);
+ wl_display* display = ecore_wl2_display_get(wl2_display);
+ if (!display) {
+ TRACKRENDERER_ERROR_P(this, "ecore_wl_display_get() failed");
+ return false;
+ }
+
+ unsigned int surfaceid = internal::GetSurfaceId(surface, display);
+ if (surfaceid == kInvalidSurfaceId) {
+ TRACKRENDERER_ERROR_P(this, "Can't get surface id!");
+ return false;
+ }
+
+ // TODO(euna7.ko) what is mean??
+ // wl_window is based on app window(param display).
+ // App window already move, so do not need to move wl_window
+ // x = 0;
+ // y = 0;
+
+ TRACKRENDERER_LEAVE_P(this);
+ return SetDisplay(type, surfaceid, x, y, w, h);
+}
+
+bool Display::SetDisplay(const DisplayType& type, const uint32_t surface_id,
+ const int x, const int y, const int w, const int h) {
+ TRACKRENDERER_ENTER_P(this);
+ // TODO(euna7.ko) State should not be idle.
+ // unsigned int pre_surface_id = surface_id_; // for backup previouse info.
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ TRACKRENDERER_INFO_P(this,
+ "type: %d, surface_id: %d, x(%d) y(%d) w(%d) h(%d)",
+ static_cast<int>(type), surface_id, x, y, w, h);
+
+ if (type == DisplayType::kNone) { /* Null serface */
+ surface_id_ = 0;
+ } else if (type == DisplayType::kOverlay) {
+ surface_id_ = surface_id;
+ } else { // plusplayer don't handle of Evas case.
+ TRACKRENDERER_ERROR_P(this, "Not Support Surface type");
+ return false;
+ }
+
+ if (type_ == DisplayType::kNone ||
+ type_ == type) { // first time or same type
+ type_ = type;
+ window_.x = x;
+ window_.y = y;
+ window_.w = w;
+ window_.h = h;
+ } else {
+ // TODO(euna7.ko)
+ // changing surface case
+ // ret = mm_player_change_videosink(handle->mm_handle, mmType, set_handle);
+ // if( ret != true) {
+ // type = DisplayType::kNone;
+ // surface_id_ = pre_surface_id;
+ // return false;
+ // }
+ }
+
+ // TODO(euna7.ko) if We need to change window, how can we update it??
+ // display_->Update(pipeline_->video_sink);
+
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool Display::SetDisplayRoi(const Geometry& roi) {
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ if (mode_ != DisplayMode::kDstRoi) {
+ TRACKRENDERER_ERROR_P(this, "DisplayMode is not Roi!");
+ return false;
+ }
+ roi_ = roi;
+ return true;
+}
+
+bool Display::SetDisplayCropArea(const CropArea& area) {
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ scale_ = area;
+ return true;
+}
+
+bool Display::ResizeRenderRect(const RenderRect& rect) {
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ //x,y should be set with 0 to avoid double calculation at TDM side
+ window_.x = 0;
+ window_.y = 0;
+ window_.w = rect.w;
+ window_.h = rect.h;
+ return true;
+}
+
+void Display::GetDisplayCropArea(CropArea* area) {
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ *area = scale_;
+}
+
+bool Display::Update(GstElement* videosink) {
+ TRACKRENDERER_ENTER_P(this);
+ if (videosink == nullptr) {
+ assert(0);
+ return false;
+ }
+
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+ // - typedef unsigned long guintptr;
+ // - gst_video_overlay_set_window_handle (GstVideoOverlay * overlay
+ // , guintptr handle)
+ if (type_ == DisplayType::kOverlay && surface_id_ != 0) { // waylandsink
+ if (has_parent_surface_)
+ g_object_set(G_OBJECT(videosink), "has-parent-surface", true, NULL);
+ TRACKRENDERER_DEBUG_P(this, "serfaceid: %d, x: %d, y: %d, w: %d, h: %d",
+ surface_id_, window_.x, window_.y, window_.w,
+ window_.h);
+ gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(videosink),
+ surface_id_);
+ gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(videosink),
+ window_.x, window_.y, window_.w,
+ window_.h);
+ TRACKRENDERER_DEBUG_P(this, "mode: %d, visible: %d, rotate: %d",
+ static_cast<int>(mode_), visible_,
+ static_cast<int>(rotate_));
+ g_object_set(G_OBJECT(videosink), "seamless-resolution-change", true, NULL);
+ int mode_value = internal::ConvertDisplayModeValue(mode_);
+ int rotate_value = static_cast<int>(rotate_);
+ g_object_set(G_OBJECT(videosink), "display-geometry-method", mode_value,
+ "visible", visible_, "rotate", rotate_value, nullptr);
+ g_object_set(G_OBJECT(videosink), "display-src-x-ratio", scale_.scale_x,
+ "display-src-y-ratio", scale_.scale_y, "display-src-w-ratio",
+ scale_.scale_w, "display-src-h-ratio", scale_.scale_h,
+ nullptr);
+ g_object_set(G_OBJECT(videosink), "video-quality-mode", qualitymode_,
+ nullptr);
+
+ if (video_quality_info_caps_.get() != nullptr) {
+ g_object_set(G_OBJECT(videosink), "video-quality-info",
+ video_quality_info_caps_.get(), NULL);
+ video_quality_info_caps_.reset();
+ }
+
+ if (mode_ == DisplayMode::kDstRoi) {
+ if (roi_.w != 0 && roi_.h != 0) {
+ TRACKRENDERER_ERROR_P(this, "Roi > x[%d] y[%d] w[%d] h[%d]", roi_.x,
+ roi_.y, roi_.w, roi_.h);
+ g_object_set(G_OBJECT(videosink), "dst-roi-x", roi_.x, "dst-roi-y",
+ roi_.y, "dst-roi-w", roi_.w, "dst-roi-h", roi_.h, nullptr);
+ }
+ }
+ }
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool Display::UpdateVisible(GstElement* videosink) {
+ TRACKRENDERER_ENTER_P(this);
+ if (videosink == nullptr) {
+ assert(0);
+ return false;
+ }
+
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+
+ if (type_ == DisplayType::kOverlay && surface_id_ != 0) { // waylandsink
+ TRACKRENDERER_DEBUG_P(this, "visible: %d", visible_);
+ g_object_set(G_OBJECT(videosink), "visible", visible_, nullptr);
+ }
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+void Display::SetDisplayQualityInfo(const GstCaps* video_quality_info_caps) {
+ video_quality_info_caps_ =
+ gstguard::make_guard(gst_caps_copy(video_quality_info_caps));
+}
+
+bool Display::UpdateCropArea(GstElement* videosink) {
+ TRACKRENDERER_ENTER_P(this);
+ if (videosink == nullptr) {
+ assert(0);
+ return false;
+ }
+
+ std::lock_guard<std::mutex> lock(settings_mutex_);
+
+ if (type_ == DisplayType::kOverlay && surface_id_ != 0) { // waylandsink
+ g_object_set(G_OBJECT(videosink), "display-src-x-ratio", scale_.scale_x,
+ "display-src-y-ratio", scale_.scale_y, "display-src-w-ratio",
+ scale_.scale_w, "display-src-h-ratio", scale_.scale_h,
+ nullptr);
+ }
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+void Display::SetDisplayMode(const DisplayMode& mode) { mode_ = mode; }
+
+void Display::SetDisplayRotate(const DisplayRotation& rotate) {
+ rotate_ = rotate;
+}
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/error.h"
+
+#include "trackrenderer/core/gst_utils.h"
+#include "trackrenderer/core/gstobject_guard.h"
+#include "trackrenderer/core/utils/log.h"
+// LCOV_EXCL_START
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace internal {
+
+ErrorType HandleOmxError(const GstMessage* message, const GError* error,
+ const bool is_music_content) {
+ if (!g_strrstr(gst_util::GetElementName(message), "omx"))
+ return ErrorType::kUnknown;
+
+ if (error->code == GST_STREAM_ERROR_FAILED) {
+ if (g_strrstr(gst_util::GetKlass(message), "Audio")) {
+ if (is_music_content)
+ return ErrorType::kNotSupportedFormat;
+ else
+ return ErrorType::kNotSupportedAudioCodec;
+ }
+ }
+ return ErrorType::kUnknown;
+}
+
+ErrorType ConvertGstCoreError(const int error_code) {
+ ErrorType type = ErrorType::kNone;
+ switch (error_code) {
+ case GST_CORE_ERROR_MISSING_PLUGIN:
+ return ErrorType::kNotSupportedFormat;
+ case GST_CORE_ERROR_STATE_CHANGE:
+ case GST_CORE_ERROR_SEEK:
+ case GST_CORE_ERROR_NOT_IMPLEMENTED:
+ case GST_CORE_ERROR_FAILED:
+ case GST_CORE_ERROR_TOO_LAZY:
+ case GST_CORE_ERROR_PAD:
+ case GST_CORE_ERROR_THREAD:
+ case GST_CORE_ERROR_NEGOTIATION:
+ case GST_CORE_ERROR_EVENT:
+ case GST_CORE_ERROR_CAPS:
+ case GST_CORE_ERROR_TAG:
+ case GST_CORE_ERROR_CLOCK:
+ case GST_CORE_ERROR_DISABLED:
+ default:
+ type = ErrorType::kInvalidOperation;
+ break;
+ }
+ return type;
+}
+
+ErrorType ConvertGstLibraryError(const int error_code) {
+ ErrorType type = ErrorType::kNone;
+ switch (error_code) {
+ case GST_LIBRARY_ERROR_FAILED:
+ case GST_LIBRARY_ERROR_TOO_LAZY:
+ case GST_LIBRARY_ERROR_INIT:
+ case GST_LIBRARY_ERROR_SHUTDOWN:
+ case GST_LIBRARY_ERROR_SETTINGS:
+ case GST_LIBRARY_ERROR_ENCODE:
+ default:
+ type = ErrorType::kInvalidOperation;
+ break;
+ }
+
+ return type;
+}
+
+ErrorType ConvertGstResourceError(const int error_code) {
+ ErrorType type = ErrorType::kNone;
+ switch (error_code) {
+ case GST_RESOURCE_ERROR_NO_SPACE_LEFT:
+ type = ErrorType::kFileNoSpaceOnDevice;
+ break;
+ case GST_RESOURCE_ERROR_NOT_FOUND:
+ case GST_RESOURCE_ERROR_OPEN_READ:
+ case GST_RESOURCE_ERROR_BUSY:
+ type = ErrorType::kConnectionFailed;
+ break;
+ case GST_RESOURCE_ERROR_READ:
+ type = ErrorType::kInvalidOperation;
+ break;
+ case GST_RESOURCE_ERROR_WRITE:
+ case GST_RESOURCE_ERROR_FAILED:
+ case GST_RESOURCE_ERROR_SEEK:
+ case GST_RESOURCE_ERROR_TOO_LAZY:
+ case GST_RESOURCE_ERROR_OPEN_WRITE:
+ case GST_RESOURCE_ERROR_OPEN_READ_WRITE:
+ case GST_RESOURCE_ERROR_CLOSE:
+ case GST_RESOURCE_ERROR_SYNC:
+ case GST_RESOURCE_ERROR_SETTINGS:
+ default:
+ type = ErrorType::kInvalidOperation;
+ break;
+ }
+ return type;
+}
+
+ErrorType ConvertGstStreamError(const GError* error) {
+ ErrorType type = ErrorType::kNone;
+ if (!error) return ErrorType::kInvalidParameter;
+
+ switch (error->code) {
+ case GST_STREAM_ERROR_DECODE:
+ return ErrorType::kInvalidOperation;
+ case GST_STREAM_ERROR_CODEC_NOT_FOUND:
+ case GST_STREAM_ERROR_TYPE_NOT_FOUND:
+ case GST_STREAM_ERROR_WRONG_TYPE:
+ return ErrorType::kNotSupportedFile;
+ case GST_STREAM_ERROR_FAILED:
+ return ErrorType::kNotSupportedFormat;
+ case GST_STREAM_ERROR_DECRYPT:
+ return ErrorType::kDrmInfo;
+ case GST_STREAM_ERROR_DECRYPT_NOKEY: {
+ TRACKRENDERER_INFO("decryption error, reason : [%s]\n", error->message);
+ if (strstr(error->message, "rights expired")) {
+ return ErrorType::kDrmExpired;
+ } else if (strstr(error->message, "no rights")) {
+ return ErrorType::kDrmNoLicense;
+ } else if (strstr(error->message, "has future rights")) {
+ return ErrorType::kDrmFutureUse;
+ } else if (strstr(error->message, "opl violation")) {
+ return ErrorType::kDrmNotPermitted;
+ }
+ return ErrorType::kDrmInfo;
+ }
+ case GST_STREAM_ERROR_NOT_IMPLEMENTED:
+ case GST_STREAM_ERROR_TOO_LAZY:
+ case GST_STREAM_ERROR_ENCODE:
+ case GST_STREAM_ERROR_DEMUX:
+ case GST_STREAM_ERROR_MUX:
+ case GST_STREAM_ERROR_FORMAT:
+ default:
+ type = ErrorType::kInvalidOperation;
+ break;
+ }
+ return type;
+}
+
+ErrorType HandleGstError(const GError* error) {
+ ErrorType ret = ErrorType::kNone;
+
+ if (error == nullptr) return ret;
+
+ if (error->domain == GST_CORE_ERROR) {
+ ret = internal::ConvertGstCoreError(error->code);
+ } else if (error->domain == GST_LIBRARY_ERROR) {
+ ret = internal::ConvertGstLibraryError(error->code);
+ } else if (error->domain == GST_RESOURCE_ERROR) {
+ ret = internal::ConvertGstResourceError(error->code);
+ } else if (error->domain == GST_STREAM_ERROR) {
+ ret = internal::ConvertGstStreamError(error);
+ } else {
+ TRACKRENDERER_INFO("This error domain is not defined.\n");
+ // we treat system error as an internal error
+ ret = ErrorType::kInvalidOperation;
+ }
+ return ret;
+}
+
+} // namespace internal
+
+ErrorType HandleError(GstMessage* message, const bool is_music_content) {
+ if (message == nullptr) return ErrorType::kNone;
+
+ GError* p_error = nullptr;
+ gchar* p_debug = nullptr;
+ gst_message_parse_error(message, &p_error, &p_debug);
+ auto error = gstguard::make_guard(p_error);
+ auto debug = gstguard::make_guard(p_debug);
+ if (error == nullptr) return ErrorType::kNone;
+
+ TRACKRENDERER_DEBUG("ERROR is posting. from %s / %s \n>> %s",
+ gst_util::GetKlass(message),
+ gst_util::GetElementName(message), error->message);
+
+ ErrorType ret =
+ internal::HandleOmxError(message, error.get(), is_music_content);
+ if (ret != ErrorType::kUnknown) return ret;
+
+ return internal::HandleGstError(error.get());
+}
+
+void HandleErrorMsg(GstMessage* message, gchar** error_msg) {
+ if (message == nullptr) return;
+
+ GError* p_error = nullptr;
+ gst_message_parse_error(message, &p_error, error_msg);
+ auto error = gstguard::make_guard(p_error);
+ if (error == nullptr || *error_msg == nullptr) return;
+
+ TRACKRENDERER_DEBUG("ERROR is posting. from %s / %s \n>> %s \n>> %s",
+ gst_util::GetKlass(message),
+ gst_util::GetElementName(message), error->message, *error_msg);
+ return;
+}
+
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+// LCOV_EXCL_STOP
+
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include "trackrenderer/core/gst_utils.h"
+
+#include <cassert>
+#include <thread>
+
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace gst_util {
+
+void ShowStateChangedMsg(GstMessage* msg, void* id) {
+ GstState old_state = GST_STATE_VOID_PENDING;
+ GstState new_state = GST_STATE_VOID_PENDING;
+ GstState pending_state = GST_STATE_VOID_PENDING;
+ gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state);
+ TRACKRENDERER_ERROR_P(id,
+ "thread[%p] msg[%p], old[%8s], new[%8s], pending[%8s] src[%s]",
+ g_thread_self(), msg, gst_element_state_get_name(old_state),
+ gst_element_state_get_name(new_state),
+ gst_element_state_get_name(pending_state), GST_MESSAGE_SRC_NAME(msg));
+}
+
+void SetGstStateToNull(GstElement* pipeline, void* id) {
+ if (!pipeline) return;
+ GstStateChangeReturn ret;
+ ret = gst_element_set_state(pipeline, GST_STATE_NULL);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ TRACKRENDERER_ERROR_P(id, "Set State to NULL failed");
+ }
+}
+
+// LCOV_EXCL_START
+const gchar* GetElementName(const GstMessage* msg) {
+ GstElement* src_element = GST_ELEMENT_CAST(msg->src);
+ if (!src_element) return nullptr;
+
+ return GST_ELEMENT_NAME(src_element);
+}
+
+const gchar* GetKlass(const GstMessage* msg) {
+ GstElement* src_element = GST_ELEMENT_CAST(msg->src);
+ if (!src_element) return nullptr;
+
+ GstElementFactory* factory = gst_element_get_factory(src_element);
+ if (!factory) return nullptr;
+
+ return gst_element_factory_get_klass(factory);
+}
+
+void GstInit() { gst_init(NULL, NULL); }
+
+void GstInit(const Json::Value& root) {
+ int argc = 1;
+ char* argv[6]{
+ nullptr,
+ };
+ std::string gstparam1 = root.get("gstparam1", "").asString();
+ argv[argc++] = const_cast<char*>(gstparam1.c_str());
+ std::string gstparam2 = root.get("gstparam2", "").asString();
+ argv[argc++] = const_cast<char*>(gstparam2.c_str());
+ std::string gstparam3 = root.get("gstparam3", "").asString();
+ argv[argc++] = const_cast<char*>(gstparam3.c_str());
+ std::string gstparam4 = root.get("gstparam4", "").asString();
+ argv[argc++] = const_cast<char*>(gstparam4.c_str());
+ std::string gstparam5 = root.get("gstparam5", "").asString();
+ argv[argc++] = const_cast<char*>(gstparam5.c_str());
+
+ for (int i = 1; i < argc; ++i) {
+ TRACKRENDERER_INFO(" %s", argv[i]);
+ }
+ char** pargv = argv;
+ gst_init(&argc, &pargv);
+}
+// LCOV_EXCL_STOP
+} // namespace gst_util
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/core/gstcaps_builder.h"
+
+#include <string>
+#include <algorithm>
+
+#include "trackrenderer/core/caps_recipes.h"
+#include "trackrenderer/core/pipeline.hpp"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace internal {
+
+constexpr guint kMaxAudioChannels = 64;
+
+static GstBuffer* MakeGstBufferWithCodecData(const std::shared_ptr<char>& data,
+ int len) {
+ constexpr int kMaxLen = 1024 * 1024;
+ if (len <= 0 || len > kMaxLen) {
+ TRACKRENDERER_WARN("There are not any codec_data length [%d]", len);
+ return nullptr;
+ }
+ GstBuffer* codec_data = gst_buffer_new_and_alloc(len);
+ GstMapInfo codec_data_info;
+ if (!gst_buffer_map(codec_data, &codec_data_info, GST_MAP_WRITE)) {
+ TRACKRENDERER_WARN("gst_buffer_map fail");
+ gst_buffer_unref(codec_data);
+ return nullptr;
+ }
+ for (int i = 0; i < len; i++) {
+ codec_data_info.data[i] = (data.get())[i];
+ }
+ TRACKRENDERER_DEBUG("codec extra data size : %d", len);
+ gst_buffer_unmap(codec_data, &codec_data_info);
+ return codec_data;
+}
+
+static const std::string GetMediaType(const Track& track) {
+ std::string media_type =
+ track.streamtype.empty() ? track.mimetype : track.streamtype;
+ if (media_type.find("_tz") != std::string::npos) {
+ media_type = media_type.substr(0, media_type.size() - 3);
+ }
+ return media_type;
+}
+
+static const std::string GetCapsName(const std::string media_type,
+ bool is_drm) {
+ if (is_drm) {
+ return "drm/eme";
+ } else {
+ return media_type;
+ }
+}
+
+static void FillStreamType(const GstCapsWrapper& caps, const Track& track) {
+ if (track.streamtype.empty()) return;
+ caps.SetValue("stream-type", G_TYPE_STRING, track.streamtype.c_str());
+}
+
+static void FillCodecData(const GstCapsWrapper& caps, const Track& track) {
+ GstBuffer* codec_data =
+ MakeGstBufferWithCodecData(track.codec_data, track.codec_data_len);
+ if (codec_data == nullptr) return;
+ caps.SetValue("codec_data", GST_TYPE_BUFFER, codec_data);
+}
+
+static void FillSecureField(const GstCapsWrapper& caps, const Track& track) {
+ if (track.mimetype.find("_tz") == std::string::npos) return;
+ caps.SetValue("secure", G_TYPE_BOOLEAN, TRUE);
+}
+
+static void FillChannelMaskField(const GstCapsWrapper& caps,
+ const Track& track) {
+ GstAudioChannelPosition position[kMaxAudioChannels];
+ guint64 channel_mask = 0;
+ guint nchannels = std::min(static_cast<guint>(track.channels),
+ kMaxAudioChannels);
+ if (nchannels <= 2) return;
+ if (nchannels == 4) {
+ position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ position[2] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
+ position[3] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
+ } else if (nchannels == 6) {
+ position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
+ position[3] = GST_AUDIO_CHANNEL_POSITION_LFE1;
+ position[4] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
+ position[5] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
+ } else if (nchannels == 8) {
+ position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
+ position[3] = GST_AUDIO_CHANNEL_POSITION_LFE1;
+ position[4] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
+ position[5] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
+ position[6] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
+ position[7] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
+ } else {
+ guint i;
+ for (i = 0; i < nchannels; i++) {
+ position[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
+ }
+ }
+ guint j;
+ for (j = 0; j < nchannels; j++) {
+ channel_mask |= (G_GUINT64_CONSTANT(1) << position[j]);
+ }
+ caps.SetValue("channel-mask", GST_TYPE_BITMASK, channel_mask);
+}
+} // namespace internal
+
+GstCapsWrapper::GstCapsWrapper(const std::string& caps_name) {
+ caps_ = gst_caps_new_empty_simple(caps_name.c_str());
+}
+
+GstCapsWrapper::GstCapsWrapper(GstCapsWrapper&& caps_wrapper) {
+ caps_ = caps_wrapper.caps_;
+ caps_wrapper.caps_ = nullptr;
+}
+
+GstCapsWrapper::~GstCapsWrapper() {
+ if (caps_ == nullptr) return;
+ gst_caps_unref(caps_);
+ caps_ = nullptr;
+}
+
+void GstCapsWrapper::PrintCapsString() {
+ if (caps_ == nullptr) return;
+ auto caps_str = gstguard::make_guard(gst_caps_to_string(caps_));
+ TRACKRENDERER_DEBUG("caps : %s", caps_str.get());
+}
+
+GstCapsBuilder::GstCapsBuilder(const Recipes& recipes) : kRecipes_(recipes) {}
+
+GstCapsWrapper GstCapsBuilder::Build(const Track& track, bool is_drm) {
+ const std::string media_type = internal::GetMediaType(track);
+ const std::string caps_name = internal::GetCapsName(track.mimetype, is_drm);
+
+ TRACKRENDERER_DEBUG("media_type[%s] caps_name[%s]", media_type.c_str(),
+ caps_name.c_str());
+
+ GstCapsWrapper caps(caps_name);
+
+ auto filler = FindCapsFiller_(track, media_type);
+ if (filler != nullptr) filler(caps, track);
+
+ FillDefaultValue_(caps, track);
+ internal::FillStreamType(caps, track);
+ internal::FillCodecData(caps, track);
+ internal::FillSecureField(caps, track);
+ internal::FillChannelMaskField(caps, track);
+
+ caps.PrintCapsString();
+
+ return caps;
+}
+
+GstCapsBuilder::Recipe::CapsFiller GstCapsBuilder::FindCapsFiller_(
+ const Track& track, const std::string& media_type) {
+ if (kRecipes_.count(media_type) == 0) return nullptr;
+ const auto& recipes = kRecipes_.at(media_type);
+ for (auto& recipe : recipes) {
+ if (recipe.type_checker(track) == false) continue;
+ return recipe.caps_filler;
+ }
+ return nullptr;
+}
+
+void GstCapsBuilder::FillDefaultValue_(const GstCapsWrapper& caps,
+ const Track& track) {
+ if (kRecipes_.count(kDefaultRecipe)) {
+ const auto recipes = kRecipes_.at(kDefaultRecipe);
+ for (auto& recipe : recipes) {
+ recipe.caps_filler(caps, track);
+ }
+ }
+}
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include "glib.h"
+
+#include "trackrenderer/core/gstobject_guard.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace gstguard {
+
+void CustomDeleter(GstCaps* obj) { gst_caps_unref(obj); }
+
+void CustomDeleter(GstObject* obj) { gst_object_unref(obj); }
+
+void CustomDeleter(GstPad* obj) { gst_object_unref(obj); }
+
+void CustomDeleter(GstBuffer* obj) { gst_buffer_unref(obj); }
+
+void CustomDeleter(GstElement* obj) { gst_object_unref(obj); }
+
+void CustomDeleter(GstQuery* obj) { gst_query_unref(obj); }
+
+void CustomDeleter(GstEvent* obj) { gst_event_unref(obj); }
+
+void CustomDeleter(GstMessage* obj) { gst_message_unref(obj); }
+
+void CustomDeleter(GstPluginFeature* obj) { gst_object_unref(obj); }
+
+void CustomDeleter(GstElementFactory* obj) { gst_object_unref(obj); }
+
+void CustomDeleter(GstBus* obj) { gst_object_unref(GST_OBJECT(obj)); }
+
+void CustomDeleter(gchar* obj) { g_free(obj); }
+
+void CustomDeleter(GError* obj) { g_error_free(obj); }
+
+void CustomDeleter(GstStructure* obj) { gst_structure_free(obj); }
+
+void CustomDeleter(GValue* obj) { g_value_unset(obj); }
+
+void CustomDeleter(GstIterator* obj) { gst_iterator_free(obj); }
+
+void CustomDeleter(GBytes* obj) { g_bytes_unref(obj); }
+
+} // namespace gstguard
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/core/gstsignal_holder.h"
+
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class GstSignalHolder::GstSignalItem {
+ public:
+ GstSignalItem(GObject* obj, const char* signal_name, GCallback handler,
+ gpointer data)
+ : obj_(obj) {
+ gst_object_ref(obj_);
+ sig_ = g_signal_connect(obj_, signal_name, handler, data);
+ if (sig_ == 0)
+ TRACKRENDERER_INFO("g_signal_connect return error. object[ %s ]",
+ GST_OBJECT_NAME(obj_));
+ }
+ ~GstSignalItem() {
+ if (g_signal_handler_is_connected(obj_, sig_))
+ g_signal_handler_disconnect(obj_, sig_);
+ TRACKRENDERER_INFO("Disconnect signal [%lu]", sig_);
+ gst_object_unref(obj_);
+ }
+
+ private:
+ GstSignalItem() = default;
+ GObject* obj_ = nullptr;
+ gulong sig_ = 0;
+};
+
+namespace internal {
+
+void DisconnectSignal(const GValue* item, gpointer user_data);
+
+} // namespace internal
+
+GstSignalHolder::GstSignalHolder() {}
+GstSignalHolder::~GstSignalHolder() {
+ std::lock_guard<std::mutex> guard(item_lock_);
+ signal_list_.clear();
+}
+
+void GstSignalHolder::Add(GObject* obj, const char* signal_name,
+ GCallback handler, gpointer data) {
+ std::lock_guard<std::mutex> guard(item_lock_);
+ std::unique_ptr<GstSignalItem> item(
+ new GstSignalItem(obj, signal_name, handler, data));
+ signal_list_.insert(std::pair<GObject*, std::unique_ptr<GstSignalItem>>(
+ obj, std::move(item)));
+}
+
+void GstSignalHolder::Delete(GObject* obj) {
+ if (!obj || !GST_IS_ELEMENT(obj)) {
+ TRACKRENDERER_ERROR("object null or object is not gst element");
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(item_lock_);
+ if (GST_IS_BIN(obj)) {
+ GstIterator* it = gst_bin_iterate_elements(GST_BIN_CAST(obj));
+ gst_iterator_foreach(it,
+ (GstIteratorForeachFunction)internal::DisconnectSignal,
+ (gpointer) this);
+ gst_iterator_free(it);
+ }
+ signal_list_.erase(obj);
+}
+
+void GstSignalHolder::DeleteAll() {
+ std::lock_guard<std::mutex> guard(item_lock_);
+ TRACKRENDERER_INFO("num of signals[ %d ]", signal_list_.size());
+ signal_list_.clear();
+}
+
+namespace internal {
+
+void DisconnectSignal(const GValue* item, gpointer user_data) {
+ GstSignalHolder* pthis = static_cast<GstSignalHolder*>(user_data);
+ GstElement* element = GST_ELEMENT_CAST(g_value_get_object(item));
+ pthis->Delete(G_OBJECT(element));
+}
+
+} // namespace internal
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_AUDIOEASING_CONTROLLER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_AUDIOEASING_CONTROLLER_H__
+
+#include <gst/gstelement.h>
+
+#include <atomic>
+#include <boost/core/noncopyable.hpp>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include "trackrenderer/core/audioeasinginfo.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class AudioEasingController : private boost::noncopyable {
+ public:
+ explicit AudioEasingController(uint32_t init_volume,
+ uint32_t init_elapsed_time,
+ const AudioEasingInfo& init_info);
+ ~AudioEasingController();
+ bool AudioEasingStart(GstElement* audiosink);
+ void AudioEasingStop();
+ bool SetInfo(const AudioEasingInfo& info);
+ bool GetInfo(uint32_t* current_volume, uint32_t* elapsed_time,
+ AudioEasingInfo* info);
+ int GetGainFromVolume(uint32_t volume);
+
+ private:
+ bool SetAudioGain_(GstElement* audiosink, uint32_t volume);
+ float CalculateVolume_(float t, float b, float delta, float d,
+ AudioEasingType type);
+ float AdjustLinearTypeVolumeDelta_(float delta, float start, int target);
+ void InterruptEasingThread_();
+ void SetVolumeWithFade_(GstElement* audiosink, bool need_lock_delay);
+ void SetEasingStartProperty_(GstElement* audiosink,
+ const AudioEasingInfo& setup_info);
+ void SetEasingStopProperty_(GstElement* audiosink);
+ void SetEasingInfoProperty_(GstElement* audiosink,
+ const AudioEasingInfo& info);
+ bool NeedToWaitLockDelay_();
+
+ private:
+ bool is_ease_interrupt_ = false;
+ bool need_reset_ = false;
+ // temporary fixed value. lock delay have dependency with chipset.
+ uint32_t lock_delay_ms_ = 150000;
+ std::atomic<uint32_t> initial_volume_;
+ std::atomic<uint32_t> volume_;
+ std::atomic<uint32_t> elapsed_time_ms_;
+ std::atomic<bool> is_stopped_;
+ AudioEasingInfo easing_info_ = {100, 0, AudioEasingType::kAudioEasingNone};
+ AudioEasingInfo pending_info_ = {100, 0, AudioEasingType::kAudioEasingNone};
+ std::thread easing_task_;
+
+ std::mutex pending_info_m_;
+ std::mutex current_info_m_;
+ std::mutex easing_m_;
+ std::mutex state_m_;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_AUDIOEASING_CONTROLLER_H__
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_DEFAULT_POLICY_HPP__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_DEFAULT_POLICY_HPP__
+
+#include "trackrenderer/audio_controller/resyncaudio_policy.h"
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+namespace trackrenderer {
+namespace resync_audio {
+class DefaultPolicy : public ResyncAudioPolicy {
+ public:
+ DefaultPolicy() = default;
+ virtual ~DefaultPolicy() = default;
+
+ public:
+ virtual bool Resync(PipelinePtr& pipeline) override {
+ TRACKRENDERER_ENTER
+ return pipeline->SetProperty(Elements::kSinkAudio, "audio-swap", TRUE);
+ }
+};
+} // namespace resync_audio
+} // namespace trackrenderer
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_DEFAULT_POLICY_HPP__
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_DUMMY_POLICY_HPP__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_DUMMY_POLICY_HPP__
+
+#include "trackrenderer/audio_controller/resyncaudio_policy.h"
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+namespace trackrenderer {
+namespace resync_audio {
+class DummyPolicy : public ResyncAudioPolicy {
+ public:
+ DummyPolicy() = default;
+ virtual ~DummyPolicy() = default;
+
+ public:
+ virtual bool Resync(PipelinePtr& pipeline) override {
+ TRACKRENDERER_ENTER
+ return true;
+ }
+};
+} // namespace resync_audio
+} // namespace trackrenderer
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_DUMMY_POLICY_HPP__
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_POLICIES_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_POLICIES_H__
+
+#include "trackrenderer/audio_controller/resyncaudio/default_policy.hpp"
+#include "trackrenderer/audio_controller/resyncaudio/dummy_policy.hpp"
+#include "trackrenderer/audio_controller/resyncaudio/swdecoder_policy.hpp"
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_POLICIES_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+// LCOV_EXCL_START
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_SWDECODER_POLICY_HPP__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_SWDECODER_POLICY_HPP__
+
+#include <boost/scope_exit.hpp>
+#include <mutex>
+#include <string>
+
+#include "trackrenderer/audio_controller/resyncaudio_policy.h"
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+namespace trackrenderer {
+namespace resync_audio {
+class SwDecoderPolicy : public ResyncAudioPolicy {
+ public:
+ explicit SwDecoderPolicy(const std::uint64_t& rstarttime,
+ const double& playback_rate)
+ : rendering_start_time_(rstarttime), playback_rate_(playback_rate) {}
+ virtual ~SwDecoderPolicy() = default;
+
+ public:
+ virtual bool Resync(PipelinePtr& pipeline) override {
+ TRACKRENDERER_ENTER
+
+ Pipeline<Elements>::Pad padprobe;
+ pipeline->PeerPadAddProbe(
+ Elements::kSinkAudio, kPadBlockProbeId_.c_str(), "sink", kPadProbeType_,
+ GstSrcPadProbeIdleAndBlockCb_, &padprobe, nullptr);
+
+ BOOST_SCOPE_EXIT(&pipeline, &kPadBlockProbeId_) {
+ if (pipeline->PadRemoveProbe(kPadBlockProbeId_.c_str()) == false) {
+ TRACKRENDERER_WARN("[WARNING] PadRemoveProbe failed");
+ }
+ }
+ BOOST_SCOPE_EXIT_END
+
+ TRACKRENDERER_INFO("Wait for Pad Idle!");
+ {
+ std::unique_lock<std::mutex> lk(padprobe.m);
+ padprobe.cv.wait(lk, [&padprobe]() { return padprobe.is_idle; });
+ }
+ TRACKRENDERER_INFO("Wait for Pad Idle! [done]");
+
+ bool ret = true;
+ ret &= pipeline->SetProperty(Elements::kSinkAudio, "audio-swap", TRUE);
+ ret &= pipeline->DirectPadFlushStartDownStream(Elements::kSinkAudio);
+ ret &= pipeline->DirectPadFlushStopDownStream(Elements::kSinkAudio, false);
+ ret &= pipeline->DirectSendSegmentEvent(
+ Elements::kSinkAudio, rendering_start_time_, playback_rate_);
+
+ TRACKRENDERER_LEAVE
+ return ret;
+ }
+
+ private:
+ static GstPadProbeReturn GstSrcPadProbeIdleAndBlockCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata) {
+ if (info->type & GST_PAD_PROBE_TYPE_BLOCK) {
+ TRACKRENDERER_INFO("Pad Blocked!");
+ }
+ if (info->type & GST_PAD_PROBE_TYPE_IDLE) {
+ TRACKRENDERER_INFO("Pad Idle!");
+ Pipeline<Elements>::Pad* padprobe =
+ static_cast<Pipeline<Elements>::Pad*>(userdata);
+ std::unique_lock<std::mutex> lk(padprobe->m);
+ padprobe->is_idle = true;
+ TRACKRENDERER_INFO("Pad Idle notify_all()!");
+ padprobe->cv.notify_all();
+ }
+ return GST_PAD_PROBE_OK;
+ }
+
+ private:
+ const std::string kPadBlockProbeId_ =
+ "RESYNC_AUDIO_SINK_PAD_IDLE_AND_BLOCK_PROBE";
+ const GstPadProbeType kPadProbeType_ = static_cast<GstPadProbeType>(
+ GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM | GST_PAD_PROBE_TYPE_IDLE);
+
+ const std::uint64_t& rendering_start_time_;
+ const double& playback_rate_;
+};
+} // namespace resync_audio
+} // namespace trackrenderer
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_AUDIO_CONTROLLER_RESYNC_AUDIO_SWDECODER_POLICY_HPP__
+// LCOV_EXCL_STOP
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_RESYNC_AUDIO_POLICY_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_RESYNC_AUDIO_POLICY_H__
+
+#include <memory>
+
+#include "trackrenderer/core/elements.h"
+#include "trackrenderer/core/pipeline.hpp"
+
+namespace plusplayer {
+namespace trackrenderer {
+struct ResyncAudioPolicy {
+ using PipelinePtr = std::unique_ptr<Pipeline<Elements>>;
+ virtual ~ResyncAudioPolicy() = default;
+ virtual bool Resync(PipelinePtr& pipeline) = 0;
+};
+
+using ResyncAudioPolicyPtr = std::unique_ptr<ResyncAudioPolicy>;
+} // namespace trackrenderer
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_RESYNC_AUDIO_POLICY_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_APPINFO_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_APPINFO_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+/**
+* @brief Player app information.
+*/
+struct PlayerAppInfo {
+ std::string id; /**< App id */
+ std::string version; /**< App version */
+ std::string type; /**< App type. ex)"MSE", "HTML5", etc.. */
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_APPINFO_H__
\ No newline at end of file
--- /dev/null
+//\r
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>\r
+//\r
+\r
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_ATTRIBUTE_HPP__\r
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_ATTRIBUTE_HPP__\r
+\r
+#include <boost/any.hpp>\r
+#include <boost/core/noncopyable.hpp>\r
+#include <memory>\r
+#include <typeinfo>\r
+\r
+#include "trackrenderer/core/pipeline.hpp"\r
+\r
+namespace plusplayer {\r
+\r
+namespace trackrenderer {\r
+\r
+template <typename ElementType>\r
+class AttributeSetter;\r
+\r
+template <typename ElementType>\r
+using AttributeChangedHandler = std::function<bool(\r
+ AttributeSetter<ElementType>&, const boost::any&, const boost::any&)>;\r
+\r
+template <typename ElementType>\r
+class AttributeGetter;\r
+\r
+template <typename ElementType>\r
+using AttributeGottenHandler =\r
+ std::function<bool(AttributeGetter<ElementType>&, boost::any*)>;\r
+\r
+template <typename ElementType>\r
+struct AttributeBinder {\r
+ const ElementType element;\r
+ const std::type_info& value_type;\r
+ const std::string property;\r
+ const bool need_to_init;\r
+ const boost::any default_value;\r
+ AttributeChangedHandler<ElementType> on_attribute_set;\r
+ AttributeGottenHandler<ElementType> on_attribute_get;\r
+};\r
+\r
+template <typename ElementType>\r
+class AttributeSetter : private boost::noncopyable {\r
+ public:\r
+ explicit AttributeSetter(std::unique_ptr<Pipeline<ElementType>>& pipeline,\r
+ const AttributeBinder<ElementType>& binder)\r
+ : pipeline_(pipeline), binder_(binder) {}\r
+\r
+ template <typename ValueType>\r
+ void Set(ValueType value) {\r
+ if (pipeline_ == nullptr) return;\r
+ pipeline_->SetProperty(binder_.element, binder_.property.c_str(), value);\r
+ }\r
+\r
+ private:\r
+ std::unique_ptr<Pipeline<ElementType>>& pipeline_;\r
+ const AttributeBinder<ElementType>& binder_;\r
+};\r
+\r
+template <typename ElementType>\r
+class AttributeGetter : private boost::noncopyable {\r
+ public:\r
+ explicit AttributeGetter(std::unique_ptr<Pipeline<ElementType>>& pipeline,\r
+ const AttributeBinder<ElementType>& binder)\r
+ : pipeline_(pipeline), binder_(binder) {}\r
+\r
+ template <typename ValueType>\r
+ bool Get(boost::any* get_value) {\r
+ ValueType value;\r
+ if (pipeline_ == nullptr) return false;\r
+ pipeline_->GetProperty(binder_.element, binder_.property.c_str(), &value);\r
+ *get_value = value;\r
+ return true;\r
+ }\r
+\r
+ private:\r
+ std::unique_ptr<Pipeline<ElementType>>& pipeline_;\r
+ const AttributeBinder<ElementType>& binder_;\r
+};\r
+\r
+} // namespace trackrenderer\r
+\r
+} // namespace plusplayer\r
+\r
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_ATTRIBUTE_HPP__\r
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_AUDIOEASINGINFO_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_AUDIOEASINGINFO_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+enum class AudioEasingType {
+ kAudioEasingLinear = 0,
+ kAudioEasingIncubic,
+ kAudioEasingOutcubic,
+ kAudioEasingNone
+};
+
+/**
+ * @brief audio easing information struct
+ */
+struct AudioEasingInfo {
+ uint32_t target_volume; /**< Audio easing target volume */
+ uint32_t duration; /**< Audio easing duration, in millisecond */
+ AudioEasingType type; /**< Audio easing type */
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_AUDIOEASINGINFO_H__
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_BUFFER_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_BUFFER_H__
+
+#include <cstdint>
+
+#include "tbm_surface.h"
+#include "tbm_surface_internal.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+/**
+ * @brief Enumerations for the buffer status
+ */
+enum class BufferStatus {
+ kUnderrun, // buffer underrun
+ kOverrun // buffer everrun
+};
+
+enum class DecodedVideoFrameBufferType { kNone, kCopy, kReference, kRaw, kScale };
+
+struct DecodedVideoPacket {
+ uint64_t pts = 0;
+ uint64_t duration = 0;
+ void* surface_data = nullptr; // tbm_surface
+ void* scaler_index = nullptr;
+};
+
+enum class DecodedVideoRawModePacketType { kPhysicalAddress, kTizenBuffer };
+
+struct DecodedVideoRawModePacket {
+ DecodedVideoRawModePacketType type =
+ DecodedVideoRawModePacketType::kPhysicalAddress;
+ uint64_t pts = 0;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ union {
+ struct {
+ int y_phyaddr = 0;
+ int y_viraddr = 0;
+ int y_linesize = 0;
+ int uv_phyaddr = 0;
+ int uv_viraddr = 0;
+ int uv_linesize = 0;
+ } raw;
+ struct {
+ tbm_key key;
+ } tbm;
+ } data = {.tbm = {0}};
+};
+
+static constexpr int kHandoffFrameBufferNum = 2;
+static constexpr int kScalerY = 0;
+static constexpr int kScalerU = 1;
+
+class TbmBufferManager {
+ public:
+ explicit TbmBufferManager() { bufmgr = tbm_bufmgr_init(-1); };
+ ~TbmBufferManager() {
+ tbm_bufmgr_deinit(bufmgr);
+ for (int i = 0; i < kHandoffFrameBufferNum; i++) {
+ if (tbm_surface[i]) {
+ tbm_surface_destroy(tbm_surface[i]);
+ tbm_surface[i] = nullptr;
+ }
+ }
+ };
+
+ bool TbmBufferAllocate(int width, int height) {
+ for (int i = 0; i < kHandoffFrameBufferNum; i++) {
+ tbm_surface[i] = tbm_surface_create(width, height, TBM_FORMAT_NV12);
+ if (!tbm_surface[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public:
+ tbm_bufmgr bufmgr = nullptr;
+ tbm_surface_h tbm_surface[kHandoffFrameBufferNum] = {nullptr};
+ int scaler_index = 0;
+}; // class TbmBufferManager
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_BUFFER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_CAPS_RECIPES_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_CAPS_RECIPES_H__
+
+#include <string>
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+static const std::string kDefaultRecipe {"*"};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_CAPS_RECIPES_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_DECODERINPUTBUFFER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_DECODERINPUTBUFFER_H__
+
+#include <atomic>
+#include <boost/core/noncopyable.hpp>
+#include <memory>
+#include <queue>
+
+#include "gst/gst.h"
+#include <drmdecrypt/drmdecrypt_api.h>
+
+#include "trackrenderer/core/track.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class DecoderInputBuffer : private boost::noncopyable {
+ public:
+ using Ptr = std::unique_ptr<DecoderInputBuffer>;
+
+ static Ptr Create(const TrackType type = kTrackTypeMax,
+ const int index = kInvalidTrackIndex,
+ GstBuffer* buffer = nullptr, bool incref = true) {
+ return Ptr(new DecoderInputBuffer(buffer, type, index, incref));
+ }
+
+ DecoderInputBuffer() = delete;
+
+ ~DecoderInputBuffer() {
+ while (std::atomic_flag_test_and_set_explicit(&buffer_lock_,
+ std::memory_order_acquire))
+ ; // spin until the lock is acquired
+ if (buffer_ && incref_) {
+ ReleaseTZHandle_(buffer_);
+ gst_buffer_unref(buffer_);
+ }
+ std::atomic_flag_clear_explicit(&buffer_lock_, std::memory_order_release);
+ }
+
+ const TrackType GetType() const { return type_; }
+ const int GetIndex() const { return index_; }
+
+ const uint64_t GetDuration() const { return duration_; }
+
+ const uint32_t GetSize() const { return buffer_size_; }
+
+ const uint8_t* GetRawData() const { return raw_data_; }
+
+ const bool IsEos() const { return is_eos_; }
+
+ GstBuffer* Release() {
+ while (std::atomic_flag_test_and_set_explicit(&buffer_lock_,
+ std::memory_order_acquire))
+ ; // spin until the lock is acquired
+ GstBuffer* tmp = buffer_;
+ buffer_ = nullptr;
+ std::atomic_flag_clear_explicit(&buffer_lock_, std::memory_order_release);
+ return tmp;
+ }
+
+ const GstBuffer* Get() const { return buffer_; }
+
+ private:
+ explicit DecoderInputBuffer(GstBuffer* buffer, const TrackType type,
+ const int index, bool incref)
+ : type_(type), index_(index), incref_(incref) {
+ if (buffer) {
+ buffer_ = incref_ ? gst_buffer_ref(buffer) : buffer;
+ duration_ = GST_TIME_AS_MSECONDS(GST_BUFFER_DURATION(buffer_));
+ if (type == kTrackTypeSubtitle) {
+ GstMapInfo info;
+ gst_buffer_map(buffer_, &info, GST_MAP_READ);
+ raw_data_ = info.data;
+ buffer_size_ = info.size;
+ gst_buffer_unmap(buffer_, &info);
+ }
+ } else {
+ is_eos_ = true;
+ }
+ }
+
+ void ReleaseTZHandle_(GstBuffer* buffer) {
+ GstStructure* tzqdata = GST_STRUCTURE(gst_mini_object_get_qdata(
+ GST_MINI_OBJECT(buffer),
+ g_quark_from_static_string("GstTzHandleData")));
+
+ if (tzqdata) {
+ gboolean ret = FALSE;
+ guint packet_handle = 0;
+ guint packet_size = 0;
+ handle_and_size_s ret_Handle;
+ memset(&ret_Handle, 0, sizeof(ret_Handle));
+
+ ret = gst_structure_get_uint(tzqdata, "packet_handle", &packet_handle);
+ if (FALSE == ret) {
+ return;
+ }
+
+ ret = gst_structure_get_uint(tzqdata, "packet_size", &packet_size);
+ if (FALSE == ret) {
+ return;
+ }
+
+ ret_Handle.handle = packet_handle;
+ ret_Handle.size = packet_size;
+ release_handle(&ret_Handle);
+ }
+ }
+
+ private:
+ std::atomic_flag buffer_lock_ = ATOMIC_FLAG_INIT;
+ const TrackType type_ = kTrackTypeMax;
+ const int index_ = kInvalidTrackIndex;
+ bool incref_ = true;
+ bool is_eos_ = false;
+ GstBuffer* buffer_ = nullptr;
+ uint32_t buffer_size_ = 0;
+ uint64_t duration_ = 0;
+ const uint8_t* raw_data_ = nullptr;
+};
+
+using DecoderInputBufferPtr = DecoderInputBuffer::Ptr;
+
+namespace decoderinputbuffer_util {
+
+bool FlushQueue(std::queue<DecoderInputBufferPtr>& queue);
+
+} // namespace decoderinputbuffer_util
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_DECODERINPUTBUFFER_H__
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_DISPLAY_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_DISPLAY_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+enum class DisplayType { kNone, kOverlay, kEvas };
+
+enum class DisplayMode {
+ kLetterBox,
+ kOriginSize,
+ kFullScreen,
+ kCroppedFull,
+ kOriginOrLetter,
+ kDstRoi,
+ kAutoAspectRatio,
+ kMax
+};
+
+enum class DisplayRotation { kNone, kRotate90, kRotate180, kRotate270 };
+
+struct Geometry {
+ int x = 0, y = 0;
+ int w = 1920, h = 1080;
+};
+
+struct CropArea {
+ double scale_x = 0.0;
+ double scale_y = 0.0;
+ double scale_w = 1.0;
+ double scale_h = 1.0;
+};
+
+struct RenderRect {
+ int x = 0, y = 0;
+ int w = 1920, h = 1080;
+};
+
+struct ParDarInfo {
+ uint64_t time_millisecond = 0;
+ uint32_t par_num = 0;
+ uint32_t par_den = 1;
+ uint32_t dar_num = 0;
+ uint32_t dar_den = 1;
+};
+
+enum class StillMode { kNone, kOff, kOn };
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_DISPLAY_H__
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_DRM_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_DRM_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace drm {
+
+using LicenseAcquiredCb = void*;
+using UserData = void*;
+using DrmHandle = int;
+
+enum class Type {
+ kNone = 0,
+ kPlayready,
+ kMarlin,
+ kVerimatrix,
+ kWidevineClassic,
+ kSecuremedia,
+ kSdrm,
+ kWidevineCdm = 8,
+ kMax
+};
+
+// post from hlsdemux for getright
+
+struct Property {
+ Type type = Type::kNone; // Drm type
+ DrmHandle handle = 0; // Drm handle
+ bool external_decryption = false; // External Decryption Mode
+ LicenseAcquiredCb license_acquired_cb = nullptr; // The cb will be invoked when license was acquired.
+ UserData license_acquired_userdata = nullptr; // The userdata will be sent by license_acquired_cb
+};
+
+} // namespace drm
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_DRM_H__
--- /dev/null
+//\r
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>\r
+//\r
+\r
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_ELEMENTS_H__\r
+#define __PLUSPLAYER_TRACKRENDERER_CORE_ELEMENTS_H__\r
+\r
+namespace plusplayer {\r
+\r
+namespace trackrenderer {\r
+\r
+enum class Elements {\r
+ kPipeline, // must be the first\r
+ kAppSrcVideo,\r
+ kAppSrcAudio,\r
+ kAppSrcSubtitle,\r
+ kDrmVideo,\r
+ kDrmAudio,\r
+ kDecVideo,\r
+ kDecAudio,\r
+ kSinkVideo,\r
+ kAudioConvert,\r
+ kCapsFillterDefault,\r
+ kCapsFillter2,\r
+ kAudioResample,\r
+ kScaleTempo,\r
+ kSinkAudio,\r
+ kSinkSubtitle,\r
+ kBinVideo,\r
+ kBinAudio,\r
+ kBinSubtitle,\r
+ kSinkCaption,\r
+ kQueueCaption,\r
+ kAiFilter,\r
+ kMaxElements // must be the last\r
+};\r
+\r
+} // namespace trackrenderer\r
+\r
+} // namespace plusplayer\r
+\r
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_ELEMENTS_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_ERROR_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_ERROR_H__
+
+#include "tizen.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+#define TRACKRENDERER_ERROR_CLASS TIZEN_ERROR_PLAYER | 0x20
+
+/* This is for custom defined player error. */
+#define TRACKRENDERER_CUSTOM_ERROR_CLASS TIZEN_ERROR_PLAYER | 0x1000
+
+
+enum class ErrorType {
+ kNone = TIZEN_ERROR_NONE, /**< Successful */
+ kOutOfMemory = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory */
+ kInvalidParameter = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */
+ kNoSuchFile = TIZEN_ERROR_NO_SUCH_FILE, /**< No such file or directory */
+ kInvalidOperation = TIZEN_ERROR_INVALID_OPERATION, /**< Invalid operation */
+ kFileNoSpaceOnDevice = TIZEN_ERROR_FILE_NO_SPACE_ON_DEVICE, /**< No space left on the device */
+ kFeatureNotSupportedOnDevice = TIZEN_ERROR_NOT_SUPPORTED, /**< Not supported */
+ kSeekFailed = TRACKRENDERER_ERROR_CLASS | 0x01, /**< Seek operation failure */
+ kInvalidState = TRACKRENDERER_ERROR_CLASS | 0x02, /**< Invalid state */
+ kNotSupportedFile = TRACKRENDERER_ERROR_CLASS | 0x03, /**< File format not supported */
+ kInvalidUri = TRACKRENDERER_ERROR_CLASS | 0x04, /**< Invalid URI */
+ kSoundPolicy = TRACKRENDERER_ERROR_CLASS | 0x05, /**< Sound policy error */
+ kConnectionFailed = TRACKRENDERER_ERROR_CLASS | 0x06, /**< Streaming connection failed */
+ kVideoCaptureFailed = TRACKRENDERER_ERROR_CLASS | 0x07, /**< Video capture failed */
+ kDrmExpired = TRACKRENDERER_ERROR_CLASS | 0x08, /**< Expired license */
+ kDrmNoLicense = TRACKRENDERER_ERROR_CLASS | 0x09, /**< No license */
+ kDrmFutureUse = TRACKRENDERER_ERROR_CLASS | 0x0a, /**< License for future use */
+ kDrmNotPermitted = TRACKRENDERER_ERROR_CLASS | 0x0b, /**< Format not permitted */
+ kResourceLimit = TRACKRENDERER_ERROR_CLASS | 0x0c, /**< Resource limit */
+ kPermissionDenied = TIZEN_ERROR_PERMISSION_DENIED, /**< Permission denied */
+ kServiceDisconnected = TRACKRENDERER_ERROR_CLASS | 0x0d, /**< Socket connection lost (Since 3.0) */
+ kBufferSpace = TIZEN_ERROR_BUFFER_SPACE, /**< No buffer space available (Since 3.0)*/
+ kNotSupportedAudioCodec = TRACKRENDERER_ERROR_CLASS | 0x0e, /**< Not supported audio codec but video can be played (Since 4.0) */
+ kNotSupportedVideoCodec = TRACKRENDERER_ERROR_CLASS | 0x0f, /**< Not supported video codec but audio can be played (Since 4.0) */
+ kNotSupportedSubtitle = TRACKRENDERER_ERROR_CLASS | 0x10, /**< Not supported subtitle format (Since 4.0) */
+
+ kDrmInfo = TRACKRENDERER_CUSTOM_ERROR_CLASS | 0x05, /**< playready drm error info */
+ kNotSupportedFormat = TRACKRENDERER_CUSTOM_ERROR_CLASS | 0x08,
+ kStreamingPlayer = TRACKRENDERER_CUSTOM_ERROR_CLASS | 0x09,
+ kDtcpFsk = TRACKRENDERER_CUSTOM_ERROR_CLASS | 0x0a,
+ kPreLoadingTimeOut =TRACKRENDERER_CUSTOM_ERROR_CLASS | 0x0b, /**< can't finish preloading in time*/
+ kNetworkError = TRACKRENDERER_CUSTOM_ERROR_CLASS | 0x0c, /**< for network error */
+ kChannelSurfingFailed = TRACKRENDERER_CUSTOM_ERROR_CLASS | 0x0d, /**< for channel surfing error */
+
+ kUnknown
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_ERROR_H__
--- /dev/null
+/**
+ * @file
+ * @brief The event for playback.
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ * @remark This is a group of C style event related enum and structure.
+ * @see N/A
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ * PROPRIETARY/CONFIDENTIAL
+ * This software is the confidential and proprietary
+ * information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+ * not disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into with
+ * SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+ * suitability of the software, either express or implied, including but not
+ * limited to the implied warranties of merchantability, fitness for a
+ * particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+ * damages suffered by licensee as a result of using, modifying or distributing
+ * this software or its derivatives.
+ */
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_EVENT_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_EVENT_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+/**
+ * @brief
+ */
+typedef struct {
+ /**
+ * @description
+ */
+ std::string data;
+ /**
+ * @description
+ */
+ uint64_t len;
+} EventMsg;
+
+/**
+ * @brief
+ */
+enum class EventType {
+ kNone,
+ kResolutionChanged,
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_EVENT_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GST_UTILS_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GST_UTILS_H__
+
+#include "gst/gst.h"
+#include "json/json.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace gst_util {
+
+void GstInit();
+void GstInit(const Json::Value& root);
+void ShowStateChangedMsg(GstMessage* msg, void* id = nullptr);
+void SetGstStateToNull(GstElement* pipeline, void* id = nullptr);
+const gchar* GetElementName(const GstMessage* msg);
+const gchar* GetKlass(const GstMessage* msg);
+
+} // namespace gst_util
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GST_UTILS_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GSTCAPS_BUILDER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GSTCAPS_BUILDER_H__
+
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+
+#include <cassert>
+#include <functional>
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "trackrenderer/core/track.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class GstCapsWrapper {
+ public:
+ template <typename T>
+ friend class Pipeline;
+
+ public:
+ explicit GstCapsWrapper(const std::string&);
+ GstCapsWrapper(const GstCapsWrapper&) = delete;
+ GstCapsWrapper(GstCapsWrapper&&);
+ virtual ~GstCapsWrapper();
+
+ public:
+ template <typename... Args>
+ void SetValue(const std::string& name, Args... args) const {
+ assert(caps_ != nullptr);
+ gst_caps_set_simple(caps_, name.c_str(), args..., nullptr);
+ }
+ void PrintCapsString();
+
+ private:
+ GstCaps* GetCaps_() const { return caps_; }
+
+ private:
+ GstCaps* caps_;
+};
+
+class GstCapsBuilder {
+ public:
+ struct Recipe {
+ using TypeChecker = std::function<bool(const Track&)>;
+ using CapsFiller =
+ std::function<void(const GstCapsWrapper& caps, const Track&)>;
+ TypeChecker type_checker;
+ CapsFiller caps_filler;
+ };
+
+ using Recipes = std::map<const std::string, const std::list<Recipe>>;
+
+ public:
+ explicit GstCapsBuilder(const Recipes&);
+ GstCapsBuilder(const GstCapsBuilder&) = delete;
+ GstCapsBuilder(GstCapsBuilder&&) = delete;
+
+ GstCapsWrapper Build(const Track& track, bool is_drm);
+
+ private:
+ Recipe::CapsFiller FindCapsFiller_(const Track&, const std::string&);
+ void FillDefaultValue_(const GstCapsWrapper&, const Track&);
+
+ private:
+ const Recipes& kRecipes_;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GSTCAPS_BUILDER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GSTOBJECT_GUARD_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GSTOBJECT_GUARD_H__
+
+#include <memory>
+
+#include "gst/gst.h"
+#include <functional>
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+// <Usage Example>
+//
+// bool CreatePipeline() {
+// // Old Way
+// // GstCaps* caps = gst_pad_get_currnet_caps();
+// // ... do somthing ...
+// // gst_caps_unref(caps);
+//
+// // New way.
+// auto caps_unique_ptr = utils::make_guard(gst_pad_get_current_caps());
+// GstCaps* caps = caps_unique_ptr.get();
+// // .. do somthing ..
+// // you don't need to call "gst_caps_unref()"
+// return true;
+// }
+
+namespace gstguard {
+
+template <typename T>
+using GstGuardPtr = std::unique_ptr<T, std::function<void(T*)>>;
+//
+// Register CustomDeleter Here (start)
+//
+void CustomDeleter(GstCaps* obj);
+void CustomDeleter(GstObject* obj);
+void CustomDeleter(GstPad* obj);
+void CustomDeleter(GstBuffer* obj);
+void CustomDeleter(GstElement* obj);
+void CustomDeleter(GstQuery* obj);
+void CustomDeleter(GstEvent* obj);
+void CustomDeleter(GstMessage* obj);
+void CustomDeleter(GstPluginFeature* obj);
+void CustomDeleter(GstElementFactory* obj);
+void CustomDeleter(GstBus* obj);
+void CustomDeleter(gchar* obj);
+void CustomDeleter(GError* obj);
+void CustomDeleter(GstStructure* obj);
+void CustomDeleter(GValue* obj);
+void CustomDeleter(GstIterator* obj);
+void CustomDeleter(GBytes* obj);
+//
+// Register CustomDeleter Here (end)
+//
+template <typename T>
+GstGuardPtr<T> make_guard(T* obj) {
+ return GstGuardPtr<T>(obj, [](T* _obj) { CustomDeleter(_obj); });
+}
+
+} // namespace gstguard
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GSTOBJECT_GUARD_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GSTSIGNAL_HOLDER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GSTSIGNAL_HOLDER_H__
+
+#include <boost/core/noncopyable.hpp>
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include "glib-object.h"
+#include "gst/gst.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class GstSignalHolder : private boost::noncopyable {
+ public:
+ GstSignalHolder();
+ ~GstSignalHolder();
+ void Add(GObject* obj, const char* signal_name, GCallback handler,
+ gpointer data);
+ void Delete(GObject* obj); // If the obj is BIN, delete not only its signal
+ // but also its children's.
+ void DeleteAll();
+
+ private:
+ std::mutex item_lock_;
+ class GstSignalItem;
+ std::multimap<GObject*, std::unique_ptr<GstSignalItem>> signal_list_;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_GSTSIGNAL_HOLDER_H__
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_LATENCY_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_LATENCY_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+enum class CatchUpSpeed {
+ kNone,
+ kSlow,
+ kMid,
+ kFast
+};
+
+enum class LatencyStatus {
+ kLow,
+ kMid,
+ kHigh
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_LATENCY_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2021] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_PICTUREQUALITY_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_PICTUREQUALITY_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+/**
+* @brief Advanced Picture Quality Type.
+*/
+enum class AdvPictureQualityType {
+ kVideoCall,
+ kUsbCamera
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_PICTUREQUALITY_H__
\ No newline at end of file
--- /dev/null
+//\r
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>\r
+//\r
+// Gstreamer facade\r
+//\r
+\r
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_PIPELINE_H__\r
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_PIPELINE_H__\r
+\r
+#include <malloc.h>\r
+\r
+#include <boost/core/noncopyable.hpp>\r
+#include <cassert>\r
+#include <condition_variable>\r
+#include <functional>\r
+#include <map>\r
+#include <memory>\r
+#include <mutex>\r
+#include <unordered_map>\r
+\r
+#include "glib-object.h"\r
+#include "gst/app/gstappsrc.h"\r
+#include "gst/gst.h"\r
+#include "trackrenderer/core/gst_utils.h"\r
+#include "trackrenderer/core/gstcaps_builder.h"\r
+#include "trackrenderer/core/gstobject_guard.h"\r
+#include "trackrenderer/core/gstsignal_holder.h"\r
+#include "trackrenderer/core/utils/log.h"\r
+\r
+namespace plusplayer {\r
+\r
+namespace trackrenderer {\r
+\r
+namespace trustzone {\r
+\r
+bool InitTzAppsrc();\r
+bool GstAppsrcPushBuffer(GstElement* appsrc, GstBuffer* buffer);\r
+\r
+} // namespace trustzone\r
+\r
+namespace pipeline_internal {\r
+\r
+inline GstElement* GetLastObj(GstElement* bin) {\r
+ GValue data;\r
+ memset(&data, 0, sizeof(data));\r
+ auto data_guard = gstguard::make_guard(&data);\r
+ GstIterator* it = gst_bin_iterate_elements(GST_BIN_CAST(bin));\r
+ auto it_guard = gstguard::make_guard(it);\r
+ GstIteratorResult ret = gst_iterator_next(it, &data);\r
+ if (ret != GST_ITERATOR_OK) {\r
+ return nullptr;\r
+ }\r
+ return static_cast<GstElement*>(g_value_get_object(&data));\r
+}\r
+\r
+} // namespace pipeline_internal\r
+\r
+template <typename T>\r
+class Pipeline : private boost::noncopyable {\r
+ public:\r
+ using Operator = std::function<bool(GstElement* element)>;\r
+ struct Pad {\r
+ std::mutex m;\r
+ std::condition_variable cv;\r
+ gulong probe_id = 0;\r
+ bool is_idle = false;\r
+ };\r
+\r
+ public:\r
+ using Ptr = std::unique_ptr<Pipeline<T>>;\r
+ static Ptr Create(const char* name) {\r
+ return Ptr(new Pipeline<T>(gst_pipeline_new(name)));\r
+ }\r
+\r
+ ~Pipeline() noexcept {\r
+ std::lock_guard<std::mutex> lock(execute_mutex_);\r
+ signals_.reset();\r
+ TRACKRENDERER_DEBUG("[%p]", mainbin_[static_cast<int>(T::kPipeline)]);\r
+ gst_object_unref(mainbin_[static_cast<int>(T::kPipeline)]);\r
+ malloc_trim(0);\r
+ for (int element_idx = static_cast<int>(T::kPipeline);\r
+ element_idx < static_cast<int>(T::kMaxElements); element_idx++)\r
+ mainbin_[element_idx] = nullptr;\r
+ }\r
+\r
+ // Purose of Execute\r
+ // - serialization of gstreamer commands\r
+ bool Execute(T type, Operator op) {\r
+ std::lock_guard<std::mutex> lock(execute_mutex_);\r
+ if (!mainbin_[static_cast<int>(type)]) return false;\r
+ return op(mainbin_[static_cast<int>(type)]);\r
+ }\r
+\r
+ //////////////// /////////////////\r
+ //////////////// Utilities (Entering) /////////////////\r
+ //////////////// /////////////////\r
+ bool FactoryMake(const T element, const char* name, const char* nickname) {\r
+ if (!name) {\r
+ TRACKRENDERER_ERROR("invalid element_name[null]");\r
+ return false;\r
+ }\r
+ GstElement* obj = gst_element_factory_make(name, nickname);\r
+\r
+ if (strstr(name, "tzappsrc")) {\r
+ is_trustzone_element_[static_cast<int>(element)] = true;\r
+ if (!InitTzAppsrc_()) return false;\r
+ }\r
+\r
+ if (!obj) {\r
+ TRACKRENDERER_ERROR("Fail to create element[%s][%s]",\r
+ name ? name : "(EMPTY)",\r
+ nickname ? nickname : "(EMPTY)");\r
+ return false;\r
+ }\r
+ return Set_(element, obj);\r
+ }\r
+\r
+ bool ElementAdd(const T element, void* obj) {\r
+ if (!obj) {\r
+ return false;\r
+ }\r
+ return Set_(element, static_cast<GstElement*>(gst_object_ref(obj)));\r
+ }\r
+\r
+ bool CreateBin(T element, const char* name) {\r
+ GstElement* obj = gst_bin_new(name);\r
+ return Set_(element, obj);\r
+ }\r
+\r
+ bool SignalConnect(T type, const char* name, GCallback handler,\r
+ gpointer data) {\r
+ std::lock_guard<std::mutex> lock(execute_mutex_);\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ signals_->Add(G_OBJECT(obj), name, handler, data);\r
+ return true;\r
+ }\r
+\r
+ bool SetSyncHandler(GstBusSyncHandler func, gpointer user_data,\r
+ GDestroyNotify notify) {\r
+ GstElement* obj = mainbin_[static_cast<int>(T::kPipeline)];\r
+ if (!obj) return false;\r
+ auto bus =\r
+ gstguard::make_guard<GstBus>(gst_pipeline_get_bus(GST_PIPELINE(obj)));\r
+ if (bus) gst_bus_set_sync_handler(bus.get(), func, user_data, notify);\r
+ return true;\r
+ }\r
+\r
+ template <typename... Args>\r
+ bool SetProperty(T type, const char* name, Args... args) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ g_object_set(G_OBJECT(obj), name, args..., NULL);\r
+ return true;\r
+ }\r
+\r
+ template <typename... Args>\r
+ bool GetProperty(T type, const char* name, Args... args) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ g_object_get(G_OBJECT(obj), name, args..., NULL);\r
+ return true;\r
+ }\r
+\r
+ //\r
+ // BinRemove\r
+ // description : remove each elements from bin\r
+ //\r
+ void BinRemove(T bin, T last) {\r
+ GstElement* obj = mainbin_[static_cast<int>(last)];\r
+ GstElement* bin_element = mainbin_[static_cast<int>(bin)];\r
+ auto sink_pad =\r
+ gstguard::make_guard(gst_element_get_static_pad(obj, "sink"));\r
+ gst_pad_unlink(GST_PAD_PEER(sink_pad.get()), sink_pad.get());\r
+\r
+ auto src_pad = gstguard::make_guard(gst_element_get_static_pad(obj, "src"));\r
+ if (src_pad) {\r
+ // gst_pad_unlink(src_pad.get(), GST_PAD_PEER(src_pad.get()));\r
+ assert(0 && "not support this feature");\r
+ }\r
+ gst_util::SetGstStateToNull(obj);\r
+ gst_bin_remove_many(GST_BIN(bin_element), obj, nullptr);\r
+ Unset_(last);\r
+ }\r
+\r
+ template <typename... Args>\r
+ void BinRemove(T bin, T element, Args... args) {\r
+ GstElement* obj = mainbin_[static_cast<int>(element)];\r
+ GstElement* bin_element = mainbin_[static_cast<int>(bin)];\r
+ if (obj) {\r
+ auto sink_pad =\r
+ gstguard::make_guard(gst_element_get_static_pad(obj, "sink"));\r
+ gst_pad_unlink(GST_PAD_PEER(sink_pad.get()), sink_pad.get());\r
+ gst_util::SetGstStateToNull(obj);\r
+ gst_bin_remove_many(GST_BIN(bin_element), obj, nullptr);\r
+ Unset_(element);\r
+ }\r
+ BinRemove(bin, args...);\r
+ }\r
+\r
+ //\r
+ // BinAdd\r
+ // description : add each elements to Bin , and link each elements\r
+ //\r
+ void BinAdd(T bin, T last) {\r
+ GstElement* bin_obj = mainbin_[static_cast<int>(bin)];\r
+ GstElement* obj = mainbin_[static_cast<int>(last)];\r
+ assert(bin_obj && obj);\r
+ GstElement* last_obj = pipeline_internal::GetLastObj(bin_obj);\r
+ gst_bin_add(GST_BIN(bin_obj), obj);\r
+ if (last_obj) {\r
+ if (!gst_element_link(last_obj, obj)) {\r
+ TRACKRENDERER_ERROR("elements link error");\r
+ gst_bin_remove(GST_BIN(bin_obj), obj);\r
+ return;\r
+ }\r
+ }\r
+ if (!gst_element_sync_state_with_parent(GST_ELEMENT_CAST(obj))) {\r
+ TRACKRENDERER_ERROR("sync state with parent error");\r
+ gst_bin_remove(GST_BIN(bin_obj), obj);\r
+ return;\r
+ }\r
+ }\r
+\r
+ template <typename... Args>\r
+ void BinAdd(T bin, T element, Args... args) {\r
+ GstElement* bin_obj = mainbin_[static_cast<int>(bin)];\r
+ GstElement* obj = mainbin_[static_cast<int>(element)];\r
+ assert(bin_obj);\r
+ if (!obj) {\r
+ BinAdd(bin, args...);\r
+ return;\r
+ }\r
+ GstElement* last_obj = pipeline_internal::GetLastObj(bin_obj);\r
+ gst_bin_add(GST_BIN(bin_obj), obj);\r
+ if (last_obj) {\r
+ // TRACKRENDERER_DEBUG("[%p] is linked to [%p]", obj, last_obj);\r
+ if (!gst_element_link(last_obj, obj)) {\r
+ TRACKRENDERER_ERROR("elements link error");\r
+ gst_bin_remove(GST_BIN(bin_obj), obj);\r
+ return;\r
+ }\r
+ }\r
+ if (!gst_element_sync_state_with_parent(GST_ELEMENT_CAST(obj))) {\r
+ TRACKRENDERER_ERROR("sync state with parent error");\r
+ gst_bin_remove(GST_BIN(bin_obj), obj);\r
+ return;\r
+ }\r
+ BinAdd(bin, args...);\r
+ }\r
+\r
+ //\r
+ // ElementLink\r
+ // description : link elements\r
+ //\r
+ void ElementLink(T src, T dest) {\r
+ GstElement* src_obj = mainbin_[static_cast<int>(src)];\r
+ GstElement* dest_obj = mainbin_[static_cast<int>(dest)];\r
+ assert(src_obj && dest_obj);\r
+ if (!gst_element_link(src_obj, dest_obj)) {\r
+ TRACKRENDERER_ERROR("elements link error");\r
+ return;\r
+ }\r
+ if (!gst_element_sync_state_with_parent(GST_ELEMENT_CAST(dest_obj))) {\r
+ TRACKRENDERER_ERROR("sync state with parent error");\r
+ gst_bin_remove(GST_BIN(gst_element_get_parent(dest_obj)), dest_obj);\r
+ return;\r
+ }\r
+ }\r
+\r
+ bool BinAddSimple(T dst, T src) {\r
+ GstElement* bin = mainbin_[static_cast<int>(dst)];\r
+ GstElement* obj = mainbin_[static_cast<int>(src)];\r
+ if (!bin || !obj) {\r
+ return false;\r
+ }\r
+ gst_bin_add(GST_BIN(bin), obj);\r
+ if (!gst_element_sync_state_with_parent(GST_ELEMENT_CAST(obj))) {\r
+ TRACKRENDERER_ERROR("sync state with parent error");\r
+ gst_bin_remove(GST_BIN(bin), obj);\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+ //\r
+ // SetState\r
+ //\r
+ bool SetState(T element, GstState state) {\r
+ GstElement* obj = mainbin_[static_cast<int>(element)];\r
+ if (!obj) return false;\r
+ if (state == GST_STATE_NULL) {\r
+ gst_util::SetGstStateToNull(obj);\r
+ return true;\r
+ }\r
+ GstStateChangeReturn ret = gst_element_set_state(obj, state);\r
+ if (ret == GST_STATE_CHANGE_FAILURE) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ bool GetState(T element, GstState* cur_state, GstState* pending_state,\r
+ GstClockTime timeout) {\r
+ GstElement* obj = mainbin_[static_cast<int>(element)];\r
+ if (!obj) return false;\r
+ GstStateChangeReturn ret =\r
+ gst_element_get_state(obj, cur_state, pending_state, timeout);\r
+ if (ret == GST_STATE_CHANGE_FAILURE) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ bool GenerateDot(const gchar* file_name) {\r
+ GstElement* pipeline = mainbin_[static_cast<int>(T::kPipeline)];\r
+ if (!pipeline) return false;\r
+ GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL,\r
+ file_name);\r
+ return true;\r
+ }\r
+\r
+ bool Seek(gdouble rate, GstFormat format, GstSeekFlags flags,\r
+ GstSeekType start_type, gint64 start, GstSeekType stop_type,\r
+ gint64 stop) {\r
+ GstElement* obj = mainbin_[static_cast<int>(T::kPipeline)];\r
+ if (!obj) return false;\r
+ if (!gst_element_seek(obj, rate, format, flags, start_type, start,\r
+ stop_type, stop)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ bool AppSrcPushBuffer(T type, GstBuffer* buffer) {\r
+ int index = static_cast<int>(type);\r
+ GstElement* obj = mainbin_[index];\r
+ if (!obj) return false;\r
+ if (is_trustzone_element_[index]) {\r
+ trustzone::GstAppsrcPushBuffer(obj, buffer);\r
+ } else {\r
+ gst_app_src_push_buffer(GST_APP_SRC(obj), buffer);\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ bool SetAppSrcCaps(T type, const GstCapsWrapper& caps) {\r
+ return SetProperty(type, "caps", caps.GetCaps_());\r
+ }\r
+\r
+ bool SetPadCaps(T type, const char* mime_type) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, "src"));\r
+ auto gstcaps = gstguard::make_guard(gst_caps_new_empty_simple(mime_type));\r
+ if (!gst_pad_set_caps(pad.get(), gstcaps.get())) {\r
+ TRACKRENDERER_ERROR("gst_pad_set_caps error");\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ template <typename... Args>\r
+ bool SetPadCaps(T type, const char* mime_type, Args... caps) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, "src"));\r
+ auto gstcaps =\r
+ gstguard::make_guard(gst_caps_new_simple(mime_type, caps..., nullptr));\r
+ if (!gst_pad_set_caps(pad.get(), gstcaps.get())) {\r
+ TRACKRENDERER_ERROR("gst_pad_set_caps error");\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ GstCaps* GetSinkPadCaps(T type) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return nullptr;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, "sink"));\r
+ auto caps = gstguard::make_guard(gst_pad_get_current_caps(pad.get()));\r
+ return gst_caps_copy(caps.get());\r
+ }\r
+\r
+ bool Flush(T type) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ bool ret = true;\r
+ if (!gst_element_send_event(GST_ELEMENT_CAST(obj),\r
+ gst_event_new_flush_start())) {\r
+ TRACKRENDERER_ERROR("Fail to send flush start");\r
+ ret = false;\r
+ }\r
+ if (!gst_element_send_event(GST_ELEMENT_CAST(obj),\r
+ gst_event_new_flush_stop(FALSE))) {\r
+ TRACKRENDERER_ERROR("Fail to send flush stop");\r
+ ret = false;\r
+ }\r
+ return ret;\r
+ }\r
+\r
+ bool FlushDownStream(T type, gboolean reset_time) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, "src"));\r
+ auto peer_pad = GST_PAD_PEER(pad.get());\r
+ if (!gst_pad_send_event(peer_pad, gst_event_new_flush_start())) {\r
+ TRACKRENDERER_ERROR("Fail to send flush start");\r
+ return false;\r
+ }\r
+ if (!gst_pad_send_event(peer_pad, gst_event_new_flush_stop(reset_time))) {\r
+ TRACKRENDERER_ERROR("Fail to send flush stop");\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ // LCOV_EXCL_START\r
+ bool DirectPadFlushStartDownStream(T type) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, "sink"));\r
+ if (!gst_pad_send_event(pad.get(), gst_event_new_flush_start())) {\r
+ TRACKRENDERER_ERROR("Fail to send flush start");\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ bool DirectPadFlushStopDownStream(T type, bool reset_time) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, "sink"));\r
+ if (!gst_pad_send_event(pad.get(), gst_event_new_flush_stop(reset_time))) {\r
+ TRACKRENDERER_ERROR("Fail to send flush stop");\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+ // LCOV_EXCL_STOP\r
+\r
+ bool PushSegmentEvent(T type, uint64_t position, gdouble rate) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, "src"));\r
+ auto peer_pad = GST_PAD_PEER(pad.get());\r
+ GstSegment segment;\r
+ gst_segment_init(&segment, GST_FORMAT_TIME);\r
+ segment.rate = rate;\r
+ segment.start = position;\r
+ segment.position = position;\r
+ segment.time = position;\r
+ GstEvent* seg_event = gst_event_new_segment(&segment);\r
+ if (!gst_pad_send_event(peer_pad, seg_event)) {\r
+ TRACKRENDERER_ERROR("Fail to send segment event");\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ // LCOV_EXCL_START\r
+ bool DirectSendSegmentEvent(T type, uint64_t position, gdouble rate) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, "sink"));\r
+ GstSegment segment;\r
+ gst_segment_init(&segment, GST_FORMAT_TIME);\r
+ segment.rate = rate;\r
+ segment.start = position;\r
+ segment.position = position;\r
+ segment.time = position;\r
+ if (!gst_pad_send_event(pad.get(), gst_event_new_segment(&segment))) {\r
+ TRACKRENDERER_ERROR("Fail to send segment event");\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+ // LCOV_EXCL_STOP\r
+\r
+ gulong GetPadProbeId(const char* key) {\r
+ auto find = [](const PadProbeMap& map, const char* _key) -> bool {\r
+ auto look = map.find(_key);\r
+ if (look == map.end()) return false;\r
+ return true;\r
+ };\r
+ if (!find(padprobe_id_, key)) return 0;\r
+ return padprobe_id_[key].first;\r
+ }\r
+\r
+ bool PadAddProbe(T type, const char* key, const char* padname,\r
+ GstPadProbeType mask, GstPadProbeCallback callback,\r
+ gpointer user_data, GDestroyNotify destroy_data) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, padname));\r
+ assert(pad);\r
+ gulong id =\r
+ gst_pad_add_probe(pad.get(), mask, callback, user_data, destroy_data);\r
+ if (key) padprobe_id_[key] = {id, pad.get()};\r
+ return true;\r
+ }\r
+\r
+ // LCOV_EXCL_START\r
+ bool PeerPadAddProbe(T type, const char* key, const char* padname,\r
+ GstPadProbeType mask, GstPadProbeCallback callback,\r
+ gpointer user_data, GDestroyNotify destroy_data) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, padname));\r
+ assert(pad);\r
+ auto peerpad = gstguard::make_guard(gst_pad_get_peer(pad.get()));\r
+ assert(peerpad);\r
+ gulong id = gst_pad_add_probe(peerpad.get(), mask, callback, user_data,\r
+ destroy_data);\r
+ if (key) padprobe_id_[key] = {id, peerpad.get()};\r
+ return true;\r
+ }\r
+ // LCOV_EXCL_STOP\r
+\r
+ bool PadRemoveProbe(T type, const char* padname, const gulong id) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, padname));\r
+ gst_pad_remove_probe(pad.get(), id);\r
+ return true;\r
+ }\r
+\r
+ bool PadRemoveProbe(const char* key) {\r
+ auto find = [](const PadProbeMap& map, const char* _key) -> bool {\r
+ auto look = map.find(_key);\r
+ if (look == map.end()) return false;\r
+ return true;\r
+ };\r
+ if (!find(padprobe_id_, key)) return false;\r
+ gst_pad_remove_probe(padprobe_id_[key].second, padprobe_id_[key].first);\r
+ return true;\r
+ }\r
+\r
+ bool SignalEmitByName(T type, const char* name) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ GstFlowReturn ret;\r
+ g_signal_emit_by_name(gpointer(obj), name, &ret);\r
+ TRACKRENDERER_DEBUG("g emit signal [%s] ret[%d]", name, ret);\r
+ return true;\r
+ }\r
+\r
+ bool QueryPosition(T type, GstFormat format, int64_t* position) {\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ return gst_element_query_position(obj, format, position);\r
+ }\r
+\r
+ bool QuerySegment(T type, GstSegment* segment) {\r
+ if (segment == nullptr) return false;\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+\r
+ auto query = gstguard::make_guard(gst_query_new_segment(GST_FORMAT_TIME));\r
+ if (gst_element_query(obj, query.get()) == false) {\r
+ TRACKRENDERER_ERROR("Fail to query segment");\r
+ return false;\r
+ }\r
+\r
+ gdouble rate;\r
+ GstFormat format;\r
+ gint64 starttime, stoptime;\r
+ gst_query_parse_segment(query.get(), &rate, &format, &starttime, &stoptime);\r
+ TRACKRENDERER_DEBUG(\r
+ "Segment : format(%d), rate(parsed:%lf), starttime(%lld), "\r
+ "stoptime(%lld)",\r
+ format, rate, starttime, stoptime);\r
+\r
+ gst_segment_init(segment, format);\r
+ segment->rate = rate;\r
+ segment->start = starttime;\r
+ segment->stop = stoptime;\r
+ return true;\r
+ }\r
+\r
+ void SetGstElementCreatedCbHandler(std::function<void(T)> handler) {\r
+ on_gstelement_created_cb_handler_ = handler;\r
+ }\r
+\r
+ bool SetParDar(T type, uint64_t timestamp, uint32_t VParN, uint32_t VParD,\r
+ uint32_t VDarN, uint32_t VDarD) {\r
+ TRACKRENDERER_ERROR("SetParDar enter");\r
+ GstElement* obj = mainbin_[static_cast<int>(type)];\r
+ if (!obj) return false;\r
+ auto pad = gstguard::make_guard(gst_element_get_static_pad(obj, "sink"));\r
+ bool ret = true;\r
+ timestamp *= G_GINT64_CONSTANT(1000000); // ms->ns\r
+ GstStructure* pardar_structure = gst_structure_new(\r
+ "GstPARDAR_Changed", "pkt_timestamp", G_TYPE_UINT64, timestamp, "vParN",\r
+ G_TYPE_UINT, VParN, "vParD", G_TYPE_UINT, VParD, "vDarN", G_TYPE_UINT,\r
+ VDarN, "vDarD", G_TYPE_UINT, VDarD, NULL);\r
+ GstEvent* pardar_event = gst_event_new_custom(\r
+ GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, pardar_structure);\r
+ if (!gst_pad_send_event(pad.get(), pardar_event)) {\r
+ TRACKRENDERER_ERROR("Fail to send par and dar");\r
+ ret = false;\r
+ }\r
+ TRACKRENDERER_ERROR("send par and dar to video decoder successfully");\r
+ return ret;\r
+ }\r
+\r
+ //////////////// /////////////////\r
+ //////////////// Utilities (Leaving) /////////////////\r
+ //////////////// /////////////////\r
+\r
+ private:\r
+ explicit Pipeline(GstElement* pipeline) noexcept {\r
+ assert(pipeline && "pipeline is null");\r
+ mainbin_[static_cast<int>(T::kPipeline)] = pipeline;\r
+ }\r
+\r
+ bool Set_(T type, GstElement* element) {\r
+ std::lock_guard<std::mutex> lock(execute_mutex_);\r
+ assert(element && (type < T::kMaxElements));\r
+ assert(mainbin_[static_cast<int>(type)] == nullptr);\r
+ TRACKRENDERER_DEBUG("TYPE[%d] ELEMENT[%p]", static_cast<int>(type),\r
+ element);\r
+ mainbin_[static_cast<int>(type)] = element;\r
+ if (on_gstelement_created_cb_handler_ != nullptr)\r
+ on_gstelement_created_cb_handler_(type);\r
+ return true;\r
+ }\r
+\r
+ bool Unset_(T type) {\r
+ assert(type < T::kMaxElements);\r
+ assert(mainbin_[static_cast<int>(type)] != nullptr);\r
+ TRACKRENDERER_DEBUG("TYPE[%d]", static_cast<int>(type));\r
+ mainbin_[static_cast<int>(type)] = nullptr;\r
+ return true;\r
+ }\r
+\r
+ bool InitTzAppsrc_() { return trustzone::InitTzAppsrc(); }\r
+\r
+ private:\r
+ using PadProbeMap =\r
+ std::unordered_map<std::string, std::pair<gulong, GstPad*>>;\r
+\r
+ private:\r
+ std::mutex execute_mutex_;\r
+ GstElement* mainbin_[static_cast<int>(T::kMaxElements)]{\r
+ nullptr,\r
+ };\r
+ std::unique_ptr<GstSignalHolder> signals_{new GstSignalHolder};\r
+ PadProbeMap padprobe_id_;\r
+ std::function<void(T)> on_gstelement_created_cb_handler_;\r
+ bool is_trustzone_element_[static_cast<int>(T::kMaxElements)]{\r
+ false,\r
+ };\r
+}; // struct TrackRenderer::Pipeline\r
+\r
+} // namespace trackrenderer\r
+\r
+} // namespace plusplayer\r
+\r
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_PIPELINE_H__\r
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_SCREENSAVER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_SCREENSAVER_H__
+
+#include <boost/core/noncopyable.hpp>
+
+#include "glib.h"
+#include "screensaver.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class ScreenSaver : private boost::noncopyable {
+ public:
+ ~ScreenSaver();
+ static void StartTimeout();
+ static void StopTimeout();
+
+ private:
+ static gboolean ResetTimeout(gpointer data);
+
+ private:
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_SCREENSAVER_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_STREAM_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_STREAM_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+/**
+ * @brief Enumerations for the stream type
+ */
+enum class StreamType { kAudio = 0, kVideo, kMax };
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_STREAM_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_SUBTITLE_ATTR_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_SUBTITLE_ATTR_H__
+
+#include <boost/any.hpp>
+#include <cstdint>
+#include <limits>
+#include <list>
+#include <memory>
+
+namespace plusplayer {
+namespace trackrenderer {
+enum SubtitleAttrType {
+ kSubAttrRegionXPos = 0, // float type
+ kSubAttrRegionYPos, // float type
+ kSubAttrRegionWidth, // float type
+ kSubAttrRegionHeight, // float type
+ kSubAttrWindowXPadding, // float type
+ kSubAttrWindowYPadding, // float type
+ kSubAttrWindowLeftMargin, // int type
+ kSubAttrWindowRightMargin, // int type
+ kSubAttrWindowTopMargin, // int type
+ kSubAttrWindowBottomMargin, // int type
+ kSubAttrWindowBgColor, // int type
+ kSubAttrWindowOpacity, // float type
+ kSubAttrWindowShowBg, // how to show window background, uint type
+ kSubAttrFontFamily, // char* type
+ kSubAttrFontSize, // float type
+ kSubAttrFontWeight, // int type
+ kSubAttrFontStyle, // int type
+ kSubAttrFontColor, // int type
+ kSubAttrFontBgColor, // int type
+ kSubAttrFontOpacity, // float type
+ kSubAttrFontBgOpacity, // float type
+ kSubAttrFontTextOutlineColor, // int type
+ kSubAttrFontTextOutlineThickness, // int type
+ kSubAttrFontTextOutlineBlurRadius, // int type
+ kSubAttrFontVerticalAlign, // int type
+ kSubAttrFontHorizontalAlign, // int type
+ kSubAttrRawSubtitle, // char* type
+ kSubAttrWebvttCueLine, // float type
+ kSubAttrWebvttCueLineNum, // int type
+ kSubAttrWebvttCueLineAlign, // int type
+ kSubAttrWebvttCueAlign, // int type
+ kSubAttrWebvttCueSize, // float type
+ kSubAttrWebvttCuePosition, // float type
+ kSubAttrWebvttCuePositionAlign, // int type
+ kSubAttrWebvttCueVertical, // int type
+ kSubAttrTimestamp,
+ kSubAttrExtsubIndex, // File index of external subtitle
+ kSubAttrTypeNone
+};
+
+enum class SubtitleType { kText, kPicture, kInvalid };
+
+struct SubtitleAttr {
+ explicit SubtitleAttr(const SubtitleAttrType _type,
+ const uint32_t _start_time, const uint32_t _stop_time,
+ const boost::any _value, const int _extsub_index)
+ : type(_type),
+ start_time(_start_time),
+ stop_time(_stop_time),
+ value(_value),
+ extsub_index(_extsub_index) {}
+ const SubtitleAttrType type = kSubAttrTypeNone;
+ const uint32_t start_time = std::numeric_limits<uint32_t>::max();
+ const uint32_t stop_time = std::numeric_limits<uint32_t>::max();
+ const boost::any value;
+ const int extsub_index = -1;
+};
+using SubtitleAttrList = std::list<SubtitleAttr>;
+using SubtitleAttrListPtr = std::unique_ptr<SubtitleAttrList>;
+} // namespace trackrenderer
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_SUBTITLE_ATTR_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_TRACKRENDERER_CORE_TRACK_H__
+#define __PLUSPLAYER_TRACKRENDERER_CORE_TRACK_H__
+
+#include <memory>
+#include <string>
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+const int kInvalidTrackIndex = -1;
+
+enum TrackType {
+ kTrackTypeAudio = 0,
+ kTrackTypeVideo,
+ kTrackTypeSubtitle,
+ kTrackTypeMax
+};
+
+struct Track {
+ int index = kInvalidTrackIndex;
+ int id = 0;
+ std::string mimetype;
+ std::string streamtype;
+ TrackType type = kTrackTypeMax;
+ std::shared_ptr<char> codec_data;
+ int codec_data_len = 0;
+ int width = 0;
+ int height = 0;
+ int maxwidth = 0;
+ int maxheight = 0;
+ int framerate_num = 0;
+ int framerate_den = 0;
+ int sample_rate = 0;
+ int sample_format = 0;
+ int channels = 0;
+ int version = 0;
+ int layer = 0;
+ int bits_per_sample = 0;
+ int block_align = 0;
+ int bitrate = 0;
+ int endianness = 1234; // little endian : 1234 others big endian
+ bool is_signed = false;
+ bool active = false;
+ bool use_swdecoder = false;
+ std::string language_code;
+ std::string subtitle_format;
+ std::string layout;
+ std::string codec_tag;
+ int flavor = 0;
+};
+
+struct Rational {
+ int num = 0; // the numerator value
+ int den = 0; // the denominator value
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_TRACKRENDERER_CORE_TRACK_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_TRACK_UTIL_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_TRACK_UTIL_H__
+
+#include <vector>
+
+#include "gst/gst.h"
+
+#include "trackrenderer/core/track.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace track_util {
+
+bool GetActiveTrack(const std::vector<Track>& track_list, TrackType type,
+ Track* track);
+bool GetActiveTrackList(const std::vector<Track>& tracklist,
+ std::vector<Track>& active_track);
+
+void ShowTrackInfo(const std::vector<Track>& trackinfo);
+void ShowTrackInfo(const Track& track);
+uint64_t GetPositionWithinBoundary(const uint64_t duration,
+ const uint64_t position,
+ const uint64_t threshold);
+bool IsValidCodecDataSize(int size);
+void FillCodecDataIntoTrack(const GValue* codec_data, Track* track);
+const std::string& GetTrackTypeString(const TrackType& type);
+} // namespace track_util
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_TRACK_UTIL_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_UTILS_LOG_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_UTILS_LOG_H__
+
+#include <dlog.h>
+#include <string.h>
+
+#undef LOG_TAG
+#define LOG_TAG "PLUSPLAYER"
+
+#ifndef __MODULE__
+#define __MODULE__ \
+ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+#endif
+
+#define TRACKRENDERER_DEBUG(fmt, arg...) \
+ ({ \
+ do { \
+ LOGI(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_DEBUG_P(id, fmt, arg...) \
+ ({ \
+ do { \
+ LOGI("[%p] > " #fmt, id, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_INFO(fmt, arg...) \
+ ({ \
+ do { \
+ LOGI(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_INFO_P(id, fmt, arg...) \
+ ({ \
+ do { \
+ LOGI("[%p] > " #fmt, id, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_WARN(fmt, arg...) \
+ ({ \
+ do { \
+ LOGW(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_WARN_P(id, fmt, arg...) \
+ ({ \
+ do { \
+ LOGW("[%p] > " #fmt, id, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_ERROR(fmt, arg...) \
+ ({ \
+ do { \
+ LOGE(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_ERROR_P(id, fmt, arg...) \
+ ({ \
+ do { \
+ LOGE("[%p] > " #fmt, id, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_FATAL(fmt, arg...) \
+ ({ \
+ do { \
+ LOGF(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_FATAL_P(id, fmt, arg...) \
+ ({ \
+ do { \
+ LOGF("[%p] > " #fmt, id, ##arg); \
+ } while (0); \
+ })
+
+#define TRACKRENDERER_ENTER \
+ { \
+ do { \
+ LOGI("ENTER"); \
+ } while (0); \
+ }
+
+#define TRACKRENDERER_ENTER_P(id) \
+ { \
+ do { \
+ LOGI("[%p] > ENTER", id); \
+ } while (0); \
+ }
+
+#define TRACKRENDERER_LEAVE \
+ { \
+ do { \
+ LOGI("LEAVE"); \
+ } while (0); \
+ }
+
+#define TRACKRENDERER_LEAVE_P(id) \
+ { \
+ do { \
+ LOGI("[%p] > LEAVE", id); \
+ } while (0); \
+ }
+
+#define TRACKRENDERER_ENTER_E \
+ { \
+ do { \
+ LOGE("ENTER"); \
+ } while (0); \
+ }
+
+#define TRACKRENDERER_ENTER_E_P(id) \
+ { \
+ do { \
+ LOGE("[%p] > ENTER", id); \
+ } while (0); \
+ }
+
+#define TRACKRENDERER_LEAVE_E \
+ { \
+ do { \
+ LOGE("LEAVE"); \
+ } while (0); \
+ }
+
+#define TRACKRENDERER_LEAVE_E_P(id) \
+ { \
+ do { \
+ LOGE("[%p] > LEAVE", id); \
+ } while (0); \
+ }
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_UTILS_LOG_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_UTILS_PRODUCT_CFG_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_UTILS_PRODUCT_CFG_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+enum class ProductType { kTv, kAv };
+// enum class ChipType { kKantM };
+
+namespace product_cfg {
+
+constexpr ProductType GetProductType() {
+#ifdef IS_AV_PRODUCT
+ return ProductType::kAv;
+#else
+ return ProductType::kTv;
+#endif
+}
+
+} // namespace product_cfg
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_CORE_UTILS_PRODUCT_CFG_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_DISPLAY_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_DISPLAY_H__
+
+#include <boost/core/noncopyable.hpp>
+#include <mutex>
+
+#include "Ecore_Wl2.h"
+#include "Evas.h"
+#include "gst/video/videooverlay.h"
+#include "trackrenderer/core/display.h"
+#include "trackrenderer/core/gstobject_guard.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class Display : private boost::noncopyable {
+ public:
+ ~Display();
+ uint32_t GetSurfaceId() const { return surface_id_; }
+ void SetDisplayQualityInfo(const GstCaps* video_appsrc);
+ void SetDisplayMode(const DisplayMode& mode);
+ void SetDisplayRotate(const DisplayRotation& rotate);
+ bool SetDisplay(const DisplayType& type, Evas_Object* obj);
+ bool SetDisplay(const DisplayType& type, Ecore_Wl2_Window* ecore_wl2_window,
+ const int x, const int y, const int w, const int h);
+ bool SetDisplay(const DisplayType& type, const uint32_t surface_id,
+ const int x, const int y, const int w, const int h);
+ bool SetDisplaySubsurface(const DisplayType& type,
+ Ecore_Wl2_Subsurface* ecore_wl2_subsurface,
+ const int x, const int y, const int w, const int h);
+ bool SetDisplayRoi(const Geometry& roi);
+ bool SetDisplayCropArea(const CropArea& area);
+ bool ResizeRenderRect(const RenderRect& rect);
+ void SetVisible(bool is_visible) { visible_ = is_visible; }
+ bool Update(GstElement* videosink);
+ bool UpdateVisible(GstElement* videosink);
+ bool UpdateCropArea(GstElement* videosink);
+ void GetDisplayCropArea(CropArea* area);
+ void GetDisplay(DisplayType* type, Geometry* area) {
+ *type = type_;
+ *area = window_;
+ }
+ void GetDisplayMode(DisplayMode* mode) { *mode = mode_; }
+ void GetDisplayRotate(DisplayRotation* rotate) { *rotate = rotate_; }
+ void SetVideoQualityMode(const uint32_t mode) { qualitymode_ = mode; }
+
+ private:
+ bool SetDisplay_(const DisplayType& type, wl_surface* wl_surface, const int x,
+ const int y, const int w, const int h);
+ wl_surface* GetWlSurface_(Ecore_Wl2_Window* ecore_wl2_window);
+
+ private:
+ uint32_t surface_id_ = 0;
+ Geometry window_;
+ Geometry roi_;
+ CropArea scale_;
+ DisplayType type_ = DisplayType::kNone;
+ DisplayMode mode_ = DisplayMode::kFullScreen;
+ DisplayRotation rotate_ = DisplayRotation::kNone;
+ uint32_t qualitymode_ = 0;
+ bool visible_ = true;
+ std::mutex settings_mutex_;
+ gstguard::GstGuardPtr<GstCaps> video_quality_info_caps_;
+ bool has_parent_surface_ = false;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_DISPLAY_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_ERROR_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_ERROR_H__
+
+#include "gst/gst.h"
+
+#include "trackrenderer/core/error.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+ErrorType HandleError(GstMessage* message, const bool is_music_content);
+
+void HandleErrorMsg(GstMessage* message, gchar** error_msg);
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_ERROR_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_LATENCY_MANAGER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_LATENCY_MANAGER_H__
+
+#include <stdint.h>
+
+#include <bitset>
+#include <future>
+#include <mutex>
+
+#include "trackrenderer/core/latency.h"
+#include "trackrenderer/core/pipeline.hpp"
+#include "trackrenderer/core/track.h"
+#include "trackrenderer/latency_status_listener.h"
+#include "trackrenderer/core/elements.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class LatencyManager {
+
+ public:
+ explicit LatencyManager(LatencyStatusListener* listener);
+ ~LatencyManager();
+
+ enum class UpdatePacketStatus {
+ kSubmit,
+ kFlush,
+ kMax
+ };
+
+ void SetVideoMidLatencyThreshold(const unsigned int threshold);
+ void SetAudioMidLatencyThreshold(const unsigned int threshold);
+ void SetVideoHighLatencyThreshold(const unsigned int threshold);
+ void SetAudioHighLatencyThreshold(const unsigned int threshold);
+ void SetCatchUpSpeed(const CatchUpSpeed& level);
+ void SetPipeline(Pipeline<Elements>* pipeline);
+ void UnsetPipeline();
+ void GetVideoLatencyStatus(LatencyStatus* status);
+ void GetAudioLatencyStatus(LatencyStatus* status);
+ void UpdateVideoFrameStatus(UpdatePacketStatus status);
+
+ private:
+ int CalculateDropRate_();
+ LatencyStatus GetVideoLatencyStatus_(const unsigned int latency);
+ LatencyStatus GetAudioLatencyStatus_(const unsigned int latency);
+ void LatencyCheckerTask_();
+ void HandleVideoLatency_(const unsigned int video_latency);
+ void HandleAudioLatency_(const unsigned int audio_latency);
+ void StartLatencyChecker_();
+
+ private:
+ enum class CheckerPrecondition {
+ kSetThreshold = 0,
+ kSetPipeline,
+ kMax
+ };
+
+ unsigned int video_mid_latency_threshold_ = UINT32_MAX;
+ unsigned int audio_mid_latency_threshold_ = UINT32_MAX;
+ unsigned int video_high_latency_threshold_ = UINT32_MAX;
+ unsigned int audio_high_latency_threshold_ = UINT32_MAX;
+ unsigned int submitted_video_frames_ = 0;
+ CatchUpSpeed speed_level_ = CatchUpSpeed::kNone;
+ LatencyStatus video_latency_status_ = LatencyStatus::kLow;
+ LatencyStatus audio_latency_status_ = LatencyStatus::kLow;
+ LatencyStatusListener* event_listener_ = nullptr;
+ std::future<void> latency_checker_task_;
+ std::bitset<static_cast<int>(CheckerPrecondition::kMax)>
+ checker_precondition_;
+ bool is_stopped_ = false;
+ std::mutex pipeline_m_;
+ Pipeline<Elements>* pipeline_ = nullptr;
+};
+} // namespace trackrenderer
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_LATENCY_MANAGER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_LATENCY_STATUS_LISTENER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_LATENCY_STATUS_LISTENER_H__
+
+#include "trackrenderer/core/track.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class LatencyStatusListener {
+ public:
+ virtual ~LatencyStatusListener() {}
+ virtual void OnVideoLatencyStatus(const LatencyStatus& latency_status) = 0;
+ virtual void OnAudioLatencyStatus(const LatencyStatus& latency_status) = 0;
+ virtual void OnVideoHighLatency() = 0;
+ virtual void OnAudioHighLatency() = 0;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_LATENCY_STATUS_LISTENER_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_RESOURCE_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_RESOURCE_H__
+
+#include <string>
+
+#include "trackrenderer/core/track.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+enum class RscType { kVideoRenderer };
+
+enum class ResourceCategory {
+ kVideoDecoder,
+ kVideoDecoderSub,
+ kVideoRenderer,
+ kVideoRendererSub,
+ kVideoRendererSub2,
+ kVideoRendererSub3,
+ kAudioDecoder,
+ kAudioDecoderSub,
+ kAudioRenderer,
+ kAudioRendererSub
+};
+
+enum class AllocatedState { kFailed, kSuccess, kSkipped };
+
+enum class RscAllocPolicy {
+ kRscAllocExclusive,
+ kRscAllocConditional,
+};
+
+struct ResourceProperty {
+ ResourceCategory category = ResourceCategory::kVideoDecoder;
+ Track track;
+ bool use_sw = false;
+ bool is_vr360 = false;
+ bool is_dual_sound = false;
+ bool is_multiview = false;
+ bool is_ndecoding = false;
+ RscAllocPolicy rsc_alloc_policy = RscAllocPolicy::kRscAllocExclusive;
+};
+
+constexpr char kSwDecoderComponentName[] = "FFMPEG.SW.Decoder";
+constexpr char kPulseSinkComponentName[] = "Pulse.Audio.Out";
+constexpr char kSkippedResource[] = "SKIP";
+
+class Resource {
+ public:
+ Resource(const ResourceProperty& property, const std::string& componentname)
+ : property_(property), componentname_(componentname) {}
+ Resource(const ResourceProperty& property, int deviceid,
+ const std::string& componentname)
+ : property_(property),
+ deviceid_(deviceid),
+ componentname_(componentname) {}
+ Resource(const ResourceProperty& property, AllocatedState state)
+ : property_(property), state_(state) {}
+ const ResourceCategory& GetResourceCategory() const {
+ return property_.category;
+ }
+ const int& GetDeviceId() const { return deviceid_; }
+ const std::string& GetComponentName() const { return componentname_; }
+ const AllocatedState& GetAllocatedState() const { return state_; }
+
+ private:
+ const ResourceProperty property_;
+ const int deviceid_ = 0;
+ const std::string componentname_;
+ const AllocatedState state_ = AllocatedState::kSuccess;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_RESOURCE_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_RESOURCE_CONFLICT_LISTENER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_RESOURCE_CONFLICT_LISTENER_H__
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class ResourceConflictListener {
+ public:
+ virtual ~ResourceConflictListener() {}
+ virtual void OnResourceConflicted() = 0;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_RESOURCE_CONFLICT_LISTENER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_RESOURCEMANAGER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_RESOURCEMANAGER_H__
+
+#include <list>
+#include <mutex>
+#include <string>
+
+#include "rm_api.h"
+#include "trackrenderer/core/track.h"
+#include "trackrenderer/resource.h"
+#include "trackrenderer/resource_conflict_listener.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+using ResourceManagerHandle = int;
+
+constexpr int kMaxUhd8kWidth = 7680;
+constexpr int kMaxUhd8kHeight = 4320;
+constexpr int kMaxUhdWidth = 4096;
+constexpr int kMaxUhdHeight = 2160;
+constexpr int kMaxFhdWidth = 1920;
+constexpr int kMaxFhdHeight = 1080;
+
+class ResourceManager {
+ public:
+ explicit ResourceManager(ResourceConflictListener* listener);
+ ~ResourceManager();
+
+ void SetAppId(const std::string& appid);
+ bool Alloc(const std::list<ResourceProperty>& properties);
+ bool Dealloc();
+ bool Dealloc(const ResourceCategory type);
+ std::string GetComponentName(const ResourceCategory type);
+ int GetRawHandle();
+ AllocatedState GetAllocatedState(const ResourceCategory type);
+ bool IsMainDevice(const ResourceCategory& type);
+ int GetDeviceId(const ResourceCategory& type);
+ bool NeedPulseResource(const ResourceProperty& property);
+ bool IsAudioFocused();
+
+ private:
+ bool Alloc_(const ResourceProperty& property);
+ bool AllocPulseResource_(const ResourceProperty& property);
+ bool AllocSwDecoderResource_(const ResourceProperty& property);
+ bool AllocHWResource_(const ResourceProperty& property);
+ bool TryAllocHWResource_(const ResourceProperty& property,
+ const rm_category_request_s& req);
+ int GetCategoryOption_(const rm_rsc_category_e& category_id,
+ const ResourceProperty& property);
+ AllocatedState MakeCategoryRequest_(const ResourceProperty& property,
+ rm_category_request_s* req);
+
+ private:
+ static rm_cb_result ResourceConflictCallback_(ResourceManagerHandle rmhandle,
+ rm_callback_type eventtype,
+ rm_device_request_s* info,
+ void* userdata);
+
+ private:
+ ResourceManagerHandle resourcemanager_handle_ = 0;
+ std::list<Resource> resourcelist_;
+ std::mutex control_lock_;
+ ResourceConflictListener* resourceconflict_listener_ = nullptr;
+ std::string app_id_ = "";
+}; // class ResourceManager
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_RESOURCEMANAGER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_CORE_SUBTITLE_ATTR_PARSER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_CORE_SUBTITLE_ATTR_PARSER_H__
+
+#include <gst/gst.h>
+
+#include <boost/core/noncopyable.hpp>
+
+#include "trackrenderer/core/subtitle_attr.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class SubtitleAttrParser : private boost::noncopyable {
+ public:
+ explicit SubtitleAttrParser(GstBuffer* buf) : gstbuf_(buf) {}
+ SubtitleAttrListPtr Parse();
+
+ private:
+ GstBuffer* gstbuf_ = nullptr;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif //__PLUSPLAYER_SRC_TRACKRENDERER_CORE_SUBTITLE_ATTR_PARSER_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERER_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERER_H__
+
+#include <gst/gst.h>
+#include <gst/video/video-info.h>
+
+#include <boost/core/noncopyable.hpp>
+#include <condition_variable> // std::condition_variable
+#include <functional>
+#include <memory>
+#include <mutex> // std::mutex , std::unique_lock
+#include <string>
+#include <vector>
+
+#ifndef SOUNDBAR_PRODUCT
+#include "avoc.h"
+#endif
+
+#include "trackrenderer/audio_controller/audio_easing_controller.h"
+#include "trackrenderer/audio_controller/resyncaudio_policy.h"
+#include "trackrenderer/core/attribute.hpp"
+#include "trackrenderer/core/buffer.h"
+#include "trackrenderer/core/decoderinputbuffer.h"
+#include "trackrenderer/core/display.h"
+#include "trackrenderer/core/drm.h"
+#include "trackrenderer/core/elements.h"
+#include "trackrenderer/core/error.h"
+#include "trackrenderer/core/event.h"
+#include "trackrenderer/core/gstcaps_builder.h"
+#include "trackrenderer/core/latency.h"
+#include "trackrenderer/core/picturequality.h"
+#include "trackrenderer/core/pipeline.hpp"
+#include "trackrenderer/core/screen_saver.h"
+#include "trackrenderer/core/stream.h"
+#include "trackrenderer/core/subtitle_attr.h"
+#include "trackrenderer/core/track.h"
+#include "trackrenderer/latency_manager.h"
+#include "trackrenderer/latency_status_listener.h"
+#include "trackrenderer/resource.h"
+#include "trackrenderer/resource_conflict_listener.h"
+#include "trackrenderer/trackrenderer_attr.h"
+#include "trackrenderer/trackrenderer_debug.h"
+#include "trackrenderer/vr360.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+class ResourceManager;
+class Display;
+class ScreenSaver;
+
+enum class SubmitStatus {
+ kNotPrepared, // not prepared to get data
+ kHold, // valid data, hold this packet
+ kFull, // buffer already full
+ kSuccess, // submit succeeded
+ kDrop, // invalid data , drop this packet
+};
+
+static const double kDefaultPlaybackRate = 1.0;
+
+class TrackRenderer : public ResourceConflictListener,
+ LatencyStatusListener,
+ private boost::noncopyable {
+ public:
+ using Ptr = std::unique_ptr<TrackRenderer>;
+ ~TrackRenderer();
+ static Ptr Create() { return Ptr(new TrackRenderer); }
+
+ public: // Types
+ using AudioStartCbHandle = std::uint32_t;
+ using AudioStopCbHandle = std::uint32_t;
+ using AudioResyncCbHandle = std::uint32_t;
+ using VideoStartCbHandle = std::uint32_t;
+ using VideoStopCbHandle = std::uint32_t;
+ // LCOV_EXCL_START
+ class EventListener {
+ public:
+ virtual ~EventListener() {}
+ virtual void OnError(const ErrorType& err_code) {}
+ virtual void OnErrorMsg(const ErrorType& error_code, char* error_msg){};
+ virtual void OnResourceConflicted() {}
+ virtual void OnSeekDone() {}
+ virtual void OnFlushDone() {}
+ virtual void OnEos() {}
+ virtual void OnFirstDecodingDone() {}
+ virtual void OnEvent(const EventType& event, const EventMsg& msg_data) {}
+ virtual void OnSubtitleData(const DecoderInputBufferPtr& buf,
+ const SubtitleType& type) {}
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+ virtual void OnSubtitleData(const char* data, const int size,
+ const SubtitleType& type,
+ const uint64_t duration,
+ SubtitleAttrListPtr attr_list) {}
+#endif
+ virtual void OnClosedCaptionData(const char* data, const int size) {}
+ virtual void OnDrmInitData(int* drmhandle, unsigned int len,
+ unsigned char* psshdata, TrackType type) {}
+ virtual void OnBufferStatus(const TrackType& type,
+ const BufferStatus& status) {}
+ virtual void OnSeekData(const TrackType& type, const uint64_t offset) {}
+ virtual void OnMediaPacketGetTbmBufPtr(void** tbm_ptr,
+ bool is_scale_change) {}
+ virtual void OnMediaPacketVideoDecoded(const DecodedVideoPacket& packet) {}
+ virtual void OnMediaRawPacketVideoDecoded(
+ DecodedVideoRawModePacket& packet) {}
+ virtual void OnVideoDecoderUnderrun() {}
+ virtual void OnVideoLatencyStatus(const LatencyStatus& latency_status) {}
+ virtual void OnAudioLatencyStatus(const LatencyStatus& latency_status) {}
+ virtual void OnVideoHighLatency() {}
+ virtual void OnAudioHighLatency() {}
+ virtual void OnMultiviewStartVideo() {}
+ virtual void OnMultiviewStopVideo() {}
+ };
+ // LCOV_EXCL_STOP
+
+ enum class State { kInit, kWorking, kResourceConflicted, kStopped };
+
+ public: // Methods
+ bool Start();
+ bool Stop();
+ bool Prepare();
+ bool Pause();
+ bool Resume();
+ bool SetTrack(const std::vector<Track>& trackinfo);
+ void SetIniProperty(const std::map<std::string, bool>& Properties);
+ bool Seek(uint64_t time_millisecond, double playback_rate);
+ bool Seek(uint64_t time_millisecond, double playback_rate, bool audio_mute);
+ bool SetPlaybackRate(double playback_rate, bool audio_mute);
+ bool GetPlayingTime(uint64_t* curtime_in_msec);
+ bool GetDroppedFrames(void* frame_counts);
+ bool GetDroppedFramesForCatchup(TrackType type, void* frame_counts);
+ bool Deactivate(TrackType type);
+ bool Activate(TrackType type, const Track& track);
+ bool SubmitPacket(const DecoderInputBufferPtr& data,
+ SubmitStatus* retval = nullptr);
+ void SetDrm(const drm::Property& drm_property) {
+ drm_property_ = drm_property;
+ }
+ bool SetMatroskaColorInfo(const std::string& color_info);
+ void DrmLicenseAcquiredDone(TrackType type);
+ bool SetDisplayMode(const DisplayMode& mode);
+ bool SetDisplayRotate(const DisplayRotation& rotate);
+ void GetDisplayRotate(DisplayRotation* rotation);
+ bool SetDisplay(const DisplayType& type, uint32_t surface_id, long x, long y,
+ long w, long h);
+ bool SetDisplay(const DisplayType& type, void* obj);
+ bool SetDisplay(const DisplayType& type, void* ecore_wl2_window, int x, int y,
+ int w, int h);
+ bool SetDisplaySubsurface(const DisplayType& type, void* ecore_wl2_subsurface,
+ int x, int y, int w, int h);
+ bool SetDisplayRoi(const Geometry& roi);
+ bool SetVideoRoi(const CropArea& area);
+ bool ResizeRenderRect(const RenderRect& rect);
+ bool SetDisplayVisible(bool is_visible);
+ void GetDisplay(DisplayType* type, Geometry* area);
+ void GetDisplayMode(DisplayMode* mode);
+ bool SetAudioMute(bool is_mute);
+ bool SetVolume(const int& volume);
+ bool GetVolume(int* volume);
+ void OnResourceConflicted() override;
+ void OnVideoLatencyStatus(const LatencyStatus& latency_status) override;
+ void OnAudioLatencyStatus(const LatencyStatus& latency_status) override;
+ void OnVideoHighLatency() override;
+ void OnAudioHighLatency() override;
+ void RegisterListener(EventListener* listener);
+ void SetVideoStillMode(const StillMode& type) { still_mode_type_ = type; }
+ void SetAttribute(const Attribute& attr, const boost::any& value);
+ void GetAttribute(const Attribute& attr, boost::any* value);
+ void SetConfig(const std::string, const boost::any& value);
+ State GetState() { return state_; }
+ void SetAppId(const std::string& app_id);
+ void SetAppInfo(const PlayerAppInfo& app_info);
+ void FlushAppsrc(TrackType type, bool setbyuser);
+ void SetVideoFrameBufferType(DecodedVideoFrameBufferType type);
+ void SetVideoFrameBufferScaleResolution(const uint32_t& target_width,
+ const uint32_t& target_height);
+ bool SetDecodedVideoFrameRate(const Rational& request_framerate);
+ bool RenderVideoFrame();
+ bool SetAiFilter(void* aifilter);
+ void SetAlternativeAudioResource(const boost::any& value);
+ void SetCatchUpSpeed(const CatchUpSpeed& level);
+ void GetVideoLatencyStatus(LatencyStatus* status);
+ void GetAudioLatencyStatus(LatencyStatus* status);
+ void SetVideoMidLatencyThreshold(const unsigned int threshold);
+ void SetAudioMidLatencyThreshold(const unsigned int threshold);
+ void SetVideoHighLatencyThreshold(const unsigned int threshold);
+ void SetAudioHighLatencyThreshold(const unsigned int threshold);
+ bool InitAudioEasingInfo(const uint32_t& init_volume,
+ const uint32_t& init_elapsed_time,
+ const AudioEasingInfo& info);
+ bool UpdateAudioEasingInfo(const AudioEasingInfo& info);
+ bool GetAudioEasingInfo(uint32_t* current_volume, uint32_t* elapsed_time,
+ AudioEasingInfo* info);
+ bool StartAudioEasing();
+ bool StopAudioEasing();
+ bool GetVirtualRscId(const RscType type, int* virtual_id);
+ bool SetAdvancedPictureQualityType(const AdvPictureQualityType type);
+ bool SetResourceAllocatePolicy(const RscAllocPolicy policy);
+ bool SetVideoRendererType(const ResourceCategory video_renderer_type);
+ void SetVideoParDar(uint64_t time_millisecond, uint32_t par_n, uint32_t par_d,
+ uint32_t dar_n, uint32_t dar_d);
+
+ private:
+ enum VolumeLevel {
+ kVolumeNone = -1,
+ kVolumeMin = 0,
+ kVolumeMax = 100,
+ };
+
+ enum LowLatencyMode {
+ kLowLatencyModeNone = 0x0000,
+ kLowLatencyModeAudio = 0x0001,
+ kLowLatencyModeVideo = 0x0010,
+ kLowLatencyModeVideoDistortionConcealment = kLowLatencyModeVideo | 0x0020,
+ kLowLatencyModeDisableAVSync = 0x0100,
+ kLowLatencyModeDisablePreroll = 0x0200,
+ kLowLatencyModeDisableVideoQuality = 0x1000
+ };
+
+ enum class DropMode {
+ kDropModeNone = 0,
+ kDropModeAccordingToRate,
+ kDropModeAccordingToTable,
+ };
+
+ enum class SubState {
+ kUnknown,
+ kPaused,
+ kPlaying,
+ };
+
+ struct TrackContext {
+ int index = kInvalidTrackIndex;
+ bool is_enough_data = false;
+ bool need_update_segment = true;
+ std::function<bool(const Track*)> create_pipeline;
+ Track* track = nullptr;
+ };
+
+ struct AttributeValue {
+ boost::any value;
+ bool value_assigned = false;
+ };
+
+ struct RenderingStartTime {
+ bool is_set = false;
+ uint64_t time = 0;
+ };
+
+ struct DropContext {
+ std::mutex drop_mutex;
+ Rational track_fps;
+ Rational request_fps;
+ bool fps_changed = false;
+ DropMode drop_mode = DropMode::kDropModeNone;
+ uint32_t drop_rate = 0;
+ uint32_t base_num = 0;
+ };
+
+ public:
+ using TrackRendererAttributeSetter = AttributeSetter<Elements>;
+ using TrackRendererAttributeGetter = AttributeGetter<Elements>;
+
+ private:
+ using TrackRendererAttributeBinder = AttributeBinder<Elements>;
+ using Attributes =
+ std::map<trackrenderer::Attribute, TrackRendererAttributeBinder>;
+ using AttributesByElement =
+ std::map<Elements, std::vector<trackrenderer::Attribute>>;
+ using Config_Setter = std::function<bool(const boost::any&)>;
+
+ private:
+ TrackRenderer() noexcept;
+
+ static GstBusSyncReply GstBusSyncHandlerCb_(GstBus* bus, GstMessage* message,
+ gpointer data);
+ static GstPadProbeReturn GstPadProbeIdleCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata);
+ static GstPadProbeReturn GstPadProbeBlockCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata);
+ static void GstSubtitleDataHandOffCb_(GstElement* object, GstBuffer* buf,
+ GstPad* pad, gpointer userdata);
+ static void GstClosedCaptionHandOffCb_(GstElement* object, GstBuffer* buf,
+ GstPad* pad, gpointer userdata);
+ static void GstClosedCaptionPadAddedCb_(GstElement* element, GstPad* srcpad,
+ gpointer userdata);
+
+ static void GstAudioNeedDataCb_(GstElement* element, guint size,
+ gpointer userdata);
+ static void GstVideoNeedDataCb_(GstElement* element, guint size,
+ gpointer userdata);
+ static void GstSubtitleNeedDataCb_(GstElement* element, guint size,
+ gpointer userdata);
+ static void GstAudioEnoughDataCb_(GstElement* element, gpointer userdata);
+ static void GstVideoEnoughDataCb_(GstElement* element, gpointer userdata);
+ static void GstSubtitleEnoughDataCb_(GstElement* element, gpointer userdata);
+ static gboolean GstAudioSeekDataCb_(GstElement* element, guint64 offset,
+ gpointer user_data);
+ static gboolean GstVideoSeekDataCb_(GstElement* element, guint64 offset,
+ gpointer user_data);
+ static gboolean GstSubtitleSeekDataCb_(GstElement* element, guint64 offset,
+ gpointer user_data);
+ static gboolean GstVideoDrmInitDataCb_(int* drmhandle, unsigned int len,
+ unsigned char* psshdata,
+ void* userdata);
+ static gboolean GstAudioDrmInitDataCb_(int* drmhandle, unsigned int len,
+ unsigned char* psshdata,
+ void* userdata);
+ static void GstDecodedVideoReferenceBufferCb_(GstElement* element,
+ GstBuffer* buffer, GstPad* pad,
+ void* userdata);
+ static void GstDecodedVideoCopyBufferCb_(GstElement* element,
+ GstBuffer* buffer, GstPad* pad,
+ void* userdata);
+ static void GstDecodedVideoRawBufferCb_(GstElement* element,
+ GstBuffer* buffer, GstPad* pad,
+ void* userdata);
+ static void GstDecodedVideoScaleBufferCb_(GstElement* element,
+ GstBuffer* buffer, GstPad* pad,
+ void* userdata);
+ static void GstAiFilterResultCb_(GstElement* element, GstStructure* structure,
+ void* userdata);
+ static AttributesByElement InitAttributeByElementType_();
+ static void MultiviewStartAudioCb_(int player_id, void* data);
+ static void MultiviewStopAudioCb_(int player_id, void* data);
+ static void MultiviewResyncAudioCb_(int player_id, void* data);
+ static void MultiviewStartVideoCb_(int player_id, void* data);
+ static void MultiviewStopVideoCb_(int player_id, void* data);
+ static void VconfCb_(const std::string& name, const std::string& value,
+ void* userdata);
+ static GstPadProbeReturn GstSrcPadProbeBlockCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata);
+ static GstPadProbeReturn GstSrcPadProbeIdleCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata);
+ static GstPadProbeReturn GstPadProbeVideoPeekBlockCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata);
+ static GstPadProbeReturn GstPadProbeVideoDecodedCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata);
+ static GstPadProbeReturn GstPadProbeVideoDecInputCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata);
+
+ bool GetResource_(const TrackType& type);
+ bool CreatePipeline_();
+ void CreateAppSrc_(TrackType type, const std::string& mimetype);
+ void CreateDrmElement_(const Track& track);
+ const char* GetAudioSinkPluginName_(bool swdecoder,
+ const std::string& mimetype);
+ void CreateAudioSink_(const std::string& sink_name);
+ void CreateVideoDecoder_(const char* dec_name);
+ bool CreateVideoSink_();
+ void SetPropertyForAiFilter_();
+ void SetPropertyForDecodedVideoBufferWithDisplay_();
+ void SetPropertyForDecodedVideoBufferWithoutDisplay_();
+ void SetPropertyForDecodedVideoBuffer_();
+ bool CreateVideoPipeline_(const Track* track);
+ bool CreateAudioPipeline_(const Track* track);
+ bool CreateSwAudioPipeline_(const Track* track);
+ bool CreateRawAudioPipeline_(const Track* track);
+ bool CreateSubtitlePipeline_(const Track* track);
+ bool NeedAvocPlayerRegister_();
+ bool AvocPlayerRegister_();
+ bool AvocPlayRequest_();
+ bool AvocPlayerUnRegister_();
+ bool AddAiFilter_(void* aifilter);
+ void RemoveAiFilter_();
+ bool ReleaseResource_();
+ bool ControlDropRate_(uint64_t packet_pts, SubmitStatus* status);
+ bool RemoveDownstreamOfAppsrc_(TrackType type);
+ const char* GetDecoderPluginName_(TrackType type,
+ const std::string& mimetype);
+ void UpdateStartSegment_(GstClockTime start_time, const TrackType& type);
+ SubtitleType GetSubtitleType_(const GstBuffer* buffer);
+ void GstElementCreatedCb_(Elements element);
+ void SetDefaultAttributeValue_();
+ void SetAttribute_(const TrackRendererAttributeBinder& binder,
+ const boost::any& original_value,
+ const boost::any& new_value);
+ bool GetPlayingTime_(uint64_t* curtime_in_msec);
+ void UpdatePlaybackInfo_(bool is_updating);
+ void SetVolume_();
+ bool ActivateAudio_();
+ bool DeactivateAudio_();
+ void FlushDownStream_(Elements element, const char* key, gboolean reset_time);
+ void InitConfigSetterTable_();
+ bool SetAccurateSeekMode_(const boost::any& value);
+ bool SetLowLatencyMode_(const boost::any& value);
+ bool SetWindowStandAloneMode_(const boost::any& value);
+ bool SetVideoFramePeekMode_(const boost::any& value);
+ void GstElementLowLatency_(const TrackType& type);
+ void CreateTbmBufferManager_();
+ void SetVr360GpuModeSecure_(bool set);
+ void SetSequentialMode_();
+ void SetDirectCrop_(const std::string& app_id);
+ void GetResolutionInfo_(EventMsg* event_msg);
+ bool SetUnlimitedMaxBufferMode_(const boost::any& value);
+ bool SetVideoPreDisplayMode_(const boost::any& value);
+ bool SetStartRenderingTime_(const boost::any& value);
+ bool SetFmmMode_(const boost::any& value);
+ bool SetAlternativeVideoResource_(const boost::any& value);
+ bool SetVideoDecodingMode_(const boost::any& value);
+ bool SetLateVideoFrameDropMode_(const boost::any& value);
+ void SetVideoQualityInfo_();
+ void CompleteSeeking_(void);
+ bool IsSegmentUpdated_() const;
+ bool HasSubtitleOnly_() const;
+ void SetDefaultAppSrcSignals_(const TrackType& type);
+ bool SetResourceCenterCallback_();
+ bool UnsetResourceCenterCallback_();
+ void SetVconfCb_();
+ void UnsetVconfCb_();
+ bool StopAudioEasing_();
+ bool NeedSyncPause_();
+ bool ResyncAudio_();
+ void SetAudioOut_();
+ void UpdateTrackFrameRate_(const int& framerate_num,
+ const int& framerate_den);
+ void UpdateDecodedDropContext_();
+ bool NeedDropThisDecodedVideoBuffer_();
+
+ private:
+ EventListener* eventlistener_ = nullptr; // eventlistener is Defaultplayer
+ std::mutex resource_m_;
+ std::mutex internal_audio_m_;
+ std::condition_variable resource_cv_;
+
+ std::unique_ptr<ResourceManager> resource_manager_;
+ std::unique_ptr<Display> display_;
+ std::unique_ptr<ScreenSaver> screen_saver_;
+ std::unique_ptr<AudioEasingController> audio_easing_controller_;
+ ResourceCategory video_decoder_id_ = ResourceCategory::kVideoDecoder;
+ ResourceCategory video_renderer_id_ = ResourceCategory::kVideoRenderer;
+ ResourceCategory audio_out_id_ = ResourceCategory::kAudioRenderer;
+ ResourceCategory audio_decoder_id_ = ResourceCategory::kAudioDecoder;
+ RscAllocPolicy rsc_alloc_policy_ = RscAllocPolicy::kRscAllocExclusive;
+ AudioStartCbHandle audio_start_cb_id_ = 0;
+ AudioStopCbHandle audio_stop_cb_id_ = 0;
+ AudioResyncCbHandle audio_resync_cb_id_ = 0;
+ VideoStartCbHandle video_start_cb_id_ = 0;
+ VideoStopCbHandle video_stop_cb_id_ = 0;
+ int avoc_id_ = -1;
+ bool need_avoc_register = false;
+
+ GstCapsBuilder caps_builder_;
+ std::map<std::string, bool> properties_;
+ std::vector<Track> trackinfo_;
+ TrackContext trackctx_[kTrackTypeMax];
+ bool is_seeking_ = false;
+ bool is_flushing_ = false;
+ gboolean is_sound_mute_ = FALSE;
+ bool is_accurate_seek_ = false;
+ bool is_async_done_ = false;
+ gboolean window_stand_alone_mode_ = FALSE;
+ bool is_video_frame_peek_ = false;
+ std::uint32_t low_latency_mode_ = 0;
+ bool unlimited_max_buffer_mode_ = 0;
+ bool video_pre_display_mode_ = false;
+ gint fmm_mode_ = 0;
+ std::uint32_t video_decoding_mode_ = 0x02; // seamless mode
+ RenderingStartTime rendering_start_time_;
+ int volume_ = kVolumeNone;
+ uint64_t last_position_ = 0;
+ std::unique_ptr<Pipeline<Elements>> pipeline_;
+ StillMode still_mode_type_ = StillMode::kNone;
+ drm::Property drm_property_;
+ std::map<trackrenderer::Attribute, AttributeValue> set_attribute_values_;
+ bool is_stopped_ = false;
+ State state_ = State::kInit;
+ std::unique_ptr<debug::PlayinfoSetter> playback_info_;
+ DecodedVideoFrameBufferType decoded_buffer_type_ =
+ DecodedVideoFrameBufferType::kNone;
+ bool is_audioactivated_ = true;
+ bool is_videoactivated_ = true;
+ bool is_multiscreen_ = false;
+ std::unique_ptr<TbmBufferManager> tbm_buffer_manager_;
+ int use_seq_mode_ = 0;
+ void* aifilter_ = nullptr;
+ bool enable_direct_crop_ = false;
+ double playback_rate_ = kDefaultPlaybackRate;
+
+ SubState target_substate_ = SubState::kUnknown;
+
+ std::map<std::string, Config_Setter> config_setter_table_;
+ bool enable_audio_track_change_ = false;
+ gboolean support_audio_codec_change_ = FALSE;
+ bool support_videodec_underflow_pause_ = false;
+ bool need_avoc_sub_source_ = false;
+#ifndef SOUNDBAR_PRODUCT
+ avoc_sub_source_e avoc_sub_source_ = AVOC_SUB_SOURCE_NONE;
+#endif
+ std::unique_ptr<LatencyManager> latency_manager_;
+ int virtual_scaler_id_ = -1;
+ bool is_error_posted_ = false;
+
+ std::unique_ptr<ResyncAudioPolicy> resync_audio_policy_;
+ DisplayType display_type_ = DisplayType::kNone;
+ uint32_t scale_target_width_ = 960;
+ uint32_t scale_target_height_ = 540;
+ bool is_scale_size_changed_ = false;
+ std::mutex tbmmgr_m_;
+ DropContext decoded_drop_ctx_;
+ std::mutex scale_target_m_;
+
+ std::unique_ptr<Vr360> vr360_;
+
+ bool drop_all_late_video_ = false;
+ ParDarInfo par_dar_;
+ bool is_pardar_updated_ = false;
+
+ private:
+ static const Attributes kAttributes_;
+ static const AttributesByElement kAttributesByElem_;
+ static const GstCapsBuilder::Recipes kCapsRecipes_;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERER_H__
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERER_ATTR_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERER_ATTR_H__
+
+/*
+ * NOTICE
+ * If there is new attribute, please write details in below documents.
+ *
+ *
+ */
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+enum class Attribute {
+ kVideoQueueMaxByte, // std::uint64_t
+ kAudioQueueMaxByte, // std::uint64_t
+ kVideoQueueCurrentLevelByte, // std::uint64_t
+ kAudioQueueCurrentLevelByte, // std::uint64_t
+ kVideoMinByteThreshold, // std::uint32_t
+ kAudioMinByteThreshold, // std::uint32_t
+ kVideoQueueMaxTime, // std::uint64_t
+ kAudioQueueMaxTime, // std::uint64_t
+ kVideoQueueCurrentLevelTime, // std::uint64_t
+ kAudioQueueCurrentLevelTime, // std::uint64_t
+ kVideoMinTimeThreshold, // std::uint32_t
+ kAudioMinTimeThreshold, // std::uint32_t
+ kVideoSupportRotation, // std::uint32_t
+ kVideoRenderTimeOffset, // std::int64_t
+ kAudioRenderTimeOffset, // std::int64_t
+ kMax,
+};
+
+enum class ValueType {
+ kUnknown,
+ kInt32,
+ kUInt32,
+ kInt64,
+ kUInt64,
+ kMax,
+};
+
+// Trackrenderer configure names
+constexpr char ConfigNameAccurateSeekMode[] = "accurate-seek-mode";
+constexpr char ConfigNameLowLatencyMode[] = "low-latency-mode";
+constexpr char ConfigNameWindowStandAloneMode[] = "window-stand-alone-mode";
+constexpr char ConfigNameVideoFramePeekMode[] = "video-frame-peek-mode";
+constexpr char ConfigNameUnlimitedMaxBufferMode[] = "unlimited-max-buffer-mode";
+constexpr char ConfigNameVideoPreDisplayMode[] = "video-pre-display-mode";
+constexpr char ConfigNameStartRenderingTime[] = "start-rendering-time";
+constexpr char ConfigNameFmmMode[] = "fmm-mode";
+constexpr char ConfigNameAlternativeVideoResource[] =
+ "alternative-video-resource";
+constexpr char ConfigNameVideoDecodingMode[] = "video-decoding-mode";
+constexpr char ConfigNameLateVideoFrameDropMode[] = "late-video-frame-drop-mode";
+
+struct TrackRendererAttrInfo {
+ const ValueType value_type;
+ const plusplayer::trackrenderer::Attribute attr_enum;
+};
+static const std::map<std::string, TrackRendererAttrInfo>
+ kPluginPropertyInfoTable = {
+ {"video-queue-max-byte",
+ {ValueType::kUInt64,
+ plusplayer::trackrenderer::Attribute::kVideoQueueMaxByte}},
+ {"audio-queue-max-byte",
+ {ValueType::kUInt64,
+ plusplayer::trackrenderer::Attribute::kAudioQueueMaxByte}},
+ {"video-current-level-byte",
+ {ValueType::kUInt64,
+ plusplayer::trackrenderer::Attribute::kVideoQueueCurrentLevelByte}},
+ {"audio-current-level-byte",
+ {ValueType::kUInt64,
+ plusplayer::trackrenderer::Attribute::kAudioQueueCurrentLevelByte}},
+ {"video-min-byte-percent",
+ {ValueType::kUInt32,
+ plusplayer::trackrenderer::Attribute::kVideoMinByteThreshold}},
+ {"audio-min-byte-percent",
+ {ValueType::kUInt32,
+ plusplayer::trackrenderer::Attribute::kAudioMinByteThreshold}},
+ {"video-queue-max-time",
+ {ValueType::kUInt64,
+ plusplayer::trackrenderer::Attribute::kVideoQueueMaxTime}},
+ {"audio-queue-max-time",
+ {ValueType::kUInt64,
+ plusplayer::trackrenderer::Attribute::kAudioQueueMaxTime}},
+ {"video-current-level-time",
+ {ValueType::kUInt64,
+ plusplayer::trackrenderer::Attribute::kVideoQueueCurrentLevelTime}},
+ {"audio-current-level-time",
+ {ValueType::kUInt64,
+ plusplayer::trackrenderer::Attribute::kAudioQueueCurrentLevelTime}},
+ {"video-min-time-percent",
+ {ValueType::kUInt32,
+ plusplayer::trackrenderer::Attribute::kVideoMinTimeThreshold}},
+ {"audio-min-time-percent",
+ {ValueType::kUInt32,
+ plusplayer::trackrenderer::Attribute::kAudioMinTimeThreshold}},
+ {"video-support-rotation",
+ {ValueType::kUInt32,
+ plusplayer::trackrenderer::Attribute::kVideoSupportRotation}},
+ {"video-render-time-offset",
+ {ValueType::kInt64,
+ plusplayer::trackrenderer::Attribute::kVideoRenderTimeOffset}},
+ {"audio-render-time-offset",
+ {ValueType::kInt64,
+ plusplayer::trackrenderer::Attribute::kAudioRenderTimeOffset}}};
+
+static const std::map<std::string, ValueType> kConfigInfoTable = {
+ {ConfigNameAccurateSeekMode, ValueType::kUInt32},
+ {ConfigNameLowLatencyMode, ValueType::kUInt32},
+ {ConfigNameWindowStandAloneMode, ValueType::kUInt32},
+ {ConfigNameVideoFramePeekMode, ValueType::kUInt32},
+ {ConfigNameUnlimitedMaxBufferMode, ValueType::kUInt32},
+ {ConfigNameVideoPreDisplayMode, ValueType::kUInt32},
+ {ConfigNameStartRenderingTime, ValueType::kUInt64},
+ {ConfigNameFmmMode, ValueType::kUInt32},
+ {ConfigNameAlternativeVideoResource, ValueType::kUInt32},
+ {ConfigNameVideoDecodingMode, ValueType::kUInt32},
+ {ConfigNameLateVideoFrameDropMode, ValueType::kUInt32}};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERER_ATTR_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERERAPI_UTILS_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERERAPI_UTILS_H__
+
+#include <cassert>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "trackrenderer/core/appinfo.h"
+#include "trackrenderer/core/audioeasinginfo.h"
+#include "trackrenderer/core/buffer.h"
+#include "trackrenderer/core/display.h"
+#include "trackrenderer/core/drm.h"
+#include "trackrenderer/core/error.h"
+#include "trackrenderer/core/event.h"
+#include "trackrenderer/core/latency.h"
+#include "trackrenderer/core/picturequality.h"
+#include "trackrenderer/core/subtitle_attr.h"
+#include "trackrenderer/core/track.h"
+#include "trackrenderer/resource.h"
+#include "trackrenderer_capi/buffer.h"
+#include "trackrenderer_capi/decoderinputbuffer.h"
+#include "trackrenderer_capi/display.h"
+#include "trackrenderer_capi/drm.h"
+#include "trackrenderer_capi/error.h"
+#include "trackrenderer_capi/iniproperty.h"
+#include "trackrenderer_capi/latency.h"
+#include "trackrenderer_capi/track.h"
+#include "trackrenderer_capi/trackrenderer_capi.h"
+#include "trackrenderer_capi/trackrenderer_internal.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace capi_utils {
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+TrackRendererSubtitleAttr InitSubtitleAttr();
+#endif
+void MakeDrmProperty(drm::Property* drm_property,
+ const TrackRendererDrmProperty& properties);
+void MakeGeometry(Geometry* output, const TrackRendererGeometry& input);
+void MakeCropArea(CropArea* output, const TrackRendererCropArea& input);
+void MakeRenderRect(RenderRect* output, const TrackRendererRenderRect& input);
+std::map<std::string, bool> MakeIniProperty(
+ TrackRendererIniProperty* properties, int size);
+std::vector<Track> MakeTrack(const TrackRendererTrack* trackrenderertrack,
+ const int size);
+std::vector<Track> MakeTrackFromTrackHandle(
+ const TrackRendererTrackHandle* track_handle, const int size);
+void MakeTrackRendererGeometry(TrackRendererGeometry* geometry,
+ const Geometry& roi);
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+void MakeTrackRendererSubtitleAttr(TrackRendererSubtitleAttr* attr,
+ const SubtitleAttr& input_attr);
+#endif
+void MakePlayerAppInfo(const TrackRendererAppInfo* app_attr,
+ PlayerAppInfo& app_info);
+
+DisplayMode ConvertToDisplayMode(TrackRendererDisplayMode typevalue);
+DisplayRotation ConvertToDisplayRotate(TrackRendererDisplayRotate rotate);
+DisplayType ConvertToDisplayType(const TrackRendererDisplayType typevalue);
+drm::Type ConvertToDrmType(TrackRendererDrmType typevalue);
+StillMode ConvertToStillMode(TrackRendererStillMode typevalue);
+TrackType ConvertToTrackType(const TrackRendererTrackType typevalue);
+
+TrackRendererDisplayMode ConvertToTrackRendererDisplayMode(
+ const DisplayMode& mode);
+TrackRendererDisplayRotate ConvertToTrackRendererDisplayRotate(
+ const DisplayRotation& rotate);
+TrackRendererDisplayType ConvertToTrackRendererDisplayType(
+ const DisplayType& type);
+TrackRendererErrorType ConvertToTrackRendererErrorType(ErrorType type);
+TrackRendererEventType ConvertToTrackRendererEventType(const EventType& event);
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+TrackRendererSubtitleAttrType ConvertToTrackRendererSubtitleAttrType(
+ const SubtitleAttrType& type);
+#endif
+TrackRendererSubtitleType ConvertToTrackRendererSubtitleType(
+ const SubtitleType typevalue);
+TrackRendererTrackType ConvertToTrackRendererTrackType(const TrackType& type);
+
+TrackRendererBufferStatus ConvertToTrackRendererBufferStatus(
+ const BufferStatus& status);
+TrackRendererDecodedVideoPacket ConvertToDecodedVideoPacket(
+ const DecodedVideoPacket& packet);
+DecodedVideoFrameBufferType ConvertToVideoFrameBufferType(
+ const TrackRendererDecodedVideoFrameBufferType& type);
+DecodedVideoFrameBufferType ConvertToVideoFrameBufferTypeExt(
+ const TrackRendererDecodedVideoFrameBufferTypeExt& type);
+
+CatchUpSpeed ConvertToCatchUpSpeed(const TrackRendererCatchUpSpeed& level);
+TrackRendererLatencyStatus ConvertToTrackrendererLatencyStatus(
+ const LatencyStatus& status);
+
+AudioEasingType ConvertToAudioEasingType(
+ const TrackRendererAudioEasingType& type);
+TrackRendererAudioEasingType ConvertToTrackRendererAudioEasingType(
+ const AudioEasingType& type);
+void MakeAudioEasingInfo(AudioEasingInfo* easing_info,
+ const TrackRendererAudioEasingInfo* easing_attr);
+void MakeTrackRendererAudioEasingInfo(TrackRendererAudioEasingInfo* easing_attr,
+ const AudioEasingInfo& easing_info);
+bool ConvertToRscType(const TrackRendererRscType& typevalue, RscType* type);
+bool ConvertToAdvPictureQualityType(
+ const TrackRendererAdvPictureQualityType& typevalue,
+ AdvPictureQualityType* type);
+void MakeRational(Rational* rational_info,
+ const TrackRendererRational& rational_attr);
+bool ConvertToRscAllocPolicy(const TrackRendererRscAllocPolicy& policyvalue,
+ RscAllocPolicy* policy);
+bool ConvertToVideoRendererType(
+ const TrackRendererVideoRendererType& renderer_type,
+ ResourceCategory* rsc_category);
+} // namespace capi_utils
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERERAPI_UTILS_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERER_DEBUG_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERER_DEBUG_H__
+
+#include <boost/core/noncopyable.hpp>
+
+#include <string>
+#include <vector>
+
+#include "gst/gst.h"
+
+#include "trackrenderer/core/drm.h"
+#include "trackrenderer/core/track.h"
+#include "trackrenderer/core/appinfo.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+/*
+ * Debugging class to display stream information during playback through
+ * InformationTicker(RunningApps) application. ( InformationTicker : display any
+ * information by Wintok to help easier debugging ) How to execute : mute + 1 +
+ * 1 + 4 + mute
+ */
+namespace debug {
+
+struct StreamInfo {
+ const char* v_codec = nullptr;
+ const char* a_codec = nullptr;
+ int v_width = 0;
+ int v_height = 0;
+ int bitrate = 0;
+ drm::Type drm_type = drm::Type::kNone;
+ float fps = 0;
+};
+
+class PlayinfoSetter : private boost::noncopyable {
+ public:
+ using Ptr = std::unique_ptr<PlayinfoSetter>;
+ static Ptr Create();
+
+ public:
+ virtual ~PlayinfoSetter() {}
+
+ virtual void SetAppInfo(const PlayerAppInfo& app_info) = 0;
+ virtual bool SetStreamInfo(GstCaps* v_sink_caps, GstCaps* v_decoder_caps,
+ GstCaps* a_decoder_caps,
+ const std::vector<Track>& tracks,
+ drm::Type drm_type) = 0;
+ virtual void VconfSetMsgShow() = 0;
+ virtual void VconfSetMsgUpdate() = 0;
+ virtual void VconfSetMsgHide() = 0;
+
+ protected:
+ PlayinfoSetter() noexcept {}
+};
+
+} // namespace debug
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_TRACKRENDERER_DEBUG_H__
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_VCONF_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_VCONF_H__
+
+#include <boost/core/noncopyable.hpp>
+#include <list>
+#include <map>
+#include <string>
+#include <utility>
+#include <mutex>
+#include <vector>
+
+#include "vconf.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+constexpr char kMultiscreenInfoVconf[] = "memory/multiscreen/info";
+constexpr char kPowerAnimationVconf[] = "memory/welcome_mode/power_animation";
+constexpr char kTV2MobileStateVconf[] =
+ "memory/menu/network/screenmirroring/smsrc_status";
+
+typedef void (*vconf_cb)(const std::string& name, const std::string& value,
+ void* userdata);
+
+class Vconf : private boost::noncopyable {
+ public:
+ using VconfPair = std::pair<vconf_cb, void*>;
+ using VconfCbList = std::list<VconfPair>;
+ using VconfMap = std::map<std::string, VconfCbList>;
+
+ Vconf(Vconf const&) = delete;
+ Vconf(Vconf&&) = delete;
+ Vconf& operator=(Vconf const&) = delete;
+ Vconf& operator=(Vconf&&) = delete;
+
+ static Vconf& Instance() {
+ static Vconf instance;
+ return instance;
+ }
+
+ bool IsMultiscreenMode();
+ void SetVconfsCb(const std::vector<std::string>& names,
+ vconf_cb callback, void* userdata);
+ void UnsetVconfsCb(const std::vector<std::string>& names,
+ vconf_cb callback, void* userdata);
+
+ private:
+ Vconf();
+ ~Vconf();
+ void SetVconfCb_(const std::string& name, vconf_cb callback,
+ void* userdata);
+ void UnsetVconfCb_(const std::string& name, vconf_cb callback,
+ void* userdata);
+ static void VconfCb_(keynode_t* key, void* userdata);
+ void Notify_(const std::string& name, const std::string& value);
+ bool ParseMlsVconf_(const char* json);
+
+ private:
+ std::mutex vconf_m_;
+ VconfMap vconf_map_;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_VCONF_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_VERSION_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_VERSION_H__
+
+#define TRACKRENDERER_VERSION_INT(a, b, c) ((a) << 16 | (b) << 8 | (c))
+#define TRACKRENDERER_VERSION_DOT(a, b, c) a ##.## b ##.## c
+#define TRACKRENDERER_VERSION(a, b, c) TRACKRENDERER_VERSION_DOT(a, b, c)
+
+/**
+ * @brief Get version components from the full ::TRACKRENDERER_VERSION_INT int
+ */
+#define TRACKRENDERER_GET_VERSION_MAJOR(a) ((a) >> 16)
+#define TRACKRENDERER_GET_VERSION_MINOR(a) (((a)&0x00FF00) >> 8)
+#define TRACKRENDERER_GET_VERSION_MICRO(a) ((a)&0xFF)
+
+#define LIB_TRACKRENDERER_VERSION_MAJOR 0
+#define LIB_TRACKRENDERER_VERSION_MINOR 0
+#define LIB_TRACKRENDERER_VERSION_MICRO 2
+
+#define LIB_TRACKRENDERER_VERSION_INT \
+ TRACKRENDERER_VERSION_INT(LIB_TRACKRENDERER_VERSION_MAJOR, \
+ LIB_TRACKRENDERER_VERSION_MINOR, \
+ LIB_TRACKRENDERER_VERSION_MICRO)
+
+#define LIB_TRACKRENDERER_VERSION \
+ TRACKRENDERER_VERSION(LIB_TRACKRENDERER_VERSION_MAJOR, \
+ LIB_TRACKRENDERER_VERSION_MINOR, \
+ LIB_TRACKRENDERER_VERSION_MICRO)
+
+#define TRACKRENDERER_STRINGIFY(s) TRACKRENDERER_TOSTRING(s)
+#define TRACKRENDERER_TOSTRING(s) #s
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_VERSION_H__
--- /dev/null
+//
+// @ Copyright [2021] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_SRC_TRACKRENDERER_VR360_POLICY_H__
+#define __PLUSPLAYER_SRC_TRACKRENDERER_VR360_POLICY_H__
+
+#include <boost/core/noncopyable.hpp>
+#include <string>
+#include <utility>
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+using Vr360TzHandle = void *;
+using Vr360Initialize = int (*)(Vr360TzHandle *handle);
+using Vr360Finalize = int (*)(Vr360TzHandle handle);
+using Vr360SetGpuMode = int (*)(Vr360TzHandle handle, int mode);
+
+class Vr360 : private boost::noncopyable {
+ public:
+ enum class GpuMode {
+ kVr360GpuModeSecure = 0x301, /**< Give GPU secure property */
+ kVr360GpuModeNonSecure, /**< Take secure property from GPU */
+ };
+
+ explicit Vr360();
+ ~Vr360();
+
+ void Vr360TzSetGpuMode(bool set);
+
+ private:
+ Vr360TzHandle vr360_tz_handle_ = nullptr;
+};
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+
+#endif // __PLUSPLAYER_SRC_TRACKRENDERER_VR360_POLICY_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <cassert>
+
+#include "trackrenderer/core/utils/log.h"
+#include "trackrenderer/latency_manager.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+LatencyManager::LatencyManager(LatencyStatusListener* listener)
+ : event_listener_(listener) {
+ assert(listener && "listener is nullptr!!");
+}
+
+LatencyManager::~LatencyManager() {
+ if (latency_checker_task_.valid()) {
+ is_stopped_ = true;
+ latency_checker_task_.wait();
+ }
+ TRACKRENDERER_INFO("Bye~");
+}
+
+void LatencyManager::SetVideoMidLatencyThreshold(const unsigned int threshold) {
+ video_mid_latency_threshold_ = threshold;
+ TRACKRENDERER_INFO("Video MidLatencyThreshold : %d",
+ video_mid_latency_threshold_);
+ checker_precondition_.set(
+ static_cast<int>(CheckerPrecondition::kSetThreshold));
+ StartLatencyChecker_();
+}
+
+void LatencyManager::SetAudioMidLatencyThreshold(const unsigned int threshold) {
+ audio_mid_latency_threshold_ = threshold;
+ TRACKRENDERER_INFO("Audio MidLatencyThreshold : %d",
+ audio_mid_latency_threshold_);
+ checker_precondition_.set(
+ static_cast<int>(CheckerPrecondition::kSetThreshold));
+ StartLatencyChecker_();
+}
+
+void LatencyManager::SetVideoHighLatencyThreshold(
+ const unsigned int threshold) {
+ video_high_latency_threshold_ = threshold;
+ TRACKRENDERER_INFO("Video HighLatencyThreshold : %d",
+ video_high_latency_threshold_);
+ checker_precondition_.set(
+ static_cast<int>(CheckerPrecondition::kSetThreshold));
+ StartLatencyChecker_();
+}
+
+void LatencyManager::SetAudioHighLatencyThreshold(
+ const unsigned int threshold) {
+ audio_high_latency_threshold_ = threshold;
+ TRACKRENDERER_INFO("Audio HighLatencyThreshold : %d",
+ audio_high_latency_threshold_);
+ checker_precondition_.set(
+ static_cast<int>(CheckerPrecondition::kSetThreshold));
+ StartLatencyChecker_();
+}
+
+void LatencyManager::SetCatchUpSpeed(const CatchUpSpeed& level) {
+ speed_level_ = level;
+
+ if (pipeline_) {
+ if (speed_level_ != CatchUpSpeed::kNone) {
+ int drop_rate = CalculateDropRate_();
+ pipeline_->SetProperty(Elements::kSinkVideo, "frame-drop-rate",
+ drop_rate);
+ pipeline_->SetProperty(Elements::kSinkAudio, "frame-drop-rate",
+ drop_rate);
+ }
+ }
+}
+
+void LatencyManager::GetVideoLatencyStatus(LatencyStatus* status) {
+ *status = video_latency_status_;
+ TRACKRENDERER_INFO("LatencyStatus is %d", static_cast<int>(*status));
+}
+
+void LatencyManager::GetAudioLatencyStatus(LatencyStatus* status) {
+ *status = audio_latency_status_;
+ TRACKRENDERER_INFO("LatencyStatus is %d", static_cast<int>(*status));
+}
+
+void LatencyManager::SetPipeline(Pipeline<Elements>* pipeline) {
+ if (pipeline_) {
+ assert(0 && "pipeline already exist");
+ }
+ pipeline_ = pipeline;
+ checker_precondition_.set(
+ static_cast<int>(CheckerPrecondition::kSetPipeline));
+ StartLatencyChecker_();
+}
+
+void LatencyManager::UnsetPipeline() {
+ TRACKRENDERER_ENTER;
+ is_stopped_ = true;
+ checker_precondition_.reset(
+ static_cast<int>(CheckerPrecondition::kSetPipeline));
+ std::lock_guard<std::mutex> mutex(pipeline_m_);
+ pipeline_ = nullptr;
+ TRACKRENDERER_LEAVE;
+}
+
+void LatencyManager::UpdateVideoFrameStatus(
+ LatencyManager::UpdatePacketStatus status) {
+ switch (status) {
+ case LatencyManager::UpdatePacketStatus::kFlush:
+ TRACKRENDERER_INFO("Flush, reset the value!");
+ submitted_video_frames_ = 0;
+ return;
+ case LatencyManager::UpdatePacketStatus::kSubmit:
+ ++submitted_video_frames_;
+ return;
+ default:
+ TRACKRENDERER_ERROR("Wrong status:%d", static_cast<int>(status));
+ }
+}
+
+void LatencyManager::StartLatencyChecker_() {
+ TRACKRENDERER_ENTER;
+ std::bitset<static_cast<int>(CheckerPrecondition::kMax)> precondition;
+ precondition.set();
+
+ if (precondition != checker_precondition_) {
+ TRACKRENDERER_INFO("Can't make checker thread, precondition = %s",
+ checker_precondition_.to_string().c_str());
+ return;
+ }
+
+ if (latency_checker_task_.valid()) {
+ TRACKRENDERER_INFO("checker task is already valid");
+ return;
+ }
+
+ pipeline_->SetProperty(Elements::kAppSrcAudio, "noqueue", FALSE);
+ pipeline_->SetProperty(Elements::kAppSrcAudio, "max-bytes", 0);
+
+ if (speed_level_ != CatchUpSpeed::kNone) {
+ int drop_rate = CalculateDropRate_();
+ pipeline_->SetProperty(Elements::kSinkVideo, "frame-drop-rate", drop_rate);
+ pipeline_->SetProperty(Elements::kSinkAudio, "frame-drop-rate", drop_rate);
+ }
+
+ latency_checker_task_ = std::async(
+ std::launch::async, &LatencyManager::LatencyCheckerTask_, this);
+ if (!latency_checker_task_.valid()) {
+ TRACKRENDERER_ERROR("checker task is Not valid");
+ return;
+ }
+ TRACKRENDERER_LEAVE;
+}
+
+void LatencyManager::LatencyCheckerTask_() {
+ TRACKRENDERER_ENTER;
+ while (!is_stopped_) {
+ std::unique_lock<std::mutex> mutex(pipeline_m_);
+ if (pipeline_ == nullptr) return;
+
+ uint consumed_frames = 0;
+ bool ret = pipeline_->GetProperty(Elements::kSinkVideo, "consumed-frames",
+ &consumed_frames);
+ int video_latency = submitted_video_frames_ - consumed_frames;
+ if (video_latency < 0) {
+ TRACKRENDERER_ERROR(
+ "ERROR!! consumed_frames = %u, submitted_video_frames_ = %u",
+ consumed_frames, submitted_video_frames_);
+ video_latency = 0;
+ }
+ if (ret == true) HandleVideoLatency_(video_latency);
+
+ uint audio_latency = 0;
+ ret = pipeline_->GetProperty(Elements::kAppSrcAudio, "frame-count",
+ &audio_latency);
+ if (ret == true) HandleAudioLatency_(audio_latency);
+ mutex.unlock();
+
+ // TODO: How to set the sleep time.
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ }
+ TRACKRENDERER_LEAVE;
+}
+
+void LatencyManager::HandleVideoLatency_(const unsigned int video_latency) {
+ if (is_stopped_) return;
+
+ LatencyStatus prev_video_latency_status_ = video_latency_status_;
+ video_latency_status_ = GetVideoLatencyStatus_(video_latency);
+ bool is_status_changed =
+ video_latency_status_ != prev_video_latency_status_ ? true : false;
+
+ if (is_status_changed) {
+ event_listener_->OnVideoLatencyStatus(video_latency_status_);
+ if (video_latency_status_ == LatencyStatus::kHigh) // To be deleted. later.
+ event_listener_->OnVideoHighLatency();
+
+ if (prev_video_latency_status_ == LatencyStatus::kLow) {
+ pipeline_->SetProperty(Elements::kSinkVideo, "is-catchup-mode", TRUE);
+ } else if (video_latency_status_ == LatencyStatus::kLow) {
+ pipeline_->SetProperty(Elements::kSinkVideo, "is-catchup-mode", FALSE);
+ }
+ }
+}
+
+void LatencyManager::HandleAudioLatency_(const unsigned int audio_latency) {
+ if (is_stopped_) return;
+
+ LatencyStatus prev_audio_latency_status_ = audio_latency_status_;
+ audio_latency_status_ = GetAudioLatencyStatus_(audio_latency);
+ bool is_status_changed =
+ audio_latency_status_ != prev_audio_latency_status_ ? true : false;
+
+ if (is_status_changed) {
+ event_listener_->OnAudioLatencyStatus(audio_latency_status_);
+ if (audio_latency_status_ == LatencyStatus::kHigh) // To be deleted. later.
+ event_listener_->OnAudioHighLatency();
+
+ if (prev_audio_latency_status_ == LatencyStatus::kLow) {
+ pipeline_->SetProperty(Elements::kSinkAudio, "is-catchup-mode", TRUE);
+ } else if (audio_latency_status_ == LatencyStatus::kLow) {
+ pipeline_->SetProperty(Elements::kSinkAudio, "is-catchup-mode", FALSE);
+ }
+ }
+}
+
+int LatencyManager::CalculateDropRate_() {
+ int ret = 0;
+ if (speed_level_ == CatchUpSpeed::kSlow) {
+ // TODO
+ ret = 4;
+ } else if (speed_level_ == CatchUpSpeed::kMid) {
+ // TODO
+ ret = 3;
+ } else {
+ // TODO
+ ret = 2;
+ }
+ return ret;
+}
+
+LatencyStatus LatencyManager::GetVideoLatencyStatus_(
+ const unsigned int latency) {
+ TRACKRENDERER_DEBUG(
+ "Latency : %d, video_mid_latency_threshold : %d, "
+ "video_high_latency_threshold : %d",
+ latency, video_mid_latency_threshold_, video_high_latency_threshold_);
+
+ if (video_mid_latency_threshold_ == UINT32_MAX) {
+ if (latency < video_high_latency_threshold_) {
+ return LatencyStatus::kLow;
+ } else {
+ return LatencyStatus::kHigh;
+ }
+ }
+
+ if (latency < video_mid_latency_threshold_) {
+ return LatencyStatus::kLow;
+ } else if (latency >= video_high_latency_threshold_) {
+ return LatencyStatus::kHigh;
+ } else {
+ return LatencyStatus::kMid;
+ }
+}
+
+LatencyStatus LatencyManager::GetAudioLatencyStatus_(
+ const unsigned int latency) {
+ TRACKRENDERER_DEBUG(
+ "Latency : %d, audio_mid_latency_threshold : %d, "
+ "audio_high_latency_threshold : %d",
+ latency, audio_mid_latency_threshold_, audio_high_latency_threshold_);
+
+ if (audio_mid_latency_threshold_ == UINT32_MAX) {
+ if (latency < audio_high_latency_threshold_) {
+ return LatencyStatus::kLow;
+ } else {
+ return LatencyStatus::kHigh;
+ }
+ }
+
+ if (latency < audio_mid_latency_threshold_) {
+ return LatencyStatus::kLow;
+ } else if (latency >= audio_high_latency_threshold_) {
+ return LatencyStatus::kHigh;
+ } else {
+ return LatencyStatus::kMid;
+ }
+}
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/core/pipeline.hpp"
+
+#include <dlfcn.h>
+
+#include <cassert>
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace trustzone {
+
+#define GSTREAMER_PLUGIN_LIB(NAME) "/usr/lib/gstreamer-1.0/" #NAME
+
+static std::once_flag lib_loaded;
+static void* tzappsrc_lib = nullptr;
+using TzAppsrcPushBuffer = int(*)(void* appsrc, void* buffer);
+static TzAppsrcPushBuffer tz_appsrc_push_buffer;
+
+bool InitTzAppsrc() {
+ bool retval = true;
+ auto loadlib = [](bool& ret) {
+ tzappsrc_lib = dlopen(GSTREAMER_PLUGIN_LIB(libgsttzappsrc.so), RTLD_LAZY);
+ if (!tzappsrc_lib) {
+ TRACKRENDERER_ERROR("libgsttzappsrc.so open failed: %s", dlerror());
+ assert(0);
+ ret = false;
+ return;
+ }
+ tz_appsrc_push_buffer =
+ (TzAppsrcPushBuffer)dlsym(tzappsrc_lib, "gst_tz_app_src_push_buffer");
+ if (!tz_appsrc_push_buffer) {
+ TRACKRENDERER_ERROR("Failed to import gst_tz_app_src_push_buffer");
+ assert(0);
+ ret = false;
+ return;
+ }
+ };
+ std::call_once(lib_loaded, loadlib, retval);
+ return retval;
+}
+
+bool GstAppsrcPushBuffer(GstElement* appsrc, GstBuffer* buffer) {
+ if(tz_appsrc_push_buffer(appsrc,buffer) != GST_FLOW_OK) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace trustzone
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/resourcemanager.h"
+
+#include <stdlib.h> // free
+
+#include <algorithm> // std::min
+#include <boost/scope_exit.hpp>
+#include <cassert> // assert()
+#include <chrono> // std::chrono
+#include <functional>
+#include <map> // std::map
+#include <stdexcept> // std::out_of_range
+#include <thread> // std::this_thread::sleep_for
+#include <unordered_map>
+
+#include "resource_center.h"
+#include "ri-api.h"
+#include "ri-module-api.h"
+#include "rm_module_api.h"
+#include "trackrenderer/core/utils/log.h"
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace internal {
+
+constexpr int kMaxFrameRate = 60;
+constexpr int kDefaultColorDepth = 8;
+constexpr int kDefaultSamplingFormat = 1;
+
+// clang-format off
+const std::map<std::string, std::string> MimeTypetoCodecNameConverter = {
+ {"video/x-h264", "H264"}, {"video/x-h265", "HEVC"},
+ {"video/mpeg1", "MPEG1"}, {"video/mpeg2", "MPEG2"},
+ {"video/mpeg4", "MPEG4"}, {"video/x-msmpeg", "MPEG4"},
+ {"video/x-wmv", "WMV"}, {"video/x-h263", "H263"},
+ {"video/x-pn-realvideo", "RV"}, {"video/x-vp8", "VP8"},
+ {"video/x-vp9", "VP9"}, {"video/x-divx", "MPEG4"},
+ {"video/x-xvid", "MPEG4"}, {"video/x-3ivx", "MPEG4"},
+ {"video/x-avs", "AVS"}, {"video/x-avs+", "AVS+"},
+ {"video/x-jpeg", "MJPEG"}, {"video/x-av1", "AV1"},
+ {"video/x-h264_tz", "H264"},
+ {"video/x-h265_tz", "HEVC"}, {"video/mpeg1_tz", "MPEG1"},
+ {"video/mpeg2_tz", "MPEG2"}, {"video/mpeg4_tz", "MPEG4"},
+ {"video/x-msmpeg_tz", "MPEG4"}, {"video/x-wmv_tz", "WMV"},
+ {"video/x-h263_tz", "H263"}, {"video/x-pn-realvideo_tz", "RV"},
+ {"video/x-vp8_tz", "VP8"}, {"video/x-vp9_tz", "VP9"},
+ {"video/x-divx_tz", "MPEG4"}, {"video/x-xvid_tz", "MPEG4"},
+ {"video/x-3ivx_tz", "MPEG4"}, {"video/x-avs_tz", "AVS"},
+ {"video/x-avs+_tz", "AVS+"}, {"video/x-jpeg_tz", "MJPEG"},
+ {"video/x-av1_tz", "AV1"},
+
+ {"audio/mpeg", "MPEG"}, {"audio/x-aac", "AAC"},
+ {"audio/x-true-hd", "TrueHD"}, {"audio/x-ac3", "AC3"},
+ {"audio/x-eac3", "E-AC3"}, {"audio/ac3", "AC3"},
+ {"audio/x-ac4", "AC4"}, {"audio/x-adpcm", "ADPCM"},
+ {"audio/x-wma", "WMA"}, {"audio/x-vorbis", "Vorbis"},
+ {"audio/x-gst-fourcc-mha1", "MPEG-H"},{"audio/x-pn-realaudio", "G2Cook"},
+ {"audio/x-gst-fourcc-mhm1", "MPEG-H"},{"audio/x-opus", "OPUS"},
+ {"audio/x-alaw", "ALAW"}, {"audio/x-mulaw", "MULAW"},
+ {"audio/mpeg_tz", "MPEG"}, {"audio/x-aac_tz", "AAC"},
+ {"audio/x-ac3_tz", "AC3"}, {"audio/x-eac3_tz", "E-AC3"},
+ {"audio/ac3_tz", "AC3"}, {"audio/x-ac4_tz", "AC4"},
+ {"audio/x-adpcm_tz", "ADPCM"}, {"audio/x-wma_tz", "WMA"},
+ {"audio/x-vorbis_tz", "Vorbis"},{"audio/x-pn-realaudio_tz", "G2Cook"},
+ {"audio/x-gst-fourcc-mha1_tz", "MPEG-H"},{"audio/x-raw_tz", "PCM"},
+ {"audio/x-gst-fourcc-mhm1_tz", "MPEG-H"},{"audio/x-opus_tz", "OPUS"},
+ {"audio/x-alaw_tz", "ALAW"}, {"audio/x-mulaw_tz", "MULAW"},
+};
+// clang-format on
+
+const std::map<ResourceCategory, rm_rsc_category_e> TypeToCategoryConverter = {
+ {ResourceCategory::kVideoDecoder, RM_CATEGORY_VIDEO_DECODER},
+ {ResourceCategory::kVideoDecoderSub, RM_CATEGORY_VIDEO_DECODER_SUB},
+ {ResourceCategory::kVideoRenderer, RM_CATEGORY_SCALER},
+ {ResourceCategory::kVideoRendererSub, RM_CATEGORY_SCALER_SUB},
+ {ResourceCategory::kVideoRendererSub2, RM_CATEGORY_SCALER_SUB2},
+ {ResourceCategory::kVideoRendererSub3, RM_CATEGORY_SCALER_SUB3},
+ {ResourceCategory::kAudioDecoder, RM_CATEGORY_AUDIO_DECODER},
+ {ResourceCategory::kAudioDecoderSub, RM_CATEGORY_AUDIO_DECODER_SUB},
+ {ResourceCategory::kAudioRenderer, RM_CATEGORY_AUDIO_MAIN_OUT},
+ {ResourceCategory::kAudioRendererSub, RM_CATEGORY_AUDIO_SUB_OUT}};
+
+const std::map<ResourceCategory, rm_rsc_category_e>
+ MultiviewTypeToCategoryConverter = {
+ {ResourceCategory::kVideoDecoder, RM_CATEGORY_VIDEO_DECODER},
+ {ResourceCategory::kVideoDecoderSub, RM_CATEGORY_VIDEO_DECODER},
+ {ResourceCategory::kVideoRenderer, RM_CATEGORY_SCALER},
+ {ResourceCategory::kVideoRendererSub, RM_CATEGORY_SCALER},
+ {ResourceCategory::kVideoRendererSub2, RM_CATEGORY_SCALER},
+ {ResourceCategory::kVideoRendererSub3, RM_CATEGORY_SCALER},
+ {ResourceCategory::kAudioDecoder, RM_CATEGORY_AUDIO_DECODER},
+ {ResourceCategory::kAudioDecoderSub, RM_CATEGORY_AUDIO_DECODER},
+ {ResourceCategory::kAudioRenderer, RM_CATEGORY_AUDIO_MAIN_OUT},
+ {ResourceCategory::kAudioRendererSub, RM_CATEGORY_AUDIO_MAIN_OUT}};
+
+const std::map<RscAllocPolicy, rm_requests_resource_state_e>
+ TypeToResourceStateConverter = {
+ {RscAllocPolicy::kRscAllocExclusive, RM_STATE_EXCLUSIVE},
+ {RscAllocPolicy::kRscAllocConditional, RM_STATE_EXCLUSIVE_CONDITIONAL}};
+
+const std::string GetVideoCodecName(const ResourceProperty& property) {
+ try {
+ std::string mime_type = property.track.mimetype;
+ const std::string video_mpeg = "video/mpeg";
+ if (mime_type.find(video_mpeg) != std::string::npos)
+ mime_type.insert(video_mpeg.size(),
+ std::to_string(property.track.version));
+ std::string codec_name = MimeTypetoCodecNameConverter.at(mime_type);
+ if (property.is_vr360) {
+ codec_name.append("_VR360");
+ }
+ return codec_name;
+ } catch (const std::out_of_range& oor) {
+ TRACKRENDERER_ERROR(" Unknown mimetype [%s] ",
+ property.track.mimetype.c_str());
+ return "";
+ }
+}
+
+const std::string GetAudioCodecName(const ResourceProperty& property) {
+ try {
+ std::string mime_type = property.track.mimetype;
+ std::string codec_name = MimeTypetoCodecNameConverter.at(mime_type);
+ if (property.track.version > 1) {
+ codec_name = "AAC";
+ // TODO: Need to check "stream-format" info according to mmplayer logic.
+ // codec_name should be changed to "HE-AAC" if "stream-format" is "loas".
+ }
+ return codec_name;
+ } catch (const std::out_of_range& oor) {
+ TRACKRENDERER_ERROR(" Unknown mimetype [%s] ",
+ property.track.mimetype.c_str());
+ return "";
+ }
+}
+
+double GetFramerate(const int& framerate_num, const int& framerate_den) {
+ int framerate = 0;
+ if (framerate_num > 0 && framerate_den > 0) {
+ framerate = framerate_num / framerate_den;
+ } else {
+ framerate = kMaxFrameRate;
+ return framerate;
+ }
+
+ framerate = std::min(framerate, kMaxFrameRate);
+ return framerate;
+}
+
+int GetWidth(const Track& trackinfo) {
+ return trackinfo.maxwidth ? trackinfo.maxwidth : trackinfo.width;
+ // TODO(euna7.ko) should i check if value is 0 ?
+ // mm get the value from caps. if value is 0, mm set the valeu to HD
+ // resolution.
+}
+
+int GetHeight(const Track& trackinfo) {
+ return trackinfo.maxheight ? trackinfo.maxheight : trackinfo.height;
+}
+
+inline bool OverFHD(const int width, const int height) {
+ return (width > kMaxFhdWidth || height > kMaxFhdHeight) ? true : false;
+}
+
+int GetExplicitDecoderOption(const ResourceProperty& property) {
+ constexpr int kAuto = 0;
+ if (property.is_multiview) return kAuto;
+ if (property.category == ResourceCategory::kVideoDecoder ||
+ property.category == ResourceCategory::kAudioDecoder)
+ return RM_DEVICE_OPT_MAIN;
+ else if (property.category == ResourceCategory::kVideoDecoderSub ||
+ property.category == ResourceCategory::kAudioDecoderSub)
+ return RM_DEVICE_OPT_SUB;
+
+ return kAuto;
+}
+
+int GetVideoDecCategoryOption(const ResourceProperty& property) {
+ int category_option = RM_CATEGORY_NONE;
+ int width = internal::GetWidth(property.track);
+ int height = internal::GetHeight(property.track);
+
+ ri_video_category_option_request_s opt;
+ memset(&opt, 0, sizeof(ri_video_category_option_request_s));
+ std::string codecname = GetVideoCodecName(property);
+ if (codecname.empty()) return RI_ERROR;
+ opt.codec_name = codecname.c_str();
+ opt.color_depth = kDefaultColorDepth;
+ opt.h_size = width;
+ opt.v_size = height;
+ opt.framerate = (int)GetFramerate(property.track.framerate_num,
+ property.track.framerate_den);
+ opt.sampling_format = kDefaultSamplingFormat;
+ if (property.is_ndecoding) {
+ category_option = (ri_get_n_decoder_category_id(&opt));
+ TRACKRENDERER_INFO(
+ " request n decoder codec_name %s, with : %d, height : %d category "
+ "option %d",
+ opt.codec_name, opt.h_size, opt.v_size, category_option);
+ } else {
+ category_option = ri_get_capable_video_category_id(&opt);
+ }
+ if (OverFHD(width, height) || property.is_ndecoding) return category_option;
+ int explicit_device = GetExplicitDecoderOption(property);
+ category_option |= explicit_device;
+ return category_option;
+}
+
+// LCOV_EXCL_START
+int GetVideoJpegDecCategoryOption(const ResourceProperty& property) {
+ std::string codecname = GetVideoCodecName(property);
+ if (codecname.empty()) return RI_ERROR;
+ int width = internal::GetWidth(property.track);
+ int height = internal::GetHeight(property.track);
+ int category_option = ri_get_jpeg_category_id(codecname.c_str(), width);
+ if (OverFHD(width, height)) return category_option;
+
+ int explicit_device = GetExplicitDecoderOption(property);
+ category_option |= explicit_device;
+ return category_option;
+}
+// LCOV_EXCL_STOP
+
+int GetAudioDecCategoryOption(const ResourceProperty& property) {
+ ri_audio_category_option_request_s opt;
+ memset(&opt, 0, sizeof(ri_audio_category_option_request_s));
+ std::string codecname = GetAudioCodecName(property);
+ if (codecname.empty()) return RI_ERROR;
+ opt.codec_name = codecname.c_str();
+ if (property.is_dual_sound) opt.mixing_mode = RI_MIXING_MODE_MULTIVIEW;
+
+ int explicit_device = GetExplicitDecoderOption(property);
+ int category_option =
+ (ri_get_capable_audio_category_id(&opt) | explicit_device);
+ return category_option;
+}
+
+int GetScalerCategoryOption(const ResourceProperty& property) {
+ constexpr int kAutoScaler = RM_CATEGORY_SCALER;
+ int explicit_device = internal::TypeToCategoryConverter.at(property.category);
+ int category_option = property.is_multiview ? kAutoScaler : explicit_device;
+ return category_option;
+}
+
+int GetAudioOutCategoryOption(const ResourceProperty& property) {
+ constexpr int kAutoAudioOut = RM_CATEGORY_AUDIO_MAIN_OUT;
+ int explicit_device = internal::TypeToCategoryConverter.at(property.category);
+ int category_option = property.is_multiview ? kAutoAudioOut : explicit_device;
+ return category_option;
+}
+
+rm_rsc_category_e GetCategoryID(const ResourceProperty& property) {
+ rm_rsc_category_e category_id;
+ if (property.is_multiview)
+ category_id =
+ internal::MultiviewTypeToCategoryConverter.at(property.category);
+ else
+ category_id = internal::TypeToCategoryConverter.at(property.category);
+
+ if (property.track.type == kTrackTypeVideo) {
+ if (GetVideoCodecName(property) == "MJPEG") {
+ return RM_CATEGORY_MJPEG_DECODER;
+ }
+ }
+ return category_id;
+}
+
+bool TryAlloc(ResourceManagerHandle rmhandle, const rm_category_request_s& req,
+ rm_device_return_s* devices) {
+ int try_count = 5;
+ bool ret = false;
+ while (try_count--) {
+ int rm_ret = rm_allocate_resources(rmhandle, &req, devices);
+ if (rm_ret == RM_ERROR) {
+ TRACKRENDERER_ERROR("rm_allocate_resources fail[RM_ERROR]");
+ break;
+ } else if (rm_ret == RM_OK) {
+ ret = true;
+ break;
+ }
+ TRACKRENDERER_ERROR("rm_allocate_resources failed rm_ret[%d]", rm_ret);
+ TRACKRENDERER_ERROR("retry_count[%d]", try_count);
+ std::this_thread::sleep_for(std::chrono::milliseconds(15));
+ }
+ if (devices->device_id[0] == 0 || devices->omx_comp_name[0] == nullptr) {
+ TRACKRENDERER_ERROR("device id or comp name error");
+ ret = false;
+ }
+ return ret;
+}
+
+bool NeedSwDecoderResource(const ResourceProperty& property) {
+ if (property.category != ResourceCategory::kVideoDecoder &&
+ property.category != ResourceCategory::kVideoDecoderSub &&
+ property.category != ResourceCategory::kAudioDecoder &&
+ property.category != ResourceCategory::kAudioDecoderSub)
+ return false;
+ if (property.use_sw) return true;
+ return false;
+}
+
+/*
+ * ResourceManager handle will be saved in rsc_list after
+ * ResourceManager created which will be removed before
+ * the handle released in ~ResourceManager(). rsc_lock is to
+ * protect that ResourceManager handle is valid if resource
+ * conflict happens before trackrenderer handle released.
+ */
+static std::mutex rsc_lock;
+static std::list<ResourceManager*> rsc_list;
+
+void AddRMHandle(ResourceManager* handle) {
+ TRACKRENDERER_INFO("Resource manager handle[%p]", handle);
+ std::lock_guard<std::mutex> lock(rsc_lock);
+ rsc_list.push_back(handle);
+}
+
+void RemoveRMHandle(ResourceManager* handle) {
+ TRACKRENDERER_INFO("Resource manager handle[%p]", handle);
+ std::lock_guard<std::mutex> lock(rsc_lock);
+ auto iter = find(rsc_list.begin(), rsc_list.end(), handle);
+ if (iter != rsc_list.end()) {
+ rsc_list.erase(iter);
+ } else {
+ TRACKRENDERER_ERROR("Invalid handle[%p]", handle);
+ }
+}
+
+using CategoryOption = std::function<int(const ResourceProperty& property)>;
+static std::unordered_map<rm_rsc_category_e, CategoryOption> CategoryOptionMap =
+ {{RM_CATEGORY_VIDEO_DECODER, internal::GetVideoDecCategoryOption},
+ {RM_CATEGORY_VIDEO_DECODER_SUB, internal::GetVideoDecCategoryOption},
+ {RM_CATEGORY_MJPEG_DECODER, internal::GetVideoJpegDecCategoryOption},
+ {RM_CATEGORY_SCALER, internal::GetScalerCategoryOption},
+ {RM_CATEGORY_SCALER_SUB, internal::GetScalerCategoryOption},
+ {RM_CATEGORY_SCALER_SUB2, internal::GetScalerCategoryOption},
+ {RM_CATEGORY_SCALER_SUB3, internal::GetScalerCategoryOption},
+ {RM_CATEGORY_AUDIO_DECODER, internal::GetAudioDecCategoryOption},
+ {RM_CATEGORY_AUDIO_DECODER_SUB, internal::GetAudioDecCategoryOption},
+ {RM_CATEGORY_AUDIO_MAIN_OUT, internal::GetAudioOutCategoryOption},
+ {RM_CATEGORY_AUDIO_SUB_OUT, internal::GetAudioOutCategoryOption}};
+
+} // namespace internal
+
+ResourceManager::ResourceManager(ResourceConflictListener* listener)
+ : resourceconflict_listener_(listener) {
+ assert(listener && "listener is nullptr!!");
+
+ internal::AddRMHandle(this);
+ // TODO(js4716.chun) :
+ // Add Exception - Assert
+ // rm_register(&handle_, ResourceConflictCallback , ...);
+ int ret = rm_register((rm_resource_cb)ResourceConflictCallback_, (void*)this,
+ &resourcemanager_handle_, NULL);
+ assert(ret == RM_OK && "rm_register() is failed");
+ TRACKRENDERER_INFO_P(resourceconflict_listener_,
+ "resource manager handle = %d, Listener = %p, this = %p",
+ resourcemanager_handle_, listener, this);
+}
+
+ResourceManager::~ResourceManager() {
+ if (resourcemanager_handle_ != -1) {
+ int ret = rm_unregister(resourcemanager_handle_);
+ assert(ret == RM_OK && "rm_unregister() is failed");
+ TRACKRENDERER_ERROR_P(resourceconflict_listener_,
+ "rm_unregister() was done");
+ }
+
+ internal::RemoveRMHandle(this);
+}
+
+// LCOV_EXCL_START
+rm_cb_result ResourceManager::ResourceConflictCallback_(
+ ResourceManagerHandle rmhandle, rm_callback_type eventtype,
+ rm_device_request_s* info, void* userdata) {
+ TRACKRENDERER_DEBUG(
+ "ResourceManagerHandle[%d], cb_type[%d], conflicted num[%d], "
+ "device_id[%d]",
+ rmhandle, eventtype, info->request_num, info->device_id[0]);
+
+ ResourceManager* resourcemanager = static_cast<ResourceManager*>(userdata);
+ /* Do not access non-static variables except callback input parameters
+ before checking whether ResourceManager handle is valid.*/
+ std::lock_guard<std::mutex> lock(internal::rsc_lock);
+ auto iter = find(internal::rsc_list.begin(), internal::rsc_list.end(),
+ resourcemanager);
+ if (iter == internal::rsc_list.end()) {
+ TRACKRENDERER_WARN("ResourceManager[%p] is released", resourcemanager);
+ return RM_CB_RESULT_OK;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock2(resourcemanager->control_lock_);
+ if (resourcemanager->resourcelist_.empty()) {
+ TRACKRENDERER_ERROR_P(resourcemanager->resourceconflict_listener_,
+ "resourcelist is empty! return.");
+ return RM_CB_RESULT_OK;
+ }
+ }
+
+ if (!resourcemanager->resourceconflict_listener_) {
+ TRACKRENDERER_ERROR("confilict listener is nullptr. ERROR!");
+ assert(0 && "resourceconflict_listener_ is nullptr");
+ return RM_CB_RESULT_ERROR;
+ }
+
+ resourcemanager->resourceconflict_listener_->OnResourceConflicted();
+ TRACKRENDERER_LEAVE_P(resourcemanager->resourceconflict_listener_);
+ return RM_CB_RESULT_OK;
+}
+// LCOV_EXCL_STOP
+
+void ResourceManager::SetAppId(const std::string& appid) {
+ if (appid.empty()) return;
+ rm_set_app_id(resourcemanager_handle_, const_cast<char*>(appid.c_str()));
+ app_id_ = appid;
+}
+
+bool ResourceManager::NeedPulseResource(const ResourceProperty& property) {
+ if (property.category != ResourceCategory::kAudioRenderer || !property.use_sw)
+ return false;
+ rm_resource_list_h list;
+
+ BOOST_SCOPE_EXIT(&list) { rm_free_resource_list(list); }
+ BOOST_SCOPE_EXIT_END
+
+ rm_get_resource_list(RM_CATEGORY_AUDIO_MAIN_OUT, &list);
+ rm_resource_h rsc = rm_resource_list_get_first(list);
+ while (rsc) {
+ if (rm_resource_get_state(rsc) != RM_RSC_STATE_FREE) {
+ bool is_main_device = ri_is_main_device(rm_resource_get_id(rsc));
+ if (is_main_device) {
+ const char* app_id = rm_resource_get_app_id(rsc);
+ if (!strcmp(app_id_.c_str(), app_id)) {
+ TRACKRENDERER_INFO_P(
+ resourceconflict_listener_,
+ "need pulsesink current app id [%s] allocated app id[%s]",
+ app_id_.c_str(), app_id);
+ return true;
+ }
+ }
+ }
+ rsc = rm_resource_list_get_next(list);
+ }
+ return false;
+}
+
+bool ResourceManager::AllocPulseResource_(const ResourceProperty& property) {
+ resourcelist_.emplace_back(property, kPulseSinkComponentName);
+ return true;
+}
+
+bool ResourceManager::AllocSwDecoderResource_(
+ const ResourceProperty& property) {
+ resourcelist_.emplace_back(property, kSwDecoderComponentName);
+ return true;
+}
+
+bool ResourceManager::AllocHWResource_(const ResourceProperty& property) {
+ rm_category_request_s req;
+ memset(&req, 0, sizeof(rm_category_request_s));
+ AllocatedState avail_state = MakeCategoryRequest_(property, &req);
+ TRACKRENDERER_ERROR_P(resourceconflict_listener_, "available state[%d]",
+ static_cast<int>(avail_state));
+
+ if (avail_state == AllocatedState::kFailed) return false;
+ if (avail_state == AllocatedState::kSkipped) {
+ resourcelist_.emplace_back(property, avail_state);
+ return true;
+ }
+ return TryAllocHWResource_(property, req);
+}
+
+bool ResourceManager::TryAllocHWResource_(const ResourceProperty& property,
+ const rm_category_request_s& req) {
+ rm_device_return_s devices;
+ memset(&devices, 0, sizeof(rm_device_return_s));
+ if (internal::TryAlloc(resourcemanager_handle_, req, &devices) == false) {
+ TRACKRENDERER_ERROR_P(resourceconflict_listener_,
+ "rm_allocate_resources FAIL.");
+ return false;
+ }
+
+ TRACKRENDERER_INFO_P(resourceconflict_listener_,
+ "resource Type[%d], device id[%d], comp_name[%s]",
+ static_cast<int>(property.category),
+ devices.device_id[0], devices.omx_comp_name[0]);
+
+ resourcelist_.emplace_back(property, devices.device_id[0],
+ devices.omx_comp_name[0]);
+
+ if (devices.device_node[0]) {
+ free(devices.device_node[0]);
+ }
+ if (devices.omx_comp_name[0]) {
+ free(devices.omx_comp_name[0]);
+ }
+ return true;
+}
+
+int ResourceManager::GetCategoryOption_(const rm_rsc_category_e& category_id,
+ const ResourceProperty& property) {
+ auto& mapper = internal::CategoryOptionMap.at(category_id);
+ int capable_category = mapper(property);
+ TRACKRENDERER_INFO_P(resourceconflict_listener_,
+ "capable_category_option : %d", capable_category);
+ int category_option = rc_get_capable_category_id(
+ resourcemanager_handle_, app_id_.c_str(), capable_category);
+ TRACKRENDERER_INFO_P(resourceconflict_listener_, "capable_category_id : %d",
+ category_option);
+ return category_option;
+}
+
+AllocatedState ResourceManager::MakeCategoryRequest_(
+ const ResourceProperty& property, rm_category_request_s* req) {
+ req->request_num = 1;
+ req->state[0] =
+ internal::TypeToResourceStateConverter.at(property.rsc_alloc_policy);
+ req->category_id[0] = internal::GetCategoryID(property);
+ req->category_option[0] = GetCategoryOption_(req->category_id[0], property);
+ if (req->category_option[0] == RI_ERROR)
+ return AllocatedState::kFailed;
+ else if (req->category_option[0] == RI_CATEGORY_NOT_PERMITTED)
+ return AllocatedState::kSkipped;
+
+ return AllocatedState::kSuccess;
+}
+
+bool ResourceManager::Alloc_(const ResourceProperty& property) {
+ if (internal::NeedSwDecoderResource(property)) {
+ return AllocSwDecoderResource_(property);
+ } else if (NeedPulseResource(property)) {
+ return AllocPulseResource_(property);
+ } else {
+ return AllocHWResource_(property);
+ }
+}
+
+bool ResourceManager::Alloc(const std::list<ResourceProperty>& properties) {
+ for (const auto& property : properties) {
+ TRACKRENDERER_INFO_P(resourceconflict_listener_,
+ "Resource Type[%d], use_sw[%d]",
+ static_cast<int>(property.category), property.use_sw);
+ if (Alloc_(property) == false) return false;
+ }
+ return true;
+}
+
+bool ResourceManager::Dealloc() {
+ TRACKRENDERER_ENTER_P(resourceconflict_listener_);
+
+ if (resourcelist_.empty()) return true;
+
+ rm_device_request_s devices;
+ memset(&devices, 0, sizeof(rm_device_request_s));
+
+ if (resourcelist_.size() == 0) return true; // There is no resource.
+
+ unsigned int device_num = 0;
+ std::for_each(resourcelist_.begin(), resourcelist_.end(),
+ [&devices, &device_num](const Resource& resource) {
+ if (resource.GetDeviceId() != 0) {
+ devices.device_id[device_num++] = resource.GetDeviceId();
+ }
+ });
+ if (device_num > 0) {
+ devices.request_num = device_num;
+ int rm_err = rm_deallocate_resources(resourcemanager_handle_, &devices);
+ if (rm_err != RM_OK) {
+ TRACKRENDERER_ERROR_P(resourceconflict_listener_,
+ "rm_deallocate_resources failed, rm_err[%d]",
+ rm_err);
+ return false;
+ }
+ } else {
+ TRACKRENDERER_INFO_P(resourceconflict_listener_,
+ "therer are no resource to dealloc");
+ return true;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(control_lock_);
+ resourcelist_.clear();
+ }
+ TRACKRENDERER_LEAVE_P(resourceconflict_listener_);
+ return true;
+}
+
+bool ResourceManager::Dealloc(const ResourceCategory type) {
+ TRACKRENDERER_ENTER_P(resourceconflict_listener_);
+ if (resourcelist_.empty()) return true;
+ rm_device_request_s devices;
+ memset(&devices, 0, sizeof(rm_device_request_s));
+ devices.request_num = 1;
+
+ auto compare = [type](Resource& item) noexcept -> bool {
+ return item.GetResourceCategory() == type;
+ };
+ auto target =
+ std::find_if(resourcelist_.begin(), resourcelist_.end(), compare);
+
+ // There is no resource.
+ if (target == resourcelist_.end()) return true;
+ if (target->GetDeviceId() == 0) {
+ std::lock_guard<std::mutex> lock(control_lock_);
+ resourcelist_.erase(target);
+ TRACKRENDERER_DEBUG_P(resourceconflict_listener_,
+ "resourcelist_.size() = %d", resourcelist_.size());
+ return true;
+ }
+
+ devices.device_id[0] = target->GetDeviceId();
+
+ int rm_err = rm_deallocate_resources(resourcemanager_handle_, &devices);
+ if (rm_err != RM_OK) {
+ TRACKRENDERER_ERROR_P(resourceconflict_listener_,
+ "rm_deallocate_resources failed, rm_err[%d]", rm_err);
+ return false;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(control_lock_);
+ resourcelist_.erase(target);
+ TRACKRENDERER_DEBUG_P(resourceconflict_listener_,
+ "resourcelist_.size() = %d", resourcelist_.size());
+ }
+ TRACKRENDERER_LEAVE_P(resourceconflict_listener_);
+ return true;
+}
+
+std::string ResourceManager::GetComponentName(const ResourceCategory type) {
+ auto compare = [type](const Resource& item) noexcept -> bool {
+ return item.GetResourceCategory() == type;
+ };
+ auto target =
+ std::find_if(resourcelist_.begin(), resourcelist_.end(), compare);
+ if (target == resourcelist_.end()) return {};
+
+ TRACKRENDERER_INFO_P(resourceconflict_listener_, "Comp name : %s",
+ (target->GetComponentName()).c_str());
+ return target->GetComponentName();
+}
+
+int ResourceManager::GetRawHandle() { return resourcemanager_handle_; }
+
+AllocatedState ResourceManager::GetAllocatedState(const ResourceCategory type) {
+ auto compare = [type](const Resource& item) noexcept -> bool {
+ return item.GetResourceCategory() == type;
+ };
+ auto target =
+ std::find_if(resourcelist_.begin(), resourcelist_.end(), compare);
+ if (target == resourcelist_.end()) return AllocatedState::kFailed;
+
+ TRACKRENDERER_INFO_P(resourceconflict_listener_, "Allocated State : %d",
+ static_cast<int>(target->GetAllocatedState()));
+ return target->GetAllocatedState();
+}
+
+bool ResourceManager::IsMainDevice(const ResourceCategory& type) {
+ TRACKRENDERER_ENTER_P(resourceconflict_listener_);
+ auto compare = [type](const Resource& item) noexcept -> bool {
+ return item.GetResourceCategory() == type;
+ };
+ auto target =
+ std::find_if(resourcelist_.begin(), resourcelist_.end(), compare);
+ if (target == resourcelist_.end()) {
+ TRACKRENDERER_WARN_P(resourceconflict_listener_,
+ "This resource[%d] is not allocated.",
+ static_cast<int>(type));
+ return true;
+ }
+
+ int device_id = target->GetDeviceId();
+ if (type == ResourceCategory::kVideoRenderer ||
+ type == ResourceCategory::kVideoRendererSub ||
+ type == ResourceCategory::kVideoRendererSub2 ||
+ type == ResourceCategory::kVideoRendererSub3) {
+ device_id = rm_find_device_id(device_id);
+ }
+ bool is_main = ri_is_main_device(device_id);
+ TRACKRENDERER_INFO_P(resourceconflict_listener_,
+ "This resrouce[%d] is [%s] device",
+ static_cast<int>(type), is_main ? "main" : "sub");
+ return is_main;
+}
+
+int ResourceManager::GetDeviceId(const ResourceCategory& type) {
+ TRACKRENDERER_ENTER_P(resourceconflict_listener_);
+ auto compare = [type](const Resource& item) noexcept -> bool {
+ return item.GetResourceCategory() == type;
+ };
+ auto target =
+ std::find_if(resourcelist_.begin(), resourcelist_.end(), compare);
+ if (target == resourcelist_.end()) {
+ TRACKRENDERER_ERROR_P(resourceconflict_listener_,
+ "This resource[%d] is not allocated ",
+ static_cast<int>(type));
+ return -1; // invalid device id
+ }
+ return target->GetDeviceId();
+}
+
+bool ResourceManager::IsAudioFocused() {
+ bool is_audio_focused = rc_is_audio_focused(app_id_.c_str());
+ TRACKRENDERER_INFO("is audio focused [%d]", is_audio_focused);
+ return is_audio_focused;
+}
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/core/screen_saver.h"
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+static int timeout_timer_id_ = 0;
+#define ResetTimeoutValue 30 // timeout second
+
+ScreenSaver::~ScreenSaver() {}
+
+void ScreenSaver::StartTimeout() {
+ if (timeout_timer_id_ == 0) {
+ int ret = screensaver_reset_timeout();
+ TRACKRENDERER_DEBUG("screensaver_reset_timeout start : ret = %d", ret);
+ timeout_timer_id_ =
+ g_timeout_add_seconds(ResetTimeoutValue, ResetTimeout, NULL);
+ TRACKRENDERER_DEBUG("timer_id = %d", timeout_timer_id_);
+ }
+}
+
+void ScreenSaver::StopTimeout() {
+ if (timeout_timer_id_ != 0) {
+ TRACKRENDERER_DEBUG("timer_id = %d", timeout_timer_id_);
+ g_source_remove(timeout_timer_id_);
+ timeout_timer_id_ = 0;
+ }
+ TRACKRENDERER_DEBUG("remove screensaver reset timer");
+}
+
+gboolean ScreenSaver::ResetTimeout(gpointer data) {
+ int ret = screensaver_reset_timeout();
+ TRACKRENDERER_DEBUG("screensaver_reset_timeout resume: ret = %d", ret);
+ return true;
+}
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/subtitle_attr_parser.h"
+
+#include <algorithm>
+#include <cassert>
+
+#include "gst/ffsubtitle/gstsubattributes.h"
+
+#include "trackrenderer/core/utils/log.h"
+// LCOV_EXCL_START
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace internal {
+void AddSubtitleAttribute(std::list<SubtitleAttr>* list,
+ const SubtitleAttrType type, const boost::any& value,
+ const unsigned int start_pos,
+ const unsigned int stop_pos) {
+ list->emplace_back(type, start_pos, stop_pos, value, -1);
+}
+
+bool ComparingStartTime(const SubtitleAttr& a, const SubtitleAttr& b) {
+ return (a.start_time < b.start_time);
+}
+
+constexpr int kAttrInvalidIntVal = -1;
+constexpr unsigned int kAttrInvalidUintVal =
+ std::numeric_limits<unsigned int>::max();
+constexpr float kAttrInvalidFloatVal = 0.0;
+
+void ParseSubtitleRegionAttr(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_ENTER;
+ while (attribute) {
+ gfloat x_pos = kAttrInvalidIntVal, y_pos = kAttrInvalidFloatVal,
+ width = kAttrInvalidFloatVal, height = kAttrInvalidFloatVal;
+ attribute = gst_sub_attribute_region_parse(attribute, &x_pos, &y_pos,
+ &width, &height);
+ TRACKRENDERER_DEBUG(
+ "parsed new region attribute: x(%f), y(%f), width(%f), "
+ "height(%f)",
+ x_pos, y_pos, width, height);
+ if (x_pos != kAttrInvalidFloatVal) {
+ boost::any value = x_pos;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrRegionXPos, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ if (y_pos != kAttrInvalidFloatVal) {
+ boost::any value = y_pos;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrRegionYPos, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ if (width != kAttrInvalidFloatVal) {
+ boost::any value = width;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrRegionWidth, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ if (height != kAttrInvalidFloatVal) {
+ boost::any value = height;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrRegionHeight, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ }
+}
+
+void ParseSubtitleWindowAttr(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_ENTER;
+ while (attribute) {
+ gfloat x_padding = kAttrInvalidFloatVal, y_padding = kAttrInvalidFloatVal;
+ gint l_margin = kAttrInvalidIntVal, r_margin = kAttrInvalidIntVal,
+ t_margin = kAttrInvalidIntVal, b_margin = kAttrInvalidIntVal;
+ guint bg_color = kAttrInvalidUintVal;
+ gfloat opacity = kAttrInvalidFloatVal;
+ guint show_bg = kAttrInvalidUintVal;
+ attribute = gst_sub_attribute_window_parse(
+ attribute, &x_padding, &y_padding, &l_margin, &r_margin, &t_margin,
+ &b_margin, &bg_color, &opacity, &show_bg);
+ TRACKRENDERER_DEBUG(
+ "parsed new window attribute: x_padding(%f), y_padding(%f), "
+ "l_margin(%d), r_margin(%d), t_margin(%d), b_margin(%d), "
+ "bg_color(%u), opacity(%f), show_bg(%u)",
+ x_padding, y_padding, l_margin, r_margin, t_margin, b_margin, bg_color,
+ opacity, show_bg);
+ if (x_padding != kAttrInvalidFloatVal) {
+ boost::any value = x_padding;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWindowXPadding, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ if (y_padding != kAttrInvalidFloatVal) {
+ boost::any value = y_padding;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWindowYPadding, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ if (l_margin != kAttrInvalidIntVal) {
+ boost::any value = l_margin;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWindowLeftMargin, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ if (r_margin != kAttrInvalidIntVal) {
+ boost::any value = r_margin;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWindowRightMargin,
+ value, kAttrInvalidUintVal,
+ kAttrInvalidUintVal);
+ }
+ if (t_margin != kAttrInvalidIntVal) {
+ boost::any value = t_margin;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWindowTopMargin, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ if (b_margin != kAttrInvalidIntVal) {
+ boost::any value = b_margin;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWindowBottomMargin,
+ value, kAttrInvalidUintVal,
+ kAttrInvalidUintVal);
+ }
+ if (bg_color != kAttrInvalidUintVal) {
+ boost::any value = bg_color;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWindowBgColor, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ if (opacity != kAttrInvalidFloatVal) {
+ boost::any value = opacity;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWindowOpacity, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ if (show_bg != kAttrInvalidUintVal) {
+ boost::any value = show_bg;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWindowShowBg, value,
+ kAttrInvalidUintVal, kAttrInvalidUintVal);
+ }
+ }
+}
+
+void ParseSubtitleFontAttr(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_DEBUG("Now parse attribute font!");
+ while (attribute) {
+ GstSubAttributeScope scope;
+ guint start_index = kAttrInvalidUintVal, stop_index = kAttrInvalidUintVal;
+ gchar* family = nullptr;
+ gfloat size = kAttrInvalidFloatVal;
+ gint weight = kAttrInvalidIntVal, style = kAttrInvalidIntVal;
+ guint color = kAttrInvalidUintVal, bg_color = kAttrInvalidUintVal;
+ gfloat opacity = kAttrInvalidFloatVal, bg_opacity = kAttrInvalidFloatVal;
+ guint text_outline_color = kAttrInvalidUintVal,
+ text_outline_tn = kAttrInvalidUintVal;
+ gint text_outline_br = kAttrInvalidIntVal, v_align = kAttrInvalidIntVal,
+ h_align = kAttrInvalidIntVal;
+ attribute = gst_sub_attribute_font_parse(
+ attribute, &scope, &start_index, &stop_index, &family, &size, &weight,
+ &style, &color, &bg_color, &opacity, &bg_opacity, &text_outline_color,
+ &text_outline_tn, &text_outline_br, &v_align, &h_align);
+ TRACKRENDERER_DEBUG(
+ "passed a font attribute: scope(%u), start_index(%u), "
+ "stop_index(%u), family(%s), size(%f),"
+ "weight(%d), style(%d), color(%u), bg_color(%u), opacity(%f), "
+ "bg_opacity(%f), text_outline_color(%u),"
+ "text_outline_tn(%u),text_outline_br(%d), v_align(%d), "
+ "h_align(%d)",
+ scope, start_index, stop_index, family, size, weight, style, color,
+ bg_color, opacity, bg_opacity, text_outline_color, text_outline_tn,
+ text_outline_br, v_align, h_align);
+ if (family != nullptr) {
+ boost::any value = std::string(family);
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontFamily, value,
+ start_index, stop_index);
+ }
+ if (size != kAttrInvalidFloatVal) {
+ boost::any value = size;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontSize, value,
+ start_index, stop_index);
+ }
+ if (weight != kAttrInvalidIntVal) {
+ boost::any value = weight;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontWeight, value,
+ start_index, stop_index);
+ }
+ if (style != kAttrInvalidIntVal) {
+ boost::any value = style;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontStyle, value,
+ start_index, stop_index);
+ }
+ if (color != kAttrInvalidUintVal) {
+ boost::any value = color;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontColor, value,
+ start_index, stop_index);
+ }
+ if (bg_color != kAttrInvalidUintVal) {
+ boost::any value = bg_color;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontBgColor, value,
+ start_index, stop_index);
+ }
+ if (opacity != kAttrInvalidFloatVal) {
+ boost::any value = opacity;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontOpacity, value,
+ start_index, stop_index);
+ }
+ if (bg_opacity != kAttrInvalidFloatVal) {
+ boost::any value = bg_opacity;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontBgOpacity, value,
+ start_index, stop_index);
+ }
+ if (text_outline_color != kAttrInvalidUintVal) {
+ boost::any value = text_outline_color;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontTextOutlineColor,
+ value, start_index, stop_index);
+ }
+ if (text_outline_tn != kAttrInvalidUintVal) {
+ boost::any value = text_outline_tn;
+ internal::AddSubtitleAttribute(attr_list,
+ kSubAttrFontTextOutlineThickness, value,
+ start_index, stop_index);
+ }
+ if (text_outline_br != kAttrInvalidIntVal) {
+ boost::any value = text_outline_br;
+ internal::AddSubtitleAttribute(attr_list,
+ kSubAttrFontTextOutlineBlurRadius, value,
+ start_index, stop_index);
+ }
+ if (v_align != kAttrInvalidIntVal) {
+ boost::any value = v_align;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontVerticalAlign,
+ value, start_index, stop_index);
+ }
+ if (h_align != kAttrInvalidIntVal) {
+ boost::any value = h_align;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontHorizontalAlign,
+ value, start_index, stop_index);
+ }
+ }
+}
+void ParseSubtitleFontSizeWeightStyleClolr(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_DEBUG("Now parse attribute font shortcut SIZE_WEIGHT_STYLE_COLOR!");
+ while (attribute) {
+ GstSubAttributeScope scope;
+ guint start_index = kAttrInvalidUintVal, stop_index = kAttrInvalidUintVal;
+ gfloat size = kAttrInvalidFloatVal;
+ gint weight = kAttrInvalidIntVal, style = kAttrInvalidIntVal;
+ guint color = kAttrInvalidUintVal;
+ attribute = gst_sub_attribute_font_sc_size_weight_style_color_parse(
+ attribute, &scope, &start_index, &stop_index, &size, &weight, &style,
+ &color);
+ TRACKRENDERER_DEBUG(
+ "passed a font attribute: scope(%u), start_index(%u), "
+ "stop_index(%u), size(%f),"
+ "weight(%d), style(%d), color(%u)",
+ scope, start_index, stop_index, size, weight, style, color);
+ if (size != kAttrInvalidFloatVal) {
+ boost::any value = size;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontSize, value,
+ start_index, stop_index);
+ }
+ if (weight != kAttrInvalidIntVal) {
+ boost::any value = weight;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontWeight, value,
+ start_index, stop_index);
+ }
+ if (style != kAttrInvalidIntVal) {
+ boost::any value = style;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontStyle, value,
+ start_index, stop_index);
+ }
+ if (color != kAttrInvalidUintVal) {
+ boost::any value = color;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontColor, value,
+ start_index, stop_index);
+ }
+ }
+}
+void ParseSubtitleFontColorOpacities(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_DEBUG("Now parse attribute font shortcut SIZE_COLORS_AND_OPACITIES!");
+ while (attribute) {
+ GstSubAttributeScope scope;
+ guint start_index = kAttrInvalidUintVal, stop_index = kAttrInvalidUintVal,
+ color = kAttrInvalidUintVal, bg_color = kAttrInvalidUintVal;
+ gfloat opacity = kAttrInvalidFloatVal, bg_opacity = kAttrInvalidFloatVal;
+ guint text_outline_color = kAttrInvalidUintVal;
+ attribute = gst_sub_attribute_font_sc_colors_and_opacities_parse(
+ attribute, &scope, &start_index, &stop_index, &color, &bg_color,
+ &opacity, &bg_opacity, &text_outline_color);
+ TRACKRENDERER_DEBUG(
+ "passed a font attribute: scope(%u), start_index(%u), "
+ "stop_index(%u),"
+ "color(%u), bg_color(%u), opacity(%f), bg_opacity(%f), "
+ "text_outline_color(%u)",
+ scope, start_index, stop_index, color, bg_color, opacity, bg_opacity,
+ text_outline_color);
+ if (color != kAttrInvalidUintVal) {
+ boost::any value = color;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontColor, value,
+ start_index, stop_index);
+ }
+ if (bg_color != kAttrInvalidUintVal) {
+ boost::any value = bg_color;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontBgColor, value,
+ start_index, stop_index);
+ }
+ if (opacity != kAttrInvalidFloatVal) {
+ boost::any value = opacity;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontOpacity, value,
+ start_index, stop_index);
+ }
+ if (bg_opacity != kAttrInvalidFloatVal) {
+ boost::any value = bg_opacity;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontBgOpacity, value,
+ start_index, stop_index);
+ }
+ if (text_outline_color != kAttrInvalidUintVal) {
+ boost::any value = text_outline_color;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontTextOutlineColor,
+ value, start_index, stop_index);
+ }
+ }
+}
+void ParseSubtitleFontSize(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_DEBUG("Now parse attribute font shortcut SIZE!");
+ while (attribute) {
+ guint start_index = kAttrInvalidUintVal, stop_index = kAttrInvalidUintVal;
+ gfloat size = kAttrInvalidFloatVal;
+ attribute = gst_sub_attribute_font_sc_size_parse(attribute, &start_index,
+ &stop_index, &size);
+ TRACKRENDERER_DEBUG(
+ "passed a font attribute: start_index(%u), stop_index(%u), "
+ "size(%f)",
+ start_index, stop_index, size);
+ if (size != kAttrInvalidFloatVal) {
+ boost::any value = size;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontSize, value,
+ start_index, stop_index);
+ }
+ }
+}
+void ParseSubtitleFontWeight(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_DEBUG("Now parse attribute font shortcut WEIGHT!");
+ while (attribute) {
+ guint start_index = kAttrInvalidUintVal, stop_index = kAttrInvalidUintVal;
+ gint weight = kAttrInvalidIntVal;
+ attribute = gst_sub_attribute_font_sc_weight_parse(attribute, &start_index,
+ &stop_index, &weight);
+ TRACKRENDERER_DEBUG(
+ "passed a font attribute: start_index(%u), stop_index(%u), "
+ "weight(%d)",
+ start_index, stop_index, weight);
+ if (weight != kAttrInvalidIntVal) {
+ boost::any value = weight;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontWeight, value,
+ start_index, stop_index);
+ }
+ }
+}
+void ParseSubtitleFontStyle(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_DEBUG("Now parse attribute font shortcut STYLE!");
+ while (attribute) {
+ guint start_index = kAttrInvalidUintVal, stop_index = kAttrInvalidUintVal;
+ gint style = kAttrInvalidIntVal;
+ attribute = gst_sub_attribute_font_sc_style_parse(attribute, &start_index,
+ &stop_index, &style);
+ TRACKRENDERER_DEBUG(
+ "passed a font attribute: start_index(%u), stop_index(%u), "
+ "style(%d)",
+ start_index, stop_index, style);
+ if (style != kAttrInvalidIntVal) {
+ boost::any value = style;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontStyle, value,
+ start_index, stop_index);
+ }
+ }
+}
+void ParseSubtitleFontColor(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_DEBUG("Now parse attribute font shortcut COLOR!");
+ while (attribute) {
+ guint start_index = kAttrInvalidUintVal, stop_index = kAttrInvalidUintVal;
+ guint color = kAttrInvalidUintVal;
+ attribute = gst_sub_attribute_font_sc_color_parse(attribute, &start_index,
+ &stop_index, &color);
+ TRACKRENDERER_DEBUG(
+ "passed a font attribute: start_index(%u), stop_index(%u), "
+ "color(%u)",
+ start_index, stop_index, color);
+ if (color != kAttrInvalidUintVal) {
+ boost::any value = color;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrFontColor, value,
+ start_index, stop_index);
+ }
+ }
+}
+void ParseSubtitleRaw(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_DEBUG("Now parse attribute raw!");
+ while (attribute) {
+ guint start_index = kAttrInvalidUintVal, stop_index = kAttrInvalidUintVal;
+ gchar* raw_subtitle = nullptr;
+ attribute = gst_sub_attribute_raw_data(attribute, &raw_subtitle);
+ TRACKRENDERER_DEBUG("passed a raw attribute: raw_subtitle(%s)", raw_subtitle);
+ if (raw_subtitle != nullptr) {
+ boost::any value = std::string(raw_subtitle);
+ internal::AddSubtitleAttribute(attr_list, kSubAttrRawSubtitle, value,
+ start_index, stop_index);
+ }
+ }
+}
+void ParseSubtitleWebvttCue(GstStructure* attribute,
+ std::list<SubtitleAttr>* attr_list) {
+ TRACKRENDERER_DEBUG("Now parse attribute of webvtt cue!");
+ while (attribute) {
+ guint start_index = kAttrInvalidUintVal, stop_index = kAttrInvalidUintVal;
+ gint line_num = kAttrInvalidIntVal, line_align = kAttrInvalidIntVal,
+ pos_align = kAttrInvalidIntVal, align = kAttrInvalidIntVal,
+ vertical = kAttrInvalidIntVal;
+ gfloat line = kAttrInvalidFloatVal, size = kAttrInvalidFloatVal,
+ position = kAttrInvalidFloatVal;
+ attribute = gst_sub_attribute_webvttcue_parse(
+ attribute, &line, &line_num, &line_align, &align, &size, &position,
+ &pos_align, &vertical);
+ TRACKRENDERER_DEBUG(
+ "passed webvttcue attributes: line(%f), line_num(%d), line_align(%d), "
+ "align(%d), size(%f), position(%f), pos_align(%d), vertical(%d)",
+ line, line_num, line_align, align, size, position, pos_align, vertical);
+ if (line != kAttrInvalidFloatVal) {
+ boost::any value = line;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWebvttCueLine, value,
+ start_index, stop_index);
+ }
+ if (line_num != kAttrInvalidIntVal) {
+ boost::any value = line_num;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWebvttCueLineNum, value,
+ start_index, stop_index);
+ }
+ if (line_align != kAttrInvalidIntVal) {
+ boost::any value = line_align;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWebvttCueLineAlign,
+ value, start_index, stop_index);
+ }
+ if (align != kAttrInvalidIntVal) {
+ boost::any value = align;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWebvttCueAlign, value,
+ start_index, stop_index);
+ }
+ if (size != kAttrInvalidFloatVal) {
+ boost::any value = size;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWebvttCueSize, value,
+ start_index, stop_index);
+ }
+ if (position != kAttrInvalidFloatVal) {
+ boost::any value = position;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWebvttCuePosition,
+ value, start_index, stop_index);
+ }
+ if (pos_align != kAttrInvalidIntVal) {
+ boost::any value = pos_align;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWebvttCuePositionAlign,
+ value, start_index, stop_index);
+ }
+ if (vertical != kAttrInvalidIntVal) {
+ boost::any value = vertical;
+ internal::AddSubtitleAttribute(attr_list, kSubAttrWebvttCueVertical,
+ value, start_index, stop_index);
+ }
+ }
+}
+
+} // namespace internal
+
+SubtitleAttrListPtr SubtitleAttrParser::Parse() {
+ SubtitleAttrListPtr attr_list{new SubtitleAttrList};
+ for (int attr_type = GST_SUB_ATTRI_TYPE_REGION;
+ attr_type < GST_SUB_ATTRI_TYPE_UNKNOWN; ++attr_type) {
+ const gchar* type_name =
+ gst_sub_attribute_type_to_name((GstSubAttributeType)attr_type);
+ GQuark attri_quark =
+ gst_sub_attribute_type_to_quark((GstSubAttributeType)attr_type);
+ if (!attri_quark) {
+ TRACKRENDERER_DEBUG("We don't have quark of this attribute type(%s)!", type_name);
+ continue;
+ }
+
+ GstStructure* attribute = GST_STRUCTURE_CAST(
+ gst_mini_object_get_qdata(GST_MINI_OBJECT(gstbuf_), attri_quark));
+
+ if (!attribute) continue;
+ TRACKRENDERER_DEBUG("attribute type(%s) from gstbuffer!", type_name);
+
+ switch (attr_type) {
+ case GST_SUB_ATTRI_TYPE_REGION:
+ internal::ParseSubtitleRegionAttr(attribute, attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_WINDOW:
+ internal::ParseSubtitleWindowAttr(attribute, attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_FONT:
+ internal::ParseSubtitleFontAttr(attribute, attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_FONT_SC_SIZE_WEIGHT_STYLE_COLOR:
+ internal::ParseSubtitleFontSizeWeightStyleClolr(attribute,
+ attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_FONT_SC_COLORS_AND_OPACITIES:
+ internal::ParseSubtitleFontColorOpacities(attribute, attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_FONT_SC_SIZE:
+ internal::ParseSubtitleFontSize(attribute, attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_FONT_SC_WEIGHT:
+ internal::ParseSubtitleFontWeight(attribute, attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_FONT_SC_STYLE:
+ internal::ParseSubtitleFontStyle(attribute, attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_FONT_SC_COLOR:
+ internal::ParseSubtitleFontColor(attribute, attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_RAW:
+ internal::ParseSubtitleRaw(attribute, attr_list.get());
+ break;
+ case GST_SUB_ATTRI_TYPE_WEBVTT_CUE:
+ internal::ParseSubtitleWebvttCue(attribute, attr_list.get());
+ break;
+ default:
+ TRACKRENDERER_ERROR("UNKNOWN ATTR TYPE");
+ return nullptr;
+ }
+ }
+
+ (attr_list.get())->sort(internal::ComparingStartTime);
+ boost::any value = 0;
+ GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
+ duration = GST_TIME_AS_MSECONDS(GST_BUFFER_DURATION(gstbuf_));
+ timestamp = GST_TIME_AS_MSECONDS(GST_BUFFER_TIMESTAMP(gstbuf_));
+ TRACKRENDERER_DEBUG("pts[%llu] duration[%llu]", timestamp, duration);
+ internal::AddSubtitleAttribute(attr_list.get(), kSubAttrTimestamp, value,
+ timestamp, timestamp + duration);
+
+ return attr_list;
+}
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
+// LCOV_EXCL_STOP
--- /dev/null
+// LCOV_EXCL_START
+#include "trackrenderer_capi/track_capi.h"
+
+#include <cstring>
+#include <memory>
+
+#include "trackrenderer/core/track.h"
+#include "trackrenderer/core/track_util.h"
+#include "trackrenderer/trackrenderer_capi_utils.h"
+
+namespace tr = plusplayer::trackrenderer;
+
+namespace {
+tr::Track* cast_(TrackRendererTrackHandle handle) {
+ return static_cast<tr::Track*>(handle);
+}
+
+int FillCodecData(tr::Track* track, const char* codec_data,
+ const int codec_data_len) {
+ auto* codec_data_ptr = new char[codec_data_len];
+ if (codec_data_ptr == nullptr) return -1;
+ std::memcpy(codec_data_ptr, codec_data, codec_data_len);
+ track->codec_data =
+ std::shared_ptr<char>(codec_data_ptr, std::default_delete<char[]>());
+ track->codec_data_len = codec_data_len;
+ return 0;
+}
+
+bool IsAudioTrackType(const tr::Track* track) {
+ return track->type == tr::kTrackTypeAudio;
+}
+bool IsVideoTrackType(const tr::Track* track) {
+ return track->type == tr::kTrackTypeVideo;
+}
+bool IsSubtitleTrackType(const tr::Track* track) {
+ return track->type == tr::kTrackTypeSubtitle;
+}
+} // namespace
+
+int trackrenderer_track_create(TrackRendererTrackHandle* handle,
+ const TrackRendererTrackType type,
+ const int index) {
+ if (handle == nullptr) return -1;
+ *handle = nullptr;
+ tr::Track* track = new tr::Track;
+ if (track == nullptr) return -1;
+ track->type = tr::capi_utils::ConvertToTrackType(type);
+ track->index = index;
+ *handle = static_cast<TrackRendererTrackHandle>(track);
+ return 0;
+}
+
+int trackrenderer_track_destroy(TrackRendererTrackHandle handle) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ delete track;
+ return 0;
+}
+
+int trackrenderer_track_set_activation(
+ TrackRendererTrackHandle handle,
+ const TrackRendererTrackActivation activation) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ track->active =
+ activation == kTrackRendererTrackActivationActive ? true : false;
+ return 0;
+}
+
+int trackrenderer_track_set_mimetype(TrackRendererTrackHandle handle,
+ const char* mimetype) {
+ if (handle == nullptr) return -1;
+ if (mimetype == nullptr) return -1;
+ auto* track = cast_(handle);
+ track->mimetype = mimetype;
+ return 0;
+}
+
+int trackrenderer_track_set_streamtype(TrackRendererTrackHandle handle,
+ const char* streamtype) {
+ if (handle == nullptr) return -1;
+ if (streamtype == nullptr) return -1;
+ auto* track = cast_(handle);
+ track->streamtype = streamtype;
+ return 0;
+}
+
+int trackrenderer_track_set_codec_data(TrackRendererTrackHandle handle,
+ const char* codec_data,
+ const int codec_data_len) {
+ if (handle == nullptr) return -1;
+ if (codec_data == nullptr) return -1;
+ if (tr::track_util::IsValidCodecDataSize(codec_data_len) == false) return -1;
+ auto* track = cast_(handle);
+ if (IsSubtitleTrackType(track)) return -1;
+ return FillCodecData(track, codec_data, codec_data_len);
+}
+
+int trackrenderer_track_set_decoder_type(
+ TrackRendererTrackHandle handle, const TrackRendererTrackDecoderType type) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsSubtitleTrackType(track)) return -1;
+ auto ret = 0;
+ switch (type) {
+ case kTrackRendererTrackDecoderTypeHwOnly:
+ track->use_swdecoder = false;
+ break;
+ case kTrackRendererTrackDecoderTypeSwOnly:
+ track->use_swdecoder = true;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+int trackrenderer_track_video_set_resolution(TrackRendererTrackHandle handle,
+ const int width,
+ const int height) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsVideoTrackType(track) == false) return -1;
+ track->width = width;
+ track->height = height;
+ return 0;
+}
+
+int trackrenderer_track_video_set_max_resolution(
+ TrackRendererTrackHandle handle, const int width, const int height) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsVideoTrackType(track) == false) return -1;
+ track->maxwidth = width;
+ track->maxheight = height;
+ return 0;
+}
+
+int trackrenderer_track_video_set_framerate(TrackRendererTrackHandle handle,
+ const int num, const int den) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsVideoTrackType(track) == false) return -1;
+ track->framerate_num = num;
+ track->framerate_den = den;
+ return 0;
+}
+
+int trackrenderer_track_video_set_codec_tag(TrackRendererTrackHandle handle,
+ const char* codec_tag) {
+ if (handle == nullptr) return -1;
+ if (codec_tag == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsVideoTrackType(track) == false) return -1;
+ track->codec_tag = codec_tag;
+ return 0;
+}
+
+int trackrenderer_track_video_set_version(TrackRendererTrackHandle handle,
+ const int version) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsVideoTrackType(track) == false) return -1;
+ track->version = version;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_sample_rate(TrackRendererTrackHandle handle,
+ const int sample_rate) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->sample_rate = sample_rate;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_sample_format(TrackRendererTrackHandle handle,
+ const int sample_format) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->sample_format = sample_format;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_channels(TrackRendererTrackHandle handle,
+ const int channels) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->channels = channels;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_version(TrackRendererTrackHandle handle,
+ const int version) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->version = version;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_layer(TrackRendererTrackHandle handle,
+ const int layer) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->layer = layer;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_bits_per_sample(
+ TrackRendererTrackHandle handle, const int bits_per_sample) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->bits_per_sample = bits_per_sample;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_block_align(TrackRendererTrackHandle handle,
+ const int block_align) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->block_align = block_align;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_bitrate(TrackRendererTrackHandle handle,
+ const int bitrate) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->bitrate = bitrate;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_layout(TrackRendererTrackHandle handle,
+ const char* layout) {
+ if (handle == nullptr) return -1;
+ if (layout == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->layout = layout;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_flavor(TrackRendererTrackHandle handle,
+ const int flavor) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->flavor = flavor;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_endianness(
+ TrackRendererTrackHandle handle,
+ const TrackRendererTrackEndianness endianness) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->endianness =
+ endianness == kTrackRendererTrackEndiannessLittle ? 1234 : 4321;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_signedness(
+ TrackRendererTrackHandle handle,
+ const TrackRendererTrackSignedness signedness) {
+ if (handle == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->is_signed =
+ signedness == kTrackRendererTrackSignednessSigned ? true : false;
+ return 0;
+}
+
+int trackrenderer_track_audio_set_codec_tag(TrackRendererTrackHandle handle,
+ const char* codec_tag) {
+ if (handle == nullptr) return -1;
+ if (codec_tag == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsAudioTrackType(track) == false) return -1;
+ track->codec_tag = codec_tag;
+ return 0;
+}
+
+int trackrenderer_track_subtitle_set_language_code(
+ TrackRendererTrackHandle handle, const char* language_code) {
+ if (handle == nullptr) return -1;
+ if (language_code == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsSubtitleTrackType(track) == false) return -1;
+ track->language_code = language_code;
+ return 0;
+}
+
+int trackrenderer_track_subtitle_set_subtitle_format(
+ TrackRendererTrackHandle handle, const char* subtitle_format) {
+ if (handle == nullptr) return -1;
+ if (subtitle_format == nullptr) return -1;
+ auto* track = cast_(handle);
+ if (IsSubtitleTrackType(track) == false) return -1;
+ track->subtitle_format = subtitle_format;
+ return 0;
+}
+// LCOV_EXCL_STOP
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/core/track_util.h"
+
+#include <algorithm>
+#include <string>
+#include <unordered_map>
+
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace track_util {
+
+bool GetActiveTrack(const std::vector<Track>& track_list, const TrackType type,
+ Track* track) {
+ if (!track) return false;
+ auto find_target = [type](const Track& item) -> bool {
+ if (item.type == type && item.active) {
+ return true;
+ } else {
+ return false;
+ }
+ };
+ auto target = std::find_if(track_list.begin(), track_list.end(), find_target);
+ if (target == track_list.end()) return false; // There is no target.
+ *track = *target;
+ // TRACKRENDERER_INFO("tracktype : %d, index : %d", track->type,
+ // track->index);
+ return true;
+}
+
+bool GetActiveTrackList(const std::vector<Track>& tracklist,
+ std::vector<Track>& active_track) {
+ unsigned int video = 0, audio = 0, text = 0;
+ for (const auto& track : tracklist) {
+ if (track.active == true) {
+ active_track.push_back(track);
+ if (track.type == kTrackTypeAudio)
+ audio++;
+ else if (track.type == kTrackTypeVideo)
+ video++;
+ else if (track.type == kTrackTypeSubtitle)
+ text++;
+ }
+ }
+ if (active_track.empty()) {
+ TRACKRENDERER_ERROR("no active track found");
+ return false;
+ }
+ if (video > 1 || audio > 1 || text > 1) {
+ TRACKRENDERER_ERROR(
+ "actived tracks are too much: video(%d), audio(%d), text(%d)", video,
+ audio, text);
+ return false;
+ }
+ return true;
+}
+
+void ShowTrackInfo(const std::vector<Track>& trackinfo) {
+ std::vector<Track> info = trackinfo;
+ TRACKRENDERER_INFO("### Track List ###");
+ for (const Track& item : info) {
+ ShowTrackInfo(item);
+ }
+ TRACKRENDERER_INFO("### ~Track List ###");
+}
+
+void ShowTrackInfo(const Track& track) {
+ TRACKRENDERER_INFO("### TrackInfo ###");
+ TRACKRENDERER_INFO("index : %d id : %d ,", track.index, track.id);
+ TRACKRENDERER_INFO("mimetype: %s", track.mimetype.c_str());
+ TRACKRENDERER_INFO("streamtype: %s", track.streamtype.c_str());
+ TRACKRENDERER_INFO("tracktype : %d", track.type);
+ TRACKRENDERER_INFO("width: %d height : %d", track.width, track.height);
+ TRACKRENDERER_INFO("maxwidth: %d maxheight : %d", track.maxwidth,
+ track.maxheight);
+ TRACKRENDERER_INFO("framerate(num : %d den : %d)", track.framerate_num,
+ track.framerate_den);
+ TRACKRENDERER_INFO("framerate(codec_data : %p )", track.codec_data.get());
+ TRACKRENDERER_INFO("framerate(codec_data_len : %d )", track.codec_data_len);
+ TRACKRENDERER_INFO(
+ "sample_rate %d sample_format : %d channel : %d version : %d layer : "
+ "%d",
+ track.sample_rate, track.sample_format, track.channels, track.version,
+ track.layer);
+ TRACKRENDERER_INFO(
+ "bits_per_sample %d block_align : %d bitrate : %d endianness : %d "
+ "is_signed : %d",
+ track.bits_per_sample, track.block_align, track.bitrate, track.endianness,
+ track.is_signed);
+ TRACKRENDERER_INFO("active %d subtitle_format : %s ", track.active,
+ track.subtitle_format.c_str());
+ TRACKRENDERER_INFO("use_swdecoder : %d", track.use_swdecoder);
+ TRACKRENDERER_INFO("language_code: %s", track.language_code.c_str());
+}
+
+uint64_t GetPositionWithinBoundary(const uint64_t duration,
+ const uint64_t position,
+ const uint64_t threshold) {
+ TRACKRENDERER_DEBUG("duration[%llu] position[%llu] threshold[%llu]", duration,
+ position, threshold);
+ if (duration < threshold) return position;
+ uint64_t safe_pos = position;
+ uint64_t boundary = duration - threshold;
+ if (position > boundary) {
+ safe_pos = boundary;
+ }
+ return safe_pos;
+}
+
+bool IsValidCodecDataSize(int size) {
+ constexpr int kMaxSize = 1024 * 1024; // 1MB
+ if (size > 0 && size < kMaxSize) return true;
+ return false;
+}
+
+void FillCodecDataIntoTrack(const GValue* codec_data, Track* track) {
+ GstBuffer* buffer = gst_value_get_buffer(codec_data);
+ GstMapInfo codec_data_info;
+ if (gst_buffer_map(buffer, &codec_data_info, GST_MAP_READ)) {
+ TRACKRENDERER_DEBUG("codec extra data [ %s ]", codec_data_info.data);
+ TRACKRENDERER_DEBUG("codec extra data size[ %d ]", codec_data_info.size);
+ if (IsValidCodecDataSize(codec_data_info.size)) {
+ std::shared_ptr<char> data(new char[codec_data_info.size],
+ std::default_delete<char[]>());
+ memcpy(data.get(), codec_data_info.data, codec_data_info.size);
+ track->codec_data = data;
+ track->codec_data_len = codec_data_info.size;
+ } else {
+ TRACKRENDERER_WARN("Warning invalid codec extra data size [%d]",
+ codec_data_info.size);
+ }
+ gst_buffer_unmap(buffer, &codec_data_info);
+ } else {
+ TRACKRENDERER_DEBUG("Fail to gst_buffer_map for codec data");
+ }
+}
+
+const std::string& GetTrackTypeString(const TrackType& type) {
+ static const std::unordered_map<TrackType, std::string> kTrackTypeStringMap =
+ {
+ {kTrackTypeAudio, "AudioTrackType"},
+ {kTrackTypeVideo, "VideoTrackType"},
+ {kTrackTypeSubtitle, "SubtitleTrackType"},
+ };
+ static const std::string kInvalidTrackTypeString = "InvalidTrackType";
+
+ const auto got = kTrackTypeStringMap.find(type);
+ if (got == kTrackTypeStringMap.end()) {
+ return std::move(kInvalidTrackTypeString);
+ } else {
+ return std::move(got->second);
+ }
+}
+
+} // namespace track_util
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/trackrenderer.h"
+
+#include <sys/prctl.h>
+
+#include <boost/scope_exit.hpp>
+#include <chrono>
+#include <limits>
+#include <list>
+#include <map>
+#include <thread>
+#include <utility>
+
+#ifdef SOUNDBAR_PRODUCT
+#include "avoc_av_audio.h"
+#else
+#include "avoc.h"
+#include "avoc_callback.h"
+#include "avoc_mls.h"
+#endif
+#include "iaudio-control.hpp"
+#include "iniparser.h"
+#include "lwipc.h"
+#include "resource_center.h"
+#include "system_info.h"
+
+// performance logs in popup log file
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+// end
+
+#ifndef SOUNDBAR_PRODUCT
+#include <capi-graphics-control.h>
+#endif
+
+#include "trackrenderer/audio_controller/resyncaudio/policies.h"
+#include "trackrenderer/core/gst_utils.h"
+#include "trackrenderer/core/gstobject_guard.h"
+#include "trackrenderer/core/track_util.h"
+#include "trackrenderer/core/utils/log.h"
+#include "trackrenderer/core/utils/product_cfg.h"
+#include "trackrenderer/display.h"
+#include "trackrenderer/error.h"
+#include "trackrenderer/resourcemanager.h"
+#include "trackrenderer/trackrenderer_vconf.h"
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+#include "trackrenderer/subtitle_attr_parser.h"
+#endif
+
+namespace {
+
+constexpr char kTrackRendererPipelineName[] = "TrackRenderer";
+
+constexpr guint64 kMaxByteOfSubtitleSrcQueue = 1024; // 1KB
+
+const char kPadProbeAudioIdle[] = "AUDIO_IDLE_DOWNSTREAM";
+const char kPadProbeAudioBlock[] = "AUDIO_BLOCK_DOWNSTREAM";
+const char kPadProbeVideoBlock[] = "VIDEO_BLOCK_DOWNSTREAM";
+const char kPadProbeSubtitleBlock[] = "SUBTITLE_BLOCK_DOWNSTREAM";
+const char kPadProbeVideoPeekBlock[] = "VIDEO_PEEK_BLOCK_DOWNSTREAM";
+const char kPadProbeVideoDecoded[] = "VIDEO_DECODED_DOWNSTREAM";
+const char kPadProbeVideoDecInputBlock[] = "VIDEO_DECODER_INPUT_DOWNSTREAM";
+
+constexpr char kDecoderPluginConfPath[] = "/etc/multimedia/gst-openmax.conf";
+constexpr char kTzDecoderPluginConfPath[] =
+ "/etc/multimedia/gst-tz-openmax.conf";
+constexpr char kPlayerIniPath[] = "/etc/multimedia/mmfw_player.ini";
+
+using ComponentWithCodec = std::pair<std::string, std::string>;
+
+static std::once_flag plugin_loaded;
+static std::map<ComponentWithCodec, std::string> PluginTable; // loadable table
+static bool IsDualSoundMode = false;
+
+enum class DeviceSinkElementType {
+ kVideoSink,
+ kAudioSink,
+ kPcmAudiosink,
+};
+static std::map<DeviceSinkElementType, std::string> SinkPluginTable;
+
+enum class PluginType {
+ kHw,
+ kSw,
+};
+static std::map<std::string, PluginType> PluginTypeTable;
+
+enum class VideoColorFormat {
+ kColorFormatI420,
+ kColorFormatNV12,
+ kColorFormatYUY2,
+ kColorFormatUYVY,
+ kColorFormatI422,
+ kColorFormatNV16,
+ kColorFormatST12,
+ kColorFormatSN12
+};
+
+#if 0
+const std::map<ComponentWithCodec, std::string> kPluginMap = {
+ // rank = 260
+ // "omx_mpeg4dec" also can handle x-h264 --> version is different.
+ {{"OMX.SDP.video_decoder.mfc0", "video/mpeg"}, "omx_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-h264"}, "omx_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-h263"}, "omx_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-wmv"}, "omx_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-vp8"}, "omx_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-pn-realvideo"}, "omx_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-avs"}, "omx_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-avs+"}, "omx_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-msmpeg"}, "omx_mpeg4dec"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-xvid"}, "omx_mpeg4dec"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-3ivx"}, "omx_mpeg4dec"},
+ {{"OMX.SDP.video_decoder.vr360dec", "video/x-vp9"},
+ "omx_uhd_videodec_vr360"},
+ {{"OMX.SDP.video_decoder.vr360dec", "video/x-h264"},
+ "omx_uhd_videodec_vr360"},
+ {{"OMX.SDP.video_decoder.vr360dec", "video/x-av1"},
+ "omx_uhd_videodec_vr360"},
+ // rank = 259
+ // "omx_mpeg4dec_1" also can handle x-h264 --> version is different.
+ {{"OMX.SDP.video_decoder.mfc1", "video/mpeg"}, "omx_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-h264"}, "omx_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-h263"}, "omx_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-wmv"}, "omx_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-vp8"}, "omx_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-pn-realvideo"}, "omx_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-avs"}, "omx_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-avs+"}, "omx_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-msmpeg"}, "omx_mpeg4dec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-xvid"}, "omx_mpeg4dec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-3ivx"}, "omx_mpeg4dec_1"},
+ // rank = 258
+ {{"OMX.SDP.video_decoder.mfc2", "video/x-h264"}, "omx_videodec_2"},
+ // rank = 257
+ {{"OMX.SDP.video_decoder.mfc3", "video/x-h264"}, "omx_videodec_3"},
+ // rank = 256
+ {{"OMX.SDP.video_decoder.mfc4", "video/x-h264"}, "omx_videodec_4"},
+ // rank = 258
+ {{"OMX.SDP.video_decoder.dvde0", "video/x-h264"}, "omx_uhd_videodec"},
+ {{"OMX.SDP.video_decoder.dvde0", "video/x-vp9"}, "omx_uhd_videodec"},
+ {{"OMX.SDP.video_decoder.dvde0", "video/x-h265"}, "omx_uhd_videodec"},
+ {{"OMX.SDP.video_decoder.dvde0", "video/x-av1"}, "omx_uhd_videodec"},
+ {{"OMX.SDP.video_decoder.hevc8k", "video/x-h265"}, "omx_uhd8k_videodec"},
+ {{"OMX.SDP.video_decoder.hevc8k", "video/x-av1"}, "omx_uhd8k_videodec"},
+ // TODO: add switch 4K 8K logic
+ // rank = 257
+ {{"OMX.SDP.video_decoder.dvde1", "video/x-vp9"}, "omx_videodec_5"},
+ {{"OMX.SDP.video_decoder.dvde1", "video/x-h265"}, "omx_videodec_5"},
+ // rank = 260
+ // "omx_uhd_mjpegdec" also can handle x-h265 --> resolution is different.
+ {{"OMX.SDP.video_decoder.mjpeg", "video/x-jpeg"}, "omx_mjpegdec"},
+ // rand = 259
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/mpeg"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-aac"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-true-hd"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-ac3"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-eac3"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/ac3"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-ac4"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-adpcm"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-wma"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-vorbis"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-pn-realaudio"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-gst-fourcc-mha1"},
+ "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-gst-fourcc-mhm1"},
+ "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-alaw"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-mulaw"}, "omx_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-opus"}, "omx_audiodec"},
+
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/mpeg"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-aac"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-true-hd"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-ac3"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-eac3"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/ac3"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-ac4"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-adpcm"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-wma"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-vorbis"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-pn-realaudio"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-gst-fourcc-mha1"},
+ "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-gst-fourcc-mhm1"},
+ "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-alaw"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-mulaw"}, "omx_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-opus"}, "omx_audiodec_1"},
+ // rand = 258
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/mpeg"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-aac"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-true-hd"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-ac3"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-eac3"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/ac3"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-ac4"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-adpcm"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-wma"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-vorbis"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-pn-realaudio"},
+ "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-gst-fourcc-mha1"},
+ "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-gst-fourcc-mhm1"},
+ "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-alaw"}, "omx_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-mulaw"}, "omx_mmaudiodec"},
+ // tz case
+ {{"OMX.SDP.video_decoder.mfc0", "video/mpeg_tz"}, "omx_tz_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-h264_tz"}, "omx_tz_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-h263_tz"}, "omx_tz_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-wmv_tz"}, "omx_tz_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-vp8_tz"}, "omx_tz_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-pn-realvideo_tz"},
+ "omx_tz_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-avs_tz"}, "omx_tz_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-avs+_tz"}, "omx_tz_videodec_0"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-msmpeg_tz"}, "omx_tz_mpeg4dec"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-xvid_tz"}, "omx_tz_mpeg4dec"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/x-3ivx_tz"}, "omx_tz_mpeg4dec"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/mpeg_tz"}, "omx_tz_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-h264_tz"}, "omx_tz_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-h263_tz"}, "omx_tz_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-wmv_tz"}, "omx_tz_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-vp8_tz"}, "omx_tz_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-pn-realvideo_tz"},
+ "omx_tz_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-avs_tz"}, "omx_tz_videodec_1"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/x-avs+_tz"}, "omx_tz_videodec_1"},
+ {{"OMX.SDP.video_decoder.dvde0", "video/x-h264_tz"}, "omx_tz_uhd_videodec"},
+ {{"OMX.SDP.video_decoder.dvde0", "video/x-vp9_tz"}, "omx_tz_uhd_videodec"},
+ {{"OMX.SDP.video_decoder.dvde0", "video/x-h265_tz"}, "omx_tz_uhd_h265dec"},
+ {{"OMX.SDP.video_decoder.dvde0", "video/x-av1_tz"}, "omx_tz_uhd_videodec"},
+ {{"OMX.SDP.video_decoder.hevc8k", "video/x-h265_tz"},
+ "omx_tz_uhd8k_videodec"},
+ {{"OMX.SDP.video_decoder.hevc8k", "video/x-av1_tz"},
+ "omx_tz_uhd8k_videodec"},
+ {{"OMX.SDP.video_decoder.dvde1", "video/x-vp9_tz"}, "omx_tz_videodec_5"},
+ {{"OMX.SDP.video_decoder.dvde1", "video/x-h265_tz"}, "omx_tz_videodec_5"},
+ {{"OMX.SDP.video_decoder.mjpeg", "video/x-jpeg_tz"}, "omx_tz_mjpegdec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/mpeg_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-aac_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-ac3_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-eac3_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/ac3_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-ac4_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-adpcm_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-wma_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-vorbis_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-alaw_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-mulaw_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-gst-fourcc-mha1_tz"},
+ "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-gst-fourcc-mhm1_tz"},
+ "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-opus_tz"}, "omx_tz_audiodec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST0", "audio/x-raw_tz"}, "omx_tz_lpcmdec"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/mpeg_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-aac_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-ac3_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-eac3_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/ac3_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-ac4_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-adpcm_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-wma_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-vorbis_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-alaw_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-mulaw_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-gst-fourcc-mha1_tz"},
+ "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-gst-fourcc-mhm1_tz"},
+ "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-opus_tz"}, "omx_tz_audiodec_1"},
+ {{"OMX.SDP.AUDIO.DECODER.INST1", "audio/x-raw_tz"}, "omx_tz_lpcmdec_1"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/mpeg_tz"}, "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-aac_tz"}, "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-ac3_tz"}, "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-eac3_tz"},
+ "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/ac3_tz"}, "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-ac4_tz"}, "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-adpcm_tz"},
+ "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-wma_tz"}, "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-vorbis_tz"},
+ "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-pn-realaudio_tz"},
+ "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-gst-fourcc-mha1_tz"},
+ "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-gst-fourcc-mhm1_tz"},
+ "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-alaw_tz"},
+ "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-mulaw_tz"},
+ "omx_tz_mmaudiodec"},
+ {{"OMX.SDP.AUDANY_PRIMARY.Decoder", "audio/x-raw_tz"}, "omx_tz_mmaudiodec"},
+ // SW decoder
+ {{"FFMPEG.SW.Decoder", "video/audio"}, "ffdec_xxx"}, };
+#endif
+
+using ComponentWithSwCodec = std::pair<std::string, int>; // mime,version
+const std::map<ComponentWithSwCodec, std::string> kSwPluginMap = {
+ {{"video/mpeg", 1}, "ffdec_mpeg1video"},
+ {{"video/mpeg", 2}, "ffdec_mpeg2video"},
+ {{"video/mpeg", 4}, "ffdec_mpeg4"},
+ {{"video/x-h264", 0}, "ffdec_h264"},
+ {{"video/x-vp9", 0}, "ffdec_vp9"},
+ {{"video/x-vp8", 0}, "ffdec_vp8"},
+ {{"video/x-vp6", 0}, "ffdec_vp6"},
+ {{"video/x-h265", 0}, "ffdec_hevc"},
+ {{"video/x-msmpeg", 41}, "ffdec_msmpeg4v1"},
+ {{"video/x-msmpeg", 42}, "ffdec_msmpeg4v2"},
+ {{"video/x-msmpeg", 43}, "ffdec_msmpeg4"},
+ {{"video/x-wmv", 1}, "ffdec_wmv1"},
+ {{"video/x-wmv", 2}, "ffdec_wmv2"},
+ {{"video/x-wmv", 3}, "ffdec_wmv3"},
+ {{"audio/mpeg", 1}, "ffdec_mp3"},
+ {{"audio/x-opus", 0}, "ffdec_opus"},
+ {{"audio/mpeg", 2}, "ffdec_aac"},
+ {{"audio/mpeg", 4}, "ffdec_aac"},
+ {{"audio/x-ac3", 0}, "ffdec_ac3"},
+ {{"audio/x-eac3", 0}, "ffdec_eac3"},
+ {{"audio/x-wma", 1}, "ffdec_wmav1"},
+ {{"audio/x-wma", 2}, "ffdec_wmav2"},
+ {{"audio/x-mulaw", 0}, "ffdec_pcm_mulaw"},
+};
+
+using ComponentWithMpeg4VideoCodec = std::pair<std::string, std::string>;
+using Mpeg4VidoePluginMapType =
+ std::map<ComponentWithMpeg4VideoCodec, std::string>;
+
+const Mpeg4VidoePluginMapType kMpeg4VidoePluginMap = {
+ {{"OMX.SDP.video_decoder.mfc0", "video/mpeg"}, "omx_mpeg4dec"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/mpeg"}, "omx_mpeg4dec_1"},
+ {{"OMX.SDP.video_decoder.mfc0", "video/mpeg_tz"}, "omx_tz_mpeg4dec"},
+ {{"OMX.SDP.video_decoder.mfc1", "video/mpeg_tz"}, "omx_tz_mpeg4dec_1"},
+};
+
+// Hw clock
+#ifndef PR_TASK_PERF_USER_TRACE
+#define PR_TASK_PERF_USER_TRACE 666
+#endif
+
+constexpr int kBufSize = 256;
+inline void PerfUsrTrace(const char* arg = nullptr) {
+ char buf[kBufSize]{
+ 0,
+ };
+ const char* prefix_str = "[PERF][MMPLAYER]";
+ bool use_arg{false};
+ if (arg) {
+ if (strlen(arg) < (kBufSize - strlen(prefix_str))) {
+ use_arg = true;
+ }
+ }
+ snprintf(buf, kBufSize, "%s %s", prefix_str, (use_arg ? arg : ""));
+ prctl(PR_TASK_PERF_USER_TRACE, buf, strlen(buf));
+}
+
+} // namespace
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace internal {
+
+enum class TrackEncryptionUnit { kNone, kTs, kEs };
+// LCOV_EXCL_START
+void TvplusPerformanceLogs() {
+ FILE* fp = NULL;
+ const char* TVPLUS_POPUP_LOG_FILE_NAME = "/tmp/play_log.txt";
+ TRACKRENDERER_DEBUG("~~~~~~~~~~~~~ [PROFILING LOG ON] ~~~~~~~~~~~~~~~~");
+ if ((fp = fopen(TVPLUS_POPUP_LOG_FILE_NAME, "a+")) != NULL) {
+ struct timespec tp;
+ char unmute[30];
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ snprintf(unmute, 30, "[um:%d.%d]", (int)tp.tv_sec,
+ (int)tp.tv_nsec / 10000000);
+ fprintf(fp, "%s", unmute);
+ chmod(TVPLUS_POPUP_LOG_FILE_NAME, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ fclose(fp);
+ TRACKRENDERER_DEBUG("~~~~~~~~~~~~~ file write passed ~~~~~~~~~~~~~~~");
+ } else {
+ TRACKRENDERER_DEBUG("~~~~~~~~~~~~~ file write failed ~~~~~~~~~~~~~~~~");
+ }
+}
+// LCOV_EXCL_STOP
+
+bool IsSdkEnabledFeature() {
+#ifdef SDK_ENABLED_FEATURE
+ return true;
+#endif
+ return false;
+}
+
+void GetSoundMode() {
+ int ret = system_info_get_custom_bool(
+ "com.samsung/featureconf/multiview.dualsound", &IsDualSoundMode);
+ if (SYSTEM_INFO_ERROR_NONE != ret) {
+ TRACKRENDERER_ERROR("system_info_get_custom_bool() return error[%d]", ret);
+ }
+}
+
+inline bool IsHwPlugin(const char* plugin_name) {
+ if (PluginTypeTable.count(plugin_name) > 0) {
+ if (PluginTypeTable[plugin_name] == ::PluginType::kHw) return true;
+ }
+ return false;
+}
+
+inline bool IsPcmMimeType(const std::string& mimetype) {
+ return (mimetype == "audio/x-raw");
+}
+
+void GetIniValue(const std::map<std::string, bool>& properties,
+ const std::string& key, bool* value) {
+ auto look = properties.find(key);
+ if (look == properties.end()) {
+ return;
+ }
+ *value = look->second;
+ return;
+}
+
+TrackEncryptionUnit GetTrackEncryptionUnit(drm::Type type) {
+ if (type == drm::Type::kPlayready) return TrackEncryptionUnit::kEs;
+ if (type == drm::Type::kWidevineCdm) return TrackEncryptionUnit::kEs;
+ if (type == drm::Type::kVerimatrix) return TrackEncryptionUnit::kTs;
+ return TrackEncryptionUnit::kNone;
+}
+
+inline bool IsFhdOrLowerResolution(int width, int height, int max_w,
+ int max_h) {
+ int w = max_w ? max_w : width;
+ int h = max_h ? max_h : height;
+ if ((w <= kMaxFhdWidth && h <= kMaxFhdHeight) ||
+ (w <= kMaxFhdHeight && h <= kMaxFhdWidth))
+ return true;
+ TRACKRENDERER_ERROR("w:%d, h:%d, mw:%d, mh:%d!", width, height, max_w, max_h);
+ return false;
+}
+
+inline bool IsUhdResolution(int w, int h) {
+ return ((w > kMaxFhdWidth || h > kMaxFhdHeight) &&
+ (w <= kMaxUhdWidth && h <= kMaxUhdHeight));
+}
+
+inline bool IsUhd8kResolution(int w, int h) {
+ return (w > kMaxUhdWidth || h > kMaxUhdHeight);
+}
+
+inline bool IsAacCodec(const Track& track) {
+ return (track.mimetype.find("audio/mpeg") != std::string::npos &&
+ track.version == 2);
+}
+
+inline bool IsEac3Codec(const std::string& mimetype) {
+ return mimetype.find("audio/x-eac3") != std::string::npos;
+}
+
+inline bool IsAc3Codec(const std::string& mimetype) {
+ return mimetype.find("audio/x-ac3") != std::string::npos;
+}
+
+inline bool IsTzMimeType(const std::string& mimetype) { // useful when external
+ // audio is present in
+ // verimatrix case as trustzone is not used in this case
+ return (mimetype.find("_tz") != std::string::npos);
+}
+
+inline bool IsVideoRawMimeType(const std::string& mimetype) {
+ return (mimetype.find("video/x-raw") != std::string::npos);
+}
+
+inline bool IsExternalDecryptionCase(const drm::Property& property) {
+ return property.external_decryption;
+}
+
+inline bool IsTzAppSrcElementNecessary(const drm::Property& property,
+ const std::string& mimetype) {
+ return IsTzMimeType(mimetype);
+}
+
+inline bool IsDrmEmeElementNecessary(const drm::Property& property,
+ const std::string& mimetype) {
+ // Some Apps call setDrm even for plain content (mostly trailers),
+ // but main contents are Drm protected. So we need to check drm_type
+ // as well as mime_type for plain/drm content. (Example app: Stan/Chili)
+ return (GetTrackEncryptionUnit(property.type) == TrackEncryptionUnit::kEs &&
+ IsTzMimeType(mimetype));
+}
+
+inline bool IsTzDecoderElementNecessary(const drm::Property& property,
+ const std::string& mimetype) {
+ return IsTzMimeType(mimetype);
+}
+
+inline bool IsDecoderElementNecessary(const std::string& mimetype) {
+ return !IsPcmMimeType(mimetype) && !IsVideoRawMimeType(mimetype);
+}
+
+inline bool IsDecodedVideoBufferNeeded(DecodedVideoFrameBufferType& type) {
+ return (type != DecodedVideoFrameBufferType::kNone);
+}
+
+inline bool IsDisplayNeeded(DisplayType& type) {
+ return (type != DisplayType::kNone);
+}
+
+inline bool IsAvailableCodecChange(const Track& track) {
+ if (internal::IsAacCodec(track) || internal::IsAc3Codec(track.mimetype) ||
+ internal::IsEac3Codec(track.mimetype))
+ return true;
+ return false;
+}
+
+constexpr int kVideoBufferPlaneMax = 4;
+struct VideoStreamDataType {
+ tbm_format format = TBM_FORMAT_NV12; //< image format
+ Geometry crop_area; //< crop info of video buffer
+ int width = 0; //< width of video buffer
+ int height = 0; //< height of video buffer
+ tbm_bo bo[kVideoBufferPlaneMax] = {nullptr}; //< TBM buffer object
+ void* internal_buffer = nullptr; //< Internal buffer pointer
+ int stride[kVideoBufferPlaneMax] = {0}; //< stride of plane
+ int elevation[kVideoBufferPlaneMax] = {0}; //< elevation of plane
+};
+
+void SetGeometry(Geometry* geom, const int& x, const int& y, const int& w,
+ const int& h) {
+ geom->x = x;
+ geom->y = y;
+ geom->w = w;
+ geom->h = h;
+ return;
+}
+
+bool CopySwCodec(GstBuffer* decodedBuffer, const VideoStreamDataType& stream) {
+ tbm_bo bo_Y = stream.bo[GST_VIDEO_COMP_Y];
+ tbm_bo bo_C = stream.bo[GST_VIDEO_COMP_U];
+
+ tbm_bo_handle ap_bo_handle = {
+ nullptr,
+ };
+
+ GstStructure* s = GST_STRUCTURE_CAST(gst_mini_object_get_qdata(
+ GST_MINI_OBJECT(decodedBuffer), g_quark_from_static_string("tbm_bo")));
+ if (s) {
+ if (!gst_structure_get(s, "tbm_bo_hnd", G_TYPE_POINTER, &ap_bo_handle,
+ NULL)) {
+ TRACKRENDERER_ERROR("Buffer don't have tbm_bo_hnd structure");
+ return false;
+ }
+ } else {
+ TRACKRENDERER_ERROR("Buffer don't have tbm_bo structure");
+ return false;
+ }
+
+ tbm_bo_handle bo_handle_Y =
+ tbm_bo_map(bo_Y, TBM_DEVICE_CPU, TBM_OPTION_WRITE);
+ if (!bo_handle_Y.ptr) {
+ TRACKRENDERER_ERROR("TBM get error : bo_handle_Y.ptr is NULL");
+ return false;
+ }
+ for (int i = 0; i < stream.height; i++) {
+ memcpy((unsigned char*)(bo_handle_Y.ptr) +
+ (i * stream.stride[GST_VIDEO_COMP_Y]),
+ (unsigned char*)(ap_bo_handle.ptr) + (i * stream.width),
+ stream.width);
+ }
+ tbm_bo_unmap(bo_Y);
+
+ tbm_bo_handle bo_handle_C =
+ tbm_bo_map(bo_C, TBM_DEVICE_CPU, TBM_OPTION_WRITE);
+ if (!bo_handle_C.ptr) {
+ TRACKRENDERER_ERROR("TBM get error : bo_handle_C.ptr is NULL");
+ return false;
+ }
+ int data_size = stream.width * stream.height;
+ for (int i = 0; i < stream.height / 2; i++) {
+ memcpy((unsigned char*)(bo_handle_C.ptr) +
+ (i * stream.stride[GST_VIDEO_COMP_U]),
+ (unsigned char*)(ap_bo_handle.ptr) + data_size + (i * stream.width),
+ stream.width);
+ }
+ tbm_bo_unmap(bo_C);
+
+ return true;
+}
+
+bool CopyHwCodec(const VideoStreamDataType& stream, int y_viraddr,
+ int c_viraddr, int y_linesize, int c_linesize,
+ VideoColorFormat color_format) {
+ tbm_bo bo_Y = stream.bo[GST_VIDEO_COMP_Y];
+ tbm_bo bo_C = stream.bo[GST_VIDEO_COMP_U];
+ tbm_bo_handle bo_handle_Y =
+ tbm_bo_map(bo_Y, TBM_DEVICE_CPU, TBM_OPTION_WRITE);
+ if (!bo_handle_Y.ptr) {
+ TRACKRENDERER_ERROR("TBM get error : bo_handle_Y.ptr is NULL");
+ return false;
+ }
+
+ for (int i = 0; i < stream.height; i++) {
+ memcpy((unsigned char*)(bo_handle_Y.ptr) +
+ (i * stream.stride[GST_VIDEO_COMP_Y]),
+ (unsigned char*)y_viraddr, stream.width);
+ y_viraddr += y_linesize;
+ }
+ tbm_bo_unmap(bo_Y);
+
+ tbm_bo_handle bo_handle_C =
+ tbm_bo_map(bo_C, TBM_DEVICE_CPU, TBM_OPTION_WRITE);
+ if (!bo_handle_C.ptr) {
+ TRACKRENDERER_ERROR("TBM get error : bo_handle_C.ptr is NULL");
+ return false;
+ }
+ if (color_format ==
+ VideoColorFormat::kColorFormatNV16) { // NV16, convert to NV12
+ for (int i = 0; i < stream.height / 2; i++) {
+ memcpy((unsigned char*)(bo_handle_C.ptr) +
+ (i * stream.stride[GST_VIDEO_COMP_U]),
+ (unsigned char*)c_viraddr, stream.width);
+ c_viraddr += c_linesize * 2;
+ }
+ } else if (color_format == VideoColorFormat::kColorFormatNV12) { // NV12
+ for (int i = 0; i < stream.height / 2; i++) {
+ memcpy((unsigned char*)(bo_handle_C.ptr) +
+ (i * stream.stride[GST_VIDEO_COMP_U]),
+ (unsigned char*)c_viraddr, stream.width);
+ c_viraddr += c_linesize;
+ }
+ }
+ tbm_bo_unmap(bo_C);
+ return true;
+}
+
+tbm_surface_h CreateTbmSurfaceWithBos(VideoStreamDataType& stream) {
+ unsigned int bo_num = 0;
+ for (int i = 0; i < internal::kVideoBufferPlaneMax; i++) {
+ if (stream.bo[i]) bo_num++;
+ }
+ tbm_surface_info_s surface_info;
+ memset(&surface_info, 0, sizeof(surface_info));
+ surface_info.width = stream.width;
+ surface_info.height = stream.height;
+ surface_info.format = TBM_FORMAT_NV12;
+ surface_info.bpp = tbm_surface_internal_get_bpp(surface_info.format);
+ surface_info.num_planes =
+ tbm_surface_internal_get_num_planes(surface_info.format);
+ for (unsigned int i = 0; i < surface_info.num_planes; i++) {
+ surface_info.planes[i].stride = stream.stride[i];
+ surface_info.planes[i].size = stream.stride[i] * stream.elevation[i];
+ if (i < bo_num) {
+ surface_info.planes[i].offset = 0;
+ } else {
+ surface_info.planes[i].offset =
+ surface_info.planes[i - 1].offset + surface_info.planes[i - 1].size;
+ }
+ surface_info.size += surface_info.planes[i].size;
+ }
+ tbm_surface_h tbm_surf =
+ tbm_surface_internal_create_with_bos(&surface_info, stream.bo, bo_num);
+ if (!tbm_surf) {
+ TRACKRENDERER_ERROR("failed to create tbm surface");
+ return nullptr;
+ }
+ return tbm_surf;
+}
+
+#ifdef SOUNDBAR_PRODUCT
+bool ScaleHwCodecWithGa(tbm_bufmgr bufmgr, const VideoStreamDataType& stream,
+ int y_phyaddr, int c_phyaddr, int y_linesize,
+ int c_linesize, VideoColorFormat color_format) {
+ return false;
+}
+#else
+bool TbmScale(tbm_bufmgr bufmgr, Graphics_ColorFormat_k colorMode,
+ std::uint32_t src_handle, const Geometry& src_rect,
+ int src_linesize, std::uint32_t dst_handle,
+ const Geometry& dst_rect, int dst_linesize) {
+ GraphicsGAScaleInfo ga_info;
+ memset(&ga_info, 0, sizeof(GraphicsGAScaleInfo));
+
+ ga_info.ga_mode = GRAPHICS_GA_SCALE_MODE;
+ ga_info.rop_mode = GRAPHICS_GA_ROP_COPY;
+ ga_info.ga_op_type = GRAPHICS_GA_SCALE;
+ ga_info.pre_alphamode = 0;
+ ga_info.ca_value = 0;
+ ga_info.rop_on_off = 0;
+ ga_info.color_format = colorMode;
+
+ ga_info.src_handle = src_handle;
+ ga_info.src_hbytesize = src_linesize;
+ ga_info.src_rect.x = src_rect.x;
+ ga_info.src_rect.y = src_rect.y;
+ ga_info.src_rect.w = src_rect.w;
+ ga_info.src_rect.h = src_rect.h;
+
+ ga_info.dst_handle = dst_handle;
+ ga_info.dst_hbytesize = dst_linesize;
+ ga_info.dst_rect.x = dst_rect.x;
+ ga_info.dst_rect.y = dst_rect.y;
+ ga_info.dst_rect.w = dst_rect.w;
+ ga_info.dst_rect.h = dst_rect.h;
+
+ if (Gfx_GA_Scale(bufmgr, &ga_info) < 0) {
+ TRACKRENDERER_ERROR("Gfx_GA_Scale fail");
+ return false;
+ }
+ return true;
+}
+
+bool ScaleHwCodecWithGa(tbm_bufmgr bufmgr, const VideoStreamDataType& stream,
+ int y_phyaddr, int c_phyaddr, int y_linesize,
+ int c_linesize, VideoColorFormat color_format) {
+ Geometry src_rect, dst_rect;
+
+ tbm_bo bo_Y = stream.bo[GST_VIDEO_COMP_Y];
+ tbm_bo bo_C = stream.bo[GST_VIDEO_COMP_U];
+ tbm_bo_handle bo_handle_Y = tbm_bo_get_handle(bo_Y, TBM_DEVICE_2D);
+ if (!bo_handle_Y.u32) {
+ TRACKRENDERER_ERROR("TBM get error : bo_handle_Y.u32 is NULL");
+ return false;
+ }
+
+ tbm_bo_handle bo_handle_C = tbm_bo_get_handle(bo_C, TBM_DEVICE_2D);
+ if (!bo_handle_C.u32) {
+ TRACKRENDERER_ERROR("TBM get error : bo_handle_C.u32 is NULL");
+ return false;
+ }
+
+ SetGeometry(&src_rect, stream.crop_area.x, stream.crop_area.y,
+ stream.crop_area.w, stream.crop_area.h);
+ SetGeometry(&dst_rect, 0, 0, stream.width, stream.height);
+ if (!TbmScale(bufmgr, GRAPHICS_GA_FORMAT_8BPP, y_phyaddr, src_rect,
+ y_linesize, bo_handle_Y.u32, dst_rect,
+ stream.stride[GST_VIDEO_COMP_Y])) {
+ TRACKRENDERER_ERROR("Y data Ga copy fail");
+ return false;
+ }
+
+ if (color_format == VideoColorFormat::kColorFormatNV16) { // NV16
+ SetGeometry(&src_rect, stream.crop_area.x / 2, stream.crop_area.y,
+ stream.crop_area.w / 2, stream.crop_area.h);
+ SetGeometry(&dst_rect, 0, 0, stream.width / 2, stream.height / 2);
+ } else if (color_format == VideoColorFormat::kColorFormatNV12) { // NV12
+ SetGeometry(&src_rect, stream.crop_area.x / 2, stream.crop_area.y / 2,
+ stream.crop_area.w / 2, stream.crop_area.h / 2);
+ SetGeometry(&dst_rect, 0, 0, stream.width / 2, stream.height / 2);
+ }
+ if (!TbmScale(bufmgr, GRAPHICS_GA_FORMAT_16BPP, c_phyaddr, src_rect,
+ c_linesize, bo_handle_C.u32, dst_rect,
+ stream.stride[GST_VIDEO_COMP_U])) {
+ TRACKRENDERER_ERROR("UV data Ga copy fail");
+ return false;
+ }
+ return true;
+}
+#endif
+
+int ToBufferTypeForSink(const DecodedVideoFrameBufferType& type) {
+ // 0:copy, 1:reference, -1:none (refer to gstwaylnadsink)
+ constexpr int kCopy = 0;
+ constexpr int kReference = 1;
+ constexpr int kNone = -1;
+
+ switch (type) {
+ case DecodedVideoFrameBufferType::kCopy:
+ return kCopy;
+ case DecodedVideoFrameBufferType::kReference:
+ return kReference;
+ default:
+ return kNone;
+ }
+ return kNone;
+}
+
+inline bool IsLowLatencyModeDisableAVSync(std::uint32_t low_latency_mode) {
+ constexpr std::uint32_t kAVSync = 0x0100;
+ return (low_latency_mode & kAVSync) ? true : false;
+}
+inline bool IsLowLatencyModeDisablePreroll(std::uint32_t low_latency_mode) {
+ constexpr std::uint32_t kPreroll = 0x0200;
+ return (low_latency_mode & kPreroll) ? true : false;
+}
+
+// LCOV_EXCL_START
+int GetVideoWidth(const GstCaps* caps) {
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+ if (!structure) return 0;
+
+ int width = 0;
+ if (!gst_structure_get_int(structure, "width", &width)) return 0;
+
+ return width;
+}
+
+int GetVideoHeight(const GstCaps* caps) {
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+ if (!structure) return 0;
+
+ int height = 0;
+ if (!gst_structure_get_int(structure, "height", &height)) return 0;
+
+ return height;
+}
+// LCOV_EXCL_STOP
+
+void LoadDecoderPluginTable_(const char* path) {
+ gchar* config = nullptr;
+ if (!g_file_get_contents(path, &config, nullptr, nullptr)) {
+ TRACKRENDERER_ERROR("could not find config file!");
+ return;
+ }
+
+ GstStructure* element_table =
+ gst_structure_new_empty("element_table"); // leak caution
+ GstStructure* element = nullptr;
+ gchar* start = config;
+
+ while ((element = gst_structure_from_string(start, &start))) {
+ const gchar* element_name = gst_structure_get_name(element);
+ gst_structure_set(element_table, element_name, GST_TYPE_STRUCTURE, element,
+ nullptr);
+ }
+
+ unsigned int cnt = gst_structure_n_fields(element_table);
+ for (unsigned int i = 0; i < cnt; i++) {
+ const gchar* element_name = gst_structure_nth_field_name(element_table, i);
+ gst_structure_get(element_table, element_name, GST_TYPE_STRUCTURE, &element,
+ nullptr);
+ const gchar* component_name =
+ gst_structure_get_string(element, "component-name");
+ const gchar* sink_caps = gst_structure_get_string(element, "sink");
+ // TRACKRENDERER_ERROR("plugin name [%s] comp-name [%s]", element_name,
+ // component_name);
+ ::PluginTypeTable.emplace(element_name, PluginType::kHw);
+
+ std::string caps_str(sink_caps);
+ std::size_t offset = 0;
+ while (1) {
+ // TRACKRENDERER_ERROR("offset %d", offset);
+ std::size_t start_pos = caps_str.find("video/", offset);
+ if (start_pos == std::string::npos) {
+ start_pos = caps_str.find("audio/", offset);
+ if (start_pos == std::string::npos) break;
+ }
+
+ std::string mimetype;
+ std::size_t end_pos = caps_str.find(';', start_pos);
+ if (end_pos == std::string::npos) {
+ end_pos = caps_str.find(',', start_pos);
+ if (end_pos == std::string::npos) {
+ mimetype = caps_str.substr(start_pos);
+ } else {
+ mimetype = caps_str.substr(start_pos, end_pos - start_pos);
+ }
+ // TRACKRENDERER_ERROR("parsed mimetype :: %s", mimetype.c_str());
+ ::PluginTable.emplace(
+ std::pair<std::string, std::string>(component_name, mimetype),
+ element_name);
+ break; // last item or only one item.
+ } else {
+ // gen sub string video/ or audio/ ~ ';'
+ std::string sub_caps_str =
+ caps_str.substr(start_pos, end_pos - start_pos);
+ // TRACKRENDERER_ERROR("sub str :: %s >>> start pos[%d] end
+ // pos [%d]", sub_caps_str.c_str(), start_pos, end_pos);
+ std::size_t sub_end_pos = sub_caps_str.find(',');
+ if (sub_end_pos == std::string::npos) {
+ mimetype = sub_caps_str;
+ } else {
+ mimetype = sub_caps_str.substr(0, sub_end_pos);
+ }
+ ::PluginTable.emplace(
+ std::pair<std::string, std::string>(component_name, mimetype),
+ element_name);
+ }
+ offset = end_pos;
+ // TRACKRENDERER_ERROR("start pos [%d], end pos [%d]", start_pos,
+ // end_pos);
+ // TRACKRENDERER_ERROR("parsed mimetype :: %s", mimetype.c_str());
+ }
+ }
+ gst_structure_free(element_table);
+}
+
+std::string GetIniString(dictionary* dict, const char* key, char* default_val) {
+ const gchar* value = iniparser_getstring(dict, key, default_val);
+ constexpr int kMaxIniStrLen = 100;
+ if (value && strlen(value) > 0 && strlen(value) < kMaxIniStrLen) {
+ return value;
+ } else {
+ return {};
+ }
+}
+
+void LoadSinkPluginTable() {
+ dictionary* dict = nullptr;
+ dict = iniparser_load(kPlayerIniPath);
+ if (!dict) {
+ TRACKRENDERER_ERROR("Fail to load ini file");
+ return;
+ }
+
+ char default_video_sink_name[] = "directvideosink";
+ SinkPluginTable.emplace(
+ DeviceSinkElementType::kVideoSink,
+ GetIniString(dict, "general:videosink element overlay",
+ default_video_sink_name));
+
+ char default_audio_sink_name[] = "mmaudiosink";
+ SinkPluginTable.emplace(
+ DeviceSinkElementType::kAudioSink,
+ GetIniString(dict, "general:audiosink element", default_audio_sink_name));
+
+ SinkPluginTable.emplace(DeviceSinkElementType::kPcmAudiosink,
+ GetIniString(dict, "general:pcmaudiosink element",
+ default_audio_sink_name));
+}
+
+void LoadPluginTable() {
+ LoadDecoderPluginTable_(kDecoderPluginConfPath);
+ LoadDecoderPluginTable_(kTzDecoderPluginConfPath);
+ LoadSinkPluginTable();
+ GetSoundMode();
+
+ /*
+ for (auto& item : ::PluginTable) {
+ TRACKRENDERER_ERROR(" PluginTable :: [%s][%s][%s]",
+ (item.first).first.c_str(),
+ (item.first).second.c_str(), item.second.c_str());
+ }
+
+ for (auto& item : ::PluginTypeTable) {
+ TRACKRENDERER_ERROR(" PluginTypeTable :: [%s][%d]", (item.first).c_str(),
+ static_cast<int>(item.second));
+ }
+ */
+ for (auto& item : ::SinkPluginTable) {
+ TRACKRENDERER_ERROR(" SinkPluginTable :: [%d][%s]",
+ static_cast<int>(item.first), (item.second).c_str());
+ }
+}
+
+bool CheckMpeg4Video(const std::string& mime_type, const int version) {
+ const std::string video_mpeg = "video/mpeg";
+ return ((mime_type.find(video_mpeg) != std::string::npos) && version == 4);
+}
+
+int GetAvocSourceType(bool is_main_device, bool swdecoder,
+ const std::string& mimetype) {
+ if (swdecoder || internal::IsPcmMimeType(mimetype)) return AUDIO_MM;
+ int sec_ctl_source =
+ is_main_device ? AUDIO_MULTIMEDIA_DEC0 : AUDIO_MULTIMEDIA_DEC1;
+ return sec_ctl_source;
+}
+
+int GetAudioOut(bool is_main_device) {
+ constexpr int kAudioMainOut = 0;
+ constexpr int kAudioSubOut = 1;
+ int audio_out = (is_main_device) ? kAudioMainOut : kAudioSubOut;
+ return audio_out;
+}
+
+bool FillVideoStreamDataAndTbmSurface(internal::VideoStreamDataType* stream,
+ tbm_surface_h* tbm_surf_t,
+ const int width, const int height) {
+ if (stream == nullptr || tbm_surf_t == nullptr) {
+ TRACKRENDERER_ERROR("stream[%p] or tbm_surf_t[%p] is nullptr", stream,
+ tbm_surf_t);
+ return false;
+ }
+
+ stream->format = TBM_FORMAT_NV12;
+ stream->width = width;
+ stream->height = height;
+ stream->elevation[GST_VIDEO_COMP_Y] = height;
+ stream->elevation[GST_VIDEO_COMP_U] = height / 2;
+
+ *tbm_surf_t =
+ tbm_surface_create(stream->width, stream->height, TBM_FORMAT_NV12);
+
+ tbm_surface_internal_get_plane_data(
+ *tbm_surf_t, GST_VIDEO_COMP_Y, NULL, NULL,
+ (uint32_t*)&stream->stride[GST_VIDEO_COMP_Y]);
+ tbm_surface_internal_get_plane_data(
+ *tbm_surf_t, GST_VIDEO_COMP_U, NULL, NULL,
+ (uint32_t*)&stream->stride[GST_VIDEO_COMP_U]);
+
+ stream->bo[GST_VIDEO_COMP_Y] =
+ tbm_surface_internal_get_bo(*tbm_surf_t, GST_VIDEO_COMP_Y);
+ if (!stream->bo[GST_VIDEO_COMP_Y]) {
+ TRACKRENDERER_ERROR("[bo Y] tbm_surface_internal_get_bo failed");
+ return false;
+ }
+
+ stream->bo[GST_VIDEO_COMP_U] =
+ tbm_surface_internal_get_bo(*tbm_surf_t, GST_VIDEO_COMP_U);
+ if (!stream->bo[GST_VIDEO_COMP_U]) {
+ TRACKRENDERER_ERROR("[bo C] tbm_surface_internal_get_bo failed");
+ return false;
+ }
+ return true;
+}
+
+} // namespace internal
+
+TrackRenderer::TrackRenderer() noexcept : caps_builder_(kCapsRecipes_) {
+ resource_manager_.reset(new ResourceManager(this));
+ display_.reset(new Display);
+ TRACKRENDERER_DEBUG_P(this, "Display instance > %p", display_.get());
+ screen_saver_.reset(new ScreenSaver);
+ latency_manager_.reset(new LatencyManager(this));
+ resync_audio_policy_.reset(new resync_audio::DummyPolicy());
+ playback_info_ = debug::PlayinfoSetter::Create();
+ SetDefaultAttributeValue_();
+ InitConfigSetterTable_();
+ std::call_once(::plugin_loaded, [this]() { internal::LoadPluginTable(); });
+}
+
+TrackRenderer::~TrackRenderer() {
+ TRACKRENDERER_ENTER_P(this);
+ resource_manager_.reset();
+ latency_manager_.reset();
+ UnsetVconfCb_();
+ TRACKRENDERER_LEAVE_P(this);
+}
+
+bool TrackRenderer::Start() {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (state_ == State::kResourceConflicted) return true;
+ if (!pipeline_) {
+ return false;
+ }
+ /*
+ if(still_mode_type_ == StillMode::kOff) {
+ pipeline_->SetProperty(Elements::kSinkVideo, "still-mode", FALSE);
+ }
+ */
+ pipeline_->PadRemoveProbe(kPadProbeVideoPeekBlock);
+ pipeline_->PadRemoveProbe(kPadProbeVideoDecInputBlock);
+ if (state_ < State::kWorking) return false;
+ target_substate_ = SubState::kPlaying;
+ pipeline_->SetState(Elements::kPipeline, GST_STATE_PLAYING);
+#if 0
+ internal::TvplusPerformanceLogs();
+#endif
+ bool value = false;
+ internal::GetIniValue(properties_, "generate_dot", &value);
+ if (value) pipeline_->GenerateDot("plusplayer_trackrenderer_start");
+
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::Stop() {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ resource_cv_.notify_one();
+ if (state_ == State::kStopped) {
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+ }
+ if (state_ != State::kResourceConflicted) state_ = State::kStopped;
+ if (!pipeline_) {
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+ }
+ /*
+ if(still_mode_type_ == StillMode::kOn) {
+ pipeline_->SetProperty(Elements::kSinkVideo, "still-mode", TRUE);
+ }
+ */
+ TRACKRENDERER_INFO_P(this, "Set pipeline state to GST_STATE_NULL.");
+ UnsetResourceCenterCallback_();
+ StopAudioEasing_();
+ pipeline_->PadRemoveProbe(kPadProbeVideoPeekBlock);
+ pipeline_->PadRemoveProbe(kPadProbeVideoDecoded);
+ pipeline_->PadRemoveProbe(kPadProbeVideoDecInputBlock);
+ pipeline_->SetState(Elements::kPipeline, GST_STATE_NULL);
+ SetVr360GpuModeSecure_(false);
+ ReleaseResource_();
+ playback_info_->VconfSetMsgHide();
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+void TrackRenderer::SetVideoQualityInfo_() {
+ GstCaps* video_caps = nullptr;
+ pipeline_->GetProperty(Elements::kAppSrcVideo, "caps", &video_caps);
+
+ if (video_caps == nullptr) return;
+ auto video_caps_guard = gstguard::make_guard(video_caps);
+ auto new_caps_guard = gstguard::make_guard(gst_caps_copy(video_caps));
+
+ GstStructure* structure = gst_caps_get_structure(new_caps_guard.get(), 0);
+ const gchar* stream_type = gst_structure_get_string(structure, "stream-type");
+ if (stream_type) gst_structure_set_name(structure, stream_type);
+
+ int max_w = 0, max_h = 0;
+ if (!gst_structure_get_int(structure, "maxwidth", &max_w))
+ gst_structure_get_int(structure, "width", &max_w);
+ if (!gst_structure_get_int(structure, "maxheight", &max_h))
+ gst_structure_get_int(structure, "height", &max_h);
+
+ if (internal::IsUhd8kResolution(max_w, max_h)) {
+ max_w = kMaxUhd8kWidth;
+ max_h = kMaxUhd8kHeight;
+ } else if (internal::IsUhdResolution(max_w, max_h)) {
+ max_w = kMaxUhdWidth;
+ max_h = kMaxUhdHeight;
+ } else {
+ max_w = kMaxFhdWidth;
+ max_h = kMaxFhdHeight;
+ }
+ gst_caps_set_simple(new_caps_guard.get(), "maxwidth", G_TYPE_INT, max_w,
+ "maxheight", G_TYPE_INT, max_h, NULL);
+
+ TRACKRENDERER_INFO_P(this, "max widh [%d], max height [%d]", max_w, max_h);
+ display_->SetDisplayQualityInfo(new_caps_guard.get());
+}
+
+bool TrackRenderer::Prepare() {
+ TRACKRENDERER_ENTER_E_P(this);
+ std::unique_lock<std::mutex> lock(resource_m_);
+ std::unique_lock<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) {
+ TRACKRENDERER_ERROR_P(this, "already stopped");
+ return false;
+ }
+ SetVconfCb_();
+
+ if (AvocPlayerRegister_() == false) {
+ TRACKRENDERER_ERROR_P(this, "AvocPlayerRegister_() failed");
+ return false;
+ }
+ SetResourceCenterCallback_();
+
+ ::PerfUsrTrace("Prepare GetResource");
+ if (GetResource_(kTrackTypeMax) == false) {
+ TRACKRENDERER_ERROR_P(this, "resource acquire failed.");
+ ReleaseResource_();
+ return false;
+ }
+ SetVr360GpuModeSecure_(true);
+ ::PerfUsrTrace("Prepare CreatePipeline");
+ if (CreatePipeline_() == false) {
+ TRACKRENDERER_ERROR_P(this, "renderer pipeline creation failed");
+ return false;
+ }
+
+ latency_manager_->SetPipeline(pipeline_.get());
+ if (!AvocPlayRequest_()) {
+ TRACKRENDERER_ERROR_P(this, "AvocPlayRequest_() failed");
+ return false;
+ }
+ ::PerfUsrTrace("Prepare InitAvoc end");
+
+ SetAudioOut_();
+
+ int max_width = 0;
+ int max_height = 0;
+ for (const Track& track : trackinfo_) {
+ if (track.type == kTrackTypeVideo) {
+ max_width = track.maxwidth;
+ max_height = track.maxheight;
+ break;
+ }
+ }
+
+ if (internal::IsUhd8kResolution(max_width, max_height)) {
+ constexpr uint32_t kAdaptiveStreaming8kMode =
+ 0x80; // refer to waylandsink properity value.
+ TRACKRENDERER_INFO_P(this, "Set 8K Video quality mode : 0x80");
+ display_->SetVideoQualityMode(kAdaptiveStreaming8kMode);
+ }
+ ::PerfUsrTrace("Prepare SetVideoQualityInfo_ begin");
+ SetVideoQualityInfo_();
+ ::PerfUsrTrace("Prepare SetVideoQualityInfo_ end");
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->Update(obj);
+ });
+ auto audio_delay_info = gstguard::make_guard(gst_structure_new(
+ "audio-delay-info", "video-max-width", G_TYPE_UINT, max_width,
+ "video-max-height", G_TYPE_UINT, max_height, NULL));
+ TRACKRENDERER_INFO_P(this, "Set AudioDelayInformation v_res [%d x %d]",
+ max_width, max_height);
+ pipeline_->SetProperty(Elements::kSinkAudio, "audio-delay-info-for-avsync",
+ audio_delay_info.get());
+ if (!pipeline_->SetState(Elements::kPipeline, GST_STATE_PAUSED)) {
+ TRACKRENDERER_ERROR_P(this, "Set State to PAUSED failed");
+ return false;
+ }
+ if (is_pardar_updated_) {
+ TRACKRENDERER_INFO_P(this, "is_pardar_updated_ is true");
+ pipeline_->SetParDar(Elements::kDecVideo, par_dar_.time_millisecond,
+ par_dar_.par_num, par_dar_.par_den, par_dar_.dar_num,
+ par_dar_.dar_den);
+ }
+ audio_lk.unlock();
+
+ if (!HasSubtitleOnly_() &&
+ !internal::IsLowLatencyModeDisableAVSync(low_latency_mode_) &&
+ !internal::IsLowLatencyModeDisablePreroll(low_latency_mode_))
+ resource_cv_.wait(lock);
+ if (pipeline_) {
+ pipeline_->PadRemoveProbe(kPadProbeVideoDecoded);
+ }
+ if (state_ != State::kWorking) {
+ TRACKRENDERER_ERROR_P(
+ this,
+ "Prepare fail, aborted by eos, error message, resource conflict or "
+ "stop called");
+ return false;
+ }
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::Pause() {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (state_ == State::kResourceConflicted) return true;
+ if (!pipeline_) {
+ return false;
+ }
+ target_substate_ = SubState::kPaused;
+ if (!NeedSyncPause_())
+ pipeline_->SetState(Elements::kPipeline, GST_STATE_PAUSED);
+
+ /*In ChangeSource_ situation, video and audio will in pause status since flush
+ event But subtitle can't in pause status since the "async" property is
+ false, so need to set to pause alone*/
+ GstState cur_state = GST_STATE_NULL;
+ bool ret = false;
+
+ ret = pipeline_->GetState(Elements::kBinSubtitle, &cur_state, NULL,
+ 50 * GST_MSECOND);
+ if ((true == ret) && (GST_STATE_PLAYING == cur_state)) {
+ TRACKRENDERER_DEBUG_P(
+ this, "kBinSubtitle paused failed, try to pause kBinSubtitle alone");
+ pipeline_->SetState(Elements::kBinSubtitle, GST_STATE_PAUSED);
+ }
+
+ ret = pipeline_->GetState(Elements::kSinkCaption, &cur_state, NULL,
+ 50 * GST_MSECOND);
+ if ((true == ret) && (GST_STATE_PLAYING == cur_state)) {
+ TRACKRENDERER_DEBUG_P(
+ this, "kSinkCaption paused failed, try to pause kSinkCaption alone");
+ pipeline_->SetState(Elements::kSinkCaption, GST_STATE_PAUSED);
+ }
+
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::Resume() {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (state_ == State::kResourceConflicted) return true;
+ if (!pipeline_) {
+ return false;
+ }
+
+ if (is_video_frame_peek_) {
+ pipeline_->PadRemoveProbe(kPadProbeVideoPeekBlock);
+ }
+ if (support_videodec_underflow_pause_) {
+ pipeline_->PadRemoveProbe(kPadProbeVideoDecInputBlock);
+ }
+
+ target_substate_ = SubState::kPlaying;
+ pipeline_->SetState(Elements::kPipeline, GST_STATE_PLAYING);
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::SetTrack(const std::vector<Track>& trackinfo) {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (enable_audio_track_change_ && state_ == State::kWorking) {
+ enable_audio_track_change_ = false;
+
+ if (!internal::IsAvailableCodecChange(*(trackctx_[kTrackTypeAudio].track)))
+ return false;
+ auto is_audio_track = [](const Track& item) noexcept->bool {
+ return item.type == kTrackTypeAudio;
+ };
+ auto new_track =
+ find_if(trackinfo.begin(), trackinfo.end(), is_audio_track);
+ if (new_track == trackinfo.end()) return false;
+ if (!internal::IsAvailableCodecChange(*new_track)) return false;
+
+ auto old_track =
+ find_if(trackinfo_.begin(), trackinfo_.end(), is_audio_track);
+ if (old_track != trackinfo_.end()) {
+ trackinfo_.erase(old_track);
+ }
+ trackinfo_.push_back(*new_track);
+ track_util::ShowTrackInfo(trackinfo_);
+ for (auto& track : trackinfo_) {
+ if (track.type == kTrackTypeAudio) {
+ trackctx_[kTrackTypeAudio].index = track.index;
+ trackctx_[kTrackTypeAudio].track = &track;
+ auto caps = caps_builder_.Build(
+ track,
+ internal::IsDrmEmeElementNecessary(drm_property_, track.mimetype));
+ pipeline_->SetAppSrcCaps(Elements::kAppSrcAudio, caps);
+ TRACKRENDERER_INFO_P(this, "change audio track after flush");
+ }
+ }
+ return true;
+ }
+
+ if (!trackinfo_.empty()) {
+ TRACKRENDERER_ERROR_P(
+ this, "trackinfo_ already was set. DO NOT CALL SetTrack()!!");
+ return false;
+ }
+ trackinfo_ = trackinfo;
+ for (auto& track : trackinfo_) {
+ if (track.type >= kTrackTypeMax) return false;
+ trackctx_[track.type].index = track.index;
+ trackctx_[track.type].track = &track;
+ if (track.type == kTrackTypeVideo) {
+ trackctx_[track.type].create_pipeline = std::bind(
+ &TrackRenderer::CreateVideoPipeline_, this, std::placeholders::_1);
+ UpdateTrackFrameRate_(track.framerate_num, track.framerate_den);
+ } else if (track.type == kTrackTypeAudio) {
+ if (track.use_swdecoder || internal::IsSdkEnabledFeature()) {
+ trackctx_[track.type].create_pipeline =
+ std::bind(&TrackRenderer::CreateSwAudioPipeline_, this,
+ std::placeholders::_1);
+ } else {
+ if (internal::IsDecoderElementNecessary(track.mimetype)) {
+ trackctx_[track.type].create_pipeline =
+ std::bind(&TrackRenderer::CreateAudioPipeline_, this,
+ std::placeholders::_1);
+ } else {
+ trackctx_[track.type].create_pipeline =
+ std::bind(&TrackRenderer::CreateRawAudioPipeline_, this,
+ std::placeholders::_1);
+ }
+ }
+ }
+ }
+ trackctx_[kTrackTypeSubtitle].create_pipeline = std::bind(
+ &TrackRenderer::CreateSubtitlePipeline_, this, std::placeholders::_1);
+ return true;
+}
+
+void TrackRenderer::SetIniProperty(
+ const std::map<std::string, bool>& properties) {
+ auto find = [](const std::map<std::string, bool>& property,
+ const std::string& key, bool& value) -> bool {
+ auto look = property.find(key);
+ if (look == property.end()) {
+ return false;
+ }
+ value = look->second;
+ return true;
+ };
+ std::string key = "generate_dot";
+ bool value = false;
+ if (find(properties, key, value)) {
+ properties_[key] = value;
+ }
+}
+
+bool TrackRenderer::Seek(unsigned long long time_millisecond,
+ double playback_rate) {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (!pipeline_) {
+ return false;
+ }
+ // mute off/on values are defined in mmaudiosink element
+ static const gint mute_off = 2;
+ static const gint mute_on = 3;
+
+ gint mute_flag = mute_off;
+ gboolean async = TRUE;
+ if (playback_rate != kDefaultPlaybackRate) {
+ mute_flag = mute_on;
+ async = FALSE;
+ }
+
+ TRACKRENDERER_INFO_P(this, "Set async property as [%d]", async);
+ auto is_audio_track = [](const Track& item) noexcept->bool {
+ return item.mimetype.find("audio") != std::string::npos;
+ };
+ auto target = find_if(trackinfo_.begin(), trackinfo_.end(), is_audio_track);
+ if (target != trackinfo_.end()) {
+ if (target->use_swdecoder || internal::IsSdkEnabledFeature())
+ pipeline_->SetProperty(Elements::kSinkAudio, "mute", mute_flag % 2,
+ "async", async);
+ else {
+ pipeline_->SetProperty(Elements::kSinkAudio, "mute-mask", mute_flag,
+ "async", async);
+ }
+ }
+
+ TRACKRENDERER_INFO_P(this, " target %llu ms rate [%lf]", time_millisecond,
+ playback_rate);
+ gint64 start = time_millisecond * GST_MSECOND, stop = GST_CLOCK_TIME_NONE;
+ if (playback_rate < 0) {
+ stop = start;
+ start = 0;
+ }
+ if (!pipeline_->Seek(playback_rate, GST_FORMAT_TIME,
+ (GstSeekFlags)(GST_SEEK_FLAG_FLUSH), GST_SEEK_TYPE_SET,
+ start, GST_SEEK_TYPE_SET, stop)) {
+ TRACKRENDERER_ERROR_P(this, "Fail to seek to [%llu] ms", time_millisecond);
+ }
+ is_seeking_ = true;
+ if (playback_rate != kDefaultPlaybackRate) {
+ TRACKRENDERER_INFO_P(this,
+ "sequential mode should be off during trickplay");
+ pipeline_->SetProperty(Elements::kSinkVideo, "is-trickplay", TRUE);
+ } else
+ pipeline_->SetProperty(Elements::kSinkVideo, "is-trickplay", FALSE);
+
+ if (is_video_frame_peek_) {
+ pipeline_->PadRemoveProbe(kPadProbeVideoPeekBlock);
+ pipeline_->PadAddProbe(Elements::kSinkVideo, kPadProbeVideoPeekBlock,
+ "sink", GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ GstPadProbeVideoPeekBlockCb_, this, nullptr);
+ }
+ rendering_start_time_.time = start;
+ if (playback_rate > 0) {
+ for (int i = 0; i < kTrackTypeMax; ++i) {
+ trackctx_[i].is_enough_data = false;
+ trackctx_[i].need_update_segment = true;
+ }
+
+ if (is_accurate_seek_) {
+ UpdateStartSegment_(time_millisecond * GST_MSECOND, kTrackTypeMax);
+ }
+ }
+ playback_rate_ = playback_rate;
+
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::Seek(unsigned long long time_millisecond,
+ double playback_rate, bool audio_mute) {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (!pipeline_) {
+ return false;
+ }
+ // mute off/on values are defined in mmaudiosink element
+ static const gint mute_off = 2;
+ static const gint mute_on = 3;
+
+ gint mute_flag = audio_mute ? mute_on : mute_off;
+
+ auto is_audio_track = [](const Track& item) noexcept->bool {
+ return item.mimetype.find("audio") != std::string::npos;
+ };
+ auto target = find_if(trackinfo_.begin(), trackinfo_.end(), is_audio_track);
+ if (target != trackinfo_.end()) {
+ if (target->use_swdecoder || internal::IsSdkEnabledFeature()) {
+ pipeline_->SetProperty(Elements::kSinkAudio, "mute", mute_flag % 2);
+ } else {
+ pipeline_->SetProperty(Elements::kSinkAudio, "mute-mask", mute_flag);
+ }
+ }
+
+ TRACKRENDERER_INFO_P(this, " target %llu ms rate [%lf] mute [%d]",
+ time_millisecond, playback_rate, audio_mute);
+ if (!pipeline_->Seek(playback_rate, GST_FORMAT_TIME,
+ (GstSeekFlags)(GST_SEEK_FLAG_FLUSH), GST_SEEK_TYPE_SET,
+ time_millisecond * GST_MSECOND, GST_SEEK_TYPE_NONE,
+ GST_CLOCK_TIME_NONE)) {
+ TRACKRENDERER_ERROR_P(this, "Fail to seek to [%llu] ms", time_millisecond);
+ }
+ is_seeking_ = true;
+ if (playback_rate != kDefaultPlaybackRate) {
+ TRACKRENDERER_INFO_P(this,
+ "sequential mode should be off during trickplay");
+ pipeline_->SetProperty(Elements::kSinkVideo, "is-trickplay", TRUE);
+ } else
+ pipeline_->SetProperty(Elements::kSinkVideo, "is-trickplay", FALSE);
+
+ if (is_video_frame_peek_) {
+ pipeline_->PadRemoveProbe(kPadProbeVideoPeekBlock);
+ pipeline_->PadAddProbe(Elements::kSinkVideo, kPadProbeVideoPeekBlock,
+ "sink", GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ GstPadProbeVideoPeekBlockCb_, this, nullptr);
+ }
+ if (support_videodec_underflow_pause_) {
+ pipeline_->PadRemoveProbe(kPadProbeVideoDecInputBlock);
+ }
+
+ rendering_start_time_.time = time_millisecond * GST_MSECOND;
+ for (int i = 0; i < kTrackTypeMax; ++i) {
+ trackctx_[i].is_enough_data = false;
+ trackctx_[i].need_update_segment = true;
+ }
+
+ if (is_accurate_seek_) {
+ UpdateStartSegment_(time_millisecond * GST_MSECOND, kTrackTypeMax);
+ }
+ playback_rate_ = playback_rate;
+
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::SetPlaybackRate(double playback_rate, bool audio_mute) {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (!pipeline_) {
+ return false;
+ }
+ // mute off/on values are defined in mmaudiosink element
+ static const gint mute_off = 2;
+ static const gint mute_on = 3;
+ uint64_t curtime_in_msec = 0;
+ gint mute_flag = audio_mute ? mute_on : mute_off;
+ GetPlayingTime_(&curtime_in_msec);
+
+ TRACKRENDERER_INFO_P(this, "Set mute-mask property as [%d] ,rate [%lf]",
+ mute_flag, playback_rate);
+ auto is_audio_track = [](const Track& item) noexcept->bool {
+ return item.mimetype.find("audio") != std::string::npos;
+ };
+ auto target = find_if(trackinfo_.begin(), trackinfo_.end(), is_audio_track);
+ if (target != trackinfo_.end()) {
+ if (target->use_swdecoder || internal::IsSdkEnabledFeature()) {
+ pipeline_->SetProperty(Elements::kSinkAudio, "mute", mute_flag % 2);
+ } else {
+ pipeline_->SetProperty(Elements::kSinkAudio, "mute-mask", mute_flag);
+ pipeline_->SetProperty(Elements::kDecAudio, "audio-playback-rate",
+ gint(playback_rate * 100));
+ pipeline_->SetProperty(Elements::kSinkAudio, "playback-rate",
+ gint(playback_rate * 100));
+ }
+ }
+
+ Elements audio_probe_element = Elements::kDecAudio;
+ Elements video_probe_element = Elements::kDecVideo;
+ Elements subtitle_probe_element = Elements::kAppSrcSubtitle;
+ Elements close_caption_probe_element = Elements::kQueueCaption;
+ for (const Track& track : trackinfo_) {
+ if (internal::IsPcmMimeType(track.mimetype)) {
+ audio_probe_element = Elements::kAppSrcAudio;
+ } else if (internal::IsVideoRawMimeType(track.mimetype)) {
+ video_probe_element = Elements::kAppSrcVideo;
+ }
+ }
+ std::map<Elements, std::pair<std::string, std::string>> probe_map = {
+ {audio_probe_element,
+ {"AUDIO_RATE_IDLE_PROBE", "AUDIO_RATE_BLOCK_PROBE"}},
+ {video_probe_element,
+ {"VIDEO_RATE_IDLE_PROBE", "VIDEO_RATE_BLOCK_PROBE"}},
+ {subtitle_probe_element,
+ {"SUBTITLE_RATE_IDLE_PROBE", "SUBTITLE_RATE_BLOCK_PROBE"}},
+ {close_caption_probe_element,
+ {"CLOSE_CAPTION_RATE_IDLE_PROBE", "CLOSE_CAPTION_RATE_BLOCK_PROBE"}}};
+
+ std::map<Elements, Elements> probe_sink_map = {
+ {audio_probe_element, Elements::kSinkAudio},
+ {video_probe_element, Elements::kSinkVideo},
+ {subtitle_probe_element, Elements::kSinkSubtitle},
+ {close_caption_probe_element, Elements::kSinkCaption}};
+
+ for (auto& kv : probe_map) {
+ const Elements& element = kv.first;
+ std::pair<std::string, std::string>& probe = kv.second;
+ pipeline_->PadAddProbe(element, (probe.second).c_str(), "src",
+ GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, nullptr,
+ nullptr, nullptr);
+ gboolean is_sync = FALSE;
+ pipeline_->GetProperty(probe_sink_map[element], "sync", &is_sync);
+ pipeline_->SetProperty(probe_sink_map[element], "sync", FALSE);
+ FlushDownStream_(element, (probe.first).c_str(), TRUE);
+ if (is_sync) pipeline_->SetProperty(probe_sink_map[element], "sync", TRUE);
+ }
+
+ if (playback_rate != kDefaultPlaybackRate) {
+ TRACKRENDERER_INFO_P(this,
+ "sequential mode should be off during trickplay");
+ pipeline_->SetProperty(Elements::kSinkVideo, "is-trickplay", TRUE);
+ } else
+ pipeline_->SetProperty(Elements::kSinkVideo, "is-trickplay", FALSE);
+
+ for (auto& kv : probe_map) {
+ const Elements& element = kv.first;
+ std::pair<std::string, std::string>& probe = kv.second;
+ pipeline_->PushSegmentEvent(element, curtime_in_msec * GST_MSECOND,
+ playback_rate);
+ pipeline_->PadRemoveProbe((probe.second).c_str());
+ }
+ rendering_start_time_.time = curtime_in_msec * GST_MSECOND;
+ playback_rate_ = playback_rate;
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+// LCOV_EXCL_START
+bool TrackRenderer::ControlDropRate_(uint64_t packet_pts,
+ SubmitStatus* status) {
+ uint64_t current_time = 0;
+ GetPlayingTime_(¤t_time);
+ const auto islate = [](uint64_t cur_time, uint64_t pts) {
+ if (pts <= cur_time) {
+ return true;
+ } else {
+ return false;
+ }
+ };
+ if (islate(current_time, packet_pts)) {
+ *status = SubmitStatus::kDrop;
+ return true;
+ }
+ *status = SubmitStatus::kHold;
+ return false;
+}
+
+bool TrackRenderer::IsSegmentUpdated_() const {
+ for (int i = 0; i < kTrackTypeMax; i++) {
+ if (trackctx_[i].need_update_segment == false) return true;
+ }
+ return false;
+}
+
+bool TrackRenderer::HasSubtitleOnly_() const {
+ return trackctx_[kTrackTypeAudio].index == kInvalidTrackIndex &&
+ trackctx_[kTrackTypeVideo].index == kInvalidTrackIndex &&
+ trackctx_[kTrackTypeSubtitle].index != kInvalidTrackIndex;
+}
+// LCOV_EXCL_STOP
+
+bool TrackRenderer::SubmitPacket(const DecoderInputBufferPtr& data,
+ SubmitStatus* status) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ SubmitStatus submitstate = SubmitStatus::kSuccess;
+ BOOST_SCOPE_EXIT(&submitstate, &status) {
+ if (status) *status = submitstate;
+ }
+ BOOST_SCOPE_EXIT_END
+ if (state_ == State::kStopped) {
+ submitstate = SubmitStatus::kNotPrepared;
+ return false;
+ }
+ if (!pipeline_) {
+ // TRACKRENDERER_DEBUG_P(this, "pipeline is nullptr , wait prepared");
+ submitstate = SubmitStatus::kNotPrepared;
+ return false;
+ }
+
+ const TrackType type = data->GetType();
+ Elements element = Elements::kAppSrcAudio;
+ if (type == kTrackTypeVideo) {
+ element = Elements::kAppSrcVideo;
+ } else if (type == kTrackTypeSubtitle) {
+ element = Elements::kAppSrcSubtitle;
+ }
+
+ if (data->IsEos()) {
+ pipeline_->SignalEmitByName(element, "end-of-stream");
+ submitstate = SubmitStatus::kSuccess;
+ return true;
+ }
+
+ // TRACKRENDERER_DEBUG_P(this,
+ // "TYPE[%d] PKT INDEX[%d] TIMESTAMP[%lld]ms SIZE[%d] EOS[%d]", type,
+ // data->GetIndex(), GST_BUFFER_TIMESTAMP(data->Get()) / 1000000,
+ // gst_buffer_get_size(const_cast<GstBuffer*>(data->Get())),
+ // data->IsEos());
+
+ if (type >= kTrackTypeMax) return false;
+ if (trackctx_[type].index == kInvalidTrackIndex) {
+ // TRACKRENDERER_ERROR_P(this, "data is not activated track. type:%d,
+ // input:acti %d:%d", type, data->GetIndex(), trackctx_[type].index);
+ return ControlDropRate_(GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(data->Get())),
+ &submitstate);
+ }
+
+ if (!unlimited_max_buffer_mode_ && trackctx_[type].is_enough_data) {
+ submitstate = SubmitStatus::kFull;
+ return false;
+ }
+
+ const bool is_segment_updated = IsSegmentUpdated_();
+ const bool request_to_hold_subtitle = (type == kTrackTypeSubtitle) &&
+ (is_segment_updated == false) &&
+ (HasSubtitleOnly_() == false);
+
+ if (request_to_hold_subtitle) {
+ TRACKRENDERER_INFO_P(this, "subtitle should be submitted after a/v packet");
+ submitstate = SubmitStatus::kHold;
+ return false;
+ }
+
+ GstBuffer* buffer = data->Release();
+
+ if (is_segment_updated == false) {
+ UpdateStartSegment_(GST_BUFFER_TIMESTAMP(buffer), type);
+ }
+
+ pipeline_->AppSrcPushBuffer(element, buffer);
+
+ if (type == kTrackTypeVideo) {
+ latency_manager_->UpdateVideoFrameStatus(
+ LatencyManager::UpdatePacketStatus::kSubmit);
+ }
+
+ submitstate = SubmitStatus::kSuccess;
+ return true;
+}
+
+bool TrackRenderer::SetMatroskaColorInfo(const std::string& color_info) {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ if (pipeline_ == nullptr) return false;
+ GstCaps* old_caps = nullptr;
+ pipeline_->GetProperty(Elements::kAppSrcVideo, "caps", &old_caps);
+ if (old_caps == nullptr) return false;
+ auto old_caps_guard = gstguard::make_guard(old_caps);
+ auto new_caps_guard = gstguard::make_guard(gst_caps_copy(old_caps));
+ gst_caps_set_simple(new_caps_guard.get(), "matroska_color_info",
+ G_TYPE_STRING, color_info.c_str(), nullptr);
+ pipeline_->SetProperty(Elements::kSinkVideo, "video-color-info",
+ new_caps_guard.get());
+ pipeline_->SetProperty(Elements::kAppSrcVideo, "caps", new_caps_guard.get());
+ return true;
+}
+
+void TrackRenderer::UpdateStartSegment_(GstClockTime start_time,
+ const TrackType& type) {
+ if (type == kTrackTypeMax) {
+ TRACKRENDERER_INFO_P(
+ this, "update src segment with start time [%llu ms] for all tracks",
+ start_time / 1000000);
+ for (int i = 0; i < kTrackTypeMax; ++i) {
+ trackctx_[i].need_update_segment = false;
+ }
+ } else {
+ const auto& track_type_str = track_util::GetTrackTypeString(type);
+ TRACKRENDERER_ERROR_P(
+ this, "update src segment with first pts [%llu ms] by %s stream",
+ start_time / 1000000, track_type_str.c_str());
+ trackctx_[type].need_update_segment = false;
+ }
+
+ if (rendering_start_time_.is_set) {
+ start_time = rendering_start_time_.time;
+ rendering_start_time_.is_set = false;
+ TRACKRENDERER_ERROR_P(
+ this,
+ "update src segment with first pts [%llu ms] by start-rendering-time",
+ start_time / 1000000);
+ }
+
+ pipeline_->SetProperty(Elements::kAppSrcVideo, "update-segment", start_time);
+ pipeline_->SetProperty(Elements::kAppSrcAudio, "update-segment", start_time);
+ pipeline_->SetProperty(Elements::kAppSrcSubtitle, "update-segment",
+ start_time);
+ rendering_start_time_.time = start_time;
+}
+
+void TrackRenderer::CreateAppSrc_(TrackType type, const std::string& mimetype) {
+ if (internal::IsTzAppSrcElementNecessary(drm_property_, mimetype)) {
+ if (type == kTrackTypeVideo) {
+ pipeline_->FactoryMake(Elements::kAppSrcVideo, "tzappsrc",
+ "tz_video_appsrc");
+ } else {
+ pipeline_->FactoryMake(Elements::kAppSrcAudio, "tzappsrc",
+ "tz_audio_appsrc");
+ }
+ } else {
+ if (type == kTrackTypeVideo) {
+ pipeline_->FactoryMake(Elements::kAppSrcVideo, "appsrc", "video_appsrc");
+ } else {
+ pipeline_->FactoryMake(Elements::kAppSrcAudio, "appsrc", "audio_appsrc");
+ }
+ }
+ SetDefaultAppSrcSignals_(type);
+
+ Elements element = Elements::kAppSrcAudio;
+ if (type == kTrackTypeVideo) element = Elements::kAppSrcVideo;
+
+ pipeline_->SetProperty(element, "format", GST_FORMAT_TIME);
+ pipeline_->SetProperty(element, "stream-type", GST_APP_STREAM_TYPE_SEEKABLE);
+}
+
+void TrackRenderer::SetDefaultAppSrcSignals_(const TrackType& type) {
+ if (type == kTrackTypeVideo) {
+ pipeline_->SignalConnect(Elements::kAppSrcVideo, "need-data",
+ G_CALLBACK(GstVideoNeedDataCb_), this);
+ pipeline_->SignalConnect(Elements::kAppSrcVideo, "enough-data",
+ G_CALLBACK(GstVideoEnoughDataCb_), this);
+ pipeline_->SignalConnect(Elements::kAppSrcVideo, "seek-data",
+ G_CALLBACK(GstVideoSeekDataCb_), this);
+ } else if (type == kTrackTypeAudio) {
+ pipeline_->SignalConnect(Elements::kAppSrcAudio, "need-data",
+ G_CALLBACK(GstAudioNeedDataCb_), this);
+ pipeline_->SignalConnect(Elements::kAppSrcAudio, "enough-data",
+ G_CALLBACK(GstAudioEnoughDataCb_), this);
+ pipeline_->SignalConnect(Elements::kAppSrcAudio, "seek-data",
+ G_CALLBACK(GstAudioSeekDataCb_), this);
+ } else if (type == kTrackTypeSubtitle) {
+ pipeline_->SignalConnect(Elements::kAppSrcSubtitle, "need-data",
+ G_CALLBACK(GstSubtitleNeedDataCb_), this);
+ pipeline_->SignalConnect(Elements::kAppSrcSubtitle, "enough-data",
+ G_CALLBACK(GstSubtitleEnoughDataCb_), this);
+ pipeline_->SignalConnect(Elements::kAppSrcSubtitle, "seek-data",
+ G_CALLBACK(GstSubtitleSeekDataCb_), this);
+ }
+}
+
+void TrackRenderer::CreateDrmElement_(const Track& track) {
+ if (!internal::IsDrmEmeElementNecessary(drm_property_, track.mimetype))
+ return;
+
+ if (track.type == kTrackTypeAudio) {
+ pipeline_->FactoryMake(Elements::kDrmAudio, "drm_eme", "audio_drm");
+ pipeline_->SetProperty(Elements::kDrmAudio, "plus-player-eme", TRUE);
+ if (internal::IsExternalDecryptionCase(drm_property_) == false) {
+ pipeline_->SetProperty(Elements::kDrmAudio, "getrights-complete-cb",
+ GstAudioDrmInitDataCb_);
+ pipeline_->SetProperty(Elements::kDrmAudio, "getrights-complete-cb-data",
+ this);
+ }
+ } else if (track.type == kTrackTypeVideo) {
+ pipeline_->FactoryMake(Elements::kDrmVideo, "drm_eme", "video_drm");
+ pipeline_->SetProperty(Elements::kDrmVideo, "plus-player-eme", TRUE);
+ if (!internal::IsExternalDecryptionCase(drm_property_)) {
+ pipeline_->SetProperty(Elements::kDrmVideo, "getrights-complete-cb",
+ GstVideoDrmInitDataCb_);
+ pipeline_->SetProperty(Elements::kDrmVideo, "getrights-complete-cb-data",
+ this);
+ }
+ }
+}
+
+const char* TrackRenderer::GetAudioSinkPluginName_(
+ bool swdecoder, const std::string& mimetype) {
+ if (internal::IsSdkEnabledFeature()) return "pulsesink";
+ if (product_cfg::GetProductType() == ProductType::kAv) {
+ return "mmaudiosink2";
+ }
+ if (swdecoder || internal::IsPcmMimeType(mimetype)) {
+ const std::string comp_name =
+ resource_manager_->GetComponentName(audio_out_id_);
+ if (comp_name == kPulseSinkComponentName) return "pulsesink";
+ return ::SinkPluginTable.count(::DeviceSinkElementType::kPcmAudiosink) > 0
+ ? ::SinkPluginTable.at(::DeviceSinkElementType::kPcmAudiosink)
+ .c_str()
+ : nullptr;
+ }
+ return ::SinkPluginTable.count(::DeviceSinkElementType::kAudioSink) > 0
+ ? ::SinkPluginTable.at(::DeviceSinkElementType::kAudioSink).c_str()
+ : nullptr;
+}
+
+void TrackRenderer::CreateAudioSink_(const std::string& sink_name) {
+ if (sink_name.find("alsasink") != std::string::npos ||
+ sink_name.find("pulsesink") != std::string::npos) {
+ pipeline_->FactoryMake(Elements::kAudioConvert, "audioconvert", nullptr);
+ pipeline_->FactoryMake(Elements::kCapsFillterDefault, "capsfilter",
+ nullptr);
+ auto caps1 = gstguard::make_guard(
+ gst_caps_from_string("audio/x-raw, "
+ "format = (string) S16LE, "
+ "layout = (string) interleaved, "
+ "channels = (int) 2"));
+ pipeline_->SetProperty(Elements::kCapsFillterDefault, "caps", caps1.get());
+ pipeline_->FactoryMake(Elements::kAudioResample, "audioresample", nullptr);
+ pipeline_->FactoryMake(Elements::kCapsFillter2, "capsfilter", nullptr);
+ auto caps2 =
+ gstguard::make_guard(gst_caps_from_string("audio/x-raw, "
+ "rate = (int) 48000"));
+ pipeline_->SetProperty(Elements::kCapsFillter2, "caps", caps2.get());
+ pipeline_->FactoryMake(Elements::kScaleTempo, "scaletempo", nullptr);
+
+ pipeline_->FactoryMake(Elements::kSinkAudio, sink_name.c_str(), NULL);
+ if (sink_name.find("pulsesink") == std::string::npos) {
+ pipeline_->SetProperty(Elements::kSinkAudio, "device", "hw:0,0");
+ }
+ pipeline_->SetProperty(Elements::kSinkAudio, "drift-tolerance",
+ ((200 * GST_MSECOND) / GST_USECOND));
+ pipeline_->SetProperty(Elements::kSinkAudio, "provide-clock", FALSE);
+ pipeline_->SetProperty(Elements::kSinkAudio, "sync", TRUE);
+ pipeline_->SetProperty(Elements::kSinkAudio, "force-render", FALSE);
+ pipeline_->SetProperty(Elements::kSinkAudio, "multiview-window-id",
+ static_cast<int>(display_->GetSurfaceId()));
+
+ if (sink_name.find("pulsesink") == std::string::npos) {
+ resync_audio_policy_.reset(new resync_audio::DummyPolicy());
+ } else {
+ resync_audio_policy_.reset(new resync_audio::SwDecoderPolicy(
+ rendering_start_time_.time, playback_rate_));
+ }
+ } else if (sink_name.find("fakesink") != std::string::npos) {
+ TRACKRENDERER_ERROR_P(this, "fake");
+ pipeline_->FactoryMake(Elements::kSinkAudio, sink_name.c_str(), NULL);
+ pipeline_->SetProperty(Elements::kSinkAudio, "async", TRUE, NULL);
+ pipeline_->SetProperty(Elements::kSinkAudio, "sync", TRUE, NULL);
+ pipeline_->SetProperty(Elements::kSinkAudio, "max-lateness",
+ static_cast<gint64>(-1), NULL);
+ pipeline_->SetProperty(Elements::kSinkAudio, "no-drop", TRUE, NULL);
+ resync_audio_policy_.reset(new resync_audio::DummyPolicy());
+ } else {
+ pipeline_->FactoryMake(Elements::kSinkAudio, sink_name.c_str(), NULL);
+ pipeline_->SetProperty(Elements::kSinkAudio, //
+ "sync", TRUE, //
+ "async", TRUE, //
+ "fast-rendering", 200000000LL, //
+ "force-render", FALSE);
+ pipeline_->SetProperty(Elements::kSinkAudio, "multiview-window-id",
+ static_cast<int>(display_->GetSurfaceId()));
+ int is_audio_decoder_sub =
+ resource_manager_->IsMainDevice(audio_decoder_id_) ? 0 : 1;
+ pipeline_->SetProperty(Elements::kSinkAudio, "device-audio-decoder",
+ is_audio_decoder_sub, NULL);
+ resync_audio_policy_.reset(new resync_audio::DefaultPolicy());
+ }
+ pipeline_->SetProperty(Elements::kSinkAudio, "mls-player-id", avoc_id_);
+}
+
+void TrackRenderer::CreateVideoDecoder_(const char* dec_name) {
+ pipeline_->FactoryMake(Elements::kDecVideo, dec_name, NULL);
+
+ pipeline_->SetProperty(Elements::kDecVideo, "extradata_flag", TRUE);
+ pipeline_->SetProperty(Elements::kDecVideo, "decoding-type",
+ static_cast<gint>(video_decoding_mode_));
+ pipeline_->SignalConnect(Elements::kDecVideo, "pad-added",
+ G_CALLBACK(GstClosedCaptionPadAddedCb_), this);
+}
+
+bool TrackRenderer::CreateVideoSink_() {
+ if (internal::IsDisplayNeeded(display_type_) == false)
+ pipeline_->FactoryMake(Elements::kSinkVideo, "fakesink", "fakesink");
+ else
+ pipeline_->FactoryMake(Elements::kSinkVideo, "directvideosink",
+ "directvideosink");
+
+ // gstbasesink property
+ pipeline_->SetProperty(Elements::kSinkVideo, "sync", TRUE, "async", TRUE,
+ "max-lateness", static_cast<gint64>(0));
+ if (video_pre_display_mode_)
+ pipeline_->SetProperty(Elements::kSinkVideo, "accurate-resume", TRUE);
+ if (drop_all_late_video_)
+ pipeline_->SetProperty(Elements::kSinkVideo, "force-render", FALSE);
+
+ if (internal::IsDisplayNeeded(display_type_) == false) return true;
+
+ // gstwaylandsink property
+ pipeline_->SetProperty(Elements::kSinkVideo, "subsurface-stand-alone",
+ window_stand_alone_mode_);
+ SetSequentialMode_();
+ pipeline_->SetProperty(Elements::kSinkVideo, "use-seq-mode", use_seq_mode_);
+ virtual_scaler_id_ = resource_manager_->GetDeviceId(video_renderer_id_);
+ if (virtual_scaler_id_ == -1) {
+ return false;
+ }
+ pipeline_->SetProperty(Elements::kSinkVideo, "device-scaler",
+ virtual_scaler_id_);
+
+ if (fmm_mode_)
+ pipeline_->SetProperty(Elements::kSinkVideo, "fmm-mode", fmm_mode_);
+ if (enable_direct_crop_)
+ pipeline_->SetProperty(Elements::kSinkVideo, "enable-direct-crop", TRUE);
+#ifndef SOUNDBAR_PRODUCT
+ if (avoc_sub_source_ != AVOC_SUB_SOURCE_NONE)
+ pipeline_->SetProperty(Elements::kSinkVideo, "avoc-sub-source",
+ static_cast<gint>(avoc_sub_source_));
+#endif
+ return true;
+}
+
+void TrackRenderer::SetPropertyForAiFilter_() {
+ if (aifilter_ == nullptr) return;
+ pipeline_->ElementAdd(Elements::kAiFilter, aifilter_);
+ pipeline_->SetProperty(Elements::kAiFilter, "scaler-id", virtual_scaler_id_);
+ pipeline_->SignalConnect(Elements::kAiFilter, "return-result",
+ G_CALLBACK(GstAiFilterResultCb_), this);
+ return;
+}
+
+void TrackRenderer::SetPropertyForDecodedVideoBufferWithDisplay_() {
+ if (pipeline_ == nullptr) return;
+ if (decoded_buffer_type_ == DecodedVideoFrameBufferType::kScale) {
+ pipeline_->SignalConnect(Elements::kDecVideo, "get-buffer",
+ G_CALLBACK(GstDecodedVideoScaleBufferCb_), this);
+ pipeline_->SetProperty(Elements::kDecVideo, "signal-outbuffer", TRUE);
+ }
+ return;
+}
+
+void TrackRenderer::SetPropertyForDecodedVideoBufferWithoutDisplay_() {
+ /*in case of omx seamless mode,if not render omx data directly,like copy
+ * omxdata to DP buffer case,need set bNoVideoOut= True .*/
+ if (pipeline_ == nullptr) return;
+
+ pipeline_->SetProperty(Elements::kDecVideo, "display-omxdata-direct", FALSE);
+ TRACKRENDERER_INFO_P(
+ this,
+ "set [ %d ] to 'display-omxdata-direct' property of omx seamless "
+ "videodec",
+ FALSE);
+ pipeline_->SetProperty(Elements::kSinkVideo, "signal-handoffs", TRUE);
+
+ if (decoded_buffer_type_ == DecodedVideoFrameBufferType::kCopy) {
+ constexpr int kTbmBoType =
+ 1; // 1:tbm bo, 0:CMA (refer to gstffmpegdec element, currently for
+ // this feature use only sw decoder(ffmpegdec) and tbm_bo)
+ pipeline_->SetProperty(Elements::kDecVideo, "tbm-buffer-type", kTbmBoType);
+ pipeline_->SignalConnect(Elements::kSinkVideo, "handoff",
+ G_CALLBACK(GstDecodedVideoCopyBufferCb_), this);
+ } else if (decoded_buffer_type_ == DecodedVideoFrameBufferType::kReference) {
+ constexpr int kModeYoutube360 =
+ 1; // 1:youtube 360, 0:usb 360, -1:normal (refer to gstomxvideo)
+ pipeline_->SetProperty(Elements::kDecVideo, "vr360-mode", kModeYoutube360);
+ pipeline_->SignalConnect(Elements::kSinkVideo, "handoff",
+ G_CALLBACK(GstDecodedVideoReferenceBufferCb_),
+ this);
+ } else if (decoded_buffer_type_ == DecodedVideoFrameBufferType::kRaw) {
+ constexpr int kTbmBoType =
+ 0; // 1:tbm bo, 0:CMA (refer to gstffmpegdec element, currently for
+ // this feature use only sw decoder(ffmpegdec) and tbm_bo)
+ pipeline_->SetProperty(Elements::kDecVideo, "tbm-buffer-type", kTbmBoType);
+ pipeline_->SignalConnect(Elements::kSinkVideo, "handoff",
+ G_CALLBACK(GstDecodedVideoRawBufferCb_), this);
+ } else if (decoded_buffer_type_ == DecodedVideoFrameBufferType::kScale) {
+ pipeline_->SignalConnect(Elements::kDecVideo, "get-buffer",
+ G_CALLBACK(GstDecodedVideoScaleBufferCb_), this);
+ pipeline_->SetProperty(Elements::kDecVideo, "signal-outbuffer", TRUE);
+ pipeline_->SetProperty(Elements::kSinkVideo, "signal-handoffs", FALSE);
+ }
+ return;
+}
+
+void TrackRenderer::SetPropertyForDecodedVideoBuffer_() {
+ if (internal::IsDecodedVideoBufferNeeded(decoded_buffer_type_) == false)
+ return;
+
+ CreateTbmBufferManager_();
+
+ if (internal::IsDisplayNeeded(display_type_))
+ SetPropertyForDecodedVideoBufferWithDisplay_();
+ else
+ SetPropertyForDecodedVideoBufferWithoutDisplay_();
+ return;
+}
+
+bool TrackRenderer::CreateVideoPipeline_(const Track* track) {
+ const char* kDecoderPluginName =
+ GetDecoderPluginName_(kTrackTypeVideo, track->mimetype);
+ if (kDecoderPluginName == nullptr &&
+ internal::IsDecoderElementNecessary(track->mimetype)) {
+ const ErrorType err = ErrorType::kNotSupportedVideoCodec;
+ eventlistener_->OnError(err);
+ eventlistener_->OnErrorMsg(err, const_cast<char*>(track->mimetype.c_str()));
+ return false;
+ }
+ if (kDecoderPluginName &&
+ (strcmp(kDecoderPluginName, kSkippedResource) == 0)) {
+ const ErrorType err = ErrorType::kResourceLimit;
+ eventlistener_->OnError(err);
+ eventlistener_->OnErrorMsg(err, const_cast<char*>(track->mimetype.c_str()));
+ return false;
+ }
+ CreateAppSrc_(kTrackTypeVideo, track->mimetype);
+
+ CreateDrmElement_(*track);
+
+ CreateVideoDecoder_(kDecoderPluginName);
+
+ if (CreateVideoSink_() == false) return false;
+
+ GstElementLowLatency_(kTrackTypeVideo);
+
+ SetPropertyForAiFilter_();
+
+ pipeline_->CreateBin(Elements::kBinVideo, "videobin");
+
+ pipeline_->BinAdd(Elements::kBinVideo, Elements::kAppSrcVideo,
+ Elements::kDrmVideo, Elements::kDecVideo,
+ Elements::kAiFilter, Elements::kSinkVideo);
+
+ pipeline_->BinAddSimple(Elements::kPipeline, Elements::kBinVideo);
+
+ auto caps = caps_builder_.Build(*track, internal::IsDrmEmeElementNecessary(
+ drm_property_, track->mimetype));
+ pipeline_->SetAppSrcCaps(Elements::kAppSrcVideo, caps);
+
+ if (is_video_frame_peek_) {
+ pipeline_->PadAddProbe(Elements::kSinkVideo, kPadProbeVideoDecoded, "sink",
+ GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
+ GstPadProbeVideoDecodedCb_, this, nullptr);
+ }
+ SetPropertyForDecodedVideoBuffer_();
+ return true;
+}
+
+bool TrackRenderer::CreateAudioPipeline_(const Track* track) {
+ const char* kDecoderPluginName =
+ GetDecoderPluginName_(kTrackTypeAudio, track->mimetype);
+ if (kDecoderPluginName == nullptr) {
+ const ErrorType err = ErrorType::kNotSupportedAudioCodec;
+ eventlistener_->OnError(err);
+ eventlistener_->OnErrorMsg(err, const_cast<char*>(track->mimetype.c_str()));
+ return false;
+ }
+ pipeline_->CreateBin(Elements::kBinAudio, "audiobin");
+
+ CreateAppSrc_(kTrackTypeAudio, track->mimetype);
+ auto caps = caps_builder_.Build(*track, internal::IsDrmEmeElementNecessary(
+ drm_property_, track->mimetype));
+ pipeline_->SetAppSrcCaps(Elements::kAppSrcAudio, caps);
+
+ CreateDrmElement_(*track);
+
+ if (strcmp(kDecoderPluginName, kSkippedResource) != 0) {
+ pipeline_->FactoryMake(Elements::kDecAudio, kDecoderPluginName, NULL);
+ pipeline_->SetProperty(Elements::kDecAudio, "support-codec-change",
+ support_audio_codec_change_);
+ if (low_latency_mode_ ==
+ static_cast<std::uint32_t>(LowLatencyMode::kLowLatencyModeNone)) {
+ constexpr int kPtsManipulationThreshold = 99000; // 99ms
+ pipeline_->SetProperty(Elements::kDecAudio, "set-usr-cal-timestamp",
+ kPtsManipulationThreshold);
+ }
+ std::string audiosink_name =
+ GetAudioSinkPluginName_(track->use_swdecoder, track->mimetype);
+ CreateAudioSink_(audiosink_name);
+ is_audioactivated_ = true;
+
+ } else { // fake sink
+ std::string sink_name("fakesink");
+ if (internal::IsTzMimeType(track->mimetype)) {
+ sink_name = "tzfakesink";
+ }
+ CreateAudioSink_(sink_name);
+ is_audioactivated_ = false;
+ }
+
+ GstElementLowLatency_(kTrackTypeAudio);
+ pipeline_->BinAdd(Elements::kBinAudio, Elements::kAppSrcAudio,
+ Elements::kDrmAudio, Elements::kDecAudio,
+ Elements::kSinkAudio);
+ pipeline_->BinAddSimple(Elements::kPipeline, Elements::kBinAudio);
+ return true;
+}
+
+bool TrackRenderer::CreateSwAudioPipeline_(const Track* track) {
+ const char* kDecoderPluginName =
+ GetDecoderPluginName_(kTrackTypeAudio, track->mimetype);
+ if (kDecoderPluginName == nullptr) {
+ const ErrorType err = ErrorType::kNotSupportedAudioCodec;
+ eventlistener_->OnError(err);
+ eventlistener_->OnErrorMsg(err, const_cast<char*>(track->mimetype.c_str()));
+ return false;
+ }
+ pipeline_->CreateBin(Elements::kBinAudio, "audiobin");
+
+ std::string appsrc_mimetype = track->mimetype;
+ CreateAppSrc_(kTrackTypeAudio, appsrc_mimetype);
+ auto caps = caps_builder_.Build(*track, false);
+ pipeline_->SetAppSrcCaps(Elements::kAppSrcAudio, caps);
+
+ if (strcmp(kDecoderPluginName, kSkippedResource) != 0) {
+ pipeline_->FactoryMake(Elements::kDecAudio, kDecoderPluginName, NULL);
+
+ std::string audiosink_name =
+ GetAudioSinkPluginName_(track->use_swdecoder, track->mimetype);
+ CreateAudioSink_(audiosink_name);
+ is_audioactivated_ = true;
+
+ } else { // fake sink
+ CreateAudioSink_("fakesink");
+ is_audioactivated_ = false;
+ }
+
+ GstElementLowLatency_(kTrackTypeAudio);
+ pipeline_->BinAdd(Elements::kBinAudio, Elements::kAppSrcAudio,
+ Elements::kDecAudio, Elements::kAudioConvert,
+ Elements::kCapsFillterDefault, Elements::kAudioResample,
+ Elements::kCapsFillter2, Elements::kScaleTempo,
+ Elements::kSinkAudio);
+ pipeline_->BinAddSimple(Elements::kPipeline, Elements::kBinAudio);
+ return true;
+}
+
+bool TrackRenderer::CreateRawAudioPipeline_(const Track* track) {
+ pipeline_->CreateBin(Elements::kBinAudio, "audiobin");
+
+ CreateAppSrc_(kTrackTypeAudio, track->mimetype);
+ auto caps = caps_builder_.Build(*track, false);
+ pipeline_->SetAppSrcCaps(Elements::kAppSrcAudio, caps);
+
+ AllocatedState state = resource_manager_->GetAllocatedState(audio_out_id_);
+ if (state == AllocatedState::kSuccess) {
+ std::string audiosink_name =
+ GetAudioSinkPluginName_(track->use_swdecoder, track->mimetype);
+ CreateAudioSink_(audiosink_name);
+ is_audioactivated_ = true;
+
+ } else if (state == AllocatedState::kSkipped) { // fake sink
+ resource_manager_->Dealloc(audio_out_id_);
+
+ CreateAudioSink_("fakesink");
+ is_audioactivated_ = false;
+
+ } else { // AllocatedState::kFailed
+ TRACKRENDERER_ERROR_P(this, "Fail to create raw audio pipeline!");
+ return false;
+ }
+
+ GstElementLowLatency_(kTrackTypeAudio);
+ pipeline_->BinAdd(Elements::kBinAudio, Elements::kAppSrcAudio,
+ Elements::kAudioConvert, Elements::kCapsFillterDefault,
+ Elements::kAudioResample, Elements::kCapsFillter2,
+ Elements::kScaleTempo, Elements::kSinkAudio);
+ pipeline_->BinAddSimple(Elements::kPipeline, Elements::kBinAudio);
+ return true;
+}
+
+bool TrackRenderer::CreateSubtitlePipeline_(const Track* track) {
+ pipeline_->FactoryMake(Elements::kAppSrcSubtitle, "appsrc",
+ "subtitle_appsrc");
+ SetDefaultAppSrcSignals_(kTrackTypeSubtitle);
+ pipeline_->FactoryMake(Elements::kSinkSubtitle, "fakesink", "subtitle_sink");
+ pipeline_->CreateBin(Elements::kBinSubtitle, "subtitlebin");
+ pipeline_->SetProperty(Elements::kAppSrcSubtitle, "format", GST_FORMAT_TIME);
+ pipeline_->BinAdd(Elements::kBinSubtitle, Elements::kAppSrcSubtitle,
+ Elements::kSinkSubtitle);
+ pipeline_->BinAddSimple(Elements::kPipeline, Elements::kBinSubtitle);
+
+ std::string mimetype = (track == nullptr) ? "" : track->mimetype;
+ auto caps = gstguard::make_guard(gst_caps_new_empty_simple(mimetype.c_str()));
+ pipeline_->SetProperty(Elements::kAppSrcSubtitle, "stream-type",
+ GST_APP_STREAM_TYPE_SEEKABLE, "max-bytes",
+ ::kMaxByteOfSubtitleSrcQueue, "caps", caps.get());
+ pipeline_->SetProperty(Elements::kSinkSubtitle, "sync", TRUE, "async", FALSE,
+ "signal-handoffs", TRUE);
+ GstElementLowLatency_(kTrackTypeSubtitle);
+ pipeline_->SignalConnect(Elements::kSinkSubtitle, "handoff",
+ G_CALLBACK(GstSubtitleDataHandOffCb_), this);
+ return true;
+}
+
+bool TrackRenderer::CreatePipeline_() {
+ TRACKRENDERER_ENTER_P(this);
+ pipeline_ = Pipeline<Elements>::Create(::kTrackRendererPipelineName);
+
+ constexpr int kPrimeNumForFamilyId = 97;
+ int gst_family_id =
+ reinterpret_cast<std::uint32_t>(this) % kPrimeNumForFamilyId;
+ TRACKRENDERER_ERROR_P(this, "gst family-id : %d", gst_family_id);
+ pipeline_->SetProperty(Elements::kPipeline, "family-id", gst_family_id);
+
+ pipeline_->SetGstElementCreatedCbHandler(std::bind(
+ &TrackRenderer::GstElementCreatedCb_, this, std::placeholders::_1));
+ for (const auto& trackctx : trackctx_) {
+ if (trackctx.index != kInvalidTrackIndex) {
+ if (!trackctx.create_pipeline(trackctx.track)) {
+ TRACKRENDERER_ERROR_P(this, "fail to create pipeline!");
+ // TODO(js4716.chun)
+ // - check if no leak.
+ return false;
+ }
+ }
+ }
+ if (trackctx_[kTrackTypeSubtitle].index == kInvalidTrackIndex) {
+ if (!trackctx_[kTrackTypeSubtitle].create_pipeline(nullptr)) {
+ TRACKRENDERER_ERROR_P(this, "fail to create pipeline!");
+ return false;
+ }
+ }
+ pipeline_->SetSyncHandler(GstBusSyncHandlerCb_, this, nullptr);
+ if (!pipeline_->SetState(Elements::kPipeline, GST_STATE_READY)) {
+ TRACKRENDERER_ERROR_P(this, "Set State to READY failed");
+ return false;
+ }
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+void TrackRenderer::SetDirectCrop_(const std::string& app_id) {
+ if (strstr(app_id.c_str(), "UsbCamApp") ||
+ strstr(app_id.c_str(), "GoogleDuoWeb")) {
+ enable_direct_crop_ = true;
+ }
+}
+
+void TrackRenderer::SetSequentialMode_() {
+ bool is_supporting_seq_mode = false;
+ int ret = system_info_get_custom_bool(
+ "com.samsung/featureconf/media.support_seq_mode",
+ &is_supporting_seq_mode);
+ if (SYSTEM_INFO_ERROR_NONE != ret) {
+ TRACKRENDERER_ERROR_P(
+ this, "system_info_get_custom_bool() return error [%d]", ret);
+ return;
+ }
+ if (!is_supporting_seq_mode) {
+ TRACKRENDERER_ERROR_P(this, "is_supporting_seq_mode [%d]",
+ is_supporting_seq_mode);
+ return;
+ }
+ if (!internal::IsDecoderElementNecessary(
+ trackctx_[kTrackTypeVideo].track->mimetype)) {
+ TRACKRENDERER_ERROR_P(this, "raw video pipeline");
+ return;
+ }
+ constexpr std::uint32_t not_need_seq =
+ kLowLatencyModeVideoDistortionConcealment | kLowLatencyModeDisableAVSync |
+ kLowLatencyModeDisablePreroll | kLowLatencyModeDisableVideoQuality;
+ /* sequential mode can cause other issue because these modes can't ensure
+ * sync by pts */
+ if (low_latency_mode_ & not_need_seq) return;
+
+ use_seq_mode_ = 1;
+}
+
+void TrackRenderer::SetAppId(const std::string& app_id) {
+ PlayerAppInfo app_info;
+ app_info.id = app_id;
+ SetAppInfo(app_info);
+}
+
+void TrackRenderer::SetAppInfo(const PlayerAppInfo& app_info) {
+ if (strstr(app_info.id.c_str(), "netflix")) {
+ support_audio_codec_change_ = TRUE;
+ support_videodec_underflow_pause_ = true;
+ }
+#ifndef SOUNDBAR_PRODUCT
+ else if (strcmp(app_info.id.c_str(), "org.tizen.tv-viewer") == 0) {
+ avoc_sub_source_ = AVOC_SUB_SOURCE_UNIPLAYER_TVPLUS;
+ }
+#endif
+ SetDirectCrop_(app_info.id);
+ resource_manager_->SetAppId(app_info.id);
+ playback_info_->SetAppInfo(app_info);
+}
+
+bool TrackRenderer::GetResource_(const TrackType& type) {
+ assert(!trackinfo_.empty());
+ std::list<ResourceProperty> properties;
+ bool need_video_rsc =
+ (type == kTrackTypeMax || type == kTrackTypeVideo) ? true : false;
+ bool need_audio_rsc =
+ (type == kTrackTypeMax || type == kTrackTypeAudio) ? true : false;
+
+ TRACKRENDERER_INFO_P(this, "Is dual sound mode[%d]", IsDualSoundMode);
+ for (const Track& track : trackinfo_) {
+ if (track.active == false) continue;
+
+ if (track.type == kTrackTypeVideo && need_video_rsc) {
+ bool need_renderer_resource = true;
+ if (internal::IsDecoderElementNecessary(track.mimetype)) {
+ ResourceProperty videodecproperty;
+ videodecproperty.category = video_decoder_id_;
+ videodecproperty.track = track;
+ videodecproperty.is_multiview = is_multiscreen_;
+ videodecproperty.rsc_alloc_policy = rsc_alloc_policy_;
+
+ if (decoded_buffer_type_ == DecodedVideoFrameBufferType::kReference) {
+ videodecproperty.is_vr360 = true;
+ } else if (internal::IsDisplayNeeded(display_type_) == false) {
+ need_renderer_resource = false;
+ }
+
+ if (internal::IsSdkEnabledFeature()) {
+ videodecproperty.use_sw = true;
+ } else {
+ videodecproperty.use_sw = track.use_swdecoder;
+ }
+ constexpr uint32_t kNdecodingMode = 0x04;
+ if (video_decoding_mode_ == kNdecodingMode) {
+ videodecproperty.is_ndecoding = true;
+ }
+ properties.push_back(videodecproperty);
+ }
+ if (need_renderer_resource) {
+ ResourceProperty videorenderproperty;
+ videorenderproperty.category = video_renderer_id_;
+ videorenderproperty.is_multiview = is_multiscreen_;
+ videorenderproperty.rsc_alloc_policy = rsc_alloc_policy_;
+ properties.push_back(videorenderproperty);
+ }
+ }
+ if (track.type == kTrackTypeAudio && need_audio_rsc) {
+ bool need_renderer_resource = true;
+ if (internal::IsDecoderElementNecessary(track.mimetype)) {
+ ResourceProperty audiodecproperty;
+ audiodecproperty.track = track;
+ if (internal::IsSdkEnabledFeature()) {
+ need_renderer_resource = false;
+ audiodecproperty.use_sw = true;
+ } else
+ audiodecproperty.use_sw = track.use_swdecoder;
+ audiodecproperty.category = audio_decoder_id_;
+ audiodecproperty.is_dual_sound = IsDualSoundMode;
+ audiodecproperty.is_multiview = is_multiscreen_;
+ audiodecproperty.rsc_alloc_policy = rsc_alloc_policy_;
+ properties.push_back(audiodecproperty);
+ }
+ if (need_renderer_resource) {
+ ResourceProperty audiorenderproperty;
+ audiorenderproperty.category = audio_out_id_;
+ audiorenderproperty.is_dual_sound = IsDualSoundMode;
+ audiorenderproperty.is_multiview = is_multiscreen_;
+ audiorenderproperty.rsc_alloc_policy = rsc_alloc_policy_;
+ audiorenderproperty.use_sw = track.use_swdecoder;
+ properties.push_back(audiorenderproperty);
+ }
+ }
+ }
+ bool ret = resource_manager_->Alloc(properties);
+ if (!ret) {
+ TRACKRENDERER_ERROR_P(this, "GetResource_ Fail");
+ }
+ return ret;
+}
+
+void TrackRenderer::SetDefaultAttributeValue_() {
+ for (auto& kv : kAttributes_) {
+ const Attribute& attr = kv.first;
+ const TrackRendererAttributeBinder& binder = kv.second;
+ set_attribute_values_[attr].value = binder.default_value;
+ set_attribute_values_[attr].value_assigned = false;
+ }
+}
+
+bool TrackRenderer::NeedAvocPlayerRegister_() {
+ TRACKRENDERER_ENTER_P(this);
+ if (internal::IsSdkEnabledFeature()) {
+ TRACKRENDERER_INFO_P(this, "SDK feature, don't need avoc register");
+ return false;
+ }
+
+ auto is_audio_track = [](const Track& item) noexcept->bool {
+ return item.mimetype.find("audio") != std::string::npos;
+ };
+ auto target = find_if(trackinfo_.begin(), trackinfo_.end(), is_audio_track);
+ if (target == trackinfo_.end()) {
+ TRACKRENDERER_INFO_P(this, "Not found audio track");
+ return false;
+ }
+
+ ResourceProperty audiorenderproperty;
+ audiorenderproperty.category = audio_out_id_;
+ audiorenderproperty.use_sw = target->use_swdecoder;
+ if (resource_manager_->NeedPulseResource(audiorenderproperty)) {
+ TRACKRENDERER_INFO_P(this,
+ "sw decoder + pulsesink, don't need avoc register");
+ return false;
+ }
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::AvocPlayerRegister_() {
+ TRACKRENDERER_ENTER_P(this);
+ avoc_id_ = resource_manager_->GetRawHandle();
+ if (!NeedAvocPlayerRegister_()) return true;
+
+ /* avoc does not care about player's audio out value, in fact avoc will check
+ audio out value from RC. so we can set them with default value to
+ avoc_av_player_register_m */
+ need_avoc_register = true;
+ int default_source_id = AVOC_AUDIO_SOURCE_MULTIMEDIA_DEC0;
+ int default_audio_out = 0;
+ ::PerfUsrTrace("avoc(_av)_player_register_m");
+
+#ifdef SOUNDBAR_PRODUCT
+ avoc_error_e ret = avoc_av_player_register_m(
+ static_cast<int>(display_->GetSurfaceId()), default_source_id,
+ default_audio_out, &avoc_id_);
+#else
+ avoc_error_e ret =
+ avoc_player_register_m(static_cast<int>(display_->GetSurfaceId()),
+ default_source_id, default_audio_out, avoc_id_);
+#endif
+
+ if (ret != AVOC_EXIT_SUCCESS) {
+ TRACKRENDERER_ERROR_P(this, "avoc(_av)_player_register_m failed, ret[ %d ]",
+ ret);
+ return false;
+ }
+
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::AvocPlayRequest_() {
+ TRACKRENDERER_ENTER_P(this);
+ if (resource_manager_->GetComponentName(audio_out_id_) ==
+ kPulseSinkComponentName) {
+ TRACKRENDERER_INFO_P(this,
+ "sw decoder + pulsesink, don't need avoc request");
+ return true;
+ }
+ auto is_audio_track = [](const Track& item) noexcept->bool {
+ return item.mimetype.find("audio") != std::string::npos;
+ };
+ auto target = find_if(trackinfo_.begin(), trackinfo_.end(), is_audio_track);
+ if (target == trackinfo_.end()) {
+ TRACKRENDERER_INFO_P(this, "Not found audio track");
+ return true;
+ }
+
+ if (resource_manager_->GetAllocatedState(audio_out_id_) !=
+ AllocatedState::kSuccess) {
+ TRACKRENDERER_INFO_P(
+ this, "AllocatedState: %d",
+ static_cast<int>(resource_manager_->GetAllocatedState(audio_out_id_)));
+ return true;
+ }
+
+ int sec_ctl_source = internal::GetAvocSourceType(
+ resource_manager_->IsMainDevice(audio_decoder_id_), target->use_swdecoder,
+ target->mimetype);
+ // AVOC uses the audio_out given by resource-center instead of player setting
+ // value. if resource-center return error, AVOC use this value in 4K model.
+ int audio_out =
+ internal::GetAudioOut(resource_manager_->IsMainDevice(audio_out_id_));
+
+ ::PerfUsrTrace("avoc(_av)_play_request_m");
+#ifdef SOUNDBAR_PRODUCT
+ avoc_error_e avoc_ret =
+ avoc_av_play_request_m(avoc_id_, sec_ctl_source, audio_out);
+#else
+ avoc_error_e avoc_ret =
+ avoc_play_request_m(avoc_id_, sec_ctl_source, audio_out);
+#endif
+
+ if (avoc_ret != AVOC_EXIT_SUCCESS) {
+ TRACKRENDERER_ERROR_P(this, "avoc(_av)_play_request_m failed, ret[ %d ]",
+ avoc_ret);
+ return false;
+ }
+
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::AvocPlayerUnRegister_() {
+ TRACKRENDERER_ENTER_P(this);
+ if (avoc_id_ < 0) {
+ TRACKRENDERER_INFO_P(this, "Avoc has been deinitialized");
+ return true;
+ }
+ if (!need_avoc_register) return true;
+ avoc_error_e ret = AVOC_EXIT_SUCCESS;
+#ifdef SOUNDBAR_PRODUCT
+ ret = avoc_av_player_unregister(avoc_id_);
+#else
+ ret = avoc_player_unregister(avoc_id_);
+#endif
+ if (ret != AVOC_EXIT_SUCCESS) {
+ TRACKRENDERER_ERROR_P(this, "avoc(_av)_player_unregister() is failed");
+ return false;
+ }
+
+ avoc_id_ = -1;
+ need_avoc_register = false;
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::AddAiFilter_(void* aifilter) {
+ TRACKRENDERER_ENTER_P(this);
+ if (aifilter == nullptr) return false;
+ RemoveAiFilter_();
+ aifilter_ = gst_object_ref(aifilter);
+ return true;
+}
+
+void TrackRenderer::RemoveAiFilter_() {
+ TRACKRENDERER_ENTER_P(this);
+ if (aifilter_ == nullptr) return;
+ gst_object_unref(aifilter_);
+ aifilter_ = nullptr;
+ return;
+}
+
+bool TrackRenderer::ReleaseResource_() {
+ TRACKRENDERER_ENTER_P(this);
+ latency_manager_->UnsetPipeline();
+ pipeline_.reset();
+ AvocPlayerUnRegister_();
+ RemoveAiFilter_();
+
+ if (!resource_manager_->Dealloc()) {
+ TRACKRENDERER_ERROR_P(this, "resource manager dealloc fail!");
+ }
+
+ for (int i = 0; i < kTrackTypeMax; ++i) {
+ trackctx_[i].index = kInvalidTrackIndex;
+ trackctx_[i].is_enough_data = false;
+ trackctx_[i].need_update_segment = true;
+ }
+ trackinfo_.clear();
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+// LCOV_EXCL_START
+void TrackRenderer::OnResourceConflicted() {
+ TRACKRENDERER_ENTER_E_P(this);
+ resource_cv_.notify_one();
+ state_ = State::kResourceConflicted;
+ eventlistener_->OnResourceConflicted();
+}
+
+void TrackRenderer::OnVideoLatencyStatus(const LatencyStatus& latency_status) {
+ TRACKRENDERER_ENTER_E_P(this);
+ eventlistener_->OnVideoLatencyStatus(latency_status);
+}
+
+void TrackRenderer::OnAudioLatencyStatus(const LatencyStatus& latency_status) {
+ TRACKRENDERER_ENTER_E_P(this);
+ eventlistener_->OnAudioLatencyStatus(latency_status);
+}
+
+void TrackRenderer::OnVideoHighLatency() {
+ TRACKRENDERER_ENTER_E_P(this);
+ eventlistener_->OnVideoHighLatency();
+}
+
+void TrackRenderer::OnAudioHighLatency() {
+ TRACKRENDERER_ENTER_E_P(this);
+ eventlistener_->OnAudioHighLatency();
+}
+
+SubtitleType TrackRenderer::GetSubtitleType_(const GstBuffer* buf) {
+ auto is_subtitle_track = [](const Track& track) noexcept->bool {
+ return ((track.type == kTrackTypeSubtitle) && (track.active));
+ };
+
+ auto subtitle_track =
+ std::find_if(trackinfo_.begin(), trackinfo_.end(), is_subtitle_track);
+ if (subtitle_track == trackinfo_.end()) {
+ TRACKRENDERER_ERROR_P(this, "Not found subtitle track info");
+ return SubtitleType::kInvalid;
+ }
+
+ TRACKRENDERER_DEBUG_P(this, "subtitle_track->mimetype = [%s]",
+ (subtitle_track->mimetype).c_str());
+
+ if ((subtitle_track->mimetype).find("x-xsub") != std::string::npos ||
+ (subtitle_track->mimetype).find("x-smpte-png") != std::string::npos ||
+ (subtitle_track->mimetype).find("video/x-raw") != std::string::npos) {
+ TRACKRENDERER_DEBUG_P(this, "It is Picture type subtitle");
+ return SubtitleType::kPicture;
+ }
+
+ if (((subtitle_track->mimetype).find("x-smpte-text") != std::string::npos) ||
+ ((subtitle_track->mimetype).find("stpp") != std::string::npos)) {
+ TRACKRENDERER_DEBUG_P(this, "Find the x-smpte-text");
+ gint* picture_flag = (gint*)gst_mini_object_get_qdata(
+ GST_MINI_OBJECT(buf), g_quark_from_static_string("picture_flag"));
+ TRACKRENDERER_DEBUG_P(this, "picture_flag = [%p]", picture_flag);
+ if (picture_flag && (1 == *picture_flag)) {
+ TRACKRENDERER_DEBUG_P(this,
+ "It is Picture type subtitle for x-smpte-text");
+ return SubtitleType::kPicture;
+ }
+ }
+
+ TRACKRENDERER_DEBUG_P(this, "It is TEXT type subtitle");
+ return SubtitleType::kText;
+}
+
+void TrackRenderer::GstSubtitleDataHandOffCb_(GstElement* object,
+ GstBuffer* buf, GstPad* pad,
+ gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ if (trackrenderer == nullptr) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "trackrenderer == nullptr");
+ return;
+ }
+ TRACKRENDERER_DEBUG("Subtitle Buffer :Timestamp:[%" GST_TIME_FORMAT "]",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buf)));
+ const SubtitleType type = trackrenderer->GetSubtitleType_(buf);
+ if (type == SubtitleType::kInvalid) return;
+ auto inbuffer = DecoderInputBuffer::Create(kTrackTypeSubtitle, 0, buf);
+ trackrenderer->eventlistener_->OnSubtitleData(std::move(inbuffer), type);
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+ SubtitleAttrParser sub_attr_parser(buf);
+ auto attr_list = sub_attr_parser.Parse();
+ if (!attr_list && gst_buffer_get_size(buf) > 0) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "attr_list is NULL");
+ return;
+ }
+ GstMapInfo info;
+ gst_buffer_map(buf, &info, GST_MAP_READ);
+ char* buffer = reinterpret_cast<char*>(info.data);
+ GstClockTime duration = GST_CLOCK_TIME_NONE;
+ duration = GST_TIME_AS_MSECONDS(GST_BUFFER_DURATION(buf));
+
+ trackrenderer->eventlistener_->OnSubtitleData(buffer, info.size, type,
+ duration, std::move(attr_list));
+ gst_buffer_unmap(buf, &info);
+#endif
+}
+
+void TrackRenderer::GstClosedCaptionHandOffCb_(GstElement* object,
+ GstBuffer* buf, GstPad* pad,
+ gpointer userdata) {
+ // need mutex for event listener
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ GstMapInfo info;
+ gst_buffer_map(buf, &info, GST_MAP_READ);
+ // TRACKRENDERER_DEBUG_P(trackrenderer, "data[%p], size[%d]", info.data,
+ // info.size);
+
+ char* buffer = reinterpret_cast<char*>(info.data);
+ trackrenderer->eventlistener_->OnClosedCaptionData(buffer, info.size);
+ gst_buffer_unmap(buf, &info);
+}
+
+void TrackRenderer::GstClosedCaptionPadAddedCb_(GstElement* element,
+ GstPad* pad,
+ gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ TRACKRENDERER_ENTER_P(trackrenderer);
+ if (!element || !pad) {
+ assert(element);
+ assert(pad);
+ TRACKRENDERER_ERROR_P(trackrenderer, "NULL object element[%p] pad[%p]",
+ element, pad);
+ return;
+ }
+ auto caps = gstguard::make_guard(gst_pad_get_current_caps(pad));
+ if (!caps.get()) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "caps is NULL");
+ return;
+ }
+ GstStructure* gststr = gst_caps_get_structure(caps.get(), 0);
+ if (!gststr) {
+ assert(gststr);
+ TRACKRENDERER_ERROR_P(trackrenderer, "GstStructure is NULL");
+ return;
+ }
+ std::string name = gst_structure_get_name(gststr);
+ if (name != "text/dtv-closed-caption") {
+ TRACKRENDERER_ERROR_P(trackrenderer, "NOT closed caption");
+ return;
+ }
+ assert(trackrenderer);
+ assert(trackrenderer->pipeline_.get());
+ if (!trackrenderer->pipeline_->FactoryMake(Elements::kQueueCaption, "queue",
+ "caption_queue")) {
+ assert(0);
+ return;
+ }
+
+ /*Not limit the time and buffer size of caption queue*/
+ trackrenderer->pipeline_->SetProperty(Elements::kQueueCaption,
+ "max-size-time", (guint64)0,
+ "max-size-buffers", (guint)0);
+
+ if (!trackrenderer->pipeline_->FactoryMake(Elements::kSinkCaption, "fakesink",
+ "caption_sink")) {
+ assert(0);
+ return;
+ }
+ trackrenderer->pipeline_->SetProperty(Elements::kSinkCaption, "sync", TRUE,
+ "async", FALSE, "signal-handoffs", TRUE,
+ "is-subtitle", TRUE);
+ trackrenderer->GstElementLowLatency_(kTrackTypeSubtitle);
+ trackrenderer->pipeline_->SignalConnect(
+ Elements::kSinkCaption, "handoff", G_CALLBACK(GstClosedCaptionHandOffCb_),
+ userdata);
+ trackrenderer->pipeline_->BinAddSimple(Elements::kBinVideo,
+ Elements::kQueueCaption);
+ trackrenderer->pipeline_->ElementLink(Elements::kDecVideo,
+ Elements::kQueueCaption);
+ trackrenderer->pipeline_->BinAdd(Elements::kBinVideo, Elements::kSinkCaption);
+ TRACKRENDERER_LEAVE_P(trackrenderer);
+}
+// LCOV_EXCL_STOP
+
+void TrackRenderer::CompleteSeeking_() {
+ if (!is_async_done_ || !is_seeking_) return;
+ TRACKRENDERER_ERROR_P(this, "Seeking has been completed SubState[%d]",
+ static_cast<int>(target_substate_));
+ eventlistener_->OnSeekDone();
+ is_seeking_ = false;
+ is_async_done_ = false;
+}
+
+GstBusSyncReply TrackRenderer::GstBusSyncHandlerCb_(GstBus* bus,
+ GstMessage* message,
+ gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ assert(trackrenderer);
+
+ switch (GST_MESSAGE_TYPE(message)) {
+ case GST_MESSAGE_UNKNOWN:
+ TRACKRENDERER_ERROR_P(trackrenderer, "GST_MESSAGE_UNKNOWN");
+ break;
+ case GST_MESSAGE_EOS: {
+ TRACKRENDERER_ERROR_P(trackrenderer, "GST_MESSAGE_EOS");
+ trackrenderer->eventlistener_->OnEos();
+ break;
+ }
+ case GST_MESSAGE_STATE_CHANGED: {
+ GstState old_state = GST_STATE_VOID_PENDING;
+ GstState new_state = GST_STATE_VOID_PENDING;
+ GstState pending_state = GST_STATE_VOID_PENDING;
+ gst_message_parse_state_changed(message, &old_state, &new_state,
+ &pending_state);
+ std::string message_src_name(GST_MESSAGE_SRC_NAME(message));
+ gst_util::ShowStateChangedMsg(message, trackrenderer);
+ if (message_src_name == ::kTrackRendererPipelineName) {
+ if (old_state == GST_STATE_NULL && new_state == GST_STATE_READY &&
+ pending_state == GST_STATE_VOID_PENDING) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "PIPELINE STATE CHANGED FROM NULL to READY");
+ } else if (old_state == GST_STATE_READY &&
+ new_state == GST_STATE_PAUSED) {
+ trackrenderer->state_ = State::kWorking;
+ trackrenderer->resource_cv_.notify_one();
+
+ trackrenderer->UpdatePlaybackInfo_(false);
+
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "PIPELINE STATE CHANGED FROM READY to PAUSED");
+ } else if (old_state == GST_STATE_PAUSED &&
+ new_state == GST_STATE_PLAYING &&
+ pending_state == GST_STATE_VOID_PENDING) {
+ trackrenderer->screen_saver_->StartTimeout();
+ TRACKRENDERER_ERROR_P(
+ trackrenderer, "PIPELINE STATE CHANGED FROM PAUSED to PLAYING");
+ trackrenderer->CompleteSeeking_();
+ } else if (old_state == GST_STATE_PLAYING &&
+ new_state == GST_STATE_PAUSED) {
+ trackrenderer->screen_saver_->StopTimeout();
+ } else if (new_state == GST_STATE_NULL &&
+ pending_state == GST_STATE_VOID_PENDING) {
+ trackrenderer->screen_saver_->StopTimeout();
+ }
+ }
+ break;
+ } // case GST_MESSAGE_STATE_CHANGED
+ case GST_MESSAGE_ELEMENT: {
+ TRACKRENDERER_INFO_P(trackrenderer, "GST_MESSAGE_ELEMENT");
+ const gchar* struct_name =
+ gst_structure_get_name(gst_message_get_structure(message));
+ if (!strcmp(struct_name, "resolution_is_changed")) {
+ int is_changed = 0;
+ gst_structure_get_int(gst_message_get_structure(message),
+ "Resolution_changed", &is_changed);
+ TRACKRENDERER_ERROR_P(trackrenderer, "resolution is changed - [%d]",
+ is_changed);
+ if (1 == is_changed) {
+ trackrenderer->UpdatePlaybackInfo_(true);
+ EventMsg event_msg;
+ trackrenderer->GetResolutionInfo_(&event_msg);
+ trackrenderer->eventlistener_->OnEvent(EventType::kResolutionChanged,
+ event_msg);
+ }
+ } else if (!strcmp(struct_name, "omx_vdec_underflow")) {
+ GstState element_state = GST_STATE_VOID_PENDING;
+ trackrenderer->pipeline_->GetState(Elements::kPipeline, &element_state,
+ NULL, 10 * GST_MSECOND);
+ TRACKRENDERER_WARN_P(
+ trackrenderer,
+ "omx_vdec_underflow will be posted, current state[%s]",
+ gst_element_state_get_name(element_state));
+ if (element_state == GST_STATE_PLAYING) {
+ trackrenderer->eventlistener_->OnVideoDecoderUnderrun();
+ }
+ }
+ break;
+ } // case GST_MESSAGE_ELEMENT
+ case GST_MESSAGE_ASYNC_DONE: {
+ TRACKRENDERER_ERROR_P(trackrenderer, "GST_MESSAGE_ASYNC_DONE");
+ trackrenderer->is_async_done_ = true;
+ if (trackrenderer->target_substate_ != SubState::kPlaying) {
+ trackrenderer->CompleteSeeking_();
+ }
+ if (trackrenderer->is_flushing_) {
+ trackrenderer->eventlistener_->OnFlushDone();
+ trackrenderer->is_flushing_ = false;
+ trackrenderer->enable_audio_track_change_ = false;
+ }
+ break;
+ }
+ // LCOV_EXCL_START
+ case GST_MESSAGE_ERROR: {
+ TRACKRENDERER_ERROR_P(trackrenderer, "GST_MESSAGE_ERROR");
+ trackrenderer->resource_cv_.notify_one();
+ bool is_music_content =
+ trackrenderer->trackctx_[kTrackTypeVideo].index != kInvalidTrackIndex
+ ? false
+ : true;
+ const ErrorType err = HandleError(message, is_music_content);
+ if (trackrenderer->is_error_posted_) {
+ TRACKRENDERER_INFO_P(trackrenderer, "error is already posted");
+ break;
+ }
+ trackrenderer->eventlistener_->OnError(err);
+ gchar* error_msg = nullptr;
+ HandleErrorMsg(message, &error_msg);
+ auto debug = gstguard::make_guard(error_msg);
+ if (debug != nullptr) {
+ trackrenderer->eventlistener_->OnErrorMsg(err, debug.get());
+ }
+ trackrenderer->is_error_posted_ = true;
+ break;
+ } // case GST_MESSAGE_ERROR
+ // LCOV_EXCL_STOP
+ default: {
+ TRACKRENDERER_INFO_P(
+ trackrenderer, "thread[ %p ], msg[ %p / %s ] src[ %s ]",
+ g_thread_self(), message, GST_MESSAGE_TYPE_NAME(message),
+ GST_MESSAGE_SRC_NAME(message));
+ }
+ }
+ gst_message_unref(message);
+ return GST_BUS_DROP;
+}
+
+GstPadProbeReturn TrackRenderer::GstPadProbeIdleCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata) {
+ TRACKRENDERER_ENTER;
+ auto gstpad = static_cast<Pipeline<Elements>::Pad*>(userdata);
+ std::unique_lock<std::mutex> pad_block_locker(gstpad->m);
+ gstpad->is_idle = true;
+ gstpad->cv.notify_one();
+ return GST_PAD_PROBE_REMOVE;
+}
+
+void TrackRenderer::GstAudioNeedDataCb_(GstElement* element, guint size,
+ gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ trackrenderer->trackctx_[kTrackTypeAudio].is_enough_data = false;
+ trackrenderer->eventlistener_->OnBufferStatus(kTrackTypeAudio,
+ BufferStatus::kUnderrun);
+}
+
+void TrackRenderer::GstVideoNeedDataCb_(GstElement* element, guint size,
+ gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ trackrenderer->trackctx_[kTrackTypeVideo].is_enough_data = false;
+ trackrenderer->eventlistener_->OnBufferStatus(kTrackTypeVideo,
+ BufferStatus::kUnderrun);
+}
+
+void TrackRenderer::GstSubtitleNeedDataCb_(GstElement* element, guint size,
+ gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ if (trackrenderer->trackctx_[kTrackTypeSubtitle].index == kInvalidTrackIndex)
+ return;
+ trackrenderer->trackctx_[kTrackTypeSubtitle].is_enough_data = false;
+}
+
+void TrackRenderer::GstAudioEnoughDataCb_(GstElement* element,
+ gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ trackrenderer->trackctx_[kTrackTypeAudio].is_enough_data = true;
+ trackrenderer->eventlistener_->OnBufferStatus(kTrackTypeAudio,
+ BufferStatus::kOverrun);
+}
+
+void TrackRenderer::GstVideoEnoughDataCb_(GstElement* element,
+ gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ trackrenderer->trackctx_[kTrackTypeVideo].is_enough_data = true;
+ trackrenderer->eventlistener_->OnBufferStatus(kTrackTypeVideo,
+ BufferStatus::kOverrun);
+}
+
+void TrackRenderer::GstSubtitleEnoughDataCb_(GstElement* element,
+ gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ if (trackrenderer->trackctx_[kTrackTypeSubtitle].index == kInvalidTrackIndex)
+ return;
+ trackrenderer->trackctx_[kTrackTypeSubtitle].is_enough_data = true;
+}
+
+gboolean TrackRenderer::GstAudioSeekDataCb_(GstElement* element, guint64 offset,
+ gpointer user_data) {
+ auto trackrenderer = static_cast<TrackRenderer*>(user_data);
+ TRACKRENDERER_INFO_P(trackrenderer, "offset : %llu", offset);
+ trackrenderer->eventlistener_->OnSeekData(kTrackTypeAudio,
+ offset / GST_MSECOND);
+ return true;
+}
+
+gboolean TrackRenderer::GstVideoSeekDataCb_(GstElement* element, guint64 offset,
+ gpointer user_data) {
+ auto trackrenderer = static_cast<TrackRenderer*>(user_data);
+ TRACKRENDERER_INFO_P(trackrenderer, "offset : %llu", offset);
+ trackrenderer->eventlistener_->OnSeekData(kTrackTypeVideo,
+ offset / GST_MSECOND);
+ return true;
+}
+
+gboolean TrackRenderer::GstSubtitleSeekDataCb_(GstElement* element,
+ guint64 offset,
+ gpointer user_data) {
+ auto trackrenderer = static_cast<TrackRenderer*>(user_data);
+ TRACKRENDERER_INFO_P(trackrenderer, "offset : %llu", offset);
+ return true;
+}
+
+// LCOV_EXCL_START
+gboolean TrackRenderer::GstAudioDrmInitDataCb_(int* drmhandle, unsigned int len,
+ unsigned char* psshdata,
+ void* userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ TRACKRENDERER_ENTER_P(trackrenderer);
+ trackrenderer->eventlistener_->OnDrmInitData(drmhandle, len, psshdata,
+ kTrackTypeAudio);
+ TRACKRENDERER_LEAVE_P(trackrenderer);
+ return TRUE;
+}
+
+gboolean TrackRenderer::GstVideoDrmInitDataCb_(int* drmhandle, unsigned int len,
+ unsigned char* psshdata,
+ void* userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ TRACKRENDERER_ENTER_P(trackrenderer);
+ trackrenderer->eventlistener_->OnDrmInitData(drmhandle, len, psshdata,
+ kTrackTypeVideo);
+ TRACKRENDERER_LEAVE_P(trackrenderer);
+ return TRUE;
+}
+// LCOV_EXCL_STOP
+
+void TrackRenderer::GstElementCreatedCb_(Elements element) {
+ auto it = kAttributesByElem_.find(element);
+ if (it == kAttributesByElem_.end()) return;
+ const std::vector<Attribute>& attrs = it->second;
+ for (const auto& attr : attrs) {
+ const auto& binder = kAttributes_.at(attr);
+ const AttributeValue& attr_value = set_attribute_values_[attr];
+ if (binder.need_to_init == false && attr_value.value_assigned == false)
+ continue;
+ const boost::any empty;
+ SetAttribute_(binder, empty, attr_value.value);
+ }
+}
+
+void TrackRenderer::GstDecodedVideoReferenceBufferCb_(GstElement* element,
+ GstBuffer* buffer,
+ GstPad* pad,
+ void* userdata) {
+ GstStructure* s = GST_STRUCTURE_CAST(gst_mini_object_get_qdata(
+ GST_MINI_OBJECT(buffer), g_quark_from_static_string("v4l2_info")));
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ if (!s) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "scaler buffer don't have v4l2_info structure");
+ return;
+ }
+ int width, height, y_linesize, c_linesize, y_fd, c_fd, plane_num;
+ gst_structure_get_int(s, "width", &width);
+ gst_structure_get_int(s, "height", &height);
+ gst_structure_get_int(s, "y_linesize", &y_linesize);
+ gst_structure_get_int(s, "u_linesize", &c_linesize);
+ gst_structure_get_int(s, "y_phyaddr", &y_fd);
+ gst_structure_get_int(s, "u_phyaddr", &c_fd);
+ gst_structure_get_int(s, "plane_num", &plane_num);
+
+ tbm_bo bo_Y =
+ tbm_bo_import_fd(trackrenderer->tbm_buffer_manager_->bufmgr, y_fd);
+ if (bo_Y == nullptr) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "[Weak Ref] tbm_bo_import_fd() error : Y bo is NULL");
+ return;
+ }
+ BOOST_SCOPE_EXIT(&bo_Y) {
+ if (bo_Y) {
+ tbm_bo_unref(bo_Y);
+ bo_Y = nullptr;
+ }
+ }
+ BOOST_SCOPE_EXIT_END
+
+ tbm_bo bo_C =
+ tbm_bo_import_fd(trackrenderer->tbm_buffer_manager_->bufmgr, c_fd);
+ if (bo_C == nullptr) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "[Weak Ref] tbm_bo_import_fd() error : C bo is NULL");
+ return;
+ }
+ BOOST_SCOPE_EXIT(&bo_C) {
+ if (bo_C) {
+ tbm_bo_unref(bo_C);
+ bo_C = nullptr;
+ }
+ }
+ BOOST_SCOPE_EXIT_END
+
+ tbm_bo bo[internal::kVideoBufferPlaneMax] = {nullptr};
+ bo[GST_VIDEO_COMP_Y] = bo_Y;
+ bo[GST_VIDEO_COMP_U] = bo_C;
+
+ tbm_surface_info_s info;
+ memset(&info, 0, sizeof(info));
+ info.width = width;
+ info.height = height;
+ info.format = TBM_FORMAT_NV12;
+
+ info.bpp = tbm_surface_internal_get_bpp(info.format);
+ info.num_planes = tbm_surface_internal_get_num_planes(info.format);
+
+ info.planes[GST_VIDEO_COMP_Y].stride = y_linesize;
+ info.planes[GST_VIDEO_COMP_Y].size = y_linesize * c_linesize;
+ info.size += info.planes[GST_VIDEO_COMP_Y].size;
+ info.planes[GST_VIDEO_COMP_U].stride = c_linesize;
+ info.planes[GST_VIDEO_COMP_U].size = c_linesize * c_linesize / 2;
+ info.size += info.planes[GST_VIDEO_COMP_U].size;
+
+ tbm_surface_h tbm_surf =
+ tbm_surface_internal_create_with_bos(&info, bo, info.num_planes);
+ if (!tbm_surf) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "failed to create tbm surface");
+ return;
+ }
+
+ DecodedVideoPacket packet;
+ packet.pts = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer)); // ns -> ms
+ packet.surface_data = tbm_surf;
+ packet.scaler_index = reinterpret_cast<void*>(plane_num);
+
+ trackrenderer->eventlistener_->OnMediaPacketVideoDecoded(packet);
+ return;
+}
+
+void TrackRenderer::GstDecodedVideoCopyBufferCb_(GstElement* element,
+ GstBuffer* buffer, GstPad* pad,
+ void* userdata) {
+ auto caps = gstguard::make_guard(gst_pad_get_current_caps(pad));
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ if (!caps.get()) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "caps is NULL");
+ return;
+ }
+ GstVideoInfo info;
+ if (!gst_video_info_from_caps(&info, caps.get())) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "fail to get gst_video_info_from_caps()");
+ return;
+ }
+ VideoColorFormat colorformat = VideoColorFormat::kColorFormatNV12;
+ GstStructure* structure = gst_caps_get_structure(caps.get(), 0);
+ const gchar* color_format =
+ gst_structure_get_string(structure, "color-format");
+ if (color_format && strstr(color_format, "NV16"))
+ colorformat = VideoColorFormat::kColorFormatNV16;
+
+ tbm_surface_h tbm_surf_t = nullptr;
+ internal::VideoStreamDataType stream = {};
+
+ BOOST_SCOPE_EXIT(&tbm_surf_t) {
+ if (tbm_surf_t) {
+ tbm_surface_destroy(tbm_surf_t);
+ tbm_surf_t = nullptr;
+ }
+ }
+ BOOST_SCOPE_EXIT_END
+
+ if (GST_VIDEO_INFO_FORMAT(&info) == GST_VIDEO_FORMAT_STV0) {
+ GstStructure* s = GST_STRUCTURE_CAST(gst_mini_object_get_qdata(
+ GST_MINI_OBJECT(buffer), g_quark_from_static_string("v4l2_info")));
+ if (!s) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "scaler buffer don't have v4l2_info structure");
+ return;
+ }
+ int y_linesize, c_linesize, y_viraddr, c_viraddr, plane_num = 0;
+ int width = 0, height = 0;
+ gst_structure_get_int(s, "width", &width);
+ gst_structure_get_int(s, "height", &height);
+ gst_structure_get_int(s, "y_viraddr", &y_viraddr);
+ gst_structure_get_int(s, "u_viraddr", &c_viraddr);
+ gst_structure_get_int(s, "y_linesize", &y_linesize);
+ gst_structure_get_int(s, "u_linesize", &c_linesize);
+ gst_structure_get_int(s, "plane_num", &plane_num);
+
+ if (internal::FillVideoStreamDataAndTbmSurface(&stream, &tbm_surf_t, width,
+ height) == false) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "FillVideoStreamDataAndTbmSurface fail");
+ return;
+ }
+
+ if (!internal::CopyHwCodec(stream, y_viraddr, c_viraddr, y_linesize,
+ c_linesize, colorformat)) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "data copy fail");
+ return;
+ }
+ } else if (GST_VIDEO_INFO_FORMAT(&info) == GST_VIDEO_FORMAT_STV1) {
+ if (internal::FillVideoStreamDataAndTbmSurface(
+ &stream, &tbm_surf_t, info.width, info.height) == false) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "FillVideoStreamDataAndTbmSurface fail");
+ return;
+ }
+
+ if (!internal::CopySwCodec(buffer, stream)) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "data copy fail");
+ return;
+ }
+ }
+ tbm_surface_h tbm_surf = internal::CreateTbmSurfaceWithBos(stream);
+
+ DecodedVideoPacket packet;
+ packet.pts = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer)); // ns -> ms
+ packet.surface_data = tbm_surf;
+
+ trackrenderer->eventlistener_->OnMediaPacketVideoDecoded(packet);
+ return;
+}
+
+// LCOV_EXCL_START
+void TrackRenderer::GstDecodedVideoRawBufferCb_(GstElement* element,
+ GstBuffer* buffer, GstPad* pad,
+ void* userdata) {
+ auto caps = gstguard::make_guard(gst_pad_get_current_caps(pad));
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ if (!caps.get()) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "caps is NULL");
+ return;
+ }
+ GstVideoInfo info;
+ if (!gst_video_info_from_caps(&info, caps.get())) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "fail to get gst_video_info_from_caps()");
+ return;
+ }
+
+ DecodedVideoRawModePacket packet;
+
+ int width = 0, height = 0;
+ if (GST_VIDEO_INFO_FORMAT(&info) == GST_VIDEO_FORMAT_STV0) {
+ GstStructure* s = GST_STRUCTURE_CAST(gst_mini_object_get_qdata(
+ GST_MINI_OBJECT(buffer), g_quark_from_static_string("v4l2_info")));
+ if (!s) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "scaler buffer doesn't have v4l2_info structure");
+ return;
+ }
+ packet.type = DecodedVideoRawModePacketType::kPhysicalAddress;
+ gst_structure_get_int(s, "width", &width);
+ gst_structure_get_int(s, "height", &height);
+ gst_structure_get_int(s, "y_phyaddr", &packet.data.raw.y_phyaddr);
+ gst_structure_get_int(s, "u_phyaddr", &packet.data.raw.uv_phyaddr);
+ gst_structure_get_int(s, "y_viraddr", &packet.data.raw.y_viraddr);
+ gst_structure_get_int(s, "u_viraddr", &packet.data.raw.uv_viraddr);
+ gst_structure_get_int(s, "y_linesize", &packet.data.raw.y_linesize);
+ gst_structure_get_int(s, "u_linesize", &packet.data.raw.uv_linesize);
+ } else if (GST_VIDEO_INFO_FORMAT(&info) == GST_VIDEO_FORMAT_STV1) {
+ GstStructure* s = GST_STRUCTURE_CAST(gst_mini_object_get_qdata(
+ GST_MINI_OBJECT(buffer), g_quark_from_static_string("tbm_bo")));
+ width = info.width;
+ height = info.height;
+ if (s) {
+ packet.type = DecodedVideoRawModePacketType::kTizenBuffer;
+ if (!gst_structure_get(s, "tbm_bo_key", G_TYPE_UINT, &packet.data.tbm.key,
+ NULL)) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "Buffer doesn't have tbm_bo_hnd structure");
+ return;
+ }
+ } else {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "Buffer doesn't have tbm_bo structure");
+ return;
+ }
+ }
+
+ packet.width = width;
+ packet.height = height;
+ packet.pts = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer)); // ns -> ms
+
+ trackrenderer->eventlistener_->OnMediaRawPacketVideoDecoded(packet);
+ return;
+}
+
+void TrackRenderer::GstDecodedVideoScaleBufferCb_(GstElement* element,
+ GstBuffer* buffer,
+ GstPad* pad, void* userdata) {
+ auto caps = gstguard::make_guard(gst_pad_get_current_caps(pad));
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ if (!caps.get()) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "caps is NULL");
+ return;
+ }
+ GstVideoInfo info;
+ if (!gst_video_info_from_caps(&info, caps.get())) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "fail to get gst_video_info_from_caps()");
+ return;
+ }
+ if (trackrenderer->NeedDropThisDecodedVideoBuffer_()) {
+ return;
+ }
+ std::lock_guard<std::mutex> lk(trackrenderer->scale_target_m_);
+ internal::VideoStreamDataType stream = {};
+ stream.format = TBM_FORMAT_NV12;
+ stream.width = trackrenderer->scale_target_width_;
+ stream.height = trackrenderer->scale_target_height_;
+ stream.elevation[GST_VIDEO_COMP_Y] = trackrenderer->scale_target_height_;
+ stream.elevation[GST_VIDEO_COMP_U] = trackrenderer->scale_target_height_ / 2;
+ VideoColorFormat colorformat = VideoColorFormat::kColorFormatNV12;
+ GstStructure* structure = gst_caps_get_structure(caps.get(), 0);
+ const gchar* color_format =
+ gst_structure_get_string(structure, "color-format");
+ if (color_format && strstr(color_format, "NV16"))
+ colorformat = VideoColorFormat::kColorFormatNV16;
+ // using cb to get the free tbm ptr, if not find then new one, else, using it
+ tbm_surface_h tbm_surf_t = nullptr;
+ void* tbm_surf = nullptr;
+ bool exception_flag = true;
+ DecodedVideoPacket packet;
+
+ BOOST_SCOPE_EXIT(&tbm_surf_t, &tbm_surf, &exception_flag) {
+ if (!tbm_surf && tbm_surf_t && exception_flag == true) {
+ tbm_surface_destroy(tbm_surf_t);
+ tbm_surf_t = nullptr;
+ }
+ }
+ BOOST_SCOPE_EXIT_END
+
+ if (GST_VIDEO_INFO_FORMAT(&info) == GST_VIDEO_FORMAT_STV0) {
+ GstStructure* s = GST_STRUCTURE_CAST(gst_mini_object_get_qdata(
+ GST_MINI_OBJECT(buffer), g_quark_from_static_string("v4l2_info")));
+ if (!s) {
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "scaler buffer don't have v4l2_info structure");
+ return;
+ }
+ int y_linesize, c_linesize, y_phyaddr, u_phyaddr, plane_num = 0;
+ int width = 0, height = 0;
+ CropArea crop_info;
+ int x, y, w, h = 0;
+ gst_structure_get_int(s, "width", &width);
+ gst_structure_get_int(s, "height", &height);
+ gst_structure_get_int(s, "y_phyaddr", &y_phyaddr);
+ gst_structure_get_int(s, "u_phyaddr", &u_phyaddr);
+ gst_structure_get_int(s, "y_linesize", &y_linesize);
+ gst_structure_get_int(s, "u_linesize", &c_linesize);
+ gst_structure_get_int(s, "plane_num", &plane_num);
+ if (!y_phyaddr || !u_phyaddr) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "invalid y_phyaddr or u_phyaddr");
+ return;
+ }
+ if (!width || !height) {
+ TRACKRENDERER_WARN_P(
+ trackrenderer,
+ "invalid width or height, using width&&height of caps_info");
+ width = info.width;
+ height = info.height;
+ }
+
+ tbm_surface_info_s surface_info;
+ trackrenderer->eventlistener_->OnMediaPacketGetTbmBufPtr(
+ &tbm_surf, trackrenderer->is_scale_size_changed_);
+ trackrenderer->is_scale_size_changed_ = false;
+ if (tbm_surf == nullptr) {
+ tbm_surf_t = tbm_surface_internal_create_with_flags(
+ stream.width, stream.height, TBM_FORMAT_NV12,
+ (1 << 17 | TBM_BO_SCANOUT));
+ TRACKRENDERER_ERROR_P(trackrenderer, "create new tbm %p", tbm_surf_t);
+ } else {
+ tbm_surf_t = static_cast<tbm_surface_h>(tbm_surf);
+ }
+ stream.bo[GST_VIDEO_COMP_Y] = tbm_surface_internal_get_bo(tbm_surf_t, 0);
+ if (!stream.bo[GST_VIDEO_COMP_Y]) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "[bo Y] tbm_bo_alloc failed");
+ return;
+ }
+ stream.bo[GST_VIDEO_COMP_U] = tbm_surface_internal_get_bo(tbm_surf_t, 1);
+ if (!stream.bo[GST_VIDEO_COMP_U]) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "[bo U] tbm_bo_alloc failed");
+ return;
+ }
+ if (TBM_SURFACE_ERROR_NONE !=
+ tbm_surface_get_info(tbm_surf_t, &surface_info)) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "tbm_surface_get_info failed");
+ return;
+ }
+ stream.stride[GST_VIDEO_COMP_Y] = surface_info.planes[0].stride;
+ stream.stride[GST_VIDEO_COMP_U] = surface_info.planes[1].stride;
+
+ trackrenderer->display_->GetDisplayCropArea(&crop_info);
+ x = width * crop_info.scale_x;
+ y = height * crop_info.scale_y;
+ w = width * crop_info.scale_w;
+ h = height * crop_info.scale_h;
+ internal::SetGeometry(&stream.crop_area, x & 0xFFFFFFFE, y & 0xFFFFFFFE, w,
+ h);
+ if (!internal::ScaleHwCodecWithGa(
+ trackrenderer->tbm_buffer_manager_->bufmgr, stream, y_phyaddr,
+ u_phyaddr, y_linesize, c_linesize, colorformat)) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "data copy fail");
+ return;
+ }
+ } else {
+ TRACKRENDERER_ERROR_P(trackrenderer, "SW decoder type, not support now");
+ return;
+ }
+ packet.pts = GST_TIME_AS_MSECONDS(GST_BUFFER_PTS(buffer)); // ns -> ms
+ packet.surface_data = tbm_surf_t;
+ trackrenderer->eventlistener_->OnMediaPacketVideoDecoded(packet);
+ exception_flag = false;
+ return;
+}
+
+void TrackRenderer::GstAiFilterResultCb_(GstElement* element,
+ GstStructure* structure,
+ void* userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ const gchar* struct_name = gst_structure_get_name(structure);
+ if (!strcmp(struct_name, "auto_zoom_crop")) {
+ CropArea roi_area;
+ gfloat x_ratio, y_ratio = 0.0f;
+ gfloat width_ratio, height_ratio = 1.0f;
+
+ gst_structure_get(structure, "x_ratio", G_TYPE_FLOAT, &x_ratio, "y_ratio",
+ G_TYPE_FLOAT, &y_ratio, "width_ratio", G_TYPE_FLOAT,
+ &width_ratio, "height_ratio", G_TYPE_FLOAT, &height_ratio,
+ NULL);
+ TRACKRENDERER_DEBUG_P(
+ trackrenderer, "x_ratio %f y_ratio %f width_ratio %f height_ratio %f",
+ x_ratio, y_ratio, width_ratio, height_ratio);
+
+ roi_area.scale_x = (double)x_ratio;
+ roi_area.scale_y = (double)y_ratio;
+ roi_area.scale_w = (double)width_ratio;
+ roi_area.scale_h = (double)height_ratio;
+ trackrenderer->display_->SetDisplayCropArea(roi_area);
+ if (!trackrenderer->pipeline_) return;
+ trackrenderer->pipeline_->Execute(
+ Elements::kSinkVideo, [trackrenderer](GstElement * obj) noexcept {
+ return trackrenderer->display_->UpdateCropArea(obj);
+ });
+ }
+ return;
+}
+// LCOV_EXCL_STOP
+
+void TrackRenderer::FlushAppsrc(TrackType type, bool setbyuser) {
+ TRACKRENDERER_ENTER_P(this);
+ if (!pipeline_) return;
+ if (type >= kTrackTypeMax) {
+ TRACKRENDERER_ERROR_P(
+ this,
+ "Support only audio and subtitle and video pipeline flush for now");
+ return;
+ }
+ Track track;
+ if (!track_util::GetActiveTrack(trackinfo_, type, &track)) {
+ TRACKRENDERER_ERROR_P(this, "There is no %s track",
+ track_util::GetTrackTypeString(type).c_str());
+ return;
+ }
+ if (setbyuser && type == kTrackTypeAudio) enable_audio_track_change_ = true;
+
+ Elements element = Elements::kAppSrcSubtitle;
+ if (type == kTrackTypeAudio) {
+ element = Elements::kAppSrcAudio;
+ } else if (type == kTrackTypeVideo) {
+ if (support_videodec_underflow_pause_) {
+ pipeline_->PadRemoveProbe(kPadProbeVideoDecInputBlock);
+ }
+ element = Elements::kAppSrcVideo;
+ latency_manager_->UpdateVideoFrameStatus(
+ LatencyManager::UpdatePacketStatus::kFlush);
+ }
+ pipeline_->Flush(element);
+ if (setbyuser) is_flushing_ = true;
+
+ // TODO: external flush api seems not to need to check pad idle status. it is
+ // necessary to check whether to remove this code.
+ Pipeline<Elements>::Pad pad;
+ pipeline_->PadAddProbe(element, nullptr, "src", GST_PAD_PROBE_TYPE_IDLE,
+ GstPadProbeIdleCb_, &pad, nullptr);
+
+ {
+ std::unique_lock<std::mutex> pad_block_locker(pad.m);
+ if (!pad.is_idle) {
+ pad.cv.wait(pad_block_locker);
+ TRACKRENDERER_INFO_P(this, "pad block wait end");
+ }
+ }
+ TRACKRENDERER_LEAVE_P(this);
+}
+
+void TrackRenderer::SetVideoFrameBufferType(DecodedVideoFrameBufferType type) {
+ TRACKRENDERER_ENTER_E_P(this);
+ decoded_buffer_type_ = type;
+ if (type == DecodedVideoFrameBufferType::kScale) {
+ CreateTbmBufferManager_();
+ SetPropertyForDecodedVideoBufferWithDisplay_();
+ }
+ if (type == DecodedVideoFrameBufferType::kReference) {
+ if (vr360_ == nullptr) {
+ vr360_.reset(new Vr360);
+ }
+ }
+}
+
+void TrackRenderer::SetVideoFrameBufferScaleResolution(
+ const uint32_t& target_width, const uint32_t& target_height) {
+ TRACKRENDERER_ENTER;
+ std::lock_guard<std::mutex> lk(scale_target_m_);
+ scale_target_width_ = target_width;
+ scale_target_height_ = target_height;
+ is_scale_size_changed_ = true;
+}
+
+bool TrackRenderer::SetDecodedVideoFrameRate(
+ const Rational& request_framerate) {
+ TRACKRENDERER_ENTER;
+ std::lock_guard<std::mutex> lk(decoded_drop_ctx_.drop_mutex);
+
+ if (request_framerate.num && !request_framerate.den) {
+ TRACKRENDERER_ERROR("invalid request frame rate: %d/%d",
+ request_framerate.num, request_framerate.den);
+ return false;
+ }
+ decoded_drop_ctx_.request_fps = request_framerate;
+ decoded_drop_ctx_.fps_changed = true;
+ return true;
+}
+
+void TrackRenderer::RegisterListener(EventListener* listener) {
+ assert(listener);
+ assert(!eventlistener_);
+ eventlistener_ = listener;
+}
+
+bool TrackRenderer::RemoveDownstreamOfAppsrc_(TrackType type) {
+ TRACKRENDERER_ENTER_P(this);
+ if (type == kTrackTypeVideo) {
+ pipeline_->BinRemove(Elements::kBinVideo, Elements::kDrmVideo,
+ Elements::kDecVideo, Elements::kQueueCaption,
+ Elements::kSinkCaption, Elements::kAiFilter,
+ Elements::kSinkVideo);
+ } else if (type == kTrackTypeAudio) {
+ pipeline_->BinRemove(Elements::kBinAudio, Elements::kDrmAudio,
+ Elements::kDecAudio, Elements::kAudioConvert,
+ Elements::kCapsFillterDefault,
+ Elements::kAudioResample, Elements::kCapsFillter2,
+ Elements::kScaleTempo, Elements::kSinkAudio);
+ }
+
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+// LCOV_EXCL_START
+GstPadProbeReturn TrackRenderer::GstPadProbeBlockCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata) {
+ TRACKRENDERER_ENTER_P(userdata);
+ if (GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
+ GstEvent* event = GST_PAD_PROBE_INFO_EVENT(info);
+
+ if (!GST_EVENT_IS_SERIALIZED(event)) {
+ TRACKRENDERER_DEBUG_P(userdata, "pass non-serialized event");
+ return GST_PAD_PROBE_PASS;
+ }
+
+ if (GST_EVENT_IS_STICKY(event) && GST_EVENT_TYPE(event) != GST_EVENT_EOS) {
+ TRACKRENDERER_DEBUG_P(userdata, "pass sticky event");
+ return GST_PAD_PROBE_PASS;
+ }
+ }
+
+ if (GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER) {
+ TRACKRENDERER_DEBUG_P(userdata, "drop buffer as pad is not re-activated");
+ return GST_PAD_PROBE_DROP;
+ }
+ return GST_PAD_PROBE_OK;
+}
+// LCOV_EXCL_STOP
+
+GstPadProbeReturn TrackRenderer::GstPadProbeVideoPeekBlockCb_(
+ GstPad* pad, GstPadProbeInfo* info, gpointer userdata) {
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ TRACKRENDERER_ENTER_P(trackrenderer);
+ if (GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
+ GstEvent* event = GST_PAD_PROBE_INFO_EVENT(info);
+
+ if (!GST_EVENT_IS_SERIALIZED(event)) {
+ TRACKRENDERER_DEBUG_P(trackrenderer, "pass non-serialized event");
+ return GST_PAD_PROBE_PASS;
+ }
+
+ if (GST_EVENT_IS_STICKY(event) && GST_EVENT_TYPE(event) != GST_EVENT_EOS) {
+ TRACKRENDERER_DEBUG_P(trackrenderer, "pass sticky event");
+ return GST_PAD_PROBE_PASS;
+ }
+ }
+
+ GstBuffer* buffer = gst_pad_probe_info_get_buffer(info);
+ if (!buffer) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "buffer is null maybe EOS event");
+ return GST_PAD_PROBE_PASS;
+ }
+ trackrenderer->last_position_ = GST_BUFFER_PTS(buffer) / GST_MSECOND;
+ TRACKRENDERER_ERROR_P(trackrenderer,
+ "Update last position for playing time [%lld ms]",
+ trackrenderer->last_position_);
+
+ if (trackrenderer->is_seeking_ && trackrenderer->state_ == State::kWorking) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "Seek Done");
+ trackrenderer->eventlistener_->OnSeekDone();
+ trackrenderer->is_seeking_ = false;
+ return GST_PAD_PROBE_OK;
+ }
+ return GST_PAD_PROBE_OK;
+}
+
+GstPadProbeReturn TrackRenderer::GstPadProbeVideoDecodedCb_(
+ GstPad* pad, GstPadProbeInfo* info, gpointer userdata) {
+ GstBuffer* buffer = gst_pad_probe_info_get_buffer(info);
+ auto trackrenderer = static_cast<TrackRenderer*>(userdata);
+ if (trackrenderer->state_ < State::kWorking && buffer) {
+ TRACKRENDERER_ERROR_P(trackrenderer, "First Decoding Done");
+ trackrenderer->eventlistener_->OnFirstDecodingDone();
+ }
+ return GST_PAD_PROBE_OK;
+}
+
+GstPadProbeReturn TrackRenderer::GstPadProbeVideoDecInputCb_(
+ GstPad* pad, GstPadProbeInfo* info, gpointer userdata) {
+ TRACKRENDERER_ENTER_P(userdata);
+ if (GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_BUFFER) {
+ TRACKRENDERER_ERROR_P(
+ userdata, "block first video input buffer when do underflow pause");
+ return GST_PAD_PROBE_OK;
+ } else
+ return GST_PAD_PROBE_PASS;
+}
+
+bool TrackRenderer::Deactivate(TrackType type) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (!pipeline_) return false;
+ TRACKRENDERER_ENTER_P(this);
+
+ Track track;
+ if (!track_util::GetActiveTrack(trackinfo_, type, &track)) {
+ TRACKRENDERER_ERROR_P(this, "There is no track to deactivate");
+ return false;
+ }
+ if (type >= kTrackTypeMax) return false;
+ trackctx_[type].index = kInvalidTrackIndex;
+ if (type == kTrackTypeVideo) {
+ pipeline_->PadAddProbe(Elements::kAppSrcVideo, kPadProbeVideoBlock, "src",
+ GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ GstPadProbeBlockCb_, nullptr, nullptr);
+ pipeline_->SetProperty(Elements::kSinkVideo, "async", FALSE);
+ FlushAppsrc(type, false);
+ RemoveDownstreamOfAppsrc_(type);
+ auto is_video_track = [](const Track& item) -> bool {
+ return item.type == kTrackTypeVideo;
+ };
+ auto target =
+ std::find_if(trackinfo_.begin(), trackinfo_.end(), is_video_track);
+ if (target != trackinfo_.end()) {
+ trackinfo_.erase(target);
+ }
+ resource_manager_->Dealloc(video_decoder_id_);
+ resource_manager_->Dealloc(video_renderer_id_);
+ } else if (type == kTrackTypeAudio) {
+ pipeline_->PadAddProbe(Elements::kAppSrcAudio, kPadProbeAudioBlock, "src",
+ GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ GstPadProbeBlockCb_, nullptr, nullptr);
+ pipeline_->SetProperty(Elements::kSinkAudio, "async", FALSE);
+ FlushAppsrc(type, false);
+ RemoveDownstreamOfAppsrc_(type);
+
+ auto is_audio_track = [](const Track& item) -> bool {
+ return item.type == kTrackTypeAudio;
+ };
+ auto target =
+ std::find_if(trackinfo_.begin(), trackinfo_.end(), is_audio_track);
+ if (target != trackinfo_.end()) {
+ trackinfo_.erase(target);
+ }
+ resource_manager_->Dealloc(audio_decoder_id_);
+ resource_manager_->Dealloc(audio_out_id_);
+ if (!AvocPlayerUnRegister_()) return false;
+ } else {
+ pipeline_->PadAddProbe(Elements::kAppSrcSubtitle, kPadProbeSubtitleBlock,
+ "src", GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, nullptr,
+ nullptr, nullptr);
+ FlushAppsrc(type, false);
+
+ auto is_subtitle_track = [](const Track& item) -> bool {
+ return item.type == kTrackTypeSubtitle;
+ };
+ auto target =
+ std::find_if(trackinfo_.begin(), trackinfo_.end(), is_subtitle_track);
+ if (target != trackinfo_.end()) {
+ trackinfo_.erase(target);
+ }
+ }
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::Activate(TrackType type, const Track& track) {
+ // construct pipeline
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (!pipeline_) return false;
+
+ Track active_track;
+ if (type >= kTrackTypeMax) return false;
+ if (trackctx_[type].track &&
+ track_util::GetActiveTrack(trackinfo_, type, &active_track)) {
+ TRACKRENDERER_ERROR_P(this,
+ "Valid and activated track, can not be activated.");
+ return false;
+ }
+ if (!trackctx_[type].track) {
+ TRACKRENDERER_INFO_P(this, "Add a new stream.");
+ if (type == kTrackTypeAudio) {
+ CreateAppSrc_(kTrackTypeAudio, track.mimetype);
+ pipeline_->SetProperty(Elements::kAppSrcAudio, "update-segment",
+ rendering_start_time_.time);
+ pipeline_->CreateBin(Elements::kBinAudio, "audiobin");
+ pipeline_->BinAddSimple(Elements::kPipeline, Elements::kBinAudio);
+ pipeline_->BinAdd(Elements::kBinAudio, Elements::kAppSrcAudio);
+ } else if (type == kTrackTypeVideo) {
+ CreateAppSrc_(kTrackTypeVideo, track.mimetype);
+ pipeline_->SetProperty(Elements::kAppSrcVideo, "update-segment",
+ rendering_start_time_.time);
+ pipeline_->CreateBin(Elements::kBinVideo, "videobin");
+ pipeline_->BinAddSimple(Elements::kPipeline, Elements::kBinVideo);
+ pipeline_->BinAdd(Elements::kBinVideo, Elements::kAppSrcVideo);
+ } else if (type == kTrackTypeSubtitle) {
+ TRACKRENDERER_INFO_P(this,
+ "set subtitle track information in track renderer");
+ trackctx_[type].index = track.index;
+ trackinfo_.push_back(track);
+ trackctx_[track.type].track = &(trackinfo_.back());
+ return true;
+ }
+ }
+
+ trackctx_[type].index = track.index;
+ trackinfo_.push_back(track);
+ trackctx_[track.type].track = &(trackinfo_.back());
+
+ if (type == kTrackTypeAudio) {
+ if (!AvocPlayerRegister_()) {
+ TRACKRENDERER_ERROR_P(this, "AvocPlayerRegister_() fail.");
+ return false;
+ }
+ }
+ if (!GetResource_(track.type)) {
+ TRACKRENDERER_ERROR_P(this,
+ "[%s] resource allocation fail! ChangeTrack fail.",
+ track_util::GetTrackTypeString(track.type).c_str());
+ return false;
+ }
+ if (type == kTrackTypeVideo) {
+ // the element name should be changed
+ CreateDrmElement_(track);
+
+ const char* kDecoderPluginName =
+ GetDecoderPluginName_(kTrackTypeVideo, track.mimetype);
+ if (kDecoderPluginName &&
+ (strcmp(kDecoderPluginName, kSkippedResource) == 0)) {
+ const ErrorType err = ErrorType::kResourceLimit;
+ eventlistener_->OnError(err);
+ eventlistener_->OnErrorMsg(err,
+ const_cast<char*>(track.mimetype.c_str()));
+ return false;
+ }
+ CreateVideoDecoder_(kDecoderPluginName);
+
+ if (CreateVideoSink_() == false) return false;
+
+ GstElementLowLatency_(kTrackTypeVideo);
+
+ SetPropertyForAiFilter_();
+
+ if (internal::IsDisplayNeeded(display_type_)) {
+ pipeline_->Execute(
+ Elements::kSinkVideo,
+ [this](GstElement * obj) noexcept { return display_->Update(obj); });
+ }
+ gboolean is_async = FALSE;
+ pipeline_->GetProperty(Elements::kSinkVideo, "async", &is_async);
+ if (is_async) pipeline_->SetProperty(Elements::kSinkVideo, "async", FALSE);
+
+ pipeline_->BinAdd(Elements::kBinVideo, Elements::kDrmVideo,
+ Elements::kDecVideo, Elements::kAiFilter,
+ Elements::kSinkVideo);
+
+ if (is_async) pipeline_->SetProperty(Elements::kSinkVideo, "async", TRUE);
+
+ auto caps = caps_builder_.Build(track, internal::IsDrmEmeElementNecessary(
+ drm_property_, track.mimetype));
+ pipeline_->SetAppSrcCaps(Elements::kAppSrcVideo, caps);
+ SetPropertyForDecodedVideoBuffer_();
+ pipeline_->PadRemoveProbe(kPadProbeVideoBlock);
+ UpdatePlaybackInfo_(true);
+ UpdateTrackFrameRate_(track.framerate_num, track.framerate_den);
+ } else if (type == kTrackTypeAudio) {
+ // the element name should be changed
+ CreateDrmElement_(track);
+ if (internal::IsDecoderElementNecessary(track.mimetype)) {
+ const char* kDecoderPluginName =
+ GetDecoderPluginName_(kTrackTypeAudio, track.mimetype);
+ pipeline_->FactoryMake(Elements::kDecAudio, kDecoderPluginName, nullptr);
+ if (internal::IsHwPlugin(kDecoderPluginName)) {
+ if (low_latency_mode_ ==
+ static_cast<std::uint32_t>(LowLatencyMode::kLowLatencyModeNone)) {
+ constexpr int kPtsManipulationThreshold = 99000; // 99ms
+ pipeline_->SetProperty(Elements::kDecAudio, "set-usr-cal-timestamp",
+ kPtsManipulationThreshold);
+ }
+ pipeline_->SetProperty(Elements::kDecAudio, "support-codec-change",
+ support_audio_codec_change_);
+ }
+ }
+ std::string audiosink_name =
+ GetAudioSinkPluginName_(track.use_swdecoder, track.mimetype);
+ CreateAudioSink_(audiosink_name);
+
+ AvocPlayRequest_();
+ SetAudioOut_();
+ GstElementLowLatency_(kTrackTypeAudio);
+
+ gboolean is_async = FALSE;
+ pipeline_->GetProperty(Elements::kSinkAudio, "async", &is_async);
+ if (is_async) pipeline_->SetProperty(Elements::kSinkAudio, "async", FALSE);
+
+ pipeline_->BinAdd(Elements::kBinAudio, Elements::kDrmAudio,
+ Elements::kDecAudio, Elements::kAudioConvert,
+ Elements::kCapsFillterDefault, Elements::kAudioResample,
+ Elements::kCapsFillter2, Elements::kScaleTempo,
+ Elements::kSinkAudio);
+ if (is_async) pipeline_->SetProperty(Elements::kSinkAudio, "async", TRUE);
+
+ auto caps = caps_builder_.Build(track, internal::IsDrmEmeElementNecessary(
+ drm_property_, track.mimetype));
+ pipeline_->SetAppSrcCaps(Elements::kAppSrcAudio, caps);
+ pipeline_->PadRemoveProbe(kPadProbeAudioBlock);
+ UpdatePlaybackInfo_(true);
+ } else {
+ auto caps =
+ gstguard::make_guard(gst_caps_new_empty_simple(track.mimetype.c_str()));
+ pipeline_->SetProperty(Elements::kAppSrcSubtitle, "caps", caps.get());
+ pipeline_->PadRemoveProbe(kPadProbeSubtitleBlock);
+ }
+ TRACKRENDERER_LEAVE_P(this);
+ return true;
+}
+
+bool TrackRenderer::GetPlayingTime(uint64_t* curtime_in_msec) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ if (state_ == State::kResourceConflicted) {
+ *curtime_in_msec = last_position_;
+ return true;
+ }
+ if (!pipeline_) return false;
+ GetPlayingTime_(curtime_in_msec);
+ return true;
+}
+
+bool TrackRenderer::GetPlayingTime_(uint64_t* curtime_in_msec) {
+ int64_t pos_video = 0, pos_audio = 0;
+ bool ret_video = pipeline_->QueryPosition(Elements::kSinkVideo,
+ GST_FORMAT_TIME, &pos_video);
+ bool ret_audio = pipeline_->QueryPosition(Elements::kSinkAudio,
+ GST_FORMAT_TIME, &pos_audio);
+ if (ret_video || ret_audio)
+ last_position_ = *curtime_in_msec = pos_audio > pos_video
+ ? (pos_audio / GST_MSECOND)
+ : (pos_video / GST_MSECOND);
+ else
+ *curtime_in_msec = last_position_;
+ // TRACKRENDERER_INFO_P(this, "playing time [%llu ms]", *curtime_in_msec);
+ return true;
+}
+
+bool TrackRenderer::GetDroppedFrames(void* frame_counts) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ if (!pipeline_) return false;
+ int64_t drop_count = 0;
+ Elements element = Elements::kSinkVideo;
+ bool ret = false;
+ if (playback_rate_ == kDefaultPlaybackRate && use_seq_mode_)
+ ret = pipeline_->GetProperty(element, "dropped-frames-count", &drop_count);
+ else
+ ret = pipeline_->GetProperty(element, "dropped-frames", &drop_count);
+ if (ret) {
+ *(int64_t*)frame_counts = drop_count;
+ return true;
+ }
+ return false;
+}
+
+bool TrackRenderer::GetDroppedFramesForCatchup(TrackType type,
+ void* frame_counts) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ if (!pipeline_) return false;
+ uint64_t drop_count = 0;
+ Elements video_element = Elements::kSinkVideo;
+ Elements audio_element = Elements::kSinkAudio;
+ bool ret = false;
+
+ if (type == kTrackTypeAudio) {
+ ret = pipeline_->GetProperty(audio_element, "dropped-frames-for-catchup",
+ &drop_count);
+ } else if (type == kTrackTypeVideo) {
+ ret = pipeline_->GetProperty(video_element, "dropped-frames-for-catchup",
+ &drop_count);
+ }
+
+ if (ret) {
+ *(int64_t*)frame_counts = drop_count;
+ return true;
+ }
+
+ return false;
+}
+
+bool TrackRenderer::SetDisplayMode(const DisplayMode& mode) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_->SetDisplayMode(mode);
+ if (!pipeline_) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->Update(obj);
+ });
+ return true;
+}
+
+bool TrackRenderer::SetDisplayRotate(const DisplayRotation& rotate) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_->SetDisplayRotate(rotate);
+ if (!pipeline_) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->Update(obj);
+ });
+ return true;
+}
+
+bool TrackRenderer::SetDisplay(const DisplayType& type, void* obj) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_type_ = type;
+ if (type == DisplayType::kNone) {
+ TRACKRENDERER_WARN_P(this, "Just Reset Display type as None");
+ return true;
+ }
+ display_->SetDisplay(type, static_cast<Evas_Object*>(obj));
+
+ if (!pipeline_) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * _obj) noexcept {
+ return display_->Update(_obj);
+ });
+ return true;
+}
+
+// LCOV_EXCL_START
+bool TrackRenderer::SetDisplay(const DisplayType& type, void* ecore_wl2_window,
+ int x, int y, int w, int h) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_type_ = type;
+ display_->SetDisplay(type, static_cast<Ecore_Wl2_Window*>(ecore_wl2_window),
+ x, y, w, h);
+ if (!pipeline_) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->Update(obj);
+ });
+ return true;
+}
+// LCOV_EXCL_STOP
+
+bool TrackRenderer::SetDisplaySubsurface(const DisplayType& type,
+ void* ecore_wl2_subsurface, int x,
+ int y, int w, int h) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_type_ = type;
+ display_->SetDisplaySubsurface(
+ type, static_cast<Ecore_Wl2_Subsurface*>(ecore_wl2_subsurface), x, y, w,
+ h);
+ if (!pipeline_) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->Update(obj);
+ });
+ return true;
+}
+
+void TrackRenderer::DrmLicenseAcquiredDone(TrackType type) {
+ TRACKRENDERER_ENTER_P(this);
+ if (type == kTrackTypeAudio) {
+ pipeline_->SetProperty(Elements::kDrmAudio, "getrights-complete-return",
+ TRUE);
+ } else if (type == kTrackTypeVideo) {
+ pipeline_->SetProperty(Elements::kDrmVideo, "getrights-complete-return",
+ TRUE);
+ } else {
+ TRACKRENDERER_ERROR_P(this, "Invalid Track Type !!");
+ }
+ TRACKRENDERER_LEAVE_P(this);
+}
+
+bool TrackRenderer::SetDisplay(const DisplayType& type, unsigned int surface_id,
+ long x, long y, long w, long h) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_type_ = type;
+ display_->SetDisplay(type, surface_id, x, y, w, h);
+ if (!pipeline_) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->Update(obj);
+ });
+ return true;
+}
+
+bool TrackRenderer::SetDisplayRoi(const Geometry& roi) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_->SetDisplayRoi(roi);
+ if (!pipeline_) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->Update(obj);
+ });
+ return true;
+}
+
+bool TrackRenderer::SetVideoRoi(const CropArea& area) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_->SetDisplayCropArea(area);
+ if (!pipeline_) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->UpdateCropArea(obj);
+ });
+ return true;
+}
+
+bool TrackRenderer::ResizeRenderRect(const RenderRect& rect) {
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_->ResizeRenderRect(rect);
+ if (!pipeline_) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->Update(obj);
+ });
+ return true;
+}
+
+bool TrackRenderer::SetDisplayVisible(bool is_visible) {
+ TRACKRENDERER_DEBUG_P(this, "visible: %d", is_visible);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ if (state_ == State::kStopped) return false;
+ display_->SetVisible(is_visible);
+ if (pipeline_ == nullptr) return true;
+ pipeline_->Execute(Elements::kSinkVideo, [this](GstElement * obj) noexcept {
+ return display_->UpdateVisible(obj);
+ });
+ return true;
+}
+
+bool TrackRenderer::SetAudioMute(bool is_mute) {
+ TRACKRENDERER_DEBUG_P(this, "mute: %d", is_mute);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ is_sound_mute_ = is_mute ? TRUE : FALSE;
+ if (!pipeline_) return true;
+ pipeline_->SetProperty(Elements::kSinkAudio, "mute", is_sound_mute_);
+ return true;
+}
+
+bool TrackRenderer::SetVolume(const int& volume) {
+ TRACKRENDERER_DEBUG_P(this, "volume: %d", volume);
+ if (volume < kVolumeMin || volume > kVolumeMax) {
+ TRACKRENDERER_ERROR_P(this, "volume: %d", volume);
+ return false;
+ }
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ volume_ = volume;
+ if (!pipeline_) return true;
+ SetVolume_();
+ return true;
+}
+
+void TrackRenderer::SetVolume_() {
+ const gchar* sink_name = nullptr;
+ pipeline_->GetProperty(Elements::kSinkAudio, "name", &sink_name);
+ if (sink_name == nullptr) {
+ TRACKRENDERER_WARN_P(this, "no audio sink");
+ return;
+ }
+ if (strstr(sink_name, "mmaudiosink")) { // mmaudiosink or mmaudiosink2
+ pipeline_->SetProperty(Elements::kSinkAudio, "device-volume", volume_);
+ } else if (strstr(sink_name, "alsasink")) {
+ pipeline_->SetProperty(Elements::kSinkAudio, "alsa-volume", volume_);
+ } else if (strstr(sink_name, "pulsesink")) {
+ TRACKRENDERER_DEBUG_P(this, "pulsesink, not set volume");
+ } else {
+ TRACKRENDERER_WARN_P(this, "unknow audio sink name: %s", sink_name);
+ }
+}
+
+void TrackRenderer::CreateTbmBufferManager_() {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(tbmmgr_m_);
+ if (tbm_buffer_manager_ == nullptr) {
+ tbm_buffer_manager_.reset(new TbmBufferManager);
+ }
+}
+
+void TrackRenderer::SetVr360GpuModeSecure_(bool set) {
+ // Fix:: This is adding for Youtube 360 playback, at the beginning playback,
+ // need to set GPU secure mode, when playback is finished, need to set
+ // GPU non secure mode ,kReference only be set for Youtube 360 feature.
+ if (decoded_buffer_type_ == DecodedVideoFrameBufferType::kReference) {
+ TRACKRENDERER_INFO_P(this, "gpu mode: [%s]", set ? "secure" : "non-secure");
+ vr360_->Vr360TzSetGpuMode(set);
+ }
+}
+
+bool TrackRenderer::GetVolume(int* volume) {
+ if (!volume) return false;
+ *volume = volume_;
+ return true;
+}
+
+const char* TrackRenderer::GetDecoderPluginName_(TrackType type,
+ const std::string& mimetype) {
+ if (type == kTrackTypeAudio) {
+ if (resource_manager_->GetAllocatedState(audio_decoder_id_) ==
+ AllocatedState::kSkipped ||
+ resource_manager_->GetAllocatedState(audio_out_id_) ==
+ AllocatedState::kSkipped) {
+ resource_manager_->Dealloc(audio_decoder_id_);
+ resource_manager_->Dealloc(audio_out_id_);
+ return kSkippedResource;
+ }
+ if (product_cfg::GetProductType() == ProductType::kAv) {
+ if (internal::IsTzDecoderElementNecessary(drm_property_, mimetype))
+ return "omx_tz_mmaudiodec";
+ return "omx_mmaudiodec";
+ }
+ const std::string comp_name =
+ resource_manager_->GetComponentName(audio_decoder_id_);
+ if (comp_name.empty()) {
+ TRACKRENDERER_ERROR_P(this, "Not found component name");
+ return nullptr;
+ }
+ auto is_audio_track = [](const Track& item) noexcept->bool {
+ return item.mimetype.find("audio") != std::string::npos;
+ };
+ auto target = find_if(trackinfo_.begin(), trackinfo_.end(), is_audio_track);
+ if (target == trackinfo_.end()) {
+ TRACKRENDERER_ERROR_P(this, "Not found audio track");
+ return nullptr;
+ }
+ TRACKRENDERER_INFO_P(this, "mime type : %s", (target->mimetype).c_str());
+ if (comp_name == kSwDecoderComponentName) {
+ return ::kSwPluginMap.count({target->mimetype, target->version}) > 0
+ ? ::kSwPluginMap.at({target->mimetype, target->version})
+ .c_str()
+ : nullptr;
+ } else {
+ return ::PluginTable.count({comp_name, target->mimetype}) > 0
+ ? ::PluginTable.at({comp_name, target->mimetype}).c_str()
+ : nullptr;
+ }
+
+ } else if (type == kTrackTypeVideo) {
+ if (resource_manager_->GetAllocatedState(video_decoder_id_) ==
+ AllocatedState::kSkipped ||
+ resource_manager_->GetAllocatedState(video_renderer_id_) ==
+ AllocatedState::kSkipped) {
+ resource_manager_->Dealloc(video_decoder_id_);
+ resource_manager_->Dealloc(video_renderer_id_);
+ return kSkippedResource;
+ }
+ const std::string comp_name =
+ resource_manager_->GetComponentName(video_decoder_id_);
+ if (comp_name.empty()) {
+ TRACKRENDERER_ERROR_P(this, "Not found component name");
+ return nullptr;
+ }
+ auto is_video_track = [](const Track& item) noexcept->bool {
+ return item.mimetype.find("video") != std::string::npos;
+ };
+ auto target = find_if(trackinfo_.begin(), trackinfo_.end(), is_video_track);
+ if (target == trackinfo_.end()) {
+ TRACKRENDERER_ERROR_P(this, "Not found video track");
+ return nullptr;
+ }
+ TRACKRENDERER_INFO_P(this, "mime type : %s", (target->mimetype).c_str());
+ if (internal::CheckMpeg4Video(target->mimetype, target->version)) {
+ return ::kMpeg4VidoePluginMap.at({comp_name, target->mimetype}).c_str();
+ }
+ if (comp_name == kSwDecoderComponentName) {
+ return ::kSwPluginMap.count({target->mimetype, target->version}) > 0
+ ? ::kSwPluginMap.at({target->mimetype, target->version})
+ .c_str()
+ : nullptr;
+ } else {
+ return ::PluginTable.count({comp_name, target->mimetype}) > 0
+ ? ::PluginTable.at({comp_name, target->mimetype}).c_str()
+ : nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
+void TrackRenderer::GetDisplay(DisplayType* type, Geometry* area) {
+ display_->GetDisplay(type, area);
+}
+
+void TrackRenderer::GetDisplayMode(DisplayMode* mode) {
+ display_->GetDisplayMode(mode);
+}
+
+void TrackRenderer::GetDisplayRotate(DisplayRotation* rotation) {
+ display_->GetDisplayRotate(rotation);
+}
+
+bool TrackRenderer::RenderVideoFrame() {
+ TRACKRENDERER_ENTER_P(this);
+ if (!is_video_frame_peek_) return false;
+ pipeline_->PadRemoveProbe(kPadProbeVideoPeekBlock);
+ return true;
+}
+
+bool TrackRenderer::SetAiFilter(void* aifilter) {
+ TRACKRENDERER_ENTER_P(this);
+ return AddAiFilter_(aifilter);
+}
+
+void TrackRenderer::SetVideoMidLatencyThreshold(const unsigned int threshold) {
+ TRACKRENDERER_ENTER_P(this);
+ latency_manager_->SetVideoMidLatencyThreshold(threshold);
+}
+
+void TrackRenderer::SetAudioMidLatencyThreshold(const unsigned int threshold) {
+ TRACKRENDERER_ENTER_P(this);
+ latency_manager_->SetAudioMidLatencyThreshold(threshold);
+}
+
+void TrackRenderer::SetVideoHighLatencyThreshold(const unsigned int threshold) {
+ TRACKRENDERER_ENTER_P(this);
+ latency_manager_->SetVideoHighLatencyThreshold(threshold);
+}
+
+void TrackRenderer::SetAudioHighLatencyThreshold(const unsigned int threshold) {
+ TRACKRENDERER_ENTER_P(this);
+ latency_manager_->SetAudioHighLatencyThreshold(threshold);
+}
+
+void TrackRenderer::SetCatchUpSpeed(const CatchUpSpeed& level) {
+ TRACKRENDERER_ENTER_P(this);
+ latency_manager_->SetCatchUpSpeed(level);
+}
+
+void TrackRenderer::GetVideoLatencyStatus(LatencyStatus* status) {
+ TRACKRENDERER_ENTER_P(this);
+ latency_manager_->GetVideoLatencyStatus(status);
+}
+
+void TrackRenderer::GetAudioLatencyStatus(LatencyStatus* status) {
+ TRACKRENDERER_ENTER_P(this);
+ latency_manager_->GetAudioLatencyStatus(status);
+}
+
+void TrackRenderer::SetAttribute_(const TrackRendererAttributeBinder& binder,
+ const boost::any& original_value,
+ const boost::any& new_value) {
+ TrackRendererAttributeSetter commiiter(pipeline_, binder);
+ bool is_set = binder.on_attribute_set(commiiter, original_value, new_value);
+ TRACKRENDERER_DEBUG_P(this, "a property(prop[%s] of elem[%d]) is_set[%d]",
+ binder.property.c_str(),
+ static_cast<int>(binder.element), is_set);
+}
+
+void TrackRenderer::SetAttribute(const Attribute& attr,
+ const boost::any& value) {
+ const TrackRendererAttributeBinder& binder = kAttributes_.at(attr);
+ if (binder.value_type != value.type()) {
+ TRACKRENDERER_ERROR_P(this,
+ "a property(prop[%s] of elem[%d]) type mismatched! "
+ "it wasn't able to be applied.",
+ binder.property.c_str(),
+ static_cast<int>(binder.element));
+ return;
+ }
+ SetAttribute_(binder, set_attribute_values_[attr], value);
+ set_attribute_values_[attr].value = value;
+ set_attribute_values_[attr].value_assigned = true;
+}
+
+void TrackRenderer::GetAttribute(const Attribute& attr, boost::any* value) {
+ if (state_ > State::kWorking) {
+ return;
+ }
+ const TrackRendererAttributeBinder& binder = kAttributes_.at(attr);
+ TrackRendererAttributeGetter commiiter(pipeline_, binder);
+ bool is_get = binder.on_attribute_get(commiiter, value);
+ if (!is_get)
+ TRACKRENDERER_ERROR_P(this, "property(prop[%s] of elem[%d]) get failed!!!",
+ binder.property.c_str(),
+ static_cast<int>(binder.element));
+}
+
+void TrackRenderer::SetConfig(const std::string name, const boost::any& value) {
+ if (config_setter_table_.count(name) > 0) {
+ Config_Setter setter = config_setter_table_.at(name);
+ setter(value);
+ } else {
+ TRACKRENDERER_ERROR_P(this,
+ "No name of setter [%s], if needed, assign setter in "
+ "config_setter_table & init",
+ name.c_str());
+ }
+}
+
+void TrackRenderer::InitConfigSetterTable_() {
+ config_setter_table_[ConfigNameAccurateSeekMode] = std::bind(
+ &TrackRenderer::SetAccurateSeekMode_, this, std::placeholders::_1);
+ config_setter_table_[ConfigNameLowLatencyMode] = std::bind(
+ &TrackRenderer::SetLowLatencyMode_, this, std::placeholders::_1);
+ config_setter_table_[ConfigNameWindowStandAloneMode] = std::bind(
+ &TrackRenderer::SetWindowStandAloneMode_, this, std::placeholders::_1);
+ config_setter_table_[ConfigNameVideoFramePeekMode] = std::bind(
+ &TrackRenderer::SetVideoFramePeekMode_, this, std::placeholders::_1);
+ config_setter_table_[ConfigNameUnlimitedMaxBufferMode] = std::bind(
+ &TrackRenderer::SetUnlimitedMaxBufferMode_, this, std::placeholders::_1);
+ config_setter_table_[ConfigNameVideoPreDisplayMode] = std::bind(
+ &TrackRenderer::SetVideoPreDisplayMode_, this, std::placeholders::_1);
+ config_setter_table_[ConfigNameStartRenderingTime] = std::bind(
+ &TrackRenderer::SetStartRenderingTime_, this, std::placeholders::_1);
+ config_setter_table_[ConfigNameFmmMode] =
+ std::bind(&TrackRenderer::SetFmmMode_, this, std::placeholders::_1);
+ config_setter_table_[ConfigNameAlternativeVideoResource] =
+ std::bind(&TrackRenderer::SetAlternativeVideoResource_, this,
+ std::placeholders::_1);
+ config_setter_table_[ConfigNameVideoDecodingMode] = std::bind(
+ &TrackRenderer::SetVideoDecodingMode_, this, std::placeholders::_1);
+ config_setter_table_[ConfigNameLateVideoFrameDropMode] = std::bind(
+ &TrackRenderer::SetLateVideoFrameDropMode_, this, std::placeholders::_1);
+}
+
+// LCOV_EXCL_START
+bool TrackRenderer::SetAccurateSeekMode_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " Mode [%s]", (val != 0) ? "TRUE" : "FALSE");
+ is_accurate_seek_ = (val != 0) ? true : false;
+ return true;
+}
+// LCOV_EXCL_STOP
+
+bool TrackRenderer::SetLowLatencyMode_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " Mode [%d]", val);
+ low_latency_mode_ = val;
+ if (low_latency_mode_ & kLowLatencyModeDisableAVSync)
+ unlimited_max_buffer_mode_ = true;
+ return true;
+}
+
+// LCOV_EXCL_START
+bool TrackRenderer::SetWindowStandAloneMode_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " Mode [%s]", (val != 0) ? "TRUE" : "FALSE");
+ window_stand_alone_mode_ = (val != 0) ? TRUE : FALSE;
+ return true;
+}
+// LCOV_EXCL_STOP
+
+bool TrackRenderer::SetVideoFramePeekMode_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " Mode [%s]", (val != 0) ? "TRUE" : "FALSE");
+ is_video_frame_peek_ = (val != 0) ? true : false;
+ return true;
+}
+
+// LCOV_EXCL_START
+bool TrackRenderer::SetUnlimitedMaxBufferMode_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " Mode [%s]", (val != 0) ? "TRUE" : "FALSE");
+ unlimited_max_buffer_mode_ = (val != 0) ? true : false;
+ return true;
+}
+
+bool TrackRenderer::SetVideoPreDisplayMode_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " Mode [%s]", (val != 0) ? "TRUE" : "FALSE");
+ video_pre_display_mode_ = (val != 0) ? true : false;
+ return true;
+}
+// LCOV_EXCL_STOP
+
+bool TrackRenderer::SetStartRenderingTime_(const boost::any& value) {
+ std::uint64_t val = boost::any_cast<std::uint64_t>(value);
+ TRACKRENDERER_ERROR_P(this, "time [%llu ms]", val);
+ rendering_start_time_.is_set = true;
+ rendering_start_time_.time = val * GST_MSECOND;
+ return true;
+}
+
+// LCOV_EXCL_START
+bool TrackRenderer::SetFmmMode_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " Mode [%s]", (val != 0) ? "TRUE" : "FALSE");
+ fmm_mode_ = (val != 0) ? 1 : 0;
+ return true;
+}
+
+bool TrackRenderer::SetAlternativeVideoResource_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " Alternative video resource [%d]", val);
+ if (val == 0) {
+ video_decoder_id_ = ResourceCategory::kVideoDecoder;
+ video_renderer_id_ = ResourceCategory::kVideoRenderer;
+ } else if (val == 1) {
+ video_decoder_id_ = ResourceCategory::kVideoDecoderSub;
+ video_renderer_id_ = ResourceCategory::kVideoRendererSub;
+ } else if (val == 2) {
+ video_decoder_id_ = ResourceCategory::kVideoDecoderSub;
+ video_renderer_id_ = ResourceCategory::kVideoRenderer;
+ } else if (val == 3) {
+ video_decoder_id_ = ResourceCategory::kVideoDecoder;
+ video_renderer_id_ = ResourceCategory::kVideoRendererSub;
+ } else
+ return false;
+ return true;
+}
+
+bool TrackRenderer::SetVideoDecodingMode_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, "Video decoding mode [%u]", val);
+ video_decoding_mode_ = val;
+ return true;
+}
+
+bool TrackRenderer::SetLateVideoFrameDropMode_(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " LateVideoFrameDropMode [%s]",
+ (val != 0) ? "TRUE" : "FALSE");
+ drop_all_late_video_ = (val != 0) ? true : false;
+ return true;
+}
+// LCOV_EXCL_STOP
+
+void TrackRenderer::GstElementLowLatency_(const TrackType& type) {
+ if (low_latency_mode_ ==
+ static_cast<std::uint32_t>(LowLatencyMode::kLowLatencyModeNone))
+ return;
+
+ switch (type) {
+ case kTrackTypeAudio:
+ if (low_latency_mode_ & kLowLatencyModeAudio) {
+ constexpr gint kDecodingType = 0x01;
+ constexpr gint kDeviceLatency = 0;
+ pipeline_->SetProperty(Elements::kDecAudio, "decoding-type",
+ kDecodingType);
+ pipeline_->SetProperty(Elements::kSinkAudio, "device-latency",
+ kDeviceLatency);
+ }
+ if (low_latency_mode_ & kLowLatencyModeDisableAVSync) {
+ constexpr guint64 kLowLatencyMaxBytes = 200;
+ constexpr guint64 kFastRendering = 0;
+ pipeline_->SetProperty(Elements::kAppSrcAudio, "max-bytes",
+ kLowLatencyMaxBytes, "noqueue", TRUE);
+ pipeline_->SetProperty(Elements::kSinkAudio, "sync", FALSE, "async",
+ FALSE);
+ pipeline_->SetProperty(Elements::kSinkAudio, "fast-rendering",
+ kFastRendering);
+ pipeline_->SetProperty(Elements::kSinkAudio, "game-mode", TRUE);
+ pipeline_->SetProperty(Elements::kSinkAudio, "free-run", TRUE);
+ }
+ if (low_latency_mode_ & kLowLatencyModeDisablePreroll) {
+ pipeline_->SetProperty(Elements::kSinkAudio, "async", FALSE);
+ }
+ break;
+ case kTrackTypeVideo:
+ if (low_latency_mode_ & kLowLatencyModeVideo) {
+ constexpr gint kDecodingType = 0x01;
+ pipeline_->SetProperty(Elements::kDecVideo, "decoding-type",
+ kDecodingType);
+
+ if ((low_latency_mode_ & kLowLatencyModeVideoDistortionConcealment) ==
+ kLowLatencyModeVideoDistortionConcealment)
+ pipeline_->SetProperty(Elements::kDecVideo, "error-concealment",
+ TRUE);
+ }
+ if (low_latency_mode_ & kLowLatencyModeDisableAVSync) {
+ pipeline_->SetProperty(Elements::kSinkVideo, "sync", FALSE, "async",
+ FALSE);
+ }
+ if (low_latency_mode_ & kLowLatencyModeDisableVideoQuality) {
+ constexpr uint32_t kDisableVideoQuality = 0x04;
+ display_->SetVideoQualityMode(kDisableVideoQuality);
+ }
+ if (low_latency_mode_ & kLowLatencyModeDisablePreroll) {
+ pipeline_->SetProperty(Elements::kSinkVideo, "async", FALSE);
+ }
+ break;
+ case kTrackTypeSubtitle:
+ if (low_latency_mode_ & kLowLatencyModeDisableAVSync) {
+ pipeline_->SetProperty(Elements::kSinkCaption, "sync", FALSE, "async",
+ FALSE);
+ pipeline_->SetProperty(Elements::kSinkSubtitle, "sync", FALSE, "async",
+ FALSE);
+ }
+ if (low_latency_mode_ & kLowLatencyModeDisablePreroll) {
+ pipeline_->SetProperty(Elements::kSinkSubtitle, "async", FALSE);
+ }
+ break;
+ default:
+ TRACKRENDERER_ERROR_P(this, "wrong track type");
+ }
+}
+
+// LCOV_EXCL_START
+void TrackRenderer::GetResolutionInfo_(EventMsg* event_msg) {
+ if (!pipeline_) return;
+ auto video_sink_caps =
+ gstguard::make_guard(pipeline_->GetSinkPadCaps(Elements::kSinkVideo));
+
+ std::string info;
+ int width = internal::GetVideoWidth(video_sink_caps.get());
+ int height = internal::GetVideoHeight(video_sink_caps.get());
+
+ if (width > 0 && height > 0) {
+ info = std::to_string(width) + "x" + std::to_string(height);
+ }
+ event_msg->data = info;
+ event_msg->len = event_msg->data.length();
+}
+// LCOV_EXCL_STOP
+
+void TrackRenderer::UpdatePlaybackInfo_(bool is_updating) {
+ if (!pipeline_ || !playback_info_) return;
+ auto video_sink_caps =
+ gstguard::make_guard(pipeline_->GetSinkPadCaps(Elements::kSinkVideo));
+ auto video_decoder_caps =
+ gstguard::make_guard(pipeline_->GetSinkPadCaps(Elements::kDecVideo));
+ auto audio_decoder_caps =
+ gstguard::make_guard(pipeline_->GetSinkPadCaps(Elements::kDecAudio));
+ if (!playback_info_->SetStreamInfo(
+ video_sink_caps.get(), video_decoder_caps.get(),
+ audio_decoder_caps.get(), trackinfo_, drm_property_.type))
+ return;
+ if (is_updating)
+ playback_info_->VconfSetMsgUpdate();
+ else
+ playback_info_->VconfSetMsgShow();
+}
+
+// LCOV_EXCL_START
+GstPadProbeReturn TrackRenderer::GstSrcPadProbeBlockCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata) {
+ TRACKRENDERER_ENTER_P(userdata);
+
+ return GST_PAD_PROBE_OK;
+}
+// LCOV_EXCL_STOP
+
+GstPadProbeReturn TrackRenderer::GstSrcPadProbeIdleCb_(GstPad* pad,
+ GstPadProbeInfo* info,
+ gpointer userdata) {
+ TRACKRENDERER_ENTER_P(userdata);
+ auto gstpad = static_cast<Pipeline<Elements>::Pad*>(userdata);
+ std::unique_lock<std::mutex> pad_block_locker(gstpad->m);
+ gstpad->is_idle = true;
+ gstpad->cv.notify_one();
+ TRACKRENDERER_LEAVE_P(userdata)
+ return GST_PAD_PROBE_DROP;
+}
+
+void TrackRenderer::FlushDownStream_(Elements element, const char* key,
+ gboolean reset_time) {
+ TRACKRENDERER_ENTER_P(this);
+ if (!pipeline_) return;
+
+ Pipeline<Elements>::Pad pad;
+
+ pipeline_->PadAddProbe(element, key, "src", GST_PAD_PROBE_TYPE_IDLE,
+ GstSrcPadProbeIdleCb_, &pad, nullptr);
+
+ if (pipeline_->FlushDownStream(element, reset_time)) {
+ std::unique_lock<std::mutex> pad_block_locker(pad.m);
+ if (!pad.is_idle) {
+ pad.cv.wait(pad_block_locker);
+ TRACKRENDERER_INFO_P(this, "pad block wait end");
+ }
+ }
+ pipeline_->PadRemoveProbe(key);
+ TRACKRENDERER_LEAVE_P(this);
+}
+
+// LCOV_EXCL_START
+bool TrackRenderer::ActivateAudio_() {
+ TRACKRENDERER_ENTER_P(this)
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (state_ == State::kResourceConflicted) {
+ TRACKRENDERER_WARN_P(this, "Resource conflict!");
+ return false;
+ }
+ if (!pipeline_) return false;
+ if (is_audioactivated_) {
+ TRACKRENDERER_WARN_P(this, "Audio has been activated!");
+ return false;
+ }
+
+ Track track;
+ if (!track_util::GetActiveTrack(trackinfo_, kTrackTypeAudio, &track)) {
+ TRACKRENDERER_ERROR_P(this, "Failed to find active audio track.");
+ return false;
+ }
+
+ if (!GetResource_(kTrackTypeAudio)) {
+ TRACKRENDERER_ERROR_P(this,
+ "Audio resource allocation fail! ChangeTrack fail.");
+ return false;
+ }
+
+ const char* kDecoderPluginName = nullptr;
+ if (internal::IsDecoderElementNecessary(track.mimetype)) {
+ kDecoderPluginName = GetDecoderPluginName_(kTrackTypeAudio, track.mimetype);
+ if (nullptr == kDecoderPluginName) {
+ TRACKRENDERER_ERROR_P(this, "Audio decoder plugin name is nullptr");
+ return false;
+ } else if (strcmp(kDecoderPluginName, kSkippedResource) == 0) {
+ TRACKRENDERER_ERROR_P(this, "The audio resource is NOT_PERMITTED.");
+ return false;
+ }
+ }
+
+ Elements element = Elements::kAppSrcAudio;
+ if (internal::IsDrmEmeElementNecessary(drm_property_, track.mimetype)) {
+ element = Elements::kDrmAudio;
+ }
+
+ pipeline_->PadAddProbe(element, kPadProbeAudioBlock, "src",
+ GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ GstSrcPadProbeBlockCb_, nullptr, nullptr);
+ pipeline_->SetProperty(Elements::kSinkAudio, "async", FALSE);
+ FlushDownStream_(element, kPadProbeAudioIdle, FALSE);
+ pipeline_->BinRemove(Elements::kBinAudio, Elements::kSinkAudio);
+
+ if (internal::IsDecoderElementNecessary(track.mimetype)) {
+ pipeline_->FactoryMake(Elements::kDecAudio, kDecoderPluginName, NULL);
+ pipeline_->SetProperty(Elements::kDecAudio, "support-codec-change",
+ support_audio_codec_change_);
+ if (low_latency_mode_ ==
+ static_cast<std::uint32_t>(LowLatencyMode::kLowLatencyModeNone)) {
+ constexpr int kPtsManipulationThreshold = 99000; // 99ms
+ pipeline_->SetProperty(Elements::kDecAudio, "set-usr-cal-timestamp",
+ kPtsManipulationThreshold);
+ }
+ }
+ std::string audiosink_name =
+ GetAudioSinkPluginName_(track.use_swdecoder, track.mimetype);
+ CreateAudioSink_(audiosink_name);
+ GstElementLowLatency_(kTrackTypeAudio);
+
+ gboolean is_async = FALSE;
+ pipeline_->GetProperty(Elements::kSinkAudio, "async", &is_async);
+ if (is_async) pipeline_->SetProperty(Elements::kSinkAudio, "async", FALSE);
+ pipeline_->BinAdd(Elements::kBinAudio, Elements::kDecAudio,
+ Elements::kAudioConvert, Elements::kCapsFillterDefault,
+ Elements::kAudioResample, Elements::kCapsFillter2,
+ Elements::kScaleTempo, Elements::kSinkAudio);
+ if (is_async) pipeline_->SetProperty(Elements::kSinkAudio, "async", TRUE);
+ pipeline_->PadRemoveProbe(kPadProbeAudioBlock);
+
+ AvocPlayRequest_();
+ SetAudioOut_();
+
+ is_audioactivated_ = true;
+ TRACKRENDERER_LEAVE_P(this)
+ return true;
+}
+
+bool TrackRenderer::DeactivateAudio_() {
+ TRACKRENDERER_ENTER_P(this)
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (state_ == State::kResourceConflicted) {
+ TRACKRENDERER_WARN_P(this, "Resource conflict!");
+ return false;
+ }
+ if (!pipeline_) return false;
+ if (!is_audioactivated_) {
+ TRACKRENDERER_WARN_P(this, "Audio has been deactivated!");
+ return false;
+ }
+
+#ifndef SOUNDBAR_PRODUCT
+ int result = avoc_player_disconnect(avoc_id_);
+ TRACKRENDERER_INFO_P(this, "avoc_player_disconnect() return value[%d]",
+ result);
+#endif
+
+ Track track;
+ if (!track_util::GetActiveTrack(trackinfo_, kTrackTypeAudio, &track)) {
+ TRACKRENDERER_ERROR_P(this, "Failed to find active audio track.");
+ return false;
+ }
+ Elements element = Elements::kAppSrcAudio;
+ if (internal::IsDrmEmeElementNecessary(drm_property_, track.mimetype)) {
+ element = Elements::kDrmAudio;
+ }
+ pipeline_->PadAddProbe(element, kPadProbeAudioBlock, "src",
+ GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ GstSrcPadProbeBlockCb_, nullptr, nullptr);
+ pipeline_->SetProperty(Elements::kSinkAudio, "async", FALSE);
+ FlushDownStream_(element, kPadProbeAudioIdle, FALSE);
+ pipeline_->BinRemove(Elements::kBinAudio, Elements::kDecAudio,
+ Elements::kAudioConvert, Elements::kCapsFillterDefault,
+ Elements::kAudioResample, Elements::kCapsFillter2,
+ Elements::kScaleTempo, Elements::kSinkAudio);
+
+ std::string sink_name("fakesink");
+ if (internal::IsTzMimeType(track.mimetype)) {
+ sink_name = "tzfakesink";
+ }
+ CreateAudioSink_(sink_name);
+ GstElementLowLatency_(kTrackTypeAudio);
+
+ gboolean is_async = FALSE;
+ pipeline_->GetProperty(Elements::kSinkAudio, "async", &is_async);
+ if (is_async) pipeline_->SetProperty(Elements::kSinkAudio, "async", FALSE);
+ pipeline_->BinAdd(Elements::kBinAudio, Elements::kSinkAudio);
+ if (is_async) pipeline_->SetProperty(Elements::kSinkAudio, "async", TRUE);
+ pipeline_->PadRemoveProbe(kPadProbeAudioBlock);
+
+ resource_manager_->Dealloc(audio_decoder_id_);
+ resource_manager_->Dealloc(audio_out_id_);
+ is_audioactivated_ = false;
+ TRACKRENDERER_LEAVE_P(this)
+ return true;
+}
+
+void TrackRenderer::MultiviewStartAudioCb_(int player_id, void* data) {
+ TRACKRENDERER_ENTER;
+ if (data == nullptr) return;
+ auto tr = static_cast<TrackRenderer*>(data);
+ TRACKRENDERER_INFO_P(tr, "player_id: %d, avoc id: %d", player_id,
+ tr->avoc_id_);
+ if (player_id == tr->avoc_id_) {
+ tr->is_multiscreen_ = Vconf::Instance().IsMultiscreenMode();
+ tr->ActivateAudio_();
+ TRACKRENDERER_INFO_P(tr, "Send audio pipeline start done event!");
+ rc_player_audio_start_done(player_id);
+ }
+ TRACKRENDERER_LEAVE_P(tr)
+}
+
+void TrackRenderer::MultiviewStopAudioCb_(int player_id, void* data) {
+ TRACKRENDERER_ENTER
+ if (data == nullptr) return;
+ auto tr = static_cast<TrackRenderer*>(data);
+ TRACKRENDERER_INFO_P(tr, "player_id: %d, avoc id: %d", player_id,
+ tr->avoc_id_);
+ if (player_id == tr->avoc_id_) {
+ tr->DeactivateAudio_();
+ rc_player_audio_stop_done(player_id);
+ TRACKRENDERER_INFO_P(tr, "Send audio pipeline stop done event!");
+ }
+ TRACKRENDERER_LEAVE_P(tr)
+}
+
+void TrackRenderer::MultiviewResyncAudioCb_(int player_id, void* data) {
+ TRACKRENDERER_ENTER
+ if (data == nullptr) return;
+ auto tr = static_cast<TrackRenderer*>(data);
+ TRACKRENDERER_INFO_P(tr, "player_id: %d, avoc id: %d", player_id,
+ tr->avoc_id_);
+ if (player_id != tr->avoc_id_) return;
+ tr->ResyncAudio_();
+ TRACKRENDERER_LEAVE_P(tr)
+}
+
+bool TrackRenderer::ResyncAudio_() {
+ TRACKRENDERER_ENTER_P(this)
+ // internal_audio_m_ is needed to prevent state-change, audio pipeline
+ // manipulation, set volume and audio mute in progress of ResyncAudio_()
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return false;
+ if (state_ == State::kResourceConflicted) {
+ TRACKRENDERER_ERROR_P(this, "Resource conflict!");
+ return false;
+ }
+ if (!pipeline_) return false;
+ AvocPlayRequest_();
+ resync_audio_policy_->Resync(pipeline_);
+#ifdef SOUNDBAR_PRODUCT
+ if (!is_sound_mute_) avoc_av_audio_mute(avoc_id_, AVOC_SETTING_OFF);
+#else
+ if (!is_sound_mute_) avoc_audio_mute(avoc_id_, AVOC_SETTING_OFF);
+#endif
+ return true;
+} // namespace trackrenderer
+
+void TrackRenderer::MultiviewStartVideoCb_(int player_id, void* data) {
+ TRACKRENDERER_ENTER;
+ if (data == nullptr) return;
+ auto tr = static_cast<TrackRenderer*>(data);
+ TRACKRENDERER_INFO_P(tr, "player_id: %d, avoc id: %d", player_id,
+ tr->avoc_id_);
+ if (player_id != tr->avoc_id_) return;
+ BOOST_SCOPE_EXIT(&player_id) { rc_player_video_start_done(player_id); }
+ BOOST_SCOPE_EXIT_END
+ if (tr->state_ == State::kStopped ||
+ tr->state_ == State::kResourceConflicted) {
+ TRACKRENDERER_WARN_P(tr, "player state: %d", static_cast<int>(tr->state_));
+ return;
+ }
+ if (!tr->pipeline_ || tr->is_videoactivated_) {
+ TRACKRENDERER_WARN_P(tr, "is_videoactivated_: %d", tr->is_videoactivated_);
+ return;
+ }
+ tr->is_multiscreen_ = Vconf::Instance().IsMultiscreenMode();
+ tr->eventlistener_->OnMultiviewStartVideo();
+ tr->is_videoactivated_ = true;
+ TRACKRENDERER_LEAVE_P(tr)
+}
+
+void TrackRenderer::MultiviewStopVideoCb_(int player_id, void* data) {
+ TRACKRENDERER_ENTER
+ if (data == nullptr) return;
+ auto tr = static_cast<TrackRenderer*>(data);
+ TRACKRENDERER_INFO_P(tr, "player_id: %d, avoc id: %d", player_id,
+ tr->avoc_id_);
+ if (player_id != tr->avoc_id_) return;
+ BOOST_SCOPE_EXIT(&player_id) { rc_player_video_stop_done(player_id); }
+ BOOST_SCOPE_EXIT_END
+ if (tr->state_ == State::kStopped ||
+ tr->state_ == State::kResourceConflicted) {
+ TRACKRENDERER_WARN_P(tr, "player state: %d", static_cast<int>(tr->state_));
+ return;
+ }
+ if (!tr->pipeline_ || !tr->is_videoactivated_) {
+ TRACKRENDERER_WARN_P(tr, "is_videoactivated_: %d", tr->is_videoactivated_);
+ return;
+ }
+ tr->eventlistener_->OnMultiviewStopVideo();
+ tr->is_videoactivated_ = false;
+ TRACKRENDERER_LEAVE_P(tr)
+}
+// LCOV_EXCL_STOP
+
+void TrackRenderer::SetVconfCb_() {
+ TRACKRENDERER_ENTER_P(this)
+ std::vector<std::string> names;
+ names.push_back(kPowerAnimationVconf);
+
+ Vconf::Instance().SetVconfsCb(names, TrackRenderer::VconfCb_, this);
+ is_multiscreen_ = Vconf::Instance().IsMultiscreenMode();
+ TRACKRENDERER_LEAVE_P(this)
+}
+
+void TrackRenderer::UnsetVconfCb_() {
+ TRACKRENDERER_ENTER_P(this)
+ std::vector<std::string> names;
+ names.push_back(kPowerAnimationVconf);
+
+ Vconf::Instance().UnsetVconfsCb(names, TrackRenderer::VconfCb_, this);
+ TRACKRENDERER_LEAVE_P(this)
+}
+
+// LCOV_EXCL_START
+void TrackRenderer::VconfCb_(const std::string& name, const std::string& value,
+ void* userdata) {
+ TRACKRENDERER_ENTER
+ if (userdata == nullptr) return;
+ auto tr = static_cast<TrackRenderer*>(userdata);
+ if (name == kPowerAnimationVconf) {
+ std::lock_guard<std::mutex> lk(tr->resource_m_);
+ if (tr->state_ == State::kStopped) return;
+ if (tr->state_ == State::kResourceConflicted) {
+ TRACKRENDERER_WARN_P(tr, "Resource conflict!");
+ return;
+ }
+ if (!tr->pipeline_) return;
+ gboolean is_mute = (value == "true") ? TRUE : FALSE;
+ tr->pipeline_->SetProperty(Elements::kSinkAudio, "mute", is_mute);
+ TRACKRENDERER_WARN_P(tr, "[%s] audio!", is_mute ? "Mute" : "Unmute");
+ } else {
+ TRACKRENDERER_ERROR_P(tr, "Unknow vconf name: %s", name.c_str());
+ }
+
+ TRACKRENDERER_LEAVE_P(tr)
+}
+// LCOV_EXCL_STOP
+
+bool TrackRenderer::SetResourceCenterCallback_() {
+ TRACKRENDERER_ENTER_P(this)
+
+ audio_start_cb_id_ = rc_register_player_audio_start_request_callback(
+ MultiviewStartAudioCb_, this);
+ audio_stop_cb_id_ = rc_register_player_audio_stop_request_callback(
+ MultiviewStopAudioCb_, this);
+ audio_resync_cb_id_ = rc_register_player_audio_resync_request_callback(
+ MultiviewResyncAudioCb_, this);
+
+ video_start_cb_id_ = rc_register_player_video_start_request_callback(
+ MultiviewStartVideoCb_, this);
+ video_stop_cb_id_ = rc_register_player_video_stop_request_callback(
+ MultiviewStopVideoCb_, this);
+
+ bool ret = true;
+ if (audio_start_cb_id_ == 0 || audio_stop_cb_id_ == 0 ||
+ audio_resync_cb_id_ == 0) {
+ TRACKRENDERER_ERROR_P(
+ this, "register audio request callback failed: %d, %d, %d",
+ audio_start_cb_id_, audio_stop_cb_id_, audio_resync_cb_id_);
+ ret = false;
+ }
+ if (video_start_cb_id_ == 0 || video_stop_cb_id_ == 0) {
+ TRACKRENDERER_ERROR_P(this,
+ "register video request callback failed: %d, %d",
+ video_start_cb_id_, video_stop_cb_id_);
+ ret = false;
+ }
+ TRACKRENDERER_LEAVE_P(this)
+ return ret;
+}
+
+bool TrackRenderer::UnsetResourceCenterCallback_() {
+ TRACKRENDERER_ENTER_P(this)
+
+ auto audio_start_cb_ret =
+ rc_unregister_player_audio_start_request_callback(audio_start_cb_id_);
+ auto audio_stop_cb_ret =
+ rc_unregister_player_audio_stop_request_callback(audio_stop_cb_id_);
+ auto audio_resync_cb_ret =
+ rc_unregister_player_audio_resync_request_callback(audio_resync_cb_id_);
+
+ auto video_start_cb_ret =
+ rc_unregister_player_video_start_request_callback(video_start_cb_id_);
+ auto video_stop_cb_ret =
+ rc_unregister_player_video_stop_request_callback(video_stop_cb_id_);
+
+ bool ret = true;
+ if (audio_start_cb_ret != 0 || audio_stop_cb_ret != 0 ||
+ audio_resync_cb_ret != 0) {
+ TRACKRENDERER_ERROR_P(
+ this, "unregister audio request callback failed: %d, %d, %d",
+ audio_start_cb_ret, audio_stop_cb_ret, audio_resync_cb_ret);
+ ret = false;
+ }
+ if (video_start_cb_ret != 0 || video_stop_cb_ret != 0) {
+ TRACKRENDERER_ERROR_P(this,
+ "unregister video request callback failed: %d, %d",
+ video_start_cb_ret, video_stop_cb_ret);
+ ret = false;
+ }
+ return ret;
+ TRACKRENDERER_LEAVE_P(this)
+}
+
+// LCOV_EXCL_START
+void TrackRenderer::SetAlternativeAudioResource(const boost::any& value) {
+ std::uint32_t val = boost::any_cast<std::uint32_t>(value);
+ TRACKRENDERER_ERROR_P(this, " Alternative audio resource [%s]",
+ (val != 0) ? "TRUE" : "FALSE");
+ if (val != 0) {
+ audio_decoder_id_ = ResourceCategory::kAudioDecoderSub;
+ audio_out_id_ = ResourceCategory::kAudioRendererSub;
+ } else {
+ audio_decoder_id_ = ResourceCategory::kAudioDecoder;
+ audio_out_id_ = ResourceCategory::kAudioRenderer;
+ }
+}
+// LCOV_EXCL_STOP
+
+bool TrackRenderer::InitAudioEasingInfo(const uint32_t& init_volume,
+ const uint32_t& init_elapsed_time,
+ const AudioEasingInfo& info) {
+ TRACKRENDERER_ENTER_P(this)
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (audio_easing_controller_) {
+ TRACKRENDERER_ERROR_P(this,
+ "audio easing controller is already Initialized");
+ return false;
+ }
+
+ if (init_volume > kVolumeMax || info.target_volume > kVolumeMax) {
+ TRACKRENDERER_ERROR_P(this, "volume: %d", info.target_volume);
+ return false;
+ }
+ audio_easing_controller_.reset(
+ new AudioEasingController(init_volume, init_elapsed_time, info));
+
+ if (info.duration == 0)
+ volume_ = audio_easing_controller_->GetGainFromVolume(info.target_volume);
+ else
+ volume_ = audio_easing_controller_->GetGainFromVolume(init_volume);
+ if (pipeline_) SetVolume_();
+
+ TRACKRENDERER_LEAVE_P(this)
+ return true;
+}
+
+bool TrackRenderer::UpdateAudioEasingInfo(const AudioEasingInfo& info) {
+ TRACKRENDERER_ENTER_P(this)
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (!audio_easing_controller_) {
+ TRACKRENDERER_ERROR_P(this, "audio easing controller is not exist");
+ return false;
+ }
+ if (info.target_volume > kVolumeMax) {
+ TRACKRENDERER_ERROR_P(this, "volume: %d", info.target_volume);
+ return false;
+ }
+ auto ret = audio_easing_controller_->SetInfo(info);
+ if (!ret) return false;
+
+ if (info.duration == 0) {
+ volume_ = audio_easing_controller_->GetGainFromVolume(info.target_volume);
+ if (pipeline_) SetVolume_();
+ }
+ TRACKRENDERER_LEAVE_P(this)
+ return true;
+}
+
+bool TrackRenderer::GetAudioEasingInfo(uint32_t* current_volume,
+ uint32_t* elapsed_time,
+ AudioEasingInfo* info) {
+ TRACKRENDERER_ENTER_P(this)
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (!audio_easing_controller_) {
+ TRACKRENDERER_ERROR_P(this, "audio easing controller is not exist");
+ return false;
+ }
+ auto ret =
+ audio_easing_controller_->GetInfo(current_volume, elapsed_time, info);
+ TRACKRENDERER_LEAVE_P(this)
+ return ret;
+}
+
+bool TrackRenderer::StartAudioEasing() {
+ TRACKRENDERER_ENTER_P(this)
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (!audio_easing_controller_) {
+ TRACKRENDERER_ERROR_P(this, "audio easing controller is not exist");
+ return false;
+ }
+ if (state_ == State::kStopped) return false;
+ if (!pipeline_) return false;
+
+ pipeline_->Execute(Elements::kSinkAudio, [this](GstElement * obj) noexcept {
+ return audio_easing_controller_->AudioEasingStart(obj);
+ });
+ return true;
+}
+
+bool TrackRenderer::StopAudioEasing() {
+ TRACKRENDERER_ENTER_P(this)
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ return StopAudioEasing_();
+}
+
+bool TrackRenderer::StopAudioEasing_() {
+ TRACKRENDERER_ENTER_P(this)
+ if (!audio_easing_controller_) {
+ TRACKRENDERER_ERROR_P(this, "audio easing controller is not exist");
+ return false;
+ }
+ audio_easing_controller_->AudioEasingStop();
+ return true;
+}
+
+bool TrackRenderer::GetVirtualRscId(const RscType type, int* virtual_id) {
+ if (virtual_id == nullptr) return false;
+ if (type != RscType::kVideoRenderer || virtual_scaler_id_ == -1) {
+ *virtual_id = -1;
+ return false;
+ }
+ *virtual_id = virtual_scaler_id_;
+ return true;
+}
+
+// LCOV_EXCL_START
+bool TrackRenderer::NeedSyncPause_() {
+ if (!support_videodec_underflow_pause_) {
+ return false;
+ }
+ TRACKRENDERER_WARN_P(this, "sync pause");
+ pipeline_->PadRemoveProbe(kPadProbeVideoDecInputBlock);
+ pipeline_->PadAddProbe(Elements::kDecVideo, kPadProbeVideoDecInputBlock,
+ "sink", GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+ GstPadProbeVideoDecInputCb_, this, nullptr);
+
+ gboolean is_async_video = FALSE, is_async_audio = FALSE;
+ pipeline_->GetProperty(Elements::kSinkVideo, "async", &is_async_video);
+ if (is_async_video)
+ pipeline_->SetProperty(Elements::kSinkVideo, "async", FALSE);
+ pipeline_->GetProperty(Elements::kSinkAudio, "async", &is_async_audio);
+ if (is_async_audio)
+ pipeline_->SetProperty(Elements::kSinkAudio, "async", FALSE);
+ pipeline_->SetState(Elements::kPipeline, GST_STATE_PAUSED);
+ if (is_async_video)
+ pipeline_->SetProperty(Elements::kSinkVideo, "async", TRUE);
+ if (is_async_audio)
+ pipeline_->SetProperty(Elements::kSinkAudio, "async", TRUE);
+ return true;
+}
+// LCOV_EXCL_STOP
+
+void TrackRenderer::SetAudioOut_() {
+ TRACKRENDERER_ENTER_P(this)
+ if (!pipeline_) return;
+
+ /* need to call this api after avoc_play_request_m() */
+ rc_player_audio_out_e audio_out;
+ rc_get_player_audio_out(avoc_id_, &audio_out);
+ TRACKRENDERER_INFO_P(this, "avoc_id_ : %d , rc_audio_out [%d]", avoc_id_,
+ audio_out);
+
+ int is_audio_sub_out_path = (audio_out == RC_PLAYER_AUDIO_OUT_SUB) ? 1 : 0;
+ pipeline_->SetProperty(Elements::kSinkAudio, "device-audio-out",
+ is_audio_sub_out_path, NULL);
+ pipeline_->SetProperty(Elements::kSinkAudio, "mute", is_sound_mute_, NULL);
+ if (volume_ != kVolumeNone) {
+ SetVolume_();
+ }
+ return;
+}
+bool TrackRenderer::SetAdvancedPictureQualityType(
+ const AdvPictureQualityType type) {
+ TRACKRENDERER_ENTER_P(this)
+#ifndef SOUNDBAR_PRODUCT
+ switch (type) {
+ case AdvPictureQualityType::kVideoCall:
+ avoc_sub_source_ = AVOC_SUB_SOURCE_UNIPLAYER_VIDEOCALL;
+ break;
+ case AdvPictureQualityType::kUsbCamera:
+ avoc_sub_source_ = AVOC_SUB_SOURCE_UNIPLAYER_CAMERA;
+ break;
+ default:
+ return false;
+ }
+#endif
+ return true;
+}
+
+bool TrackRenderer::SetResourceAllocatePolicy(const RscAllocPolicy policy) {
+ TRACKRENDERER_ENTER_P(this)
+ rsc_alloc_policy_ = policy;
+ return true;
+}
+
+void TrackRenderer::UpdateTrackFrameRate_(const int& framerate_num,
+ const int& framerate_den) {
+ std::lock_guard<std::mutex> lk(decoded_drop_ctx_.drop_mutex);
+ decoded_drop_ctx_.track_fps.num = framerate_num;
+ decoded_drop_ctx_.track_fps.den = framerate_den;
+ decoded_drop_ctx_.fps_changed = true;
+}
+
+void TrackRenderer::UpdateDecodedDropContext_() {
+ std::lock_guard<std::mutex> lk(decoded_drop_ctx_.drop_mutex);
+
+ BOOST_SCOPE_EXIT(&decoded_drop_ctx_) {
+ decoded_drop_ctx_.fps_changed = false;
+ }
+ BOOST_SCOPE_EXIT_END
+
+ if (decoded_drop_ctx_.fps_changed) {
+ Rational track_framerate = decoded_drop_ctx_.track_fps;
+ Rational request_framerate = decoded_drop_ctx_.request_fps;
+ if (!track_framerate.num || !track_framerate.den ||
+ !request_framerate.den) {
+ TRACKRENDERER_ERROR("invalid track or request frame rate: %d/%d",
+ track_framerate.num, track_framerate.den);
+ return;
+ }
+ if (!request_framerate.num) {
+ decoded_drop_ctx_.drop_mode = DropMode::kDropModeAccordingToRate;
+ decoded_drop_ctx_.drop_rate = 1;
+ decoded_drop_ctx_.base_num = 0;
+ return;
+ }
+ const int64_t tmp1 = track_framerate.num * (int64_t)request_framerate.den;
+ const int64_t tmp2 =
+ tmp1 - request_framerate.num * (int64_t)track_framerate.den;
+ if (tmp2 < 0) {
+ TRACKRENDERER_ERROR(
+ "request frame rate(%d/%d) can not be larger than track frame "
+ "rate(%d/%d)",
+ request_framerate.num, request_framerate.den, track_framerate.num,
+ track_framerate.den);
+ return;
+ } else if (!tmp2) {
+ decoded_drop_ctx_.drop_mode = DropMode::kDropModeNone;
+ } else {
+ if (tmp1 % tmp2 == 0) {
+ decoded_drop_ctx_.drop_mode = DropMode::kDropModeAccordingToRate;
+ decoded_drop_ctx_.drop_rate = static_cast<uint32_t>(tmp1 / tmp2);
+ decoded_drop_ctx_.base_num = 0;
+ } else if (track_framerate.num == 24 * track_framerate.den &&
+ request_framerate.num == 15 * request_framerate.den) {
+ decoded_drop_ctx_.drop_mode = DropMode::kDropModeAccordingToTable;
+ decoded_drop_ctx_.base_num = 0;
+ } else {
+ TRACKRENDERER_ERROR(
+ "not support case: request frame rate(%d/%d) track frame "
+ "rate(%d/%d)",
+ request_framerate.num, request_framerate.den, track_framerate.num,
+ track_framerate.den);
+ return;
+ }
+ }
+ }
+ return;
+}
+
+bool TrackRenderer::NeedDropThisDecodedVideoBuffer_() {
+ bool ret = false;
+
+ UpdateDecodedDropContext_();
+ if (decoded_drop_ctx_.drop_mode == DropMode::kDropModeAccordingToRate &&
+ decoded_drop_ctx_.drop_rate) {
+ uint32_t drop_rate = decoded_drop_ctx_.drop_rate;
+ constexpr uint32_t kDropAll = 1;
+ decoded_drop_ctx_.base_num++;
+ if (drop_rate == kDropAll ||
+ (decoded_drop_ctx_.base_num % drop_rate == 0)) {
+ decoded_drop_ctx_.base_num = 0;
+ ret = true;
+ }
+ } else if (decoded_drop_ctx_.drop_mode ==
+ DropMode::kDropModeAccordingToTable) {
+ constexpr uint32_t kDropTable[8] = {0, 0, 1, 0, 0, 1, 0, 1};
+
+ decoded_drop_ctx_.base_num = decoded_drop_ctx_.base_num & 0x7;
+ if (kDropTable[decoded_drop_ctx_.base_num]) {
+ ret = true;
+ }
+ decoded_drop_ctx_.base_num++;
+ }
+ return ret;
+}
+
+bool TrackRenderer::SetVideoRendererType(
+ const ResourceCategory video_renderer_type) {
+ if (video_renderer_type < ResourceCategory::kVideoRenderer) return false;
+ if (video_renderer_type > ResourceCategory::kVideoRendererSub3) return false;
+ video_renderer_id_ = video_renderer_type;
+ return true;
+}
+
+void TrackRenderer::SetVideoParDar(uint64_t time_millisecond, uint32_t par_num,
+ uint32_t par_den, uint32_t dar_num,
+ uint32_t dar_den) {
+ TRACKRENDERER_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(resource_m_);
+ std::lock_guard<std::mutex> audio_lk(internal_audio_m_);
+ if (state_ == State::kStopped) return;
+ if (state_ == State::kResourceConflicted) return;
+
+ if (!pipeline_) {
+ par_dar_.time_millisecond = time_millisecond;
+ par_dar_.par_num = par_num;
+ par_dar_.par_den = par_den;
+ par_dar_.dar_num = dar_num;
+ par_dar_.dar_den = dar_den;
+ is_pardar_updated_ = true;
+ } else {
+ pipeline_->SetParDar(Elements::kDecVideo, time_millisecond, par_num,
+ par_den, dar_num, dar_den);
+ }
+ TRACKRENDERER_LEAVE_P(this);
+}
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/trackrenderer.h"
+#include "trackrenderer/core/elements.h"
+
+#define MAKE_ATTRIBUTE(attr, elem, type, name, init, value) \
+ { \
+ trackrenderer::Attribute::attr, { \
+ trackrenderer::Elements::elem, typeid(type), name, init, value, \
+ &::OnAttributeSetWithCasting<type>, \
+ &::OnAttributeGetWithCasting<type> \
+ } \
+ }
+#define ELEMENT(elem) elem
+#define VALUE_TYPE(type) type
+#define PROPERTY(name) name
+#define WITHOUT_INIT false
+#define WITH_INIT true
+#define DEFAULT_VALUE(value) value
+
+namespace {
+constexpr std::uint64_t kMaxByteOfVideoSrcQueue = 1024 * 1024; // 1MB
+constexpr std::uint64_t kMaxByteOfAudioSrcQueue = 1024; // 1KB
+constexpr std::uint64_t kCurrentLevelByteOfVideo = 0; // 0KB
+constexpr std::uint64_t kCurrentLevelByteOfAudio = 0; // 0KB
+constexpr std::uint32_t kMinByteThresholdOfVideo = 0; // 0%
+constexpr std::uint32_t kMinByteThresholdOfAudio = 0; // 0%
+constexpr std::uint64_t kMaxTimeOfVideoSrcQueue = 0; // 0ns
+constexpr std::uint64_t kMaxTimeOfAudioSrcQueue = 0; // 0ns
+constexpr std::uint64_t kCurrentLevelTimeOfVideo = 0; // 0ns
+constexpr std::uint64_t kCurrentLevelTimeOfAudio = 0; // 0ns
+constexpr std::uint32_t kMinTimeThresholdOfVideo = 0; // 0%
+constexpr std::uint32_t kMinTimeThresholdOfAudio = 0; // 0%
+constexpr std::int32_t kInvalidExternalDrmHandle = -1;
+constexpr std::int32_t kIsSupportRotate = 0;
+constexpr std::int64_t kDefaultRenderTimeOffset = 0;
+
+template <typename ValueType>
+bool IsSameValue(const boost::any& v1, const boost::any& v2) {
+ if (v1.type() != v2.type()) return false;
+ return (boost::any_cast<ValueType>(v1) == boost::any_cast<ValueType>(v2));
+}
+
+template <typename ValueType>
+static bool OnAttributeSetWithCasting(
+ plusplayer::trackrenderer::TrackRenderer::TrackRendererAttributeSetter&
+ setter,
+ const boost::any& original_value, const boost::any& new_value) {
+ if (original_value.empty() == false &&
+ IsSameValue<ValueType>(original_value, new_value))
+ return false;
+ setter.Set(boost::any_cast<ValueType>(new_value));
+ return true;
+}
+
+template <typename ValueType>
+static bool OnAttributeGetWithCasting(
+ plusplayer::trackrenderer::TrackRenderer::TrackRendererAttributeGetter&
+ getter,
+ boost::any* get_value) {
+ getter.Get<ValueType>(get_value);
+ return true;
+}
+} // namespace
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+TrackRenderer::AttributesByElement
+TrackRenderer::InitAttributeByElementType_() {
+ assert(kAttributes_.size() != 0);
+ TrackRenderer::AttributesByElement attirbutes_by_elem;
+ for (auto& kv : kAttributes_) {
+ const trackrenderer::Attribute& attr = kv.first;
+ const TrackRendererAttributeBinder& binder = kv.second;
+ attirbutes_by_elem[binder.element].push_back(attr);
+ }
+ return attirbutes_by_elem;
+}
+
+const TrackRenderer::Attributes TrackRenderer::kAttributes_ = {
+ MAKE_ATTRIBUTE(kVideoQueueMaxByte, //
+ ELEMENT(kAppSrcVideo), //
+ VALUE_TYPE(std::uint64_t), //
+ PROPERTY("max-bytes"), //
+ WITH_INIT, //
+ DEFAULT_VALUE(::kMaxByteOfVideoSrcQueue)), //
+ MAKE_ATTRIBUTE(kAudioQueueMaxByte, //
+ ELEMENT(kAppSrcAudio), //
+ VALUE_TYPE(std::uint64_t), //
+ PROPERTY("max-bytes"), //
+ WITH_INIT, //
+ DEFAULT_VALUE(::kMaxByteOfAudioSrcQueue)), //
+ MAKE_ATTRIBUTE(kVideoQueueCurrentLevelByte, //
+ ELEMENT(kAppSrcVideo), //
+ VALUE_TYPE(std::uint64_t), //
+ PROPERTY("current-level-bytes"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kCurrentLevelByteOfVideo)), //
+ MAKE_ATTRIBUTE(kAudioQueueCurrentLevelByte, //
+ ELEMENT(kAppSrcAudio), //
+ VALUE_TYPE(std::uint64_t), //
+ PROPERTY("current-level-bytes"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kCurrentLevelByteOfAudio)), //
+ MAKE_ATTRIBUTE(kVideoMinByteThreshold, //
+ ELEMENT(kAppSrcVideo), //
+ VALUE_TYPE(std::uint32_t), //
+ PROPERTY("min-percent"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kMinByteThresholdOfVideo)), //
+ MAKE_ATTRIBUTE(kAudioMinByteThreshold, //
+ ELEMENT(kAppSrcAudio), //
+ VALUE_TYPE(std::uint32_t), //
+ PROPERTY("min-percent"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kMinByteThresholdOfAudio)), //
+ MAKE_ATTRIBUTE(kVideoQueueMaxTime, //
+ ELEMENT(kAppSrcVideo), //
+ VALUE_TYPE(std::uint64_t), //
+ PROPERTY("max-time"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kMaxTimeOfVideoSrcQueue)), //
+ MAKE_ATTRIBUTE(kAudioQueueMaxTime, //
+ ELEMENT(kAppSrcAudio), //
+ VALUE_TYPE(std::uint64_t), //
+ PROPERTY("max-time"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kMaxTimeOfAudioSrcQueue)), //
+ MAKE_ATTRIBUTE(kVideoQueueCurrentLevelTime, //
+ ELEMENT(kAppSrcVideo), //
+ VALUE_TYPE(std::uint64_t), //
+ PROPERTY("current-level-time"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kCurrentLevelTimeOfVideo)), //
+ MAKE_ATTRIBUTE(kAudioQueueCurrentLevelTime, //
+ ELEMENT(kAppSrcAudio), //
+ VALUE_TYPE(std::uint64_t), //
+ PROPERTY("current-level-time"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kCurrentLevelTimeOfAudio)), //
+ MAKE_ATTRIBUTE(kVideoMinTimeThreshold, //
+ ELEMENT(kAppSrcVideo), //
+ VALUE_TYPE(std::uint32_t), //
+ PROPERTY("min-time-percent"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kMinTimeThresholdOfVideo)), //
+ MAKE_ATTRIBUTE(kAudioMinTimeThreshold, //
+ ELEMENT(kAppSrcAudio), //
+ VALUE_TYPE(std::uint32_t), //
+ PROPERTY("min-time-percent"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kMinTimeThresholdOfAudio)), //
+ MAKE_ATTRIBUTE(kVideoSupportRotation, //
+ ELEMENT(kSinkVideo), //
+ VALUE_TYPE(std::uint32_t), //
+ PROPERTY("support-rotation"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kIsSupportRotate)), //
+ MAKE_ATTRIBUTE(kVideoRenderTimeOffset, //
+ ELEMENT(kSinkVideo), //
+ VALUE_TYPE(std::int64_t), //
+ PROPERTY("ts-offset"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kDefaultRenderTimeOffset)), //
+ MAKE_ATTRIBUTE(kAudioRenderTimeOffset, //
+ ELEMENT(kSinkAudio), //
+ VALUE_TYPE(std::int64_t), //
+ PROPERTY("ts-offset"), //
+ WITHOUT_INIT, //
+ DEFAULT_VALUE(::kDefaultRenderTimeOffset)), //
+};
+
+const TrackRenderer::AttributesByElement TrackRenderer::kAttributesByElem_ =
+ TrackRenderer::InitAttributeByElementType_();
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
\ No newline at end of file
--- /dev/null
+#include "trackrenderer_capi/trackrenderer_capi.h"
+
+#include <cassert>
+#include <cstdarg>
+#include <map>
+#include <memory>
+#include <string>
+#include <typeinfo>
+
+#include "trackrenderer/core/display.h"
+#include "trackrenderer/core/track.h"
+#include "trackrenderer/trackrenderer.h"
+#include "trackrenderer/trackrenderer_capi_utils.h"
+#include "trackrenderer/version.h"
+#include "trackrenderer_capi/trackrenderer_internal.h"
+
+//#define DUMP_SUBTITLE_PICTURE
+#ifdef DUMP_SUBTITLE_PICTURE
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#pragma pack(1)
+typedef struct tagBITMAPFILEHEADER {
+ short bfType;
+ int bfSize;
+ short bfReserved1;
+ short bfReserved2;
+ int bfOffBits;
+} BITMAPFILEHEADER;
+
+typedef struct tagBITMAPINFOHEADER {
+ int biSize;
+ int biWidth;
+ int biHeight;
+ short biPlanes;
+ short biBitCount;
+ int biCompression;
+ int biSizeImage;
+ int biXPelsPerMeter;
+ int biYPelsPerMeter;
+ int biClrUsed;
+ int biClrImportant;
+} BITMAPINFOHEADER;
+#pragma pack()
+
+static int CreateWindowsBMP(unsigned char* outARGBadd, int width, int height,
+ int file_index) {
+ TRACKRENDERER_DEBUG("==CreateWindowsBMP==");
+
+ BITMAPFILEHEADER bFileHeader;
+ BITMAPINFOHEADER bInfoHeader;
+
+ /*file header*/
+ bFileHeader.bfType = 0x4d42;
+ bFileHeader.bfReserved1 = 0;
+ bFileHeader.bfReserved2 = 0;
+ bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
+
+ /*info header*/
+ bInfoHeader.biSize = 40;
+ bInfoHeader.biWidth = width;
+ bInfoHeader.biHeight = height;
+ bInfoHeader.biPlanes = 1;
+ bInfoHeader.biCompression = 0;
+ bInfoHeader.biXPelsPerMeter = 0;
+ bInfoHeader.biYPelsPerMeter = 0;
+ bInfoHeader.biClrUsed = 0;
+ bInfoHeader.biClrImportant = 0;
+
+ unsigned char* outARGBadd_bmp = NULL;
+ int height_count;
+
+ FILE* fp = NULL;
+ struct stat ls;
+ char path[256] = {
+ 0,
+ };
+ snprintf(path, 256, "/media/USBDriveA1/subtitle_picture_plusplayer_%d.bmp",
+ file_index);
+
+ /*Check the soft symbol, refer to
+ * */
+ /*File information is obtained by using lstat function.*/
+ if (lstat(path, &ls) == -1) {
+ if (errno != ENOENT) {
+ TRACKRENDERER_DEBUG("lstat error");
+ return -1;
+ }
+ } else {
+ /*In case of existing file, checking symbolic link*/
+ if (S_ISLNK(ls.st_mode)) {
+ TRACKRENDERER_DEBUG("symbolic link is detected");
+ return -1;
+ }
+ }
+
+ if ((fp = fopen(path, "wb")) == NULL) {
+ TRACKRENDERER_DEBUG("Error in opening file");
+ return 1;
+ }
+
+ bInfoHeader.biBitCount = 32;
+ bInfoHeader.biSizeImage = width * height * 4;
+ bFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
+ (4 * (width * height));
+ outARGBadd_bmp = (unsigned char*)malloc(width * height * 4);
+ if (!outARGBadd_bmp) return -1;
+ for (height_count = 0; height_count < height; ++height_count) {
+ memcpy(outARGBadd_bmp + (height - height_count - 1) * width * 4,
+ outARGBadd + height_count * width * 4, width * 4);
+ }
+ fwrite(&bFileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
+ fwrite(&bInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);
+ fwrite(outARGBadd_bmp, height * width * 4, 1, fp);
+
+ TRACKRENDERER_DEBUG("==Create pictuer over!==");
+ free(outARGBadd_bmp);
+ fclose(fp);
+ fp = NULL;
+ return 1;
+}
+#endif
+
+const char* trackrenderer_get_version(void) {
+ TRACKRENDERER_ERROR("libtrackrenderer.so version [%s]",
+ TRACKRENDERER_STRINGIFY(LIB_TRACKRENDERER_VERSION));
+ return TRACKRENDERER_STRINGIFY(LIB_TRACKRENDERER_VERSION);
+}
+
+uint32_t trackrenderer_get_version_int(void) {
+ TRACKRENDERER_ERROR("libtrackrenderer.so version [%u]",
+ LIB_TRACKRENDERER_VERSION_INT);
+ return LIB_TRACKRENDERER_VERSION_INT;
+}
+
+struct TrackRendererPriv;
+
+class TrackRendererEventListener
+ : public plusplayer::trackrenderer::TrackRenderer::EventListener {
+ public:
+ explicit TrackRendererEventListener(TrackRendererPriv* handler)
+ : handler_(handler) {}
+ void OnError(const plusplayer::trackrenderer::ErrorType& error_code) override;
+ void OnErrorMsg(const plusplayer::trackrenderer::ErrorType& error_code,
+ char* error_msg) override;
+ void OnResourceConflicted() override;
+ void OnSeekDone() override;
+ void OnFlushDone() override;
+ void OnEos() override;
+ void OnEvent(const plusplayer::trackrenderer::EventType& event,
+ const plusplayer::trackrenderer::EventMsg& msg_data) override;
+ void OnFirstDecodingDone() override;
+ void OnSubtitleData(
+ const plusplayer::trackrenderer::DecoderInputBufferPtr& buf,
+ const plusplayer::trackrenderer::SubtitleType& type) override;
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+ void OnSubtitleData(
+ const char* data, const int size,
+ const plusplayer::trackrenderer::SubtitleType& type,
+ const uint64_t duration,
+ plusplayer::trackrenderer::SubtitleAttrListPtr attr_list) override;
+#endif
+ void OnClosedCaptionData(const char* data, const int size) override;
+ void OnDrmInitData(int* drmhandle, unsigned int len, unsigned char* psshdata,
+ plusplayer::trackrenderer::TrackType type) override;
+ void OnBufferStatus(
+ const plusplayer::trackrenderer::TrackType& type,
+ const plusplayer::trackrenderer::BufferStatus& status) override;
+ void OnSeekData(const plusplayer::trackrenderer::TrackType& type,
+ const uint64_t offset) override;
+ void OnMediaPacketGetTbmBufPtr(void** ptr, bool is_scale_change) override;
+ void OnMediaPacketVideoDecoded(
+ const plusplayer::trackrenderer::DecodedVideoPacket& packet) override;
+ void OnMediaRawPacketVideoDecoded(
+ plusplayer::trackrenderer::DecodedVideoRawModePacket& packet) override;
+ void OnVideoDecoderUnderrun() override;
+ void OnVideoLatencyStatus(
+ const plusplayer::trackrenderer::LatencyStatus& latency_status) override;
+ void OnAudioLatencyStatus(
+ const plusplayer::trackrenderer::LatencyStatus& latency_status) override;
+ void OnVideoHighLatency() override;
+ void OnAudioHighLatency() override;
+ void OnMultiviewStartVideo() override;
+ void OnMultiviewStopVideo() override;
+
+ private:
+ TrackRendererPriv* handler_ = nullptr;
+}; // class TrackRendererEventListener
+
+struct TrackRendererPriv {
+ std::unique_ptr<plusplayer::trackrenderer::TrackRenderer> renderer;
+
+ trackrenderer_error_cb error_cb = nullptr;
+ void* error_cb_userdata = nullptr;
+
+ trackrenderer_error_msg_cb error_msg_cb = nullptr;
+ void* error_msg_cb_userdata = nullptr;
+
+ trackrenderer_resource_conflicted_cb resourceconflict_cb = nullptr;
+ void* resourceconflict_cb_userdata = nullptr;
+
+ trackrenderer_seekdone_cb seekdone_cb = nullptr;
+ void* seekdone_cb_userdata = nullptr;
+
+ trackrenderer_flushdone_cb flushdone_cb = nullptr;
+ void* flushdone_cb_userdata = nullptr;
+
+ trackrenderer_eos_cb eos_cb = nullptr;
+ void* eos_cb_userdata = nullptr;
+
+ trackrenderer_event_cb event_cb = nullptr;
+ void* event_cb_userdata = nullptr;
+
+ trackrenderer_first_decoding_done_cb first_decoding_done_cb = nullptr;
+ void* first_decoding_done_cb_userdata = nullptr;
+
+ trackrenderer_subtitle_rawdata_cb subtitle_rawdata_cb = nullptr;
+ void* subtitle_rawdata_cb_userdata = nullptr;
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+ trackrenderer_subtitledata_cb subtitledata_cb = nullptr;
+ void* subtitledata_cb_userdata = nullptr;
+#endif
+
+ trackrenderer_closedcaption_cb closedcaption_cb = nullptr;
+ void* closedcaption_cb_userdata = nullptr;
+
+ trackrenderer_drminitdata_cb drminitdata_cb = nullptr;
+ void* drminitdata_cb_userdata = nullptr;
+
+ trackrenderer_bufferstatus_cb bufferstatus_cb = nullptr;
+ void* bufferstatus_cb_userdata = nullptr;
+
+ trackrenderer_seekdata_cb seekdata_cb = nullptr;
+ void* seekdata_cb_userdata = nullptr;
+
+ trackrenderer_media_packet_video_tbmptr_cb video_tbmptr_cb = nullptr;
+ void* video_tbmptr_cb_userdata = nullptr;
+
+ trackrenderer_media_packet_video_decoded_cb video_decoded_cb = nullptr;
+ void* video_decoded_cb_userdata = nullptr;
+
+ trackrenderer_media_packet_video_raw_decoded_cb video_raw_decoded_cb =
+ nullptr;
+ void* video_raw_decoded_cb_userdata = nullptr;
+
+ trackrenderer_decoder_underrun_cb video_decoder_underrun_cb = nullptr;
+ void* video_decoder_underrun_cb_userdata = nullptr;
+
+ trackrenderer_video_latency_status_cb video_latency_status_cb = nullptr;
+ trackrenderer_audio_latency_status_cb audio_latency_status_cb = nullptr;
+ void* video_latency_status_cb_userdata = nullptr;
+ void* audio_latency_status_cb_userdata = nullptr;
+ trackrenderer_video_high_latency_cb video_high_latency_cb = nullptr;
+ trackrenderer_audio_high_latency_cb audio_high_latency_cb = nullptr;
+ void* video_high_latency_cb_userdata = nullptr;
+ void* audio_high_latency_cb_userdata = nullptr;
+
+ trackrenderer_multiview_start_video_cb multiview_start_video_cb = nullptr;
+ trackrenderer_multiview_stop_video_cb multiview_stop_video_cb = nullptr;
+ void* multiview_start_video_cb_userdata = nullptr;
+ void* multiview_stop_video_cb_userdata = nullptr;
+
+ std::unique_ptr<TrackRendererEventListener> eventlistener{
+ new TrackRendererEventListener(this)};
+
+ using Ptr = TrackRendererPriv*;
+ friend Ptr Create();
+ friend void Destroy(Ptr& instance);
+
+ private:
+ TrackRendererPriv() {}
+ ~TrackRendererPriv() {}
+};
+
+void TrackRendererEventListener::OnError(
+ const plusplayer::trackrenderer::ErrorType& error_code) {
+ if (handler_->error_cb == nullptr) return;
+ handler_->error_cb(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererErrorType(
+ error_code),
+ handler_->error_cb_userdata);
+}
+
+void TrackRendererEventListener::OnErrorMsg(
+ const plusplayer::trackrenderer::ErrorType& error_code, char* error_msg) {
+ if (handler_->error_msg_cb == nullptr) return;
+ handler_->error_msg_cb(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererErrorType(
+ error_code),
+ error_msg, handler_->error_cb_userdata);
+}
+
+void TrackRendererEventListener::OnResourceConflicted() {
+ if (handler_->resourceconflict_cb == nullptr) return;
+ handler_->resourceconflict_cb(handler_->resourceconflict_cb_userdata);
+}
+void TrackRendererEventListener::OnSeekDone() {
+ if (handler_->seekdone_cb == nullptr) return;
+ handler_->seekdone_cb(handler_->seekdone_cb_userdata);
+}
+
+void TrackRendererEventListener::OnFlushDone() {
+ if (handler_->flushdone_cb == nullptr) return;
+ handler_->flushdone_cb(handler_->flushdone_cb_userdata);
+}
+
+void TrackRendererEventListener::OnEos() {
+ if (handler_->eos_cb == nullptr) return;
+ handler_->eos_cb(handler_->eos_cb_userdata);
+}
+
+void TrackRendererEventListener::OnEvent(
+ const plusplayer::trackrenderer::EventType& event,
+ const plusplayer::trackrenderer::EventMsg& msg_data) {
+ if (handler_->event_cb == nullptr) return;
+ TrackrendererEventMsg event_msg;
+ event_msg.data = const_cast<char*>(msg_data.data.c_str());
+ event_msg.len = msg_data.len;
+ handler_->event_cb(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererEventType(
+ event),
+ event_msg, handler_->event_cb_userdata);
+}
+
+void TrackRendererEventListener::OnFirstDecodingDone() {
+ if (handler_->first_decoding_done_cb == nullptr) return;
+ handler_->first_decoding_done_cb(handler_->first_decoding_done_cb_userdata);
+}
+
+void TrackRendererEventListener::OnSubtitleData(
+ const plusplayer::trackrenderer::DecoderInputBufferPtr& buf,
+ const plusplayer::trackrenderer::SubtitleType& type) {
+ if (handler_->subtitle_rawdata_cb == nullptr) return;
+ if (!buf) return;
+
+#ifdef TRACKRENDERER_GST_DEPENDENCY_REMOVAL
+ TrackRendererDecoderInputBuffer buffer{
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererTrackType(
+ buf->GetType()),
+ 0, static_cast<void*>(const_cast<GstBuffer*>(buf->Get()))};
+#else
+ TrackRendererDecoderInputBuffer buffer{
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererTrackType(
+ buf->GetType()),
+ 0, const_cast<GstBuffer*>(buf->Get())};
+#endif
+ handler_->subtitle_rawdata_cb(
+ &buffer,
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererSubtitleType(
+ type),
+ handler_->subtitle_rawdata_cb_userdata);
+}
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+void TrackRendererEventListener::OnSubtitleData(
+ const char* data, const int size,
+ const plusplayer::trackrenderer::SubtitleType& type,
+ const uint64_t duration,
+ plusplayer::trackrenderer::SubtitleAttrListPtr attr_list) {
+ if (plusplayer::trackrenderer::SubtitleType::kPicture == type && size > 0) {
+ gfloat width = 0, height = 0;
+
+ for (plusplayer::trackrenderer::SubtitleAttrList::iterator it =
+ attr_list->begin();
+ it != attr_list->end(); ++it) {
+ TRACKRENDERER_DEBUG("it->type = [%d]", (int)(it->type));
+ switch (it->type) {
+ case plusplayer::trackrenderer::kSubAttrRegionWidth:
+ width = boost::any_cast<gfloat>(it->value);
+ break;
+ case plusplayer::trackrenderer::kSubAttrRegionHeight:
+ height = boost::any_cast<gfloat>(it->value);
+ break;
+ default:
+ break;
+ }
+ if (width && height) {
+ break;
+ }
+ }
+ TRACKRENDERER_DEBUG("Picture subtitle >> width [%f],height [%f]", width,
+ height);
+#ifdef DUMP_SUBTITLE_PICTURE
+ static unsigned int picture_index = 0;
+ CreateWindowsBMP((unsigned char*)data, (int)width, (int)height,
+ picture_index++);
+#endif
+ } else if (plusplayer::trackrenderer::SubtitleType::kText == type &&
+ size > 0) {
+ TRACKRENDERER_DEBUG("Text subtitle >> %s", data);
+ }
+
+ if (handler_->subtitledata_cb == nullptr) return;
+ int attr_list_size = attr_list->size();
+ if (attr_list_size <= 0 && size > 0) {
+ return;
+ }
+ TrackRendererSubtitleAttr subtitle_attr_list[attr_list_size];
+ int index = 0;
+ for (index = 0; index < attr_list_size; index++) {
+ subtitle_attr_list[index] =
+ plusplayer::trackrenderer::capi_utils::InitSubtitleAttr();
+ }
+ index = 0;
+ for (const auto& attr : *attr_list) {
+ plusplayer::trackrenderer::capi_utils::MakeTrackRendererSubtitleAttr(
+ &subtitle_attr_list[index], attr);
+ index++;
+ }
+ handler_->subtitledata_cb(
+ const_cast<char*>(data), size,
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererSubtitleType(
+ type),
+ duration, subtitle_attr_list, attr_list_size,
+ handler_->subtitledata_cb_userdata);
+}
+#endif
+
+void TrackRendererEventListener::OnClosedCaptionData(const char* data,
+ const int size) {
+ if (handler_->closedcaption_cb == nullptr) return;
+ handler_->closedcaption_cb(const_cast<char*>(data), size,
+ handler_->closedcaption_cb_userdata);
+}
+void TrackRendererEventListener::OnDrmInitData(
+ int* drmhandle, unsigned int len, unsigned char* psshdata,
+ plusplayer::trackrenderer::TrackType type) {
+ if (handler_->drminitdata_cb == nullptr) return;
+ handler_->drminitdata_cb(
+ drmhandle, len, psshdata,
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererTrackType(
+ type),
+ handler_->drminitdata_cb_userdata);
+}
+
+void TrackRendererEventListener::OnBufferStatus(
+ const plusplayer::trackrenderer::TrackType& type,
+ const plusplayer::trackrenderer::BufferStatus& status) {
+ if (handler_->bufferstatus_cb == nullptr) return;
+ handler_->bufferstatus_cb(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererTrackType(
+ type),
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererBufferStatus(
+ status),
+ handler_->bufferstatus_cb_userdata);
+}
+
+void TrackRendererEventListener::OnSeekData(
+ const plusplayer::trackrenderer::TrackType& type, const uint64_t offset) {
+ if (handler_->seekdata_cb == nullptr) return;
+ handler_->seekdata_cb(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererTrackType(
+ type),
+ offset, handler_->seekdata_cb_userdata);
+}
+
+void TrackRendererEventListener::OnMediaPacketGetTbmBufPtr(
+ void** ptr, bool is_scale_change) {
+ if (handler_->video_tbmptr_cb == nullptr) return;
+ handler_->video_tbmptr_cb(ptr, is_scale_change,
+ handler_->video_tbmptr_cb_userdata);
+}
+
+void TrackRendererEventListener::OnMediaPacketVideoDecoded(
+ const plusplayer::trackrenderer::DecodedVideoPacket& packet) {
+ if (handler_->video_decoded_cb == nullptr) return;
+ TrackRendererDecodedVideoPacket _packet =
+ plusplayer::trackrenderer::capi_utils::ConvertToDecodedVideoPacket(
+ packet);
+ handler_->video_decoded_cb(&_packet, handler_->video_decoded_cb_userdata);
+}
+
+void TrackRendererEventListener::OnMediaRawPacketVideoDecoded(
+ plusplayer::trackrenderer::DecodedVideoRawModePacket& packet) {
+ if (handler_->video_raw_decoded_cb == nullptr) return;
+ TrackRendererDecodedVideoRawModePacket _packet;
+ _packet.pts = packet.pts;
+ _packet.width = packet.width;
+ _packet.height = packet.height;
+ _packet.internal_data = static_cast<void*>(&packet.data);
+ handler_->video_raw_decoded_cb(
+ &_packet, static_cast<TrackRendererDecodedVideoType>(packet.type),
+ handler_->video_raw_decoded_cb_userdata);
+}
+
+void TrackRendererEventListener::OnVideoDecoderUnderrun() {
+ if (handler_->video_decoder_underrun_cb == nullptr) return;
+ handler_->video_decoder_underrun_cb(
+ handler_->video_decoder_underrun_cb_userdata);
+}
+
+void TrackRendererEventListener::OnVideoLatencyStatus(
+ const plusplayer::trackrenderer::LatencyStatus& latency_status) {
+ if (handler_->video_latency_status_cb == nullptr) return;
+ handler_->video_latency_status_cb(
+ plusplayer::trackrenderer::capi_utils::
+ ConvertToTrackrendererLatencyStatus(latency_status),
+ handler_->video_latency_status_cb_userdata);
+}
+
+void TrackRendererEventListener::OnAudioLatencyStatus(
+ const plusplayer::trackrenderer::LatencyStatus& latency_status) {
+ if (handler_->audio_latency_status_cb == nullptr) return;
+ handler_->audio_latency_status_cb(
+ plusplayer::trackrenderer::capi_utils::
+ ConvertToTrackrendererLatencyStatus(latency_status),
+ handler_->audio_latency_status_cb_userdata);
+}
+
+void TrackRendererEventListener::OnVideoHighLatency() {
+ if (handler_->video_high_latency_cb == nullptr) return;
+ handler_->video_high_latency_cb(handler_->video_high_latency_cb_userdata);
+}
+
+void TrackRendererEventListener::OnAudioHighLatency() {
+ if (handler_->audio_high_latency_cb == nullptr) return;
+ handler_->audio_high_latency_cb(handler_->audio_high_latency_cb_userdata);
+}
+
+void TrackRendererEventListener::OnMultiviewStartVideo() {
+ if (handler_->multiview_start_video_cb == nullptr) return;
+ handler_->multiview_start_video_cb(
+ handler_->multiview_start_video_cb_userdata);
+}
+
+void TrackRendererEventListener::OnMultiviewStopVideo() {
+ if (handler_->multiview_stop_video_cb == nullptr) return;
+ handler_->multiview_stop_video_cb(handler_->multiview_stop_video_cb_userdata);
+}
+
+using TrackRendererPrivPtr = TrackRendererPriv::Ptr;
+
+TrackRendererPrivPtr Create() {
+ auto instance = new TrackRendererPriv;
+ instance->renderer = plusplayer::trackrenderer::TrackRenderer::Create();
+ instance->renderer->RegisterListener(instance->eventlistener.get());
+ return instance;
+}
+
+void Destroy(TrackRendererPrivPtr& instance) {
+ if (instance) delete instance;
+ instance = nullptr;
+}
+
+constexpr int kSuccess = 0;
+constexpr int kFailed = -1;
+
+int trackrenderer_create(TrackRendererHandle* handle) {
+ *handle = static_cast<void*>(Create());
+ return kSuccess;
+}
+
+int trackrenderer_destroy(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ Destroy(priv);
+ return kSuccess;
+}
+
+int trackrenderer_start(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->Start() == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_stop(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ priv->renderer->Stop();
+ return kSuccess;
+}
+
+int trackrenderer_prepare(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->Prepare() == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_pause(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->Pause() == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_resume(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->Resume() == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+constexpr int kTrackRendererMaxStreamNumber = 3;
+int trackrenderer_set_track(TrackRendererHandle handle,
+ const TrackRendererTrack* track, const int size) {
+ if (size <= 0 || size > kTrackRendererMaxStreamNumber) return kFailed;
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ auto trackinfo =
+ plusplayer::trackrenderer::capi_utils::MakeTrack(track, size);
+ priv->renderer->SetTrack(trackinfo);
+ return kSuccess;
+}
+
+int trackrenderer_set_track_handle(
+ TrackRendererHandle handle, const TrackRendererTrackHandle* handles_array,
+ const int array_size) {
+ if (array_size <= 0 || array_size > kTrackRendererMaxStreamNumber)
+ return kFailed;
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ auto trackinfo =
+ plusplayer::trackrenderer::capi_utils::MakeTrackFromTrackHandle(
+ handles_array, array_size);
+ return priv->renderer->SetTrack(trackinfo) ? kSuccess : kFailed;
+}
+
+void trackrenderer_set_ini_property(TrackRendererHandle handle,
+ TrackRendererIniProperty* properties,
+ int property_size) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ std::map<std::string, bool> iniproperty =
+ plusplayer::trackrenderer::capi_utils::MakeIniProperty(properties,
+ property_size);
+ priv->renderer->SetIniProperty(iniproperty);
+}
+
+int trackrenderer_seek(TrackRendererHandle handle,
+ unsigned long long time_millisecond,
+ double playback_rate) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->Seek(time_millisecond, playback_rate) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_seek2(TrackRendererHandle handle,
+ unsigned long long time_millisecond,
+ double playback_rate, bool audio_mute) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->Seek(time_millisecond, playback_rate, audio_mute) ==
+ false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_playback_rate(TrackRendererHandle handle,
+ double playback_rate, bool audio_mute) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->SetPlaybackRate(playback_rate, audio_mute) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_get_playing_time(TrackRendererHandle handle,
+ unsigned long long* time_millisecond) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->GetPlayingTime(time_millisecond) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_get_dropped_frames(TrackRendererHandle handle, void* counts) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->GetDroppedFrames(counts) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_get_dropped_frames_for_catchup(TrackRendererHandle handle,
+ TrackRendererTrackType type,
+ void* counts) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->GetDroppedFramesForCatchup(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackType(type),
+ counts) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_deactivate(TrackRendererHandle handle,
+ TrackRendererTrackType type) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->Deactivate(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackType(type)) ==
+ false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_activate(TrackRendererHandle handle,
+ TrackRendererTrackType type,
+ TrackRendererTrack* trackinfo) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ // TODO(sy0207.ju):MakeTrack return vector. use magic number.
+ auto tracks = plusplayer::trackrenderer::capi_utils::MakeTrack(trackinfo, 1);
+
+ if (priv->renderer->Activate(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackType(type),
+ tracks.front()) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+using SubmitStatus = plusplayer::trackrenderer::SubmitStatus;
+
+TrackRendererSubmitStatus ConvertToTrackRendererSubmitStatus(
+ SubmitStatus status) {
+ switch (status) {
+ case SubmitStatus::kNotPrepared: {
+ return TrackRendererSubmitStatus::kTrackRendererSubmitStatusNotPrepared;
+ }
+ case SubmitStatus::kHold: {
+ return TrackRendererSubmitStatus::kTrackRendererSubmitStatusHold;
+ }
+ case SubmitStatus::kFull: {
+ return TrackRendererSubmitStatus::kTrackRendererSubmitStatusFull;
+ }
+ case SubmitStatus::kSuccess: {
+ return TrackRendererSubmitStatus::kTrackRendererSubmitStatusSuccess;
+ }
+ case SubmitStatus::kDrop: {
+ return TrackRendererSubmitStatus::kTrackRendererSubmitStatusDrop;
+ }
+ default:
+ assert(0 && "unknown submitstatus");
+ return TrackRendererSubmitStatus::kTrackRendererSubmitStatusFailed;
+ }
+}
+
+int trackrenderer_submit_packet(TrackRendererHandle handle,
+ TrackRendererDecoderInputBuffer* data,
+ TrackRendererSubmitStatus* retval) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+#ifdef TRACKRENDERER_GST_DEPENDENCY_REMOVAL
+ auto inputbuffer = plusplayer::trackrenderer::DecoderInputBuffer::Create(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackType(data->type),
+ data->index, static_cast<GstBuffer*>(data->buffer));
+#else
+ auto inputbuffer = plusplayer::trackrenderer::DecoderInputBuffer::Create(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackType(data->type),
+ data->index, data->buffer);
+#endif
+
+ SubmitStatus status;
+ if (priv->renderer->SubmitPacket(std::move(inputbuffer), &status) == false) {
+ return kFailed;
+ }
+ if (retval) {
+ *retval = ConvertToTrackRendererSubmitStatus(status);
+ }
+
+ return kSuccess;
+}
+
+int trackrenderer_submit_packet2(TrackRendererHandle handle,
+ TrackRendererDecoderInputBuffer* data,
+ TrackRendererSubmitStatus* retval) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+#ifdef TRACKRENDERER_GST_DEPENDENCY_REMOVAL
+ auto inputbuffer = plusplayer::trackrenderer::DecoderInputBuffer::Create(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackType(data->type),
+ data->index, static_cast<GstBuffer*>(data->buffer), false);
+#else
+ auto inputbuffer = plusplayer::trackrenderer::DecoderInputBuffer::Create(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackType(data->type),
+ data->index, data->buffer, false);
+#endif
+
+ SubmitStatus status;
+ if (priv->renderer->SubmitPacket(std::move(inputbuffer), &status) == false) {
+ if (retval) {
+ *retval = ConvertToTrackRendererSubmitStatus(status);
+ }
+ return kFailed;
+ }
+ if (retval) {
+ *retval = ConvertToTrackRendererSubmitStatus(status);
+ }
+
+ return kSuccess;
+}
+
+using State = plusplayer::trackrenderer::TrackRenderer::State;
+
+TrackRendererState ConvertToTrackRendererState(State state) {
+ switch (state) {
+ case State::kInit: {
+ return kTrackRendererStateInit;
+ }
+ case State::kWorking: {
+ return kTrackRendererStateWorking;
+ }
+ case State::kResourceConflicted: {
+ return kTrackRendererStateResourceConflicted;
+ }
+ case State::kStopped: {
+ return kTrackRendererStateStopped;
+ }
+ default:
+ assert(0 && "unknown state");
+ return kTrackRendererStateUnknown;
+ }
+}
+
+TrackRendererState trackrenderer_get_state(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kTrackRendererStateUnknown;
+ return ConvertToTrackRendererState(priv->renderer->GetState());
+}
+
+void trackrenderer_set_drm(TrackRendererHandle handle,
+ TrackRendererDrmProperty* properties) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ plusplayer::trackrenderer::drm::Property drm_property;
+ plusplayer::trackrenderer::capi_utils::MakeDrmProperty(&drm_property,
+ *properties);
+ priv->renderer->SetDrm(drm_property);
+}
+
+void trackrenderer_drm_license_acquired_done(TrackRendererHandle handle,
+ TrackRendererTrackType type) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->renderer->DrmLicenseAcquiredDone(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackType(type));
+}
+
+int trackrenderer_set_display_mode(TrackRendererHandle handle,
+ TrackRendererDisplayMode mode) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->SetDisplayMode(
+ plusplayer::trackrenderer::capi_utils::ConvertToDisplayMode(mode)) ==
+ false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_display_rotate(TrackRendererHandle handle,
+ TrackRendererDisplayRotate rotate) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->SetDisplayRotate(
+ plusplayer::trackrenderer::capi_utils::ConvertToDisplayRotate(
+ rotate)) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_get_display_rotate(TrackRendererHandle handle,
+ TrackRendererDisplayRotate* rotate) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::DisplayRotation display_rotate;
+ priv->renderer->GetDisplayRotate(&display_rotate);
+ *rotate = plusplayer::trackrenderer::capi_utils::
+ ConvertToTrackRendererDisplayRotate(display_rotate);
+ return kSuccess;
+}
+
+int trackrenderer_set_display_surface(TrackRendererHandle handle,
+ TrackRendererDisplayType type,
+ unsigned int surface_id, long x, long y,
+ long w, long h) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->SetDisplay(
+ plusplayer::trackrenderer::capi_utils::ConvertToDisplayType(type),
+ surface_id, x, y, w, h) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_display(TrackRendererHandle handle,
+ TrackRendererDisplayType type, void* obj) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->SetDisplay(
+ plusplayer::trackrenderer::capi_utils::ConvertToDisplayType(type),
+ obj) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_display_ecore_wl2_window(TrackRendererHandle handle,
+ TrackRendererDisplayType type,
+ void* ecore_wl2_window, int x,
+ int y, int w, int h) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->SetDisplay(
+ plusplayer::trackrenderer::capi_utils::ConvertToDisplayType(type),
+ ecore_wl2_window, x, y, w, h) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_display_ecore_wl2_subsurface(
+ TrackRendererHandle handle, TrackRendererDisplayType type,
+ void* ecore_wl2_subsurface, int x, int y, int w, int h) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (!ecore_wl2_subsurface) return kFailed;
+ if (priv->renderer->SetDisplaySubsurface(
+ plusplayer::trackrenderer::capi_utils::ConvertToDisplayType(type),
+ ecore_wl2_subsurface, x, y, w, h) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_display_roi(TrackRendererHandle handle,
+ TrackRendererGeometry* geometry) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::Geometry result;
+ plusplayer::trackrenderer::capi_utils::MakeGeometry(&result, *geometry);
+ if (!priv->renderer->SetDisplayRoi(result)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_video_roi(TrackRendererHandle handle,
+ TrackRendererCropArea* crop) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::CropArea result;
+ plusplayer::trackrenderer::capi_utils::MakeCropArea(&result, *crop);
+ if (!priv->renderer->SetVideoRoi(result)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_resize_render_rect(TrackRendererHandle handle,
+ TrackRendererRenderRect* rect) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::RenderRect result;
+ plusplayer::trackrenderer::capi_utils::MakeRenderRect(&result, *rect);
+ if (!priv->renderer->ResizeRenderRect(result)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+void trackrenderer_get_display(TrackRendererHandle handle,
+ TrackRendererDisplayType* type,
+ TrackRendererGeometry* area) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ plusplayer::trackrenderer::Geometry geometry;
+ plusplayer::trackrenderer::DisplayType display_type;
+
+ priv->renderer->GetDisplay(&display_type, &geometry);
+ plusplayer::trackrenderer::capi_utils::MakeTrackRendererGeometry(area,
+ geometry);
+ *type =
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererDisplayType(
+ display_type);
+}
+
+void trackrenderer_get_display_mode(TrackRendererHandle handle,
+ TrackRendererDisplayMode* mode) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+
+ plusplayer::trackrenderer::DisplayMode display_mode;
+ priv->renderer->GetDisplayMode(&display_mode);
+ *mode =
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackRendererDisplayMode(
+ display_mode);
+}
+
+int trackrenderer_set_display_visible(TrackRendererHandle handle,
+ bool is_visible) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->SetDisplayVisible(is_visible) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+void trackrenderer_set_app_id(TrackRendererHandle handle, const char* app_id) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ std::string appid{app_id};
+ priv->renderer->SetAppId(appid);
+}
+
+void trackrenderer_set_app_info(TrackRendererHandle handle,
+ const TrackRendererAppInfo* app_info) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ plusplayer::trackrenderer::PlayerAppInfo info;
+ plusplayer::trackrenderer::capi_utils::MakePlayerAppInfo(app_info, info);
+ priv->renderer->SetAppInfo(info);
+}
+
+int trackrenderer_set_audio_mute(TrackRendererHandle handle, bool is_mute) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (priv->renderer->SetAudioMute(is_mute) == false) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+void trackrenderer_set_video_still_mode(TrackRendererHandle handle,
+ TrackRendererStillMode type) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+
+ plusplayer::trackrenderer::StillMode still_mode =
+ plusplayer::trackrenderer::capi_utils::ConvertToStillMode(type);
+ priv->renderer->SetVideoStillMode(still_mode);
+}
+
+void trackrenderer_set_attribute(TrackRendererHandle handle,
+ const char* attr_name, ...) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ va_list arg_ptr;
+
+ va_start(arg_ptr, attr_name);
+ const char* attr = attr_name;
+ while (attr != nullptr) {
+ if (plusplayer::trackrenderer::kPluginPropertyInfoTable.count(attr) > 0) {
+ const plusplayer::trackrenderer::TrackRendererAttrInfo& attr_info =
+ plusplayer::trackrenderer::kPluginPropertyInfoTable.at(attr);
+
+ switch (attr_info.value_type) {
+ case plusplayer::trackrenderer::ValueType::kInt32: {
+ std::int32_t value = va_arg(arg_ptr, std::int32_t);
+ priv->renderer->SetAttribute(attr_info.attr_enum, value);
+ } break;
+ case plusplayer::trackrenderer::ValueType::kUInt32: {
+ std::uint32_t value = va_arg(arg_ptr, std::uint32_t);
+ priv->renderer->SetAttribute(attr_info.attr_enum, value);
+ } break;
+ case plusplayer::trackrenderer::ValueType::kInt64: {
+ std::int64_t value = va_arg(arg_ptr, std::int64_t);
+ priv->renderer->SetAttribute(attr_info.attr_enum, value);
+ } break;
+ case plusplayer::trackrenderer::ValueType::kUInt64: {
+ std::uint64_t value = va_arg(arg_ptr, std::uint64_t);
+ priv->renderer->SetAttribute(attr_info.attr_enum, value);
+ } break;
+ default:
+ assert(0 && "Unsupported Value Type");
+ break;
+ }
+ } else if (plusplayer::trackrenderer::kConfigInfoTable.count(attr) > 0) {
+ plusplayer::trackrenderer::ValueType value_type =
+ plusplayer::trackrenderer::kConfigInfoTable.at(attr);
+ switch (value_type) {
+ case plusplayer::trackrenderer::ValueType::kUInt32: {
+ std::uint32_t value = va_arg(arg_ptr, std::uint32_t);
+ priv->renderer->SetConfig(attr, value);
+ } break;
+ case plusplayer::trackrenderer::ValueType::kUInt64: {
+ std::uint64_t value = va_arg(arg_ptr, std::uint64_t);
+ priv->renderer->SetConfig(attr, value);
+ } break;
+ default:
+ assert(0 && "Unsupported Value Type");
+ break;
+ }
+ } else {
+ TRACKRENDERER_ERROR("Unsupported attribute name");
+ break;
+ }
+ attr = va_arg(arg_ptr, const char*);
+ }
+ va_end(arg_ptr);
+ return;
+}
+
+void trackrenderer_get_attribute(TrackRendererHandle handle,
+ const char* attr_name, ...) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ va_list arg_ptr;
+
+ va_start(arg_ptr, attr_name);
+ const char* attr = attr_name;
+ while (attr != nullptr) {
+ if (plusplayer::trackrenderer::kPluginPropertyInfoTable.count(attr) == 0) {
+ TRACKRENDERER_ERROR("Unknown Attribute [%s]", attr);
+ break;
+ }
+ const plusplayer::trackrenderer::TrackRendererAttrInfo& attr_info =
+ plusplayer::trackrenderer::kPluginPropertyInfoTable.at(attr);
+
+ switch (attr_info.value_type) {
+ case plusplayer::trackrenderer::ValueType::kInt32: {
+ std::int32_t* value = va_arg(arg_ptr, std::int32_t*);
+ boost::any _value = std::int32_t(0);
+ priv->renderer->GetAttribute(attr_info.attr_enum, &_value);
+ *value = boost::any_cast<std::int32_t>(_value);
+ } break;
+ case plusplayer::trackrenderer::ValueType::kUInt32: {
+ std::uint32_t* value = va_arg(arg_ptr, std::uint32_t*);
+ boost::any _value = std::uint32_t(0);
+ priv->renderer->GetAttribute(attr_info.attr_enum, &_value);
+ *value = boost::any_cast<std::uint32_t>(_value);
+ } break;
+ case plusplayer::trackrenderer::ValueType::kInt64: {
+ std::int64_t* value = va_arg(arg_ptr, std::int64_t*);
+ boost::any _value = std::int64_t(0);
+ priv->renderer->GetAttribute(attr_info.attr_enum, &_value);
+ *value = boost::any_cast<std::int64_t>(_value);
+ } break;
+ case plusplayer::trackrenderer::ValueType::kUInt64: {
+ std::uint64_t* value = va_arg(arg_ptr, std::uint64_t*);
+ boost::any _value = std::uint64_t(0);
+ priv->renderer->GetAttribute(attr_info.attr_enum, &_value);
+ *value = boost::any_cast<std::uint64_t>(_value);
+ } break;
+ default:
+ assert(0 && "Unsupported Value Type");
+ break;
+ }
+ attr = va_arg(arg_ptr, const char*);
+ }
+ va_end(arg_ptr);
+ return;
+}
+
+int trackrenderer_set_matroska_color_info(TrackRendererHandle handle,
+ const char* color_info) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (!priv->renderer->SetMatroskaColorInfo(color_info)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_volume(TrackRendererHandle handle, const int volume) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (!priv->renderer->SetVolume(volume)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_get_volume(TrackRendererHandle handle, int* volume) {
+ if (!volume) return kFailed;
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (!priv->renderer->GetVolume(volume)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+void trackrenderer_set_video_frame_buffer_type(
+ TrackRendererHandle handle, TrackRendererDecodedVideoFrameBufferType type) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->renderer->SetVideoFrameBufferType(
+ plusplayer::trackrenderer::capi_utils::ConvertToVideoFrameBufferType(
+ type));
+}
+
+void trackrenderer_set_video_frame_buffer_type_ext(
+ TrackRendererHandle handle,
+ TrackRendererDecodedVideoFrameBufferTypeExt type) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->renderer->SetVideoFrameBufferType(
+ plusplayer::trackrenderer::capi_utils::ConvertToVideoFrameBufferTypeExt(
+ type));
+}
+
+int trackrenderer_set_video_frame_buffer_scale_resolution(
+ TrackRendererHandle handle, uint32_t target_width, uint32_t target_height) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (!target_width || !target_height) return kFailed;
+ priv->renderer->SetVideoFrameBufferScaleResolution(target_width,
+ target_height);
+ return kSuccess;
+}
+
+int trackrenderer_set_decoded_video_frame_rate(
+ TrackRendererHandle handle, TrackRendererRational request_framerate) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::Rational request_fps;
+ plusplayer::trackrenderer::capi_utils::MakeRational(&request_fps,
+ request_framerate);
+ if (!priv->renderer->SetDecodedVideoFrameRate(request_fps)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_flush(TrackRendererHandle handle,
+ TrackRendererTrackType type) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ priv->renderer->FlushAppsrc(
+ plusplayer::trackrenderer::capi_utils::ConvertToTrackType(type), true);
+ return kSuccess;
+}
+
+int trackrenderer_render_video_frame(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (!priv->renderer->RenderVideoFrame()) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_aifilter(TrackRendererHandle handle, void* aifilter) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ if (!priv->renderer->SetAiFilter(aifilter)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_catch_up_speed(TrackRendererHandle handle,
+ const TrackRendererCatchUpSpeed level) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ priv->renderer->SetCatchUpSpeed(
+ plusplayer::trackrenderer::capi_utils::ConvertToCatchUpSpeed(level));
+ return kSuccess;
+}
+
+int trackrenderer_get_video_latency_status(TrackRendererHandle handle,
+ TrackRendererLatencyStatus* status) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ plusplayer::trackrenderer::LatencyStatus current =
+ plusplayer::trackrenderer::LatencyStatus::kLow;
+ priv->renderer->GetVideoLatencyStatus(¤t);
+ *status = plusplayer::trackrenderer::capi_utils::
+ ConvertToTrackrendererLatencyStatus(current);
+ return kSuccess;
+}
+
+int trackrenderer_get_audio_latency_status(TrackRendererHandle handle,
+ TrackRendererLatencyStatus* status) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ plusplayer::trackrenderer::LatencyStatus current =
+ plusplayer::trackrenderer::LatencyStatus::kLow;
+ priv->renderer->GetAudioLatencyStatus(¤t);
+ *status = plusplayer::trackrenderer::capi_utils::
+ ConvertToTrackrendererLatencyStatus(current);
+ return kSuccess;
+}
+
+int trackrenderer_set_video_mid_latency_threshold(
+ TrackRendererHandle handle, const unsigned int threshold) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ priv->renderer->SetVideoMidLatencyThreshold(threshold);
+ return kSuccess;
+}
+
+int trackrenderer_set_audio_mid_latency_threshold(
+ TrackRendererHandle handle, const unsigned int threshold) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ priv->renderer->SetAudioMidLatencyThreshold(threshold);
+ return kSuccess;
+}
+
+int trackrenderer_set_video_high_latency_threshold(
+ TrackRendererHandle handle, const unsigned int threshold) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ priv->renderer->SetVideoHighLatencyThreshold(threshold);
+ return kSuccess;
+}
+
+int trackrenderer_set_audio_high_latency_threshold(
+ TrackRendererHandle handle, const unsigned int threshold) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ priv->renderer->SetAudioHighLatencyThreshold(threshold);
+ return kSuccess;
+}
+
+/* CALL back*/
+void trackrenderer_set_error_cb(TrackRendererHandle handle,
+ trackrenderer_error_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->error_cb = callback;
+ priv->error_cb_userdata = userdata;
+}
+
+void trackrenderer_set_error_msg_cb(TrackRendererHandle handle,
+ trackrenderer_error_msg_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->error_msg_cb = callback;
+ priv->error_msg_cb_userdata = userdata;
+}
+
+void trackrenderer_set_resourceconflict_cb(
+ TrackRendererHandle handle, trackrenderer_resource_conflicted_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->resourceconflict_cb = callback;
+ priv->resourceconflict_cb_userdata = userdata;
+}
+
+void trackrenderer_set_seekdone_cb(TrackRendererHandle handle,
+ trackrenderer_seekdone_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->seekdone_cb = callback;
+ priv->seekdone_cb_userdata = userdata;
+}
+
+void trackrenderer_set_flushdone_cb(TrackRendererHandle handle,
+ trackrenderer_flushdone_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->flushdone_cb = callback;
+ priv->flushdone_cb_userdata = userdata;
+}
+
+void trackrenderer_set_eos_cb(TrackRendererHandle handle,
+ trackrenderer_eos_cb callback, void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->eos_cb = callback;
+ priv->eos_cb_userdata = userdata;
+}
+
+void trackrenderer_set_event_cb(TrackRendererHandle handle,
+ trackrenderer_event_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->event_cb = callback;
+ priv->event_cb_userdata = userdata;
+}
+
+void trackrenderer_set_first_decoding_done_cb(
+ TrackRendererHandle handle, trackrenderer_first_decoding_done_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->first_decoding_done_cb = callback;
+ priv->first_decoding_done_cb_userdata = userdata;
+}
+
+void trackrenderer_set_subtitle_rawdata_cb(
+ TrackRendererHandle handle, trackrenderer_subtitle_rawdata_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->subtitle_rawdata_cb = callback;
+ priv->subtitle_rawdata_cb_userdata = userdata;
+}
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+void trackrenderer_set_subtitledata_cb(TrackRendererHandle handle,
+ trackrenderer_subtitledata_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->subtitledata_cb = callback;
+ priv->subtitledata_cb_userdata = userdata;
+}
+#endif
+
+void trackrenderer_set_closedcaption_cb(TrackRendererHandle handle,
+ trackrenderer_closedcaption_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->closedcaption_cb = callback;
+ priv->closedcaption_cb_userdata = userdata;
+}
+
+void trackrenderer_set_drminitdata_cb(TrackRendererHandle handle,
+ trackrenderer_drminitdata_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->drminitdata_cb = callback;
+ priv->drminitdata_cb_userdata = userdata;
+}
+
+void trackrenderer_set_bufferstatus_cb(TrackRendererHandle handle,
+ trackrenderer_bufferstatus_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->bufferstatus_cb = callback;
+ priv->bufferstatus_cb_userdata = userdata;
+}
+
+void trackrenderer_set_seekdata_cb(TrackRendererHandle handle,
+ trackrenderer_seekdata_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->seekdata_cb = callback;
+ priv->seekdata_cb_userdata = userdata;
+}
+
+void trackrenderer_set_media_packet_video_tbmptr_cb(
+ TrackRendererHandle handle,
+ trackrenderer_media_packet_video_tbmptr_cb callback, void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->video_tbmptr_cb = callback;
+ priv->video_tbmptr_cb_userdata = userdata;
+}
+
+void trackrenderer_set_media_packet_video_decoded_cb(
+ TrackRendererHandle handle,
+ trackrenderer_media_packet_video_decoded_cb callback, void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->video_decoded_cb = callback;
+ priv->video_decoded_cb_userdata = userdata;
+}
+
+void trackrenderer_set_media_packet_video_raw_decoded_cb(
+ TrackRendererHandle handle,
+ trackrenderer_media_packet_video_raw_decoded_cb callback, void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->video_raw_decoded_cb = callback;
+ priv->video_raw_decoded_cb_userdata = userdata;
+}
+
+void trackrenderer_set_video_decoder_underrun_cb(
+ TrackRendererHandle handle, trackrenderer_decoder_underrun_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->video_decoder_underrun_cb = callback;
+ priv->video_decoder_underrun_cb_userdata = userdata;
+}
+
+int trackrenderer_set_alternative_audio_resource(TrackRendererHandle handle,
+ unsigned int rsc_type) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ priv->renderer->SetAlternativeAudioResource(rsc_type);
+ return kSuccess;
+}
+
+void trackrenderer_set_video_latency_status_cb(
+ TrackRendererHandle handle, trackrenderer_video_latency_status_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->video_latency_status_cb = callback;
+ priv->video_latency_status_cb_userdata = userdata;
+}
+
+void trackrenderer_set_audio_latency_status_cb(
+ TrackRendererHandle handle, trackrenderer_audio_latency_status_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->audio_latency_status_cb = callback;
+ priv->audio_latency_status_cb_userdata = userdata;
+}
+
+void trackrenderer_set_video_high_latency_cb(
+ TrackRendererHandle handle, trackrenderer_video_high_latency_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->video_high_latency_cb = callback;
+ priv->video_high_latency_cb_userdata = userdata;
+}
+
+void trackrenderer_set_audio_high_latency_cb(
+ TrackRendererHandle handle, trackrenderer_audio_high_latency_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->audio_high_latency_cb = callback;
+ priv->audio_high_latency_cb_userdata = userdata;
+}
+
+void trackrenderer_set_multiview_start_video_cb(
+ TrackRendererHandle handle, trackrenderer_multiview_start_video_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->multiview_start_video_cb = callback;
+ priv->multiview_start_video_cb_userdata = userdata;
+}
+
+void trackrenderer_set_multiview_stop_video_cb(
+ TrackRendererHandle handle, trackrenderer_multiview_stop_video_cb callback,
+ void* userdata) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return;
+ priv->multiview_stop_video_cb = callback;
+ priv->multiview_stop_video_cb_userdata = userdata;
+}
+
+int trackrenderer_init_audio_easing_info(
+ TrackRendererHandle handle, const uint32_t init_volume,
+ const uint32_t init_elapsed_time,
+ const TrackRendererAudioEasingInfo* easing_info) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ plusplayer::trackrenderer::AudioEasingInfo info;
+ plusplayer::trackrenderer::capi_utils::MakeAudioEasingInfo(&info,
+ easing_info);
+ if (!priv->renderer->InitAudioEasingInfo(init_volume, init_elapsed_time,
+ info)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_update_audio_easing_info(
+ TrackRendererHandle handle,
+ const TrackRendererAudioEasingInfo* easing_info) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::AudioEasingInfo info;
+ plusplayer::trackrenderer::capi_utils::MakeAudioEasingInfo(&info,
+ easing_info);
+ if (!priv->renderer->UpdateAudioEasingInfo(info)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_get_audio_easing_info(
+ TrackRendererHandle handle, uint32_t* current_volume,
+ uint32_t* elapsed_time, TrackRendererAudioEasingInfo* easing_info) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::AudioEasingInfo info;
+ if (!priv->renderer->GetAudioEasingInfo(current_volume, elapsed_time,
+ &info)) {
+ return kFailed;
+ }
+ plusplayer::trackrenderer::capi_utils::MakeTrackRendererAudioEasingInfo(
+ easing_info, info);
+ return kSuccess;
+}
+
+int trackrenderer_start_audio_easing(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ if (!priv->renderer->StartAudioEasing()) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_stop_audio_easing(TrackRendererHandle handle) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+
+ if (!priv->renderer->StopAudioEasing()) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_get_virtual_rsc_id(TrackRendererHandle handle,
+ TrackRendererRscType type,
+ int* virtual_id) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::RscType converted_type =
+ plusplayer::trackrenderer::RscType::kVideoRenderer;
+ if (!plusplayer::trackrenderer::capi_utils::ConvertToRscType(type,
+ &converted_type))
+ return kFailed;
+ if (!priv->renderer->GetVirtualRscId(converted_type, virtual_id)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_advanced_picture_quality_type(
+ TrackRendererHandle handle, TrackRendererAdvPictureQualityType type) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::AdvPictureQualityType converted_type =
+ plusplayer::trackrenderer::AdvPictureQualityType::kVideoCall;
+ if (!plusplayer::trackrenderer::capi_utils::ConvertToAdvPictureQualityType(
+ type, &converted_type))
+ return kFailed;
+ if (!priv->renderer->SetAdvancedPictureQualityType(converted_type)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_resource_allocate_policy(
+ TrackRendererHandle handle, TrackRendererRscAllocPolicy policy) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::RscAllocPolicy converted_policy =
+ plusplayer::trackrenderer::RscAllocPolicy::kRscAllocExclusive;
+ if (!plusplayer::trackrenderer::capi_utils::ConvertToRscAllocPolicy(
+ policy, &converted_policy))
+ return kFailed;
+ if (!priv->renderer->SetResourceAllocatePolicy(converted_policy)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_video_renderer_type(
+ TrackRendererHandle handle, TrackRendererVideoRendererType renderer_type) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ plusplayer::trackrenderer::ResourceCategory converted_renderer_type;
+ if (!plusplayer::trackrenderer::capi_utils::ConvertToVideoRendererType(
+ renderer_type, &converted_renderer_type))
+ return kFailed;
+ if (!priv->renderer->SetVideoRendererType(converted_renderer_type)) {
+ return kFailed;
+ }
+ return kSuccess;
+}
+
+int trackrenderer_set_video_par_dar(TrackRendererHandle handle,
+ uint64_t time_millisecond, uint32_t par_num,
+ uint32_t par_den, uint32_t dar_num,
+ uint32_t dar_den) {
+ auto priv = static_cast<TrackRendererPrivPtr>(handle);
+ if (!priv) return kFailed;
+ priv->renderer->SetVideoParDar(time_millisecond, par_num, par_den, dar_num,
+ dar_den);
+ return kSuccess;
+}
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/trackrenderer_capi_utils.h"
+
+#include "trackrenderer/core/track_util.h"
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace capi_utils {
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+TrackRendererSubtitleAttr InitSubtitleAttr() {
+ TrackRendererSubtitleAttr subtitle_attr{
+ .type = kTrackRendererSubtitleAttrTypeTypeNone,
+ .start_time = std::numeric_limits<uint32_t>::max(),
+ .stop_time = std::numeric_limits<uint32_t>::max(),
+ {.f = 0},
+ .extsub_index = -1};
+
+ return subtitle_attr;
+}
+#endif
+
+void MakeDrmProperty(drm::Property* drm_property,
+ const TrackRendererDrmProperty& properties) {
+ drm_property->type = ConvertToDrmType(properties.type);
+ drm_property->handle = properties.handle;
+ drm_property->external_decryption = properties.external_decryption;
+ drm_property->license_acquired_cb = properties.license_acquired_cb;
+ drm_property->license_acquired_userdata =
+ properties.license_acquired_userdata;
+}
+
+void MakeGeometry(Geometry* output, const TrackRendererGeometry& input) {
+ output->x = input.x;
+ output->y = input.y;
+ output->w = input.w;
+ output->h = input.h;
+}
+
+void MakeCropArea(CropArea* output, const TrackRendererCropArea& input) {
+ output->scale_x = input.scale_x;
+ output->scale_y = input.scale_y;
+ output->scale_w = input.scale_w;
+ output->scale_h = input.scale_h;
+}
+
+void MakeRenderRect(RenderRect* output, const TrackRendererRenderRect& input) {
+ output->x = input.x;
+ output->y = input.y;
+ output->w = input.w;
+ output->h = input.h;
+}
+
+std::map<std::string, bool> MakeIniProperty(
+ TrackRendererIniProperty* properties, int size) {
+ std::map<std::string, bool> iniproperty;
+ for (int index = 0; index < size; index++) {
+ iniproperty.insert(std::pair<std::string, bool>(properties[index].key,
+ properties[index].value));
+ }
+ return iniproperty;
+}
+
+std::vector<Track> MakeTrack(const TrackRendererTrack* trackrenderertrack,
+ const int size) {
+ std::vector<Track> trackvector;
+ for (int index = 0; index < size; index++) {
+ Track track;
+ track.index = trackrenderertrack[index].index;
+ track.id = trackrenderertrack[index].id;
+ track.mimetype = trackrenderertrack[index].mimetype
+ ? trackrenderertrack[index].mimetype
+ : "";
+ track.streamtype = trackrenderertrack[index].streamtype
+ ? trackrenderertrack[index].streamtype
+ : "";
+ track.type = ConvertToTrackType(trackrenderertrack[index].type);
+ if (track_util::IsValidCodecDataSize(
+ trackrenderertrack[index].codec_data_len)) {
+ track.codec_data = std::shared_ptr<char>(
+ new char[trackrenderertrack[index].codec_data_len],
+ std::default_delete<char[]>());
+ memcpy(track.codec_data.get(), trackrenderertrack[index].codec_data,
+ trackrenderertrack[index].codec_data_len);
+ track.codec_data_len = trackrenderertrack[index].codec_data_len;
+ }
+ track.width = trackrenderertrack[index].width;
+ track.height = trackrenderertrack[index].height;
+ track.maxwidth = trackrenderertrack[index].maxwidth;
+ track.maxheight = trackrenderertrack[index].maxheight;
+ track.framerate_num = trackrenderertrack[index].framerate_num;
+ track.framerate_den = trackrenderertrack[index].framerate_den;
+ track.sample_rate = trackrenderertrack[index].sample_rate;
+ track.sample_format = trackrenderertrack[index].sample_format;
+ track.channels = trackrenderertrack[index].channels;
+ track.version = trackrenderertrack[index].version;
+ track.layer = trackrenderertrack[index].layer;
+ track.bits_per_sample = trackrenderertrack[index].bits_per_sample;
+ track.block_align = trackrenderertrack[index].block_align;
+ track.bitrate = trackrenderertrack[index].bitrate;
+ track.endianness = trackrenderertrack[index].endianness;
+ track.is_signed = trackrenderertrack[index].is_signed;
+ track.active = trackrenderertrack[index].active;
+ track.use_swdecoder = trackrenderertrack[index].use_swdecoder;
+ track.language_code = trackrenderertrack[index].language_code
+ ? trackrenderertrack[index].language_code
+ : "";
+ track.subtitle_format = trackrenderertrack[index].subtitle_format
+ ? trackrenderertrack[index].subtitle_format
+ : "";
+ trackvector.push_back(std::move(track));
+ }
+ return trackvector;
+}
+
+std::vector<Track> MakeTrackFromTrackHandle(
+ const TrackRendererTrackHandle* track_handle, const int size) {
+ std::vector<Track> trackvector;
+ for (int index = 0; index < size; index++) {
+ trackvector.push_back(*static_cast<Track*>(track_handle[index]));
+ }
+ return trackvector;
+}
+
+void MakeTrackRendererGeometry(TrackRendererGeometry* geometry,
+ const Geometry& roi) {
+ geometry->x = roi.x;
+ geometry->y = roi.y;
+ geometry->w = roi.w;
+ geometry->h = roi.h;
+}
+
+// LCOV_EXCL_START
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+void MakeTrackRendererSubtitleAttr(TrackRendererSubtitleAttr* attr,
+ const SubtitleAttr& input_attr) {
+ attr->type = ConvertToTrackRendererSubtitleAttrType(input_attr.type);
+ attr->start_time = input_attr.start_time;
+ attr->stop_time = input_attr.stop_time;
+ attr->extsub_index = input_attr.extsub_index;
+
+ switch (input_attr.type) {
+ case kSubAttrRegionXPos: // fall through
+ case kSubAttrRegionYPos: // fall through
+ case kSubAttrRegionWidth: // fall through
+ case kSubAttrRegionHeight: // fall through
+ case kSubAttrWindowXPadding: // fall through
+ case kSubAttrWindowYPadding: // fall through
+ case kSubAttrWindowOpacity: // fall through
+ case kSubAttrWindowShowBg: // fall through
+ case kSubAttrFontSize: // fall through
+ case kSubAttrFontOpacity: // fall through
+ case kSubAttrFontBgOpacity: // fall through
+ case kSubAttrWebvttCueLine: // fall through
+ case kSubAttrWebvttCueSize: // fall through
+ case kSubAttrWebvttCuePosition: // fall through
+ case kSubAttrWebvttCueVertical: {
+ const float* value = boost::any_cast<float>(&input_attr.value);
+ if (value) attr->value.f = *value;
+ break;
+ }
+ case kSubAttrWindowLeftMargin: // fall through
+ case kSubAttrWindowRightMargin: // fall through
+ case kSubAttrWindowTopMargin: // fall through
+ case kSubAttrWindowBottomMargin: // fall through
+ case kSubAttrWindowBgColor: // fall through
+ case kSubAttrFontWeight: // fall through
+ case kSubAttrFontStyle: // fall through
+ case kSubAttrFontColor: // fall through
+ case kSubAttrFontBgColor: // fall through
+ case kSubAttrFontTextOutlineColor: // fall through
+ case kSubAttrFontTextOutlineThickness: // fall through
+ case kSubAttrFontTextOutlineBlurRadius: // fall through
+ case kSubAttrFontVerticalAlign: // fall through
+ case kSubAttrFontHorizontalAlign: // fall through
+ case kSubAttrWebvttCueLineNum: // fall through
+ case kSubAttrWebvttCueLineAlign: // fall through
+ case kSubAttrWebvttCueAlign: // fall through
+ case kSubAttrWebvttCuePositionAlign: {
+ const int32_t* value = boost::any_cast<int32_t>(&input_attr.value);
+ if (value) attr->value.i32 = *value;
+ break;
+ }
+ case kSubAttrFontFamily: // fall through
+ case kSubAttrRawSubtitle: {
+ const std::string* value =
+ boost::any_cast<std::string>(&input_attr.value);
+ if (!value->empty()) attr->value.str = (*value).c_str();
+ break;
+ }
+ case kSubAttrTimestamp:
+ case kSubAttrExtsubIndex:
+ break;
+ default:
+ TRACKRENDERER_ERROR("Unknown subtitle attr type");
+ }
+}
+#endif
+// LCOV_EXCL_STOP
+
+void MakePlayerAppInfo(const TrackRendererAppInfo* app_attr,
+ PlayerAppInfo& app_info) {
+ if (app_attr->id) app_info.id = app_attr->id;
+ if (app_attr->version) app_info.version = app_attr->version;
+ if (app_attr->type) app_info.type = app_attr->type;
+}
+
+DisplayMode ConvertToDisplayMode(TrackRendererDisplayMode typevalue) {
+ switch (typevalue) {
+ case kTrackRendererDisplayModeLetterBox: {
+ return DisplayMode::kLetterBox;
+ }
+ case kTrackRendererDisplayModeOriginSize: {
+ return DisplayMode::kOriginSize;
+ }
+ case kTrackRendererDisplayModeFullScreen: {
+ return DisplayMode::kFullScreen;
+ }
+ case kTrackRendererDisplayModeCroppedFull: {
+ return DisplayMode::kCroppedFull;
+ }
+ case kTrackRendererDisplayModeOriginOrLetter: {
+ return DisplayMode::kOriginOrLetter;
+ }
+ case kTrackRendererDisplayModeDstRoi: {
+ return DisplayMode::kDstRoi;
+ }
+ case kTrackRendererDisplayModeAutoAspectRatio: {
+ return DisplayMode::kAutoAspectRatio;
+ }
+ case kTrackRendererDisplayModeDisplayMax: {
+ return DisplayMode::kMax;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown DisplayMode");
+ return DisplayMode::kFullScreen;
+ }
+}
+
+DisplayRotation ConvertToDisplayRotate(TrackRendererDisplayRotate rotate) {
+ switch (rotate) {
+ case kTrackRendererDisplayRotateNone: {
+ return DisplayRotation::kNone;
+ }
+ case kTrackRendererDisplayRotate90: {
+ return DisplayRotation::kRotate90;
+ }
+ case kTrackRendererDisplayRotate180: {
+ return DisplayRotation::kRotate180;
+ }
+ case kTrackRendererDisplayRotate270: {
+ return DisplayRotation::kRotate270;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown DisplayRotation");
+ return DisplayRotation::kNone;
+ }
+}
+
+DisplayType ConvertToDisplayType(const TrackRendererDisplayType typevalue) {
+ switch (typevalue) {
+ case kTrackRendererDisplayTypeNone: {
+ return DisplayType::kNone;
+ }
+ case kTrackRendererDisplayTypeOverlay: {
+ return DisplayType::kOverlay;
+ }
+ case kTrackRendererDisplayTypeEvas: {
+ return DisplayType::kEvas;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown DisplayType");
+ return DisplayType::kNone;
+ }
+}
+
+drm::Type ConvertToDrmType(TrackRendererDrmType typevalue) {
+ switch (typevalue) {
+ case kTrackRendererDrmTypeNone: {
+ return drm::Type::kNone;
+ }
+ case kTrackRendererDrmTypePlayready: {
+ return drm::Type::kPlayready;
+ }
+ case kTrackRendererDrmTypeMarlin: {
+ return drm::Type::kMarlin;
+ }
+ case kTrackRendererDrmTypeVerimatrix: {
+ return drm::Type::kVerimatrix;
+ }
+ case kTrackRendererDrmTypeWidevineClassic: {
+ return drm::Type::kWidevineClassic;
+ }
+ case kTrackRendererDrmTypeSecuremedia: {
+ return drm::Type::kSecuremedia;
+ }
+ case kTrackRendererDrmTypeSdrm: {
+ return drm::Type::kSdrm;
+ }
+ case kTrackRendererDrmTypeWidevineCdm: {
+ return drm::Type::kWidevineCdm;
+ }
+ case kTrackRendererDrmTypeDrmMax: {
+ return drm::Type::kMax;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown drm type");
+ return drm::Type::kNone;
+ }
+}
+
+StillMode ConvertToStillMode(TrackRendererStillMode typevalue) {
+ switch (typevalue) {
+ case kTrackRendererStillModeNone: {
+ return StillMode::kNone;
+ }
+ case kTrackRendererStillModeOff: {
+ return StillMode::kOff;
+ }
+ case kTrackRendererStillModeOn: {
+ return StillMode::kOn;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown StillMode");
+ return StillMode::kNone;
+ }
+}
+
+TrackType ConvertToTrackType(TrackRendererTrackType typevalue) {
+ switch (typevalue) {
+ case kTrackRendererTrackTypeAudio: {
+ return TrackType::kTrackTypeAudio;
+ }
+ case kTrackRendererTrackTypeVideo: {
+ return TrackType::kTrackTypeVideo;
+ }
+ case kTrackRendererTrackTypeSubtitle: {
+ return TrackType::kTrackTypeSubtitle;
+ }
+ case kTrackRendererTrackTypeMax: {
+ return TrackType::kTrackTypeMax;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown tracktype");
+ return TrackType::kTrackTypeMax;
+ }
+}
+
+TrackRendererDisplayMode ConvertToTrackRendererDisplayMode(
+ const DisplayMode& mode) {
+ switch (mode) {
+ case DisplayMode::kLetterBox: {
+ return kTrackRendererDisplayModeLetterBox;
+ }
+ case DisplayMode::kOriginSize: {
+ return kTrackRendererDisplayModeOriginSize;
+ }
+ case DisplayMode::kFullScreen: {
+ return kTrackRendererDisplayModeFullScreen;
+ }
+ case DisplayMode::kCroppedFull: {
+ return kTrackRendererDisplayModeCroppedFull;
+ }
+ case DisplayMode::kOriginOrLetter: {
+ return kTrackRendererDisplayModeOriginOrLetter;
+ }
+ case DisplayMode::kDstRoi: {
+ return kTrackRendererDisplayModeDstRoi;
+ }
+ case DisplayMode::kAutoAspectRatio: {
+ return kTrackRendererDisplayModeAutoAspectRatio;
+ }
+ case DisplayMode::kMax: {
+ return kTrackRendererDisplayModeDisplayMax;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown displaymode");
+ return kTrackRendererDisplayModeFullScreen;
+ }
+}
+
+TrackRendererDisplayRotate ConvertToTrackRendererDisplayRotate(
+ const DisplayRotation& rotate) {
+ switch (rotate) {
+ case DisplayRotation::kNone: {
+ return kTrackRendererDisplayRotateNone;
+ }
+ case DisplayRotation::kRotate90: {
+ return kTrackRendererDisplayRotate90;
+ }
+ case DisplayRotation::kRotate180: {
+ return kTrackRendererDisplayRotate180;
+ }
+ case DisplayRotation::kRotate270: {
+ return kTrackRendererDisplayRotate270;
+ }
+ default:
+ return kTrackRendererDisplayRotateNone;
+ }
+}
+TrackRendererDisplayType ConvertToTrackRendererDisplayType(
+ const DisplayType& type) {
+ switch (type) {
+ case DisplayType::kNone: {
+ return kTrackRendererDisplayTypeNone;
+ }
+ case DisplayType::kOverlay: {
+ return kTrackRendererDisplayTypeOverlay;
+ }
+ case DisplayType::kEvas: {
+ return kTrackRendererDisplayTypeEvas;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown displaytype");
+ return kTrackRendererDisplayTypeNone;
+ }
+}
+
+// LCOV_EXCL_START
+TrackRendererErrorType ConvertToTrackRendererErrorType(ErrorType type) {
+ switch (type) {
+ case ErrorType::kNone: {
+ return kTrackRendererErrorTypeErrorNone;
+ }
+ case ErrorType::kOutOfMemory: {
+ return kTrackRendererErrorTypeOutOfMemory;
+ }
+ case ErrorType::kInvalidParameter: {
+ return kTrackRendererErrorTypeInvalidParameter;
+ }
+ case ErrorType::kNoSuchFile: {
+ return kTrackRendererErrorTypeNoSuchFile;
+ }
+ case ErrorType::kInvalidOperation: {
+ return kTrackRendererErrorTypeInvalidOperation;
+ }
+ case ErrorType::kFileNoSpaceOnDevice: {
+ return kTrackRendererErrorTypeFileNoSpaceOnDevice;
+ }
+ case ErrorType::kFeatureNotSupportedOnDevice: {
+ return kTrackRendererErrorTypeFeatureNotSupportedOnDevice;
+ }
+ case ErrorType::kSeekFailed: {
+ return kTrackRendererErrorTypeSeekFailed;
+ }
+ case ErrorType::kInvalidState: {
+ return kTrackRendererErrorTypeInvalidState;
+ }
+ case ErrorType::kNotSupportedFile: {
+ return kTrackRendererErrorTypeNotSupportedFile;
+ }
+ case ErrorType::kInvalidUri: {
+ return kTrackRendererErrorTypeInvalidUri;
+ }
+ case ErrorType::kSoundPolicy: {
+ return kTrackRendererErrorTypeSoundPolicy;
+ }
+ case ErrorType::kConnectionFailed: {
+ return kTrackRendererErrorTypeConnectionFailed;
+ }
+ case ErrorType::kVideoCaptureFailed: {
+ return kTrackRendererErrorTypeVideoCaptureFailed;
+ }
+ case ErrorType::kDrmExpired: {
+ return kTrackRendererErrorTypeDrmExpired;
+ }
+ case ErrorType::kDrmNoLicense: {
+ return kTrackRendererErrorTypeDrmNoLicense;
+ }
+ case ErrorType::kDrmFutureUse: {
+ return kTrackRendererErrorTypeDrmFutureUse;
+ }
+ case ErrorType::kDrmNotPermitted: {
+ return kTrackRendererErrorTypeDrmNotPermitted;
+ }
+ case ErrorType::kResourceLimit: {
+ return kTrackRendererErrorTypeResourceLimit;
+ }
+ case ErrorType::kPermissionDenied: {
+ return kTrackRendererErrorTypePermissionDenied;
+ }
+ case ErrorType::kServiceDisconnected: {
+ return kTrackRendererErrorTypeServiceDisconnected;
+ }
+ case ErrorType::kBufferSpace: {
+ return kTrackRendererErrorTypeBufferSpace;
+ }
+ case ErrorType::kNotSupportedAudioCodec: {
+ return kTrackRendererErrorTypeNotSupportedAudioCodec;
+ }
+ case ErrorType::kNotSupportedVideoCodec: {
+ return kTrackRendererErrorTypeNotSupportedVideoCodec;
+ }
+ case ErrorType::kNotSupportedSubtitle: {
+ return kTrackRendererErrorTypeNotSupportedSubtitle;
+ }
+ case ErrorType::kDrmInfo: {
+ return kTrackRendererErrorTypeDrmInfo;
+ }
+ case ErrorType::kNotSupportedFormat: {
+ return kTrackRendererErrorTypeNotSupportedFormat;
+ }
+ case ErrorType::kStreamingPlayer: {
+ return kTrackRendererErrorTypeStreamingPlayer;
+ }
+ case ErrorType::kDtcpFsk: {
+ return kTrackRendererErrorTypeDtcpFsk;
+ }
+ case ErrorType::kPreLoadingTimeOut: {
+ return kTrackRendererErrorTypePreLoadingTimeOut;
+ }
+ case ErrorType::kNetworkError: {
+ return kTrackRendererErrorTypeNetworkError;
+ }
+ case ErrorType::kChannelSurfingFailed: {
+ return kTrackRendererErrorTypeChannelSurfingFailed;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown error type");
+ return kTrackRendererErrorTypeUnknown;
+ }
+}
+
+TrackRendererEventType ConvertToTrackRendererEventType(const EventType& event) {
+ switch (event) {
+ case EventType::kNone: {
+ return kTrackRendererEventTypeNone;
+ }
+ case EventType::kResolutionChanged: {
+ return kTrackRendererEventTypeResolutionChanged;
+ }
+ default:
+ return kTrackRendererEventTypeNone;
+ }
+}
+// LCOV_EXCL_STOP
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+TrackRendererSubtitleAttrType ConvertToTrackRendererSubtitleAttrType(
+ const SubtitleAttrType& type) {
+ switch (type) {
+ case SubtitleAttrType::kSubAttrRegionXPos: {
+ return kTrackRendererSubtitleAttrTypeRegionXPos;
+ }
+ case SubtitleAttrType::kSubAttrRegionYPos: {
+ return kTrackRendererSubtitleAttrTypeRegionYPos;
+ }
+ case SubtitleAttrType::kSubAttrRegionWidth: {
+ return kTrackRendererSubtitleAttrTypeRegionWidth;
+ }
+ case SubtitleAttrType::kSubAttrRegionHeight: {
+ return kTrackRendererSubtitleAttrTypeRegionHeight;
+ }
+ case SubtitleAttrType::kSubAttrWindowXPadding: {
+ return kTrackRendererSubtitleAttrTypeWindowXPadding;
+ }
+ case SubtitleAttrType::kSubAttrWindowYPadding: {
+ return kTrackRendererSubtitleAttrTypeWindowYPadding;
+ }
+ case SubtitleAttrType::kSubAttrWindowLeftMargin: {
+ return kTrackRendererSubtitleAttrTypeWindowLeftMargin;
+ }
+ case SubtitleAttrType::kSubAttrWindowRightMargin: {
+ return kTrackRendererSubtitleAttrTypeWindowRightMargin;
+ }
+ case SubtitleAttrType::kSubAttrWindowTopMargin: {
+ return kTrackRendererSubtitleAttrTypeWindowTopMargin;
+ }
+ case SubtitleAttrType::kSubAttrWindowBottomMargin: {
+ return kTrackRendererSubtitleAttrTypeWindowBottomMargin;
+ }
+ case SubtitleAttrType::kSubAttrWindowBgColor: {
+ return kTrackRendererSubtitleAttrTypeWindowBgColor;
+ }
+ case SubtitleAttrType::kSubAttrWindowOpacity: {
+ return kTrackRendererSubtitleAttrTypeWindowOpacity;
+ }
+ case SubtitleAttrType::kSubAttrWindowShowBg: {
+ return kTrackRendererSubtitleAttrTypeWindowShowBg;
+ }
+ case SubtitleAttrType::kSubAttrFontFamily: {
+ return kTrackRendererSubtitleAttrTypeFontFamily;
+ }
+ case SubtitleAttrType::kSubAttrFontSize: {
+ return kTrackRendererSubtitleAttrTypeFontSize;
+ }
+ case SubtitleAttrType::kSubAttrFontWeight: {
+ return kTrackRendererSubtitleAttrTypeFontWeight;
+ }
+ case SubtitleAttrType::kSubAttrFontStyle: {
+ return kTrackRendererSubtitleAttrTypeFontStyle;
+ }
+ case SubtitleAttrType::kSubAttrFontColor: {
+ return kTrackRendererSubtitleAttrTypeFontColor;
+ }
+ case SubtitleAttrType::kSubAttrFontBgColor: {
+ return kTrackRendererSubtitleAttrTypeFontBgColor;
+ }
+ case SubtitleAttrType::kSubAttrFontOpacity: {
+ return kTrackRendererSubtitleAttrTypeFontOpacity;
+ }
+ case SubtitleAttrType::kSubAttrFontBgOpacity: {
+ return kTrackRendererSubtitleAttrTypeFontBgOpacity;
+ }
+ case SubtitleAttrType::kSubAttrFontTextOutlineColor: {
+ return kTrackRendererSubtitleAttrTypeFontTextOutlineColor;
+ }
+ case SubtitleAttrType::kSubAttrFontTextOutlineThickness: {
+ return kTrackRendererSubtitleAttrTypeFontTextOutlineThickness;
+ }
+ case SubtitleAttrType::kSubAttrFontTextOutlineBlurRadius: {
+ return kTrackRendererSubtitleAttrTypeFontTextOutlineBlurRadius;
+ }
+ case SubtitleAttrType::kSubAttrFontVerticalAlign: {
+ return kTrackRendererSubtitleAttrTypeFontVerticalAlign;
+ }
+ case SubtitleAttrType::kSubAttrFontHorizontalAlign: {
+ return kTrackRendererSubtitleAttrTypeFontHorizontalAlign;
+ }
+ case SubtitleAttrType::kSubAttrRawSubtitle: {
+ return kTrackRendererSubtitleAttrTypeRawSubtitle;
+ }
+ case SubtitleAttrType::kSubAttrWebvttCueLine: {
+ return kTrackRendererSubtitleAttrTypeWebvttCueLine;
+ }
+ case SubtitleAttrType::kSubAttrWebvttCueLineNum: {
+ return kTrackRendererSubtitleAttrTypeWebvttCueLineNum;
+ }
+ case SubtitleAttrType::kSubAttrWebvttCueLineAlign: {
+ return kTrackRendererSubtitleAttrTypeWebvttCueLineAlign;
+ }
+ case SubtitleAttrType::kSubAttrWebvttCueAlign: {
+ return kTrackRendererSubtitleAttrTypeWebvttCueAlign;
+ }
+ case SubtitleAttrType::kSubAttrWebvttCueSize: {
+ return kTrackRendererSubtitleAttrTypeWebvttCueSize;
+ }
+ case SubtitleAttrType::kSubAttrWebvttCuePosition: {
+ return kTrackRendererSubtitleAttrTypeWebvttCuePosition;
+ }
+ case SubtitleAttrType::kSubAttrWebvttCuePositionAlign: {
+ return kTrackRendererSubtitleAttrTypeWebvttCuePositionAlign;
+ }
+ case SubtitleAttrType::kSubAttrWebvttCueVertical: {
+ return kTrackRendererSubtitleAttrTypeWebvttCueVertical;
+ }
+ case SubtitleAttrType::kSubAttrTimestamp: {
+ return kTrackRendererSubtitleAttrTypeTimestamp;
+ }
+ case SubtitleAttrType::kSubAttrExtsubIndex: {
+ return kTrackRendererSubtitleAttrTypeExtsubIndex;
+ }
+ case SubtitleAttrType::kSubAttrTypeNone: {
+ return kTrackRendererSubtitleAttrTypeTypeNone;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown subtitle attr type");
+ return kTrackRendererSubtitleAttrTypeTypeNone;
+ }
+}
+#endif
+
+TrackRendererSubtitleType ConvertToTrackRendererSubtitleType(
+ SubtitleType typevalue) {
+ switch (typevalue) {
+ case SubtitleType::kText: {
+ return kTrackRendererSubtitleTypeText;
+ }
+ case SubtitleType::kPicture: {
+ return kTrackRendererSubtitleTypePicture;
+ }
+ case SubtitleType::kInvalid: {
+ return kTrackRendererSubtitleTypeInvalid;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown subtitle type");
+ return kTrackRendererSubtitleTypeInvalid;
+ }
+}
+
+TrackRendererTrackType ConvertToTrackRendererTrackType(const TrackType& type) {
+ switch (type) {
+ case TrackType::kTrackTypeAudio: {
+ return kTrackRendererTrackTypeAudio;
+ }
+ case TrackType::kTrackTypeVideo: {
+ return kTrackRendererTrackTypeVideo;
+ }
+ case TrackType::kTrackTypeSubtitle: {
+ return kTrackRendererTrackTypeSubtitle;
+ }
+ case TrackType::kTrackTypeMax: {
+ return kTrackRendererTrackTypeMax;
+ }
+ default:
+ TRACKRENDERER_ERROR("unknown tracktype");
+ return kTrackRendererTrackTypeMax;
+ }
+}
+
+TrackRendererBufferStatus ConvertToTrackRendererBufferStatus(
+ const BufferStatus& status) {
+ switch (status) {
+ case BufferStatus::kUnderrun: {
+ return kTrackRendererBufferStatusUnderrun;
+ }
+ case BufferStatus::kOverrun: {
+ return kTrackRendererBufferStatusOverrun;
+ }
+ }
+ TRACKRENDERER_ERROR("Unknown buffern status");
+ return kTrackRendererBufferStatusUnderrun;
+}
+TrackRendererDecodedVideoPacket ConvertToDecodedVideoPacket(
+ const DecodedVideoPacket& packet) {
+ TrackRendererDecodedVideoPacket _packet;
+ _packet.pts = packet.pts;
+ _packet.duration = packet.duration;
+ _packet.surface_data = static_cast<void*>(packet.surface_data);
+ _packet.scaler_index = packet.scaler_index;
+ return _packet;
+}
+DecodedVideoFrameBufferType ConvertToVideoFrameBufferType(
+ const TrackRendererDecodedVideoFrameBufferType& type) {
+ switch (type) {
+ case kTrackRendererDecodedVideoFrameBufferCopy: {
+ return DecodedVideoFrameBufferType::kCopy;
+ }
+ case kTrackRendererDecodedVideoFrameBufferReference: {
+ return DecodedVideoFrameBufferType::kReference;
+ }
+ case kTrackRendererDecodedVideoFrameBufferScale: {
+ return DecodedVideoFrameBufferType::kScale;
+ }
+ case kTrackRendererDecodedVideoFrameBufferNone: {
+ return DecodedVideoFrameBufferType::kNone;
+ }
+ default:
+ TRACKRENDERER_ERROR("wrong buffer type");
+ return DecodedVideoFrameBufferType::kNone;
+ }
+}
+DecodedVideoFrameBufferType ConvertToVideoFrameBufferTypeExt(
+ const TrackRendererDecodedVideoFrameBufferTypeExt& type) {
+ switch (type) {
+ case kTrackRendererDecodedVideoFrameBufferExtNone: {
+ return DecodedVideoFrameBufferType::kNone;
+ }
+ case kTrackRendererDecodedVideoFrameBufferExtRaw: {
+ return DecodedVideoFrameBufferType::kRaw;
+ }
+ default:
+ TRACKRENDERER_ERROR("wrong buffer type");
+ return DecodedVideoFrameBufferType::kNone;
+ }
+}
+
+CatchUpSpeed ConvertToCatchUpSpeed(const TrackRendererCatchUpSpeed& level) {
+ switch (level) {
+ case kTrackRendererCatchUpSpeedNone: {
+ return CatchUpSpeed::kNone;
+ }
+ case kTrackRendererCatchUpSpeedSlow: {
+ return CatchUpSpeed::kSlow;
+ }
+ case kTrackRendererCatchUpSpeedNormal: {
+ return CatchUpSpeed::kMid;
+ }
+ case kTrackRendererCatchUpSpeedFast: {
+ return CatchUpSpeed::kFast;
+ }
+ }
+ TRACKRENDERER_ERROR("Unknown catch up speed");
+ return CatchUpSpeed::kNone;
+}
+
+TrackRendererLatencyStatus ConvertToTrackrendererLatencyStatus(
+ const LatencyStatus& status) {
+ switch (status) {
+ case LatencyStatus::kLow: {
+ return kTrackRendererLatencyStatusLow;
+ }
+ case LatencyStatus::kMid: {
+ return kTrackRendererLatencyStatusMid;
+ }
+ case LatencyStatus::kHigh: {
+ return kTrackRendererLatencyStatusHigh;
+ }
+ default:
+ TRACKRENDERER_ERROR("Unknown latency status");
+ return kTrackRendererLatencyStatusLow;
+ }
+}
+
+AudioEasingType ConvertToAudioEasingType(
+ const TrackRendererAudioEasingType& type) {
+ switch (type) {
+ case kTrackRendererAudioEasingLinear: {
+ return AudioEasingType::kAudioEasingLinear;
+ }
+ case kTrackRendererAudioEasingIncubic: {
+ return AudioEasingType::kAudioEasingIncubic;
+ }
+ case kTrackRendererAudioEasingOutcubic: {
+ return AudioEasingType::kAudioEasingOutcubic;
+ }
+ default:
+ TRACKRENDERER_ERROR("wrong audio easing type");
+ return AudioEasingType::kAudioEasingNone;
+ }
+}
+
+TrackRendererAudioEasingType ConvertToTrackRendererAudioEasingType(
+ const AudioEasingType& type) {
+ switch (type) {
+ case AudioEasingType::kAudioEasingLinear: {
+ return kTrackRendererAudioEasingLinear;
+ }
+ case AudioEasingType::kAudioEasingIncubic: {
+ return kTrackRendererAudioEasingIncubic;
+ }
+ case AudioEasingType::kAudioEasingOutcubic: {
+ return kTrackRendererAudioEasingOutcubic;
+ }
+ default:
+ TRACKRENDERER_ERROR("wrong audio easing type");
+ return kTrackRendererAudioEasingNone;
+ }
+}
+
+void MakeAudioEasingInfo(AudioEasingInfo* easing_info,
+ const TrackRendererAudioEasingInfo* easing_attr) {
+ easing_info->target_volume = easing_attr->target_volume;
+ easing_info->duration = easing_attr->duration;
+ easing_info->type = ConvertToAudioEasingType(easing_attr->type);
+}
+
+void MakeTrackRendererAudioEasingInfo(TrackRendererAudioEasingInfo* easing_attr,
+ const AudioEasingInfo& easing_info) {
+ easing_attr->target_volume = easing_info.target_volume;
+ easing_attr->duration = easing_info.duration;
+ easing_attr->type = ConvertToTrackRendererAudioEasingType(easing_info.type);
+}
+
+bool ConvertToRscType(const TrackRendererRscType& typevalue, RscType* type) {
+ switch (typevalue) {
+ case kTrackRendererRscTypeVideoRenderer:
+ *type = RscType::kVideoRenderer;
+ return true;
+ default:
+ TRACKRENDERER_ERROR("unknown resource type");
+ return false;
+ }
+}
+
+bool ConvertToAdvPictureQualityType(
+ const TrackRendererAdvPictureQualityType& typevalue,
+ AdvPictureQualityType* type) {
+ switch (typevalue) {
+ case kTrackRendererAdvPictureQualityTypeVideoCall:
+ *type = AdvPictureQualityType::kVideoCall;
+ return true;
+ case kTrackRendererAdvPictureQualityTypeUsbCamera:
+ *type = AdvPictureQualityType::kUsbCamera;
+ return true;
+ default:
+ TRACKRENDERER_ERROR("unknown resource type");
+ return false;
+ }
+}
+
+bool ConvertToRscAllocPolicy(const TrackRendererRscAllocPolicy& policyvalue,
+ RscAllocPolicy* policy) {
+ switch (policyvalue) {
+ case kTrackRendererRscAllocExclusive:
+ *policy = RscAllocPolicy::kRscAllocExclusive;
+ return true;
+ case kTrackRendererRscAllocConditional:
+ *policy = RscAllocPolicy::kRscAllocConditional;
+ return true;
+ default:
+ TRACKRENDERER_ERROR("unknown policy");
+ return false;
+ }
+}
+
+void MakeRational(Rational* rational_info,
+ const TrackRendererRational& rational_attr) {
+ if (!rational_info) return;
+ rational_info->num = rational_attr.num;
+ rational_info->den = rational_attr.den;
+}
+
+bool ConvertToVideoRendererType(
+ const TrackRendererVideoRendererType& renderer_type,
+ ResourceCategory* rsc_category) {
+ if (rsc_category == nullptr) return false;
+ bool ret = true;
+ if (renderer_type == TrackRendererVideoRendererTypeMain) {
+ *rsc_category = ResourceCategory::kVideoRenderer;
+ } else if (renderer_type == TrackRendererVideoRendererTypeSub) {
+ *rsc_category = ResourceCategory::kVideoRendererSub;
+ } else if (renderer_type == TrackRendererVideoRendererTypeSub2) {
+ *rsc_category = ResourceCategory::kVideoRendererSub2;
+ } else if (renderer_type == TrackRendererVideoRendererTypeSub3) {
+ *rsc_category = ResourceCategory::kVideoRendererSub3;
+ } else {
+ ret = false;
+ }
+ return ret;
+}
+
+} // namespace capi_utils
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/trackrenderer_debug.h"
+
+#include <vconf.h>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include "trackrenderer/core/pipeline.hpp"
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+namespace debug {
+
+class ConcretePlayinfoSetter : public PlayinfoSetter {
+ public:
+ ConcretePlayinfoSetter() noexcept {}
+ ~ConcretePlayinfoSetter() {}
+
+ void SetAppInfo(const PlayerAppInfo& app_info) override;
+ bool SetStreamInfo(GstCaps* v_sink_caps, GstCaps* v_decoder_caps,
+ GstCaps* a_decoder_caps, const std::vector<Track>& tracks,
+ drm::Type drm_type) override;
+ void VconfSetMsgShow() override;
+ void VconfSetMsgUpdate() override;
+ void VconfSetMsgHide() override;
+
+ private:
+ bool is_shown_ = false;
+ StreamInfo stream_info_;
+ PlayerAppInfo app_info_;
+};
+
+static const char* VCONF_KEY_FOR_PLAYBACK_INFO = "memory/mm/playinfo";
+
+static gboolean SendMessage(gpointer data) {
+ std::stringstream* msg = (std::stringstream*)data;
+ // TRACKRENDERER_INFO("%s", msg->str().c_str());
+ vconf_set_str(VCONF_KEY_FOR_PLAYBACK_INFO, msg->str().c_str());
+ return false;
+}
+
+static void MessageDestructor(gpointer data) {
+ std::stringstream* msg = (std::stringstream*)data;
+ // TRACKRENDERER_INFO("%s", msg->str().c_str());
+ delete msg;
+ return;
+}
+
+std::string toString(drm::Type type) {
+ const char* drm_type = NULL;
+ switch (type) {
+ case drm::Type::kNone:
+ drm_type = "NONE";
+ break;
+ case drm::Type::kPlayready:
+ drm_type = "PLAYREADY";
+ break;
+ case drm::Type::kMarlin:
+ drm_type = "MARLIN";
+ break;
+ case drm::Type::kVerimatrix:
+ drm_type = "VERIMATRIX";
+ break;
+ case drm::Type::kWidevineClassic:
+ drm_type = "WIDEVINE CLASSIC";
+ break;
+ case drm::Type::kSecuremedia:
+ drm_type = "SECUREMEDIA";
+ break;
+ case drm::Type::kSdrm:
+ drm_type = "SDRM";
+ break;
+ case drm::Type::kWidevineCdm:
+ drm_type = "WIDEVINE CDM";
+ break;
+ default:
+ drm_type = "";
+ break;
+ }
+ return std::string(drm_type);
+}
+
+const char* GetAudioCodec(const GstCaps* caps) {
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+ if (!structure) return NULL;
+
+ const char* codec = gst_structure_get_name(structure);
+ if (g_strrstr(codec, "drm/")) {
+ codec = gst_structure_get_string(structure, "stream-type");
+ }
+
+ if (g_strrstr(codec, "audio/mpeg")) {
+ static char modifiedCodec[100] = {
+ 0,
+ };
+ int version = 0;
+ int layer = 0;
+ gst_structure_get_int(structure, "mpegversion", &version);
+ gst_structure_get_int(structure, "mpeglayer", &layer);
+ snprintf(modifiedCodec, 100, "%s_v%d_layer%d", codec, version, layer);
+ codec = modifiedCodec;
+ }
+ return codec;
+}
+
+const char* GetVideoCodec(const GstCaps* caps) {
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+ if (!structure) return NULL;
+
+ const char* codec = gst_structure_get_name(structure);
+ if (g_strrstr(codec, "drm/")) {
+ codec = gst_structure_get_string(structure, "stream-type");
+ }
+ return codec;
+}
+
+int GetVideoWidth(const GstCaps* caps) {
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+ if (!structure) return 0;
+
+ int width = 0;
+ if (!gst_structure_get_int(structure, "width", &width)) return 0;
+
+ return width;
+}
+
+int GetVideoHeight(const GstCaps* caps) {
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+ if (!structure) return 0;
+
+ int height = 0;
+ if (!gst_structure_get_int(structure, "height", &height)) return 0;
+
+ return height;
+}
+
+float GetVideoFrameRate(const GstCaps* caps) {
+ GstStructure* structure = gst_caps_get_structure(caps, 0);
+ if (!structure) return 0;
+
+ int num = 0, den = 0;
+ if (!gst_structure_get_fraction(structure, "framerate", &num, &den)) return 0;
+
+ if (den <= 0 || num <= 0) return 0;
+
+ return ((float)num) / den;
+}
+
+void SetVideoInfoFromSink(GstCaps* caps, StreamInfo& out_info) {
+ if (!caps) return;
+
+ out_info.v_width = GetVideoWidth(caps);
+ out_info.v_height = GetVideoHeight(caps);
+ out_info.fps = GetVideoFrameRate(caps);
+ return;
+}
+
+void SetVideoInfoFromDecoder(GstCaps* caps, StreamInfo& out_info) {
+ if (!caps) return;
+
+ out_info.v_codec = GetVideoCodec(caps);
+ return;
+}
+
+void SetVideoInfoFromDemuxer(const std::vector<Track>& tracks,
+ StreamInfo& out_info) {
+ for (const Track& track : tracks) {
+ if (track.type == kTrackTypeVideo) {
+ out_info.bitrate = track.bitrate;
+ }
+ }
+ return;
+}
+
+void SetAudioInfoFromDecoder(GstCaps* caps, StreamInfo& out_info) {
+ if (!caps) return;
+
+ out_info.a_codec = GetAudioCodec(caps);
+ return;
+}
+
+void FillMsgWithAppInfo(const PlayerAppInfo& app_info,
+ std::stringstream* out_message) {
+ if (app_info.id.empty())
+ *out_message << "appid="
+ << "_";
+ else
+ *out_message << "appid=" << app_info.id.c_str();
+
+ if (app_info.version.empty())
+ *out_message << "&app_version="
+ << "_";
+ else
+ *out_message << "&app_version=" << app_info.version.c_str();
+
+ if (app_info.type.empty())
+ *out_message << "&app_type="
+ << "_";
+ else
+ *out_message << "&app_type=" << app_info.type.c_str();
+
+ return;
+}
+
+void FillMsgWithStreamInfo(const StreamInfo& in_info,
+ std::stringstream* out_message) {
+ // InformationTicker will parse the keys by this delimeter '&'
+ if (in_info.a_codec) {
+ *out_message << "&a_codec=" << in_info.a_codec;
+ }
+
+ if (in_info.v_codec) {
+ *out_message << "&v_codec=" << in_info.v_codec;
+ }
+
+ if (in_info.v_width > 0 && in_info.v_height > 0) {
+ *out_message << "&v_resolution=" << in_info.v_width << "x"
+ << in_info.v_height;
+ }
+
+ if (in_info.fps > 0.0) {
+ *out_message << "&v_framerate=" << std::fixed << std::setprecision(2)
+ << in_info.fps << "fps";
+ }
+
+ if (in_info.drm_type > drm::Type::kNone) {
+ *out_message << "&drm_type=" << toString((drm::Type)in_info.drm_type);
+ }
+
+ if (in_info.bitrate > 0) {
+ *out_message << "&bitrate=" << in_info.bitrate << "bps";
+ }
+ return;
+}
+
+PlayinfoSetter::Ptr PlayinfoSetter::Create() {
+ return PlayinfoSetter::Ptr(new ConcretePlayinfoSetter);
+}
+
+void ConcretePlayinfoSetter::SetAppInfo(const PlayerAppInfo& app_info) {
+ app_info_ = app_info;
+}
+
+bool ConcretePlayinfoSetter::SetStreamInfo(GstCaps* v_sink_caps,
+ GstCaps* v_decoder_caps,
+ GstCaps* a_decoder_caps,
+ const std::vector<Track>& tracks,
+ drm::Type drm_type) {
+ StreamInfo info;
+ SetVideoInfoFromSink(v_sink_caps, info);
+ SetVideoInfoFromDecoder(v_decoder_caps, info);
+ SetAudioInfoFromDecoder(a_decoder_caps, info);
+ SetVideoInfoFromDemuxer(tracks, info);
+ info.drm_type = drm_type;
+
+ stream_info_ = info;
+ return true;
+}
+
+void ConcretePlayinfoSetter::VconfSetMsgShow() {
+ auto message = std::unique_ptr<std::stringstream>(new std::stringstream);
+ FillMsgWithAppInfo(app_info_, message.get());
+ *message.get() << "&display_mode=show";
+ FillMsgWithStreamInfo(stream_info_, message.get());
+
+ TRACKRENDERER_INFO("display : %s", message.get()->str().c_str());
+ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, (GSourceFunc)SendMessage,
+ (gpointer)message.release(), MessageDestructor);
+ is_shown_ = true;
+ return;
+}
+
+void ConcretePlayinfoSetter::VconfSetMsgUpdate() {
+ if (!is_shown_) return;
+
+ auto message = std::unique_ptr<std::stringstream>(new std::stringstream);
+ FillMsgWithAppInfo(app_info_, message.get());
+ *message.get() << "&display_mode=update";
+ FillMsgWithStreamInfo(stream_info_, message.get());
+
+ TRACKRENDERER_INFO("display : %s", message.get()->str().c_str());
+ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, (GSourceFunc)SendMessage,
+ (gpointer)message.release(), MessageDestructor);
+ return;
+}
+
+void ConcretePlayinfoSetter::VconfSetMsgHide() {
+ if (!is_shown_) return;
+ auto message = std::unique_ptr<std::stringstream>(new std::stringstream);
+ FillMsgWithAppInfo(app_info_, message.get());
+ *message.get() << "&display_mode=hide";
+
+ TRACKRENDERER_INFO("display : %s", message.get()->str().c_str());
+ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, (GSourceFunc)SendMessage,
+ (gpointer)message.release(), MessageDestructor);
+ is_shown_ = false;
+ return;
+}
+
+} // namespace debug
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/trackrenderer_vconf.h"
+
+#include <algorithm>
+
+#include "json/json.h"
+#include "trackrenderer/core/utils/log.h"
+
+namespace plusplayer {
+
+namespace trackrenderer {
+
+Vconf::Vconf() {
+ VconfCbList vconf_cb_list;
+ vconf_map_[kPowerAnimationVconf] = vconf_cb_list;
+}
+
+Vconf::~Vconf() {
+ vconf_map_[kPowerAnimationVconf].clear();
+ vconf_map_.clear();
+}
+
+bool Vconf::IsMultiscreenMode() {
+ TRACKRENDERER_ENTER
+ char* mls_json_info = vconf_get_str(kMultiscreenInfoVconf);
+ if (NULL == mls_json_info) {
+ TRACKRENDERER_ERROR("Fail to get memory/multiscreen/info vconf info.");
+ return false;
+ }
+ bool ret = ParseMlsVconf_(mls_json_info);
+ free(mls_json_info);
+
+ return ret;
+}
+
+void Vconf::SetVconfsCb(const std::vector<std::string>& names,
+ vconf_cb callback, void* userdata) {
+ TRACKRENDERER_ENTER
+ if (names.empty() || nullptr == callback || nullptr == userdata) return;
+
+ std::lock_guard<std::mutex> lk(vconf_m_);
+ for (auto& name : names) {
+ SetVconfCb_(name, callback, userdata);
+ }
+ TRACKRENDERER_LEAVE
+}
+
+void Vconf::UnsetVconfsCb(const std::vector<std::string>& names,
+ vconf_cb callback, void* userdata) {
+ if (names.empty() || nullptr == callback || nullptr == userdata) return;
+
+ std::lock_guard<std::mutex> lk(vconf_m_);
+ for (auto& name : names) {
+ UnsetVconfCb_(name, callback, userdata);
+ }
+}
+
+void Vconf::VconfCb_(keynode_t* key, void* userdata) {
+ TRACKRENDERER_ENTER
+ Vconf* vf = static_cast<Vconf*>(userdata);
+ if (nullptr == vf) return;
+ char* name = vconf_keynode_get_name(key);
+ if (nullptr == name) {
+ TRACKRENDERER_ERROR("vconf name is NULL!");
+ return;
+ }
+
+ TRACKRENDERER_INFO("vconf name[%s]", name);
+ std::string vf_name(name);
+ std::string value;
+ if (vf_name == kPowerAnimationVconf) {
+ int vf_value = vconf_keynode_get_int(key);
+ TRACKRENDERER_INFO("vconf memory/welcome_mode/power_animation, value[%d]",
+ vf_value);
+ if (vf_value > 1 || vf_value < 0) {
+ TRACKRENDERER_WARN("Skip power_animation vconf value[%d]", vf_value);
+ return;
+ }
+ int smsrc_status = 0;
+ if (vconf_get_int(kTV2MobileStateVconf, &smsrc_status)) {
+ TRACKRENDERER_ERROR("Fail to get[%s] value!", kTV2MobileStateVconf);
+ return;
+ }
+ if (smsrc_status) {
+ TRACKRENDERER_WARN("Skip TV2Mobile state[%d]", smsrc_status);
+ return;
+ }
+
+ value = (vf_value == 1) ? "true" : "false";
+ } else {
+ TRACKRENDERER_ERROR("Unknow vconf name: %s", name);
+ return;
+ }
+
+ vf->Notify_(vf_name, value);
+ TRACKRENDERER_LEAVE
+}
+
+void Vconf::Notify_(const std::string& name, const std::string& value) {
+ TRACKRENDERER_ENTER
+ std::lock_guard<std::mutex> lk(vconf_m_);
+ TRACKRENDERER_INFO("name[%s], value[%s]", name.c_str(), value.c_str());
+ for (auto& item : vconf_map_[name]) {
+ if (item.first) {
+ item.first(name, value, item.second);
+ TRACKRENDERER_INFO("Notify user[%p]", item.second);
+ }
+ }
+ TRACKRENDERER_LEAVE
+}
+
+void Vconf::SetVconfCb_(const std::string& name, vconf_cb callback,
+ void* userdata) {
+ TRACKRENDERER_INFO("name[%s], callback[%p], userdata[%p]", name.c_str(),
+ callback, userdata);
+ if (vconf_map_.count(name) == 0) {
+ TRACKRENDERER_ERROR("Not support vconf[%s]", name.c_str());
+ return;
+ }
+ if (vconf_map_[name].empty()) {
+ int ret = vconf_notify_key_changed(name.c_str(), Vconf::VconfCb_, this);
+ if (ret < 0) {
+ TRACKRENDERER_ERROR("vconf_notify_key_changed() return error[%d]", ret);
+ return;
+ }
+ TRACKRENDERER_INFO("Register vconf[%s] successfully", name.c_str());
+ }
+
+ auto is_exist = [callback, userdata](const VconfPair& item) {
+ return (callback == item.first) && (userdata == item.second);
+ };
+ auto target =
+ std::find_if(vconf_map_[name].begin(), vconf_map_[name].end(), is_exist);
+ if (target != vconf_map_[name].end()) {
+ TRACKRENDERER_ERROR("User[%p] has existed!", userdata);
+ return;
+ }
+ vconf_map_[name].push_back(std::make_pair(callback, userdata));
+}
+
+void Vconf::UnsetVconfCb_(const std::string& name, vconf_cb callback,
+ void* userdata) {
+ TRACKRENDERER_INFO("name[%s], callback[%p], userdata[%p]", name.c_str(),
+ callback, userdata);
+ if (vconf_map_.count(name) == 0) {
+ TRACKRENDERER_ERROR("Not support vconf[%s]", name.c_str());
+ return;
+ }
+ if (vconf_map_[name].empty()) return;
+
+ auto is_exist = [callback, userdata](const VconfPair& item) {
+ return (callback == item.first) && (userdata == item.second);
+ };
+ vconf_map_[name].remove_if(is_exist);
+
+ if (vconf_map_[name].empty()) {
+ int ret = vconf_ignore_key_changed(name.c_str(), Vconf::VconfCb_);
+ if (ret < 0) {
+ TRACKRENDERER_ERROR("vconf_ignore_key_changed() return error[%d]", ret);
+ } else {
+ TRACKRENDERER_INFO("Unregister vconf[%s] successfully.", name.c_str());
+ }
+ }
+}
+
+bool Vconf::ParseMlsVconf_(const char* json) {
+ TRACKRENDERER_INFO("memory/multiscreen/info: %s", json);
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(json, root)) {
+ TRACKRENDERER_ERROR(
+ "Fail to parse memory/multiscreen/info content info[%s], error[%s]",
+ json, (reader.getFormatedErrorMessages()).c_str());
+ return false;
+ }
+ std::string mode = root["mode"].asString();
+ TRACKRENDERER_INFO("Multiscreen mode[%s]", mode.c_str());
+ return (mode == "on") ? true : false;
+}
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+//
+// @ Copyright [2021] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "trackrenderer/core/utils/log.h"
+#include "trackrenderer/vr360.h"
+
+#include <dlfcn.h>
+#include <mutex>
+namespace plusplayer {
+
+namespace trackrenderer {
+
+#define VR360_LIB_PATH "/usr/lib/libvr360.so"
+
+static std::once_flag lib_loaded;
+static void* vr360_lib = nullptr;
+
+static Vr360Initialize vr360_tz_initialize = nullptr;
+static Vr360Finalize vr360_tz_finalize = nullptr;
+static Vr360SetGpuMode vr360_tz_set_gpu_mode = nullptr;
+
+Vr360::Vr360() {
+ auto loadlib = []() {
+ TRACKRENDERER_ENTER
+ vr360_lib = dlopen(VR360_LIB_PATH, RTLD_LAZY);
+ if (!vr360_lib) {
+ TRACKRENDERER_ERROR("libvr360.so open failed: %s", dlerror());
+ return;
+ }
+ vr360_tz_initialize =
+ (Vr360Initialize)dlsym(vr360_lib, "vr360_tz_initialize");
+ if (!vr360_tz_initialize) {
+ TRACKRENDERER_ERROR("Failed to import vr360_tz_initialize");
+ return;
+ }
+ vr360_tz_finalize = (Vr360Finalize)dlsym(vr360_lib, "vr360_tz_finalize");
+ if (!vr360_tz_finalize) {
+ TRACKRENDERER_ERROR("Failed to import vr360_tz_finalize");
+ return;
+ }
+ vr360_tz_set_gpu_mode =
+ (Vr360SetGpuMode)dlsym(vr360_lib, "vr360_tz_set_gpu_mode");
+ if (!vr360_tz_set_gpu_mode) {
+ TRACKRENDERER_ERROR("Failed to import vr360_tz_set_gpu_mode");
+ return;
+ }
+ };
+ std::call_once(lib_loaded, loadlib);
+}
+
+Vr360::~Vr360() {}
+
+void Vr360::Vr360TzSetGpuMode(bool set) {
+ TRACKRENDERER_ENTER
+ if (vr360_tz_initialize == nullptr || vr360_tz_finalize == nullptr ||
+ vr360_tz_set_gpu_mode == nullptr)
+ return;
+ vr360_tz_initialize(&vr360_tz_handle_);
+ if (set) {
+ vr360_tz_set_gpu_mode(vr360_tz_handle_,
+ static_cast<int>(GpuMode::kVr360GpuModeSecure));
+ } else {
+ vr360_tz_set_gpu_mode(vr360_tz_handle_,
+ static_cast<int>(GpuMode::kVr360GpuModeNonSecure));
+ }
+ vr360_tz_finalize(vr360_tz_handle_);
+ vr360_tz_handle_ = nullptr;
+ TRACKRENDERER_LEAVE
+}
+
+} // namespace trackrenderer
+
+} // namespace plusplayer
--- /dev/null
+unit_test/ut_plusplayer_all.xml
\ No newline at end of file
--- /dev/null
+unit_test/ut_plusplayer_1.xml
\ No newline at end of file
--- /dev/null
+unit_test/ut_plusplayer_2.xml
\ No newline at end of file
--- /dev/null
+unit_test/ut_plusplayer_3.xml
\ No newline at end of file
--- /dev/null
+unit_test/ut_plusplayer_4.xml
\ No newline at end of file
--- /dev/null
+unit_test/ut_plusplayer_5.xml
\ No newline at end of file
--- /dev/null
+unit_test/ut_plusplayer_6.xml
\ No newline at end of file
--- /dev/null
+unit_test/ut_plusplayer_7.xml
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestFarm>
+ <Target AssignTargets="1">
+ <TestPackage Name="plusplayer" RpmName="plusplayer-ut-component-tomato" DatFile="TCList_test_1.dat"/>
+ </Target>
+ <Target AssignTargets="1">
+ <TestPackage Name="plusplayer" RpmName="plusplayer-ut-component-tomato" DatFile="TCList_test_2.dat"/>
+ </Target>
+ <Target AssignTargets="1">
+ <TestPackage Name="plusplayer" RpmName="plusplayer-ut-component-tomato" DatFile="TCList_test_3.dat"/>
+ </Target>
+ <Target AssignTargets="1">
+ <TestPackage Name="plusplayer" RpmName="plusplayer-ut-component-tomato" DatFile="TCList_test_4.dat"/>
+ </Target>
+ <Target AssignTargets="1">
+ <TestPackage Name="plusplayer" RpmName="plusplayer-ut-component-tomato" DatFile="TCList_test_5.dat"/>
+ </Target>
+ <Target AssignTargets="1">
+ <TestPackage Name="plusplayer" RpmName="plusplayer-ut-component-tomato" DatFile="TCList_test_6.dat"/>
+ </Target>
+ <Target AssignTargets="1">
+ <TestPackage Name="plusplayer" RpmName="plusplayer-ut-component-tomato" DatFile="TCList_test_7.dat"/>
+ </Target>
+</TestFarm>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestCase Name="Plusplayer API Test" Description="Plusplayer APIs unit test" LogFilter="GST_LOG:* TOMATO:* PLUSPLAYER:* STREAMING_ENGINE:*">
+ <Procedure Number="1" Description="Plusplayer unit test - 1/7">
+ <Step Name="Plusplayer prepare test" Type="EXT_TEST_PACKAGE" Command="/usr/bin/player-ut --gtest_filter=*PlusplayerTest_Prepare* --gtest_output=xml:/usr/etc/plusplayer_prepare_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_prepare_tests_result.xml"/>
+ </Step>
+
+ <Step Name="Plusplayer unit test - step : 1" Type="EXT_TEST_PACKAGE" Command="export GTEST_TOTAL_SHARDS=7 GTEST_SHARD_INDEX=0;/usr/bin/plusplayer_ut --gtest_output=xml:/usr/etc/plusplayer_tests_result_1.xml" Permission="ROOT">
+ <Input Expirytime="3600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_tests_result_1.xml"/>
+ </Step>
+ </Procedure>
+</TestCase>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestCase Name="Plusplayer API Test" Description="Plusplayer APIs unit test" LogFilter="GST_LOG:* TOMATO:* PLUSPLAYER:* STREAMING_ENGINE:*">
+ <Procedure Number="1" Description="Plusplayer unit test - 2/7">
+ <Step Name="Plusplayer prepare test" Type="EXT_TEST_PACKAGE" Command="/usr/bin/player-ut --gtest_filter=*PlusplayerTest_Prepare* --gtest_output=xml:/usr/etc/plusplayer_prepare_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_prepare_tests_result.xml"/>
+ </Step>
+
+ <Step Name="Plusplayer unit test - step : 2" Type="EXT_TEST_PACKAGE" Command="export GTEST_TOTAL_SHARDS=7 GTEST_SHARD_INDEX=1;/usr/bin/plusplayer_ut --gtest_output=xml:/usr/etc/plusplayer_tests_result_2.xml" Permission="ROOT">
+ <Input Expirytime="3600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_tests_result_2.xml"/>
+ </Step>
+ </Procedure>
+</TestCase>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestCase Name="Plusplayer API Test" Description="Plusplayer APIs unit test" LogFilter="GST_LOG:* TOMATO:* PLUSPLAYER:* STREAMING_ENGINE:*">
+ <Procedure Number="1" Description="Plusplayer unit test - 3/7">
+ <Step Name="Plusplayer prepare test" Type="EXT_TEST_PACKAGE" Command="/usr/bin/player-ut --gtest_filter=*PlusplayerTest_Prepare* --gtest_output=xml:/usr/etc/plusplayer_prepare_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_prepare_tests_result.xml"/>
+ </Step>
+
+ <Step Name="Plusplayer unit test - step : 3" Type="EXT_TEST_PACKAGE" Command="export GTEST_TOTAL_SHARDS=7 GTEST_SHARD_INDEX=2;/usr/bin/plusplayer_ut --gtest_output=xml:/usr/etc/plusplayer_tests_result_3.xml" Permission="ROOT">
+ <Input Expirytime="3600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_tests_result_3.xml"/>
+ </Step>
+ </Procedure>
+</TestCase>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestCase Name="Plusplayer API Test" Description="Plusplayer APIs unit test" LogFilter="GST_LOG:* TOMATO:* PLUSPLAYER:* STREAMING_ENGINE:*">
+ <Procedure Number="1" Description="Plusplayer unit test - 4/7">
+ <Step Name="Plusplayer prepare test" Type="EXT_TEST_PACKAGE" Command="/usr/bin/player-ut --gtest_filter=*PlusplayerTest_Prepare* --gtest_output=xml:/usr/etc/plusplayer_prepare_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_prepare_tests_result.xml"/>
+ </Step>
+
+ <Step Name="Plusplayer unit test - step : 4" Type="EXT_TEST_PACKAGE" Command="export GTEST_TOTAL_SHARDS=7 GTEST_SHARD_INDEX=3;/usr/bin/plusplayer_ut --gtest_output=xml:/usr/etc/plusplayer_tests_result_4.xml" Permission="ROOT">
+ <Input Expirytime="3600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_tests_result_4.xml"/>
+ </Step>
+ </Procedure>
+</TestCase>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestCase Name="Plusplayer API Test" Description="Plusplayer APIs unit test" LogFilter="GST_LOG:* TOMATO:* PLUSPLAYER:* STREAMING_ENGINE:*">
+ <Procedure Number="1" Description="Plusplayer unit test - 5/7">
+ <Step Name="Plusplayer prepare test" Type="EXT_TEST_PACKAGE" Command="/usr/bin/player-ut --gtest_filter=*PlusplayerTest_Prepare* --gtest_output=xml:/usr/etc/plusplayer_prepare_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_prepare_tests_result.xml"/>
+ </Step>
+
+ <Step Name="Plusplayer unit test - step : 5" Type="EXT_TEST_PACKAGE" Command="export GTEST_TOTAL_SHARDS=7 GTEST_SHARD_INDEX=4;/usr/bin/plusplayer_ut --gtest_output=xml:/usr/etc/plusplayer_tests_result_5.xml" Permission="ROOT">
+ <Input Expirytime="3600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_tests_result_5.xml"/>
+ </Step>
+ </Procedure>
+</TestCase>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestCase Name="Plusplayer API Test" Description="Plusplayer APIs unit test" LogFilter="GST_LOG:* TOMATO:* PLUSPLAYER:* STREAMING_ENGINE:*">
+ <Procedure Number="1" Description="Plusplayer unit test - 6/7">
+ <Step Name="Plusplayer prepare test" Type="EXT_TEST_PACKAGE" Command="/usr/bin/player-ut --gtest_filter=*PlusplayerTest_Prepare* --gtest_output=xml:/usr/etc/plusplayer_prepare_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_prepare_tests_result.xml"/>
+ </Step>
+
+ <Step Name="Plusplayer unit test - step : 6" Type="EXT_TEST_PACKAGE" Command="export GTEST_TOTAL_SHARDS=7 GTEST_SHARD_INDEX=5;/usr/bin/plusplayer_ut --gtest_output=xml:/usr/etc/plusplayer_tests_result_6.xml" Permission="ROOT">
+ <Input Expirytime="3600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_tests_result_6.xml"/>
+ </Step>
+ </Procedure>
+</TestCase>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestCase Name="Plusplayer API Test" Description="Plusplayer APIs unit test" LogFilter="GST_LOG:* TOMATO:* PLUSPLAYER:* STREAMING_ENGINE:*">
+ <Procedure Number="1" Description="Plusplayer unit test - 7/7">
+ <Step Name="Plusplayer prepare test" Type="EXT_TEST_PACKAGE" Command="/usr/bin/player-ut --gtest_filter=*PlusplayerTest_Prepare* --gtest_output=xml:/usr/etc/plusplayer_prepare_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_prepare_tests_result.xml"/>
+ </Step>
+
+ <Step Name="Plusplayer unit test - step : 7" Type="EXT_TEST_PACKAGE" Command="export GTEST_TOTAL_SHARDS=7 GTEST_SHARD_INDEX=6;/usr/bin/plusplayer_ut --gtest_output=xml:/usr/etc/plusplayer_tests_result_7.xml" Permission="ROOT">
+ <Input Expirytime="3600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_tests_result_7.xml"/>
+ </Step>
+ </Procedure>
+</TestCase>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestCase Name="Plusplayer API Test" Description="Plusplayer APIs unit test" LogFilter="GST_LOG:* TOMATO:* PLUSPLAYER:* STREAMING_ENGINE:*">
+ <Procedure Number="1" Description="Plusplayer unit test">
+ <Step Name="Plusplayer prepare test" Type="EXT_TEST_PACKAGE" Command="/usr/bin/player-ut --gtest_filter=*PlusplayerTest_Prepare* --gtest_output=xml:/usr/etc/plusplayer_prepare_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_prepare_tests_result.xml"/>
+ </Step>
+
+ <Step Name="Plusplayer unit test" Type="EXT_TEST_PACKAGE" Command="/usr/bin/plusplayer_ut --gtest_output=xml:/usr/etc/plusplayer_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="3600"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/plusplayer_tests_result.xml"/>
+ </Step>
+ </Procedure>
+</TestCase>
--- /dev/null
+SET(fw_name "${PROJECT_NAME}_ut")
+
+SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
+SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
+
+SET(${fw_name}_CXXFLAGS "-Wall -Werror -std=c++11 -pthread -fPIE -Wl,-z,relro -fstack-protector -fno-delete-null-pointer-checks -DEFL_BETA_API_SUPPORT")
+
+SET(${fw_name}_LDFLAGS)
+
+SET(ADD_LIBS
+ "trackrenderer"
+ "gstvideo-1.0"
+)
+
+SET(dependents "gstreamer-1.0 glib-2.0 gstreamer-plugins-base-1.0 gstreamer-app-1.0 dlog gtest_gmock"
+ "boost"
+ "tv-resource-information tv-resource-manager libresourced appcore-efl elementary ecore evas ecore-wl2 audio-control"
+ "capi-media-player"
+ )
+IF(SUPPORT_SOUNDBAR)
+SET(dependents ${dependents} "libavoc-av")
+ELSE(SUPPORT_SOUNDBAR)
+SET(dependents ${dependents} "libavoc")
+ENDIF(SUPPORT_SOUNDBAR)
+
+INCLUDE(FindPkgConfig)
+IF(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l)
+pkg_check_modules(${fw_name} REQUIRED ${dependents})
+ELSE(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l)
+pkg_check_modules(${fw_name} REQUIRED ${dependents})
+ENDIF(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l)
+
+FOREACH(flag ${${fw_name}_CFLAGS})
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+FOREACH(flag ${${fw_name}_CXXFLAGS})
+SET(EXTRA_CXXFLAGS "${EXTRA_CXXFLAGS} ${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS}")
+
+INCLUDE_DIRECTORIES(
+ ${PROJECT_SOURCE_DIR}/src
+ ${PROJECT_SOURCE_DIR}/include
+ ${PROJECT_SOURCE_DIR}
+ ${PROJECT_SOURCE_DIR}/src/plusplayer-core/include_internal
+ ${PROJECT_SOURCE_DIR}/src/plusplayer/include_internal
+ ${PROJECT_SOURCE_DIR}/src/esplusplayer/include_internal
+ ${PROJECT_SOURCE_DIR}/src/tracksource/include_internal
+ ${PROJECT_SOURCE_DIR}/src/trackrenderer/include_internal
+)
+
+SET(UT_SRC
+ # src/ut_defaultplayer.cpp
+ # src/ut_espacket.cpp
+ # src/ut_esplayer.cpp
+ # src/ut_esplayer2.cpp
+ # src/ut_esplayer_trackrenderer.cpp
+ # src/ut_dashplayback.cpp
+ # src/ut_display.cpp
+ # src/ut_gst.cpp
+ # src/ut_gstdashtracksource.cpp
+ # src/ut_gsthlstracksource.cpp
+ # src/ut_gsttrackrenderer.cpp
+ # src/ut_gsttypefinder.cpp
+ # src/ut_hlstracksource.cpp
+ # src/ut_tracksource.cpp
+ # src/ut_external_subtitle.cpp
+ src/ut_main.cpp
+ # src/ut_miscellaneous.cpp
+ # src/ut_pipeline.cpp
+ # src/ut_resource_manager.cpp
+ # src/ut_serializer.cpp
+ # src/ut_streamreader.cpp
+ # src/ut_typefinder.cpp
+ # src/ut_statemanager.cpp
+ # src/ut_external_subtitle.cpp
+ # src/ut_example.cpp
+ # temporarily disabled - invalid tc , build errors : improve it or delete it
+ # src/ut_trackrendereradapter.cpp
+ # src/ut_trackrenderer.cpp
+ # src/ut_trackrenderer_capi.cpp
+ #src/esplusplayer/ut_basic.cpp
+ #src/esplusplayer/ut_display.cpp
+)
+
+ADD_EXECUTABLE(${fw_name} ${UT_SRC})
+
+LINK_DIRECTORIES(${LIB_INSTALL_DIR})
+
+TARGET_LINK_LIBRARIES(${fw_name}
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${ADD_LIBS}
+ ${${fw_name}_LDFLAGS}
+ "-pie"
+)
+
+INSTALL(
+ TARGETS ${fw_name}
+ DESTINATION bin
+)
--- /dev/null
+**GTest guide** \r
+===============\r
+ For unit test for plusplayer\r
+\r
+---\r
+### Reference \r
+- <https://github.com/google/googletest>\r
+\r
+## Assertion \r
+* ASSERT_* : 실패시 해당 테스트를 바로 종료 <br>\r
+* EXPECT_* : 실패하여도 테스트 계속 진행 <br>\r
+\r
+#### Basic Assertions ###\r
+\r
+These assertions do basic true/false condition testing.\r
+\r
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |\r
+|:--------------------|:-----------------------|:-------------|\r
+| `ASSERT_TRUE(`_condition_`)`; | `EXPECT_TRUE(`_condition_`)`; | _condition_ is true |\r
+| `ASSERT_FALSE(`_condition_`)`; | `EXPECT_FALSE(`_condition_`)`; | _condition_ is false |\r
+\r
+#### Binary Comparison ###\r
+\r
+This section describes assertions that compare two values.\r
+\r
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |\r
+|:--------------------|:-----------------------|:-------------|\r
+|`ASSERT_EQ(`_val1_`, `_val2_`);`|`EXPECT_EQ(`_val1_`, `_val2_`);`| _val1_ `==` _val2_ |\r
+|`ASSERT_NE(`_val1_`, `_val2_`);`|`EXPECT_NE(`_val1_`, `_val2_`);`| _val1_ `!=` _val2_ |\r
+|`ASSERT_LT(`_val1_`, `_val2_`);`|`EXPECT_LT(`_val1_`, `_val2_`);`| _val1_ `<` _val2_ |\r
+|`ASSERT_LE(`_val1_`, `_val2_`);`|`EXPECT_LE(`_val1_`, `_val2_`);`| _val1_ `<=` _val2_ |\r
+|`ASSERT_GT(`_val1_`, `_val2_`);`|`EXPECT_GT(`_val1_`, `_val2_`);`| _val1_ `>` _val2_ |\r
+|`ASSERT_GE(`_val1_`, `_val2_`);`|`EXPECT_GE(`_val1_`, `_val2_`);`| _val1_ `>=` _val2_ |\r
+\r
+\r
+#### String Comparison ###\r
+\r
+The assertions in this group compare two **C strings**.<br>\r
+If you want to compare two `string` objects, use `EXPECT_EQ`, `EXPECT_NE`, and etc instead.\r
+\r
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |\r
+|:--------------------|:-----------------------|:-------------|\r
+| `ASSERT_STREQ(`_str1_`, `_str2_`);` | `EXPECT_STREQ(`_str1_`, `_str_2`);` | the two C strings have the same content |\r
+| `ASSERT_STRNE(`_str1_`, `_str2_`);` | `EXPECT_STRNE(`_str1_`, `_str2_`);` | the two C strings have different content |\r
+| `ASSERT_STRCASEEQ(`_str1_`, `_str2_`);`| `EXPECT_STRCASEEQ(`_str1_`, `_str2_`);` | the two C strings have the same content, ignoring case |\r
+| `ASSERT_STRCASENE(`_str1_`, `_str2_`);`| `EXPECT_STRCASENE(`_str1_`, `_str2_`);` | the two C strings have different content, ignoring case |\r
+\r
+## Text Fixtures : Using the Same Data Configuration for Multiple Tests ##\r
+\r
+사용자가 유사한 data를 사용해서 하나 이상의 test를 작성한다면, test fixture를 사용할 수 있다. 이 test fixture를 사용한다는 것은 여러개의 다양한 test를 작성하는 과정에서 같은 object의 configuration을 재사용한다는 것을 의미한다.\r
+\r
+Fixture를 작성할 때에는 아래의 내용대로 수행하면 된다.\r
+\r
+1. ::testing::Test 로부터 class를 derive한다. Sub-class 에서 fixture member에 접근해야 하기 때문에 protected 혹은 public 으로 작성해야 한다. \r
+2. Class 내부에서 사용자가 원하는대로 object들을 선언해 사용한다. \r
+3. 필요하다면, 생성자나 SetUp() function을 작성해둔다. \r
+4. 생성자나 SetUp() function을 정의해서 사용하고 있다면, 해당 function에서 사용했던 resource를 반환하기 위해 소멸자나 TearDown() function을 작성한다. \r
+5. Subroutine 들을 작성한다. \r
+\r
+Fixture를 사용하기 위해서는 TEST() 대신에 TEST_F()를 사용해야만 한다.\r
+TEST()에서는 첫번째 argument가 testcase의 이름이었지만 TEST_F()를 사용할 때는 첫번째 argument로 test fixture class의 이름을 사용해야만 한다.\r
+\r
+**Fixture class 기본 구현 Form**\r
+* 관례에 따라 테스트할 클래스가 Foo라면 이름을 FooTest라고 하는게 좋다.\r
+~~~\r
+class PlusPlayerTest : public ::testing::Test {\r
+public:\r
+ PlusPlayerTest(std::string url)\r
+ : plusplayer_(nullptr), url_(url)\r
+ {\r
+ }\r
+\r
+ void SetUp() override\r
+ {\r
+ plusplayer_ = new PlusPlayer();\r
+ create(url_);\r
+ }\r
+\r
+ void TearDown() override\r
+ {\r
+ destory(plusplayer_);\r
+ }\r
+\r
+private:\r
+ std::string url_;\r
+ PlusPlayer* plusplayer_;\r
+\r
+}\r
+~~~\r
+\r
+**실행 순서** \r
+1. 모든 구글 테스트 플래그 상태를 저장한다. \r
+2. 첫 번째 테스트에 대해 테스트 fixture 객체를 생성한다. \r
+3. 만든 객체를 SetUp()에서 초기화한다. \r
+4. 픽스처 객체에 대해 테스트를 실행한다. \r
+5. TearDown()에서 해당 픽스처를 정리한다. \r
+6. 해당 픽스처를 삭제한다. \r
+7. 모든 구글 테스트 플래그 상태를 복원한다. \r
+8. 모든 테스트를 마칠 때까지 다음 테스트에 대해 위 과정을 반복한다. \r
+\r
+---\r
+\r
+## Arguments \r
+reference\r
+\r
+1. test selection\r
+ * --gtest_list_tests <br>\r
+ > 테스트할 항목을 보여준다. (테스트 실시 X)\r
+ * --gtest_also_run_disabled_tests <br>\r
+ > DISABLED_ 로 막아둔 test case 를 일시적으로 실행\r
+ * --gtest_filter <br>\r
+ > 특정 case 들만 실행 가능<br>\r
+ Ex) --gtest_filter="*.create*" : 모든 TC중에서 create 로 시작하는 모든 TC 실행. <br>\r
+\r
+2. test Execution\r
+ * --gtest_repeat <br>\r
+ > test 반복 가능. -1일 경우 무한히 반복<br>\r
+ --gtest_break_on_failure와 함께 사용하면 실패할 경우 멈춤.\r
+ * --gtest_shuffle <br>\r
+ > 무작위로 실행 가능 (test case 간 dependency 가 없어야 하기 때문이다)<br>\r
+ gtest는 기본적으로 현재시간을 랜덤함수의 시드값으로 사용하나, --gtest_random_seed로 조절가능하다\r
+ * --gtest_random_seed \r
+ > 1 ~ 99999까지의 값을 --gtest_shuffle에서 사용할 랜덤함수의 시드로 사용.\r
+\r
+---\r
+## Reference\r
+ * Gtest Primer(EN)<br> <https://github.com/google/googletest/blob/master/googletest/docs/Primer.md>\r
--- /dev/null
+#!/usr/bin/env python\r
+#\r
+# Copyright (c) 2009 Google Inc. All rights reserved.\r
+#\r
+# Redistribution and use in source and binary forms, with or without\r
+# modification, are permitted provided that the following conditions are\r
+# met:\r
+#\r
+# * Redistributions of source code must retain the above copyright\r
+# notice, this list of conditions and the following disclaimer.\r
+# * Redistributions in binary form must reproduce the above\r
+# copyright notice, this list of conditions and the following disclaimer\r
+# in the documentation and/or other materials provided with the\r
+# distribution.\r
+# * Neither the name of Google Inc. nor the names of its\r
+# contributors may be used to endorse or promote products derived from\r
+# this software without specific prior written permission.\r
+#\r
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+"""Does google-lint on c++ files.\r
+\r
+The goal of this script is to identify places in the code that *may*\r
+be in non-compliance with google style. It does not attempt to fix\r
+up these problems -- the point is to educate. It does also not\r
+attempt to find all problems, or to ensure that everything it does\r
+find is legitimately a problem.\r
+\r
+In particular, we can get very confused by /* and // inside strings!\r
+We do a small hack, which is to ignore //'s with "'s after them on the\r
+same line, but it is far from perfect (in either direction).\r
+"""\r
+\r
+import codecs\r
+import copy\r
+import getopt\r
+import math # for log\r
+import os\r
+import re\r
+import sre_compile\r
+import string\r
+import sys\r
+import unicodedata\r
+\r
+\r
+_USAGE = """\r
+Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]\r
+ [--counting=total|toplevel|detailed] [--root=subdir]\r
+ [--linelength=digits] [--headers=x,y,...]\r
+ <file> [file] ...\r
+\r
+ The style guidelines this tries to follow are those in\r
+ https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml\r
+\r
+ Every problem is given a confidence score from 1-5, with 5 meaning we are\r
+ certain of the problem, and 1 meaning it could be a legitimate construct.\r
+ This will miss some errors, and is not a substitute for a code review.\r
+\r
+ To suppress false-positive errors of a certain category, add a\r
+ 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*)\r
+ suppresses errors of all categories on that line.\r
+\r
+ The files passed in will be linted; at least one file must be provided.\r
+ Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the\r
+ extensions with the --extensions flag.\r
+\r
+ Flags:\r
+\r
+ output=vs7\r
+ By default, the output is formatted to ease emacs parsing. Visual Studio\r
+ compatible output (vs7) may also be used. Other formats are unsupported.\r
+\r
+ verbose=#\r
+ Specify a number 0-5 to restrict errors to certain verbosity levels.\r
+\r
+ filter=-x,+y,...\r
+ Specify a comma-separated list of category-filters to apply: only\r
+ error messages whose category names pass the filters will be printed.\r
+ (Category names are printed with the message and look like\r
+ "[whitespace/indent]".) Filters are evaluated left to right.\r
+ "-FOO" and "FOO" means "do not print categories that start with FOO".\r
+ "+FOO" means "do print categories that start with FOO".\r
+\r
+ Examples: --filter=-whitespace,+whitespace/braces\r
+ --filter=whitespace,runtime/printf,+runtime/printf_format\r
+ --filter=-,+build/include_what_you_use\r
+\r
+ To see a list of all the categories used in cpplint, pass no arg:\r
+ --filter=\r
+\r
+ counting=total|toplevel|detailed\r
+ The total number of errors found is always printed. If\r
+ 'toplevel' is provided, then the count of errors in each of\r
+ the top-level categories like 'build' and 'whitespace' will\r
+ also be printed. If 'detailed' is provided, then a count\r
+ is provided for each category like 'build/class'.\r
+\r
+ root=subdir\r
+ The root directory used for deriving header guard CPP variable.\r
+ By default, the header guard CPP variable is calculated as the relative\r
+ path to the directory that contains .git, .hg, or .svn. When this flag\r
+ is specified, the relative path is calculated from the specified\r
+ directory. If the specified directory does not exist, this flag is\r
+ ignored.\r
+\r
+ Examples:\r
+ Assuming that src/.git exists, the header guard CPP variables for\r
+ src/chrome/browser/ui/browser.h are:\r
+\r
+ No flag => CHROME_BROWSER_UI_BROWSER_H_\r
+ --root=chrome => BROWSER_UI_BROWSER_H_\r
+ --root=chrome/browser => UI_BROWSER_H_\r
+\r
+ linelength=digits\r
+ This is the allowed line length for the project. The default value is\r
+ 80 characters.\r
+\r
+ Examples:\r
+ --linelength=120\r
+\r
+ extensions=extension,extension,...\r
+ The allowed file extensions that cpplint will check\r
+\r
+ Examples:\r
+ --extensions=hpp,cpp\r
+\r
+ headers=x,y,...\r
+ The header extensions that cpplint will treat as .h in checks. Values are\r
+ automatically added to --extensions list.\r
+\r
+ Examples:\r
+ --headers=hpp,hxx\r
+ --headers=hpp\r
+\r
+ cpplint.py supports per-directory configurations specified in CPPLINT.cfg\r
+ files. CPPLINT.cfg file can contain a number of key=value pairs.\r
+ Currently the following options are supported:\r
+\r
+ set noparent\r
+ filter=+filter1,-filter2,...\r
+ exclude_files=regex\r
+ linelength=80\r
+ root=subdir\r
+ headers=x,y,...\r
+\r
+ "set noparent" option prevents cpplint from traversing directory tree\r
+ upwards looking for more .cfg files in parent directories. This option\r
+ is usually placed in the top-level project directory.\r
+\r
+ The "filter" option is similar in function to --filter flag. It specifies\r
+ message filters in addition to the |_DEFAULT_FILTERS| and those specified\r
+ through --filter command-line flag.\r
+\r
+ "exclude_files" allows to specify a regular expression to be matched against\r
+ a file name. If the expression matches, the file is skipped and not run\r
+ through liner.\r
+\r
+ "linelength" allows to specify the allowed line length for the project.\r
+\r
+ The "root" option is similar in function to the --root flag (see example\r
+ above).\r
+ \r
+ The "headers" option is similar in function to the --headers flag \r
+ (see example above).\r
+\r
+ CPPLINT.cfg has an effect on files in the same directory and all\r
+ sub-directories, unless overridden by a nested configuration file.\r
+\r
+ Example file:\r
+ filter=-build/include_order,+build/include_alpha\r
+ exclude_files=.*\.cc\r
+\r
+ The above example disables build/include_order warning and enables\r
+ build/include_alpha as well as excludes all .cc from being\r
+ processed by linter, in the current directory (where the .cfg\r
+ file is located) and all sub-directories.\r
+"""\r
+\r
+# We categorize each error message we print. Here are the categories.\r
+# We want an explicit list so we can list them all in cpplint --filter=.\r
+# If you add a new error message with a new category, add it to the list\r
+# here! cpplint_unittest.py should tell you if you forget to do this.\r
+_ERROR_CATEGORIES = [\r
+ 'build/class',\r
+ 'build/c++11',\r
+ 'build/c++14',\r
+ 'build/c++tr1',\r
+ 'build/deprecated',\r
+ 'build/endif_comment',\r
+ 'build/explicit_make_pair',\r
+ 'build/forward_decl',\r
+ 'build/header_guard',\r
+ 'build/include',\r
+ 'build/include_alpha',\r
+ 'build/include_order',\r
+ 'build/include_what_you_use',\r
+ 'build/namespaces',\r
+ 'build/printf_format',\r
+ 'build/storage_class',\r
+ 'legal/copyright',\r
+ 'readability/alt_tokens',\r
+ 'readability/braces',\r
+ 'readability/casting',\r
+ 'readability/check',\r
+ 'readability/constructors',\r
+ 'readability/fn_size',\r
+ 'readability/inheritance',\r
+ 'readability/multiline_comment',\r
+ 'readability/multiline_string',\r
+ 'readability/namespace',\r
+ 'readability/nolint',\r
+ 'readability/nul',\r
+ 'readability/strings',\r
+ 'readability/todo',\r
+ 'readability/utf8',\r
+ 'runtime/arrays',\r
+ 'runtime/casting',\r
+ 'runtime/explicit',\r
+ 'runtime/int',\r
+ 'runtime/init',\r
+ 'runtime/invalid_increment',\r
+ 'runtime/member_string_references',\r
+ 'runtime/memset',\r
+ 'runtime/indentation_namespace',\r
+ 'runtime/operator',\r
+ 'runtime/printf',\r
+ 'runtime/printf_format',\r
+ 'runtime/references',\r
+ 'runtime/string',\r
+ 'runtime/threadsafe_fn',\r
+ 'runtime/vlog',\r
+ 'whitespace/blank_line',\r
+ 'whitespace/braces',\r
+ 'whitespace/comma',\r
+ 'whitespace/comments',\r
+ 'whitespace/empty_conditional_body',\r
+ 'whitespace/empty_if_body',\r
+ 'whitespace/empty_loop_body',\r
+ 'whitespace/end_of_line',\r
+ 'whitespace/ending_newline',\r
+ 'whitespace/forcolon',\r
+ 'whitespace/indent',\r
+ 'whitespace/line_length',\r
+ 'whitespace/newline',\r
+ 'whitespace/operators',\r
+ 'whitespace/parens',\r
+ 'whitespace/semicolon',\r
+ 'whitespace/tab',\r
+ 'whitespace/todo',\r
+ ]\r
+\r
+# These error categories are no longer enforced by cpplint, but for backwards-\r
+# compatibility they may still appear in NOLINT comments.\r
+_LEGACY_ERROR_CATEGORIES = [\r
+ 'readability/streams',\r
+ 'readability/function',\r
+ ]\r
+\r
+# The default state of the category filter. This is overridden by the --filter=\r
+# flag. By default all errors are on, so only add here categories that should be\r
+# off by default (i.e., categories that must be enabled by the --filter= flags).\r
+# All entries here should start with a '-' or '+', as in the --filter= flag.\r
+_DEFAULT_FILTERS = ['-build/include_alpha']\r
+\r
+# The default list of categories suppressed for C (not C++) files.\r
+_DEFAULT_C_SUPPRESSED_CATEGORIES = [\r
+ 'readability/casting',\r
+ ]\r
+\r
+# The default list of categories suppressed for Linux Kernel files.\r
+_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [\r
+ 'whitespace/tab',\r
+ ]\r
+\r
+# We used to check for high-bit characters, but after much discussion we\r
+# decided those were OK, as long as they were in UTF-8 and didn't represent\r
+# hard-coded international strings, which belong in a separate i18n file.\r
+\r
+# C++ headers\r
+_CPP_HEADERS = frozenset([\r
+ # Legacy\r
+ 'algobase.h',\r
+ 'algo.h',\r
+ 'alloc.h',\r
+ 'builtinbuf.h',\r
+ 'bvector.h',\r
+ 'complex.h',\r
+ 'defalloc.h',\r
+ 'deque.h',\r
+ 'editbuf.h',\r
+ 'fstream.h',\r
+ 'function.h',\r
+ 'hash_map',\r
+ 'hash_map.h',\r
+ 'hash_set',\r
+ 'hash_set.h',\r
+ 'hashtable.h',\r
+ 'heap.h',\r
+ 'indstream.h',\r
+ 'iomanip.h',\r
+ 'iostream.h',\r
+ 'istream.h',\r
+ 'iterator.h',\r
+ 'list.h',\r
+ 'map.h',\r
+ 'multimap.h',\r
+ 'multiset.h',\r
+ 'ostream.h',\r
+ 'pair.h',\r
+ 'parsestream.h',\r
+ 'pfstream.h',\r
+ 'procbuf.h',\r
+ 'pthread_alloc',\r
+ 'pthread_alloc.h',\r
+ 'rope',\r
+ 'rope.h',\r
+ 'ropeimpl.h',\r
+ 'set.h',\r
+ 'slist',\r
+ 'slist.h',\r
+ 'stack.h',\r
+ 'stdiostream.h',\r
+ 'stl_alloc.h',\r
+ 'stl_relops.h',\r
+ 'streambuf.h',\r
+ 'stream.h',\r
+ 'strfile.h',\r
+ 'strstream.h',\r
+ 'tempbuf.h',\r
+ 'tree.h',\r
+ 'type_traits.h',\r
+ 'vector.h',\r
+ # 17.6.1.2 C++ library headers\r
+ 'algorithm',\r
+ 'array',\r
+ 'atomic',\r
+ 'bitset',\r
+ 'chrono',\r
+ 'codecvt',\r
+ 'complex',\r
+ 'condition_variable',\r
+ 'deque',\r
+ 'exception',\r
+ 'forward_list',\r
+ 'fstream',\r
+ 'functional',\r
+ 'future',\r
+ 'initializer_list',\r
+ 'iomanip',\r
+ 'ios',\r
+ 'iosfwd',\r
+ 'iostream',\r
+ 'istream',\r
+ 'iterator',\r
+ 'limits',\r
+ 'list',\r
+ 'locale',\r
+ 'map',\r
+ 'memory',\r
+ 'mutex',\r
+ 'new',\r
+ 'numeric',\r
+ 'ostream',\r
+ 'queue',\r
+ 'random',\r
+ 'ratio',\r
+ 'regex',\r
+ 'scoped_allocator',\r
+ 'set',\r
+ 'sstream',\r
+ 'stack',\r
+ 'stdexcept',\r
+ 'streambuf',\r
+ 'string',\r
+ 'strstream',\r
+ 'system_error',\r
+ 'thread',\r
+ 'tuple',\r
+ 'typeindex',\r
+ 'typeinfo',\r
+ 'type_traits',\r
+ 'unordered_map',\r
+ 'unordered_set',\r
+ 'utility',\r
+ 'valarray',\r
+ 'vector',\r
+ # 17.6.1.2 C++ headers for C library facilities\r
+ 'cassert',\r
+ 'ccomplex',\r
+ 'cctype',\r
+ 'cerrno',\r
+ 'cfenv',\r
+ 'cfloat',\r
+ 'cinttypes',\r
+ 'ciso646',\r
+ 'climits',\r
+ 'clocale',\r
+ 'cmath',\r
+ 'csetjmp',\r
+ 'csignal',\r
+ 'cstdalign',\r
+ 'cstdarg',\r
+ 'cstdbool',\r
+ 'cstddef',\r
+ 'cstdint',\r
+ 'cstdio',\r
+ 'cstdlib',\r
+ 'cstring',\r
+ 'ctgmath',\r
+ 'ctime',\r
+ 'cuchar',\r
+ 'cwchar',\r
+ 'cwctype',\r
+ ])\r
+\r
+# Type names\r
+_TYPES = re.compile(\r
+ r'^(?:'\r
+ # [dcl.type.simple]\r
+ r'(char(16_t|32_t)?)|wchar_t|'\r
+ r'bool|short|int|long|signed|unsigned|float|double|'\r
+ # [support.types]\r
+ r'(ptrdiff_t|size_t|max_align_t|nullptr_t)|'\r
+ # [cstdint.syn]\r
+ r'(u?int(_fast|_least)?(8|16|32|64)_t)|'\r
+ r'(u?int(max|ptr)_t)|'\r
+ r')$')\r
+\r
+\r
+# These headers are excluded from [build/include] and [build/include_order]\r
+# checks:\r
+# - Anything not following google file name conventions (containing an\r
+# uppercase character, such as Python.h or nsStringAPI.h, for example).\r
+# - Lua headers.\r
+_THIRD_PARTY_HEADERS_PATTERN = re.compile(\r
+ r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$')\r
+\r
+# Pattern for matching FileInfo.BaseName() against test file name\r
+_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$'\r
+\r
+# Pattern that matches only complete whitespace, possibly across multiple lines.\r
+_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL)\r
+\r
+# Assertion macros. These are defined in base/logging.h and\r
+# testing/base/public/gunit.h.\r
+_CHECK_MACROS = [\r
+ 'DCHECK', 'CHECK',\r
+ 'EXPECT_TRUE', 'ASSERT_TRUE',\r
+ 'EXPECT_FALSE', 'ASSERT_FALSE',\r
+ ]\r
+\r
+# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE\r
+_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS])\r
+\r
+for op, replacement in [('==', 'EQ'), ('!=', 'NE'),\r
+ ('>=', 'GE'), ('>', 'GT'),\r
+ ('<=', 'LE'), ('<', 'LT')]:\r
+ _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement\r
+ _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement\r
+ _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement\r
+ _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement\r
+\r
+for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'),\r
+ ('>=', 'LT'), ('>', 'LE'),\r
+ ('<=', 'GT'), ('<', 'GE')]:\r
+ _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement\r
+ _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement\r
+\r
+# Alternative tokens and their replacements. For full list, see section 2.5\r
+# Alternative tokens [lex.digraph] in the C++ standard.\r
+#\r
+# Digraphs (such as '%:') are not included here since it's a mess to\r
+# match those on a word boundary.\r
+_ALT_TOKEN_REPLACEMENT = {\r
+ 'and': '&&',\r
+ 'bitor': '|',\r
+ 'or': '||',\r
+ 'xor': '^',\r
+ 'compl': '~',\r
+ 'bitand': '&',\r
+ 'and_eq': '&=',\r
+ 'or_eq': '|=',\r
+ 'xor_eq': '^=',\r
+ 'not': '!',\r
+ 'not_eq': '!='\r
+ }\r
+\r
+# Compile regular expression that matches all the above keywords. The "[ =()]"\r
+# bit is meant to avoid matching these keywords outside of boolean expressions.\r
+#\r
+# False positives include C-style multi-line comments and multi-line strings\r
+# but those have always been troublesome for cpplint.\r
+_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile(\r
+ r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)')\r
+\r
+\r
+# These constants define types of headers for use with\r
+# _IncludeState.CheckNextIncludeOrder().\r
+_C_SYS_HEADER = 1\r
+_CPP_SYS_HEADER = 2\r
+_LIKELY_MY_HEADER = 3\r
+_POSSIBLE_MY_HEADER = 4\r
+_OTHER_HEADER = 5\r
+\r
+# These constants define the current inline assembly state\r
+_NO_ASM = 0 # Outside of inline assembly block\r
+_INSIDE_ASM = 1 # Inside inline assembly block\r
+_END_ASM = 2 # Last line of inline assembly block\r
+_BLOCK_ASM = 3 # The whole block is an inline assembly block\r
+\r
+# Match start of assembly blocks\r
+_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)'\r
+ r'(?:\s+(volatile|__volatile__))?'\r
+ r'\s*[{(]')\r
+\r
+# Match strings that indicate we're working on a C (not C++) file.\r
+_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|'\r
+ r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))')\r
+\r
+# Match string that indicates we're working on a Linux Kernel file.\r
+_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)')\r
+\r
+_regexp_compile_cache = {}\r
+\r
+# {str, set(int)}: a map from error categories to sets of linenumbers\r
+# on which those errors are expected and should be suppressed.\r
+_error_suppressions = {}\r
+\r
+# The root directory used for deriving header guard CPP variable.\r
+# This is set by --root flag.\r
+_root = None\r
+\r
+# The allowed line length of files.\r
+# This is set by --linelength flag.\r
+_line_length = 80\r
+\r
+# The allowed extensions for file names\r
+# This is set by --extensions flag.\r
+_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh'])\r
+\r
+# Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc.\r
+# This is set by --headers flag.\r
+_hpp_headers = set(['h'])\r
+\r
+# {str, bool}: a map from error categories to booleans which indicate if the\r
+# category should be suppressed for every line.\r
+_global_error_suppressions = {}\r
+\r
+def ProcessHppHeadersOption(val):\r
+ global _hpp_headers\r
+ try:\r
+ _hpp_headers = set(val.split(','))\r
+ # Automatically append to extensions list so it does not have to be set 2 times\r
+ _valid_extensions.update(_hpp_headers)\r
+ except ValueError:\r
+ PrintUsage('Header extensions must be comma seperated list.')\r
+\r
+def IsHeaderExtension(file_extension):\r
+ return file_extension in _hpp_headers\r
+\r
+def ParseNolintSuppressions(filename, raw_line, linenum, error):\r
+ """Updates the global list of line error-suppressions.\r
+\r
+ Parses any NOLINT comments on the current line, updating the global\r
+ error_suppressions store. Reports an error if the NOLINT comment\r
+ was malformed.\r
+\r
+ Args:\r
+ filename: str, the name of the input file.\r
+ raw_line: str, the line of input text, with comments.\r
+ linenum: int, the number of the current line.\r
+ error: function, an error handler.\r
+ """\r
+ matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line)\r
+ if matched:\r
+ if matched.group(1):\r
+ suppressed_line = linenum + 1\r
+ else:\r
+ suppressed_line = linenum\r
+ category = matched.group(2)\r
+ if category in (None, '(*)'): # => "suppress all"\r
+ _error_suppressions.setdefault(None, set()).add(suppressed_line)\r
+ else:\r
+ if category.startswith('(') and category.endswith(')'):\r
+ category = category[1:-1]\r
+ if category in _ERROR_CATEGORIES:\r
+ _error_suppressions.setdefault(category, set()).add(suppressed_line)\r
+ elif category not in _LEGACY_ERROR_CATEGORIES:\r
+ error(filename, linenum, 'readability/nolint', 5,\r
+ 'Unknown NOLINT error category: %s' % category)\r
+\r
+\r
+def ProcessGlobalSuppresions(lines):\r
+ """Updates the list of global error suppressions.\r
+\r
+ Parses any lint directives in the file that have global effect.\r
+\r
+ Args:\r
+ lines: An array of strings, each representing a line of the file, with the\r
+ last element being empty if the file is terminated with a newline.\r
+ """\r
+ for line in lines:\r
+ if _SEARCH_C_FILE.search(line):\r
+ for category in _DEFAULT_C_SUPPRESSED_CATEGORIES:\r
+ _global_error_suppressions[category] = True\r
+ if _SEARCH_KERNEL_FILE.search(line):\r
+ for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES:\r
+ _global_error_suppressions[category] = True\r
+\r
+\r
+def ResetNolintSuppressions():\r
+ """Resets the set of NOLINT suppressions to empty."""\r
+ _error_suppressions.clear()\r
+ _global_error_suppressions.clear()\r
+\r
+\r
+def IsErrorSuppressedByNolint(category, linenum):\r
+ """Returns true if the specified error category is suppressed on this line.\r
+\r
+ Consults the global error_suppressions map populated by\r
+ ParseNolintSuppressions/ProcessGlobalSuppresions/ResetNolintSuppressions.\r
+\r
+ Args:\r
+ category: str, the category of the error.\r
+ linenum: int, the current line number.\r
+ Returns:\r
+ bool, True iff the error should be suppressed due to a NOLINT comment or\r
+ global suppression.\r
+ """\r
+ return (_global_error_suppressions.get(category, False) or\r
+ linenum in _error_suppressions.get(category, set()) or\r
+ linenum in _error_suppressions.get(None, set()))\r
+\r
+\r
+def Match(pattern, s):\r
+ """Matches the string with the pattern, caching the compiled regexp."""\r
+ # The regexp compilation caching is inlined in both Match and Search for\r
+ # performance reasons; factoring it out into a separate function turns out\r
+ # to be noticeably expensive.\r
+ if pattern not in _regexp_compile_cache:\r
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)\r
+ return _regexp_compile_cache[pattern].match(s)\r
+\r
+\r
+def ReplaceAll(pattern, rep, s):\r
+ """Replaces instances of pattern in a string with a replacement.\r
+\r
+ The compiled regex is kept in a cache shared by Match and Search.\r
+\r
+ Args:\r
+ pattern: regex pattern\r
+ rep: replacement text\r
+ s: search string\r
+\r
+ Returns:\r
+ string with replacements made (or original string if no replacements)\r
+ """\r
+ if pattern not in _regexp_compile_cache:\r
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)\r
+ return _regexp_compile_cache[pattern].sub(rep, s)\r
+\r
+\r
+def Search(pattern, s):\r
+ """Searches the string for the pattern, caching the compiled regexp."""\r
+ if pattern not in _regexp_compile_cache:\r
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)\r
+ return _regexp_compile_cache[pattern].search(s)\r
+\r
+\r
+def _IsSourceExtension(s):\r
+ """File extension (excluding dot) matches a source file extension."""\r
+ return s in ('c', 'cc', 'cpp', 'cxx')\r
+\r
+\r
+class _IncludeState(object):\r
+ """Tracks line numbers for includes, and the order in which includes appear.\r
+\r
+ include_list contains list of lists of (header, line number) pairs.\r
+ It's a lists of lists rather than just one flat list to make it\r
+ easier to update across preprocessor boundaries.\r
+\r
+ Call CheckNextIncludeOrder() once for each header in the file, passing\r
+ in the type constants defined above. Calls in an illegal order will\r
+ raise an _IncludeError with an appropriate error message.\r
+\r
+ """\r
+ # self._section will move monotonically through this set. If it ever\r
+ # needs to move backwards, CheckNextIncludeOrder will raise an error.\r
+ _INITIAL_SECTION = 0\r
+ _MY_H_SECTION = 1\r
+ _C_SECTION = 2\r
+ _CPP_SECTION = 3\r
+ _OTHER_H_SECTION = 4\r
+\r
+ _TYPE_NAMES = {\r
+ _C_SYS_HEADER: 'C system header',\r
+ _CPP_SYS_HEADER: 'C++ system header',\r
+ _LIKELY_MY_HEADER: 'header this file implements',\r
+ _POSSIBLE_MY_HEADER: 'header this file may implement',\r
+ _OTHER_HEADER: 'other header',\r
+ }\r
+ _SECTION_NAMES = {\r
+ _INITIAL_SECTION: "... nothing. (This can't be an error.)",\r
+ _MY_H_SECTION: 'a header this file implements',\r
+ _C_SECTION: 'C system header',\r
+ _CPP_SECTION: 'C++ system header',\r
+ _OTHER_H_SECTION: 'other header',\r
+ }\r
+\r
+ def __init__(self):\r
+ self.include_list = [[]]\r
+ self.ResetSection('')\r
+\r
+ def FindHeader(self, header):\r
+ """Check if a header has already been included.\r
+\r
+ Args:\r
+ header: header to check.\r
+ Returns:\r
+ Line number of previous occurrence, or -1 if the header has not\r
+ been seen before.\r
+ """\r
+ for section_list in self.include_list:\r
+ for f in section_list:\r
+ if f[0] == header:\r
+ return f[1]\r
+ return -1\r
+\r
+ def ResetSection(self, directive):\r
+ """Reset section checking for preprocessor directive.\r
+\r
+ Args:\r
+ directive: preprocessor directive (e.g. "if", "else").\r
+ """\r
+ # The name of the current section.\r
+ self._section = self._INITIAL_SECTION\r
+ # The path of last found header.\r
+ self._last_header = ''\r
+\r
+ # Update list of includes. Note that we never pop from the\r
+ # include list.\r
+ if directive in ('if', 'ifdef', 'ifndef'):\r
+ self.include_list.append([])\r
+ elif directive in ('else', 'elif'):\r
+ self.include_list[-1] = []\r
+\r
+ def SetLastHeader(self, header_path):\r
+ self._last_header = header_path\r
+\r
+ def CanonicalizeAlphabeticalOrder(self, header_path):\r
+ """Returns a path canonicalized for alphabetical comparison.\r
+\r
+ - replaces "-" with "_" so they both cmp the same.\r
+ - removes '-inl' since we don't require them to be after the main header.\r
+ - lowercase everything, just in case.\r
+\r
+ Args:\r
+ header_path: Path to be canonicalized.\r
+\r
+ Returns:\r
+ Canonicalized path.\r
+ """\r
+ return header_path.replace('-inl.h', '.h').replace('-', '_').lower()\r
+\r
+ def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):\r
+ """Check if a header is in alphabetical order with the previous header.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ header_path: Canonicalized header to be checked.\r
+\r
+ Returns:\r
+ Returns true if the header is in alphabetical order.\r
+ """\r
+ # If previous section is different from current section, _last_header will\r
+ # be reset to empty string, so it's always less than current header.\r
+ #\r
+ # If previous line was a blank line, assume that the headers are\r
+ # intentionally sorted the way they are.\r
+ if (self._last_header > header_path and\r
+ Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])):\r
+ return False\r
+ return True\r
+\r
+ def CheckNextIncludeOrder(self, header_type):\r
+ """Returns a non-empty error message if the next header is out of order.\r
+\r
+ This function also updates the internal state to be ready to check\r
+ the next include.\r
+\r
+ Args:\r
+ header_type: One of the _XXX_HEADER constants defined above.\r
+\r
+ Returns:\r
+ The empty string if the header is in the right order, or an\r
+ error message describing what's wrong.\r
+\r
+ """\r
+ error_message = ('Found %s after %s' %\r
+ (self._TYPE_NAMES[header_type],\r
+ self._SECTION_NAMES[self._section]))\r
+\r
+ last_section = self._section\r
+\r
+ if header_type == _C_SYS_HEADER:\r
+ if self._section <= self._C_SECTION:\r
+ self._section = self._C_SECTION\r
+ else:\r
+ self._last_header = ''\r
+ return error_message\r
+ elif header_type == _CPP_SYS_HEADER:\r
+ if self._section <= self._CPP_SECTION:\r
+ self._section = self._CPP_SECTION\r
+ else:\r
+ self._last_header = ''\r
+ return error_message\r
+ elif header_type == _LIKELY_MY_HEADER:\r
+ if self._section <= self._MY_H_SECTION:\r
+ self._section = self._MY_H_SECTION\r
+ else:\r
+ self._section = self._OTHER_H_SECTION\r
+ elif header_type == _POSSIBLE_MY_HEADER:\r
+ if self._section <= self._MY_H_SECTION:\r
+ self._section = self._MY_H_SECTION\r
+ else:\r
+ # This will always be the fallback because we're not sure\r
+ # enough that the header is associated with this file.\r
+ self._section = self._OTHER_H_SECTION\r
+ else:\r
+ assert header_type == _OTHER_HEADER\r
+ self._section = self._OTHER_H_SECTION\r
+\r
+ if last_section != self._section:\r
+ self._last_header = ''\r
+\r
+ return ''\r
+\r
+\r
+class _CppLintState(object):\r
+ """Maintains module-wide state.."""\r
+\r
+ def __init__(self):\r
+ self.verbose_level = 1 # global setting.\r
+ self.error_count = 0 # global count of reported errors\r
+ # filters to apply when emitting error messages\r
+ self.filters = _DEFAULT_FILTERS[:]\r
+ # backup of filter list. Used to restore the state after each file.\r
+ self._filters_backup = self.filters[:]\r
+ self.counting = 'total' # In what way are we counting errors?\r
+ self.errors_by_category = {} # string to int dict storing error counts\r
+\r
+ # output format:\r
+ # "emacs" - format that emacs can parse (default)\r
+ # "vs7" - format that Microsoft Visual Studio 7 can parse\r
+ self.output_format = 'emacs'\r
+\r
+ def SetOutputFormat(self, output_format):\r
+ """Sets the output format for errors."""\r
+ self.output_format = output_format\r
+\r
+ def SetVerboseLevel(self, level):\r
+ """Sets the module's verbosity, and returns the previous setting."""\r
+ last_verbose_level = self.verbose_level\r
+ self.verbose_level = level\r
+ return last_verbose_level\r
+\r
+ def SetCountingStyle(self, counting_style):\r
+ """Sets the module's counting options."""\r
+ self.counting = counting_style\r
+\r
+ def SetFilters(self, filters):\r
+ """Sets the error-message filters.\r
+\r
+ These filters are applied when deciding whether to emit a given\r
+ error message.\r
+\r
+ Args:\r
+ filters: A string of comma-separated filters (eg "+whitespace/indent").\r
+ Each filter should start with + or -; else we die.\r
+\r
+ Raises:\r
+ ValueError: The comma-separated filters did not all start with '+' or '-'.\r
+ E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"\r
+ """\r
+ # Default filters always have less priority than the flag ones.\r
+ self.filters = _DEFAULT_FILTERS[:]\r
+ self.AddFilters(filters)\r
+\r
+ def AddFilters(self, filters):\r
+ """ Adds more filters to the existing list of error-message filters. """\r
+ for filt in filters.split(','):\r
+ clean_filt = filt.strip()\r
+ if clean_filt:\r
+ self.filters.append(clean_filt)\r
+ for filt in self.filters:\r
+ if not (filt.startswith('+') or filt.startswith('-')):\r
+ raise ValueError('Every filter in --filters must start with + or -'\r
+ ' (%s does not)' % filt)\r
+\r
+ def BackupFilters(self):\r
+ """ Saves the current filter list to backup storage."""\r
+ self._filters_backup = self.filters[:]\r
+\r
+ def RestoreFilters(self):\r
+ """ Restores filters previously backed up."""\r
+ self.filters = self._filters_backup[:]\r
+\r
+ def ResetErrorCounts(self):\r
+ """Sets the module's error statistic back to zero."""\r
+ self.error_count = 0\r
+ self.errors_by_category = {}\r
+\r
+ def IncrementErrorCount(self, category):\r
+ """Bumps the module's error statistic."""\r
+ self.error_count += 1\r
+ if self.counting in ('toplevel', 'detailed'):\r
+ if self.counting != 'detailed':\r
+ category = category.split('/')[0]\r
+ if category not in self.errors_by_category:\r
+ self.errors_by_category[category] = 0\r
+ self.errors_by_category[category] += 1\r
+\r
+ def PrintErrorCounts(self):\r
+ """Print a summary of errors by category, and the total."""\r
+ for category, count in self.errors_by_category.iteritems():\r
+ sys.stderr.write('Category \'%s\' errors found: %d\n' %\r
+ (category, count))\r
+ sys.stdout.write('Total errors found: %d\n' % self.error_count)\r
+\r
+_cpplint_state = _CppLintState()\r
+\r
+\r
+def _OutputFormat():\r
+ """Gets the module's output format."""\r
+ return _cpplint_state.output_format\r
+\r
+\r
+def _SetOutputFormat(output_format):\r
+ """Sets the module's output format."""\r
+ _cpplint_state.SetOutputFormat(output_format)\r
+\r
+\r
+def _VerboseLevel():\r
+ """Returns the module's verbosity setting."""\r
+ return _cpplint_state.verbose_level\r
+\r
+\r
+def _SetVerboseLevel(level):\r
+ """Sets the module's verbosity, and returns the previous setting."""\r
+ return _cpplint_state.SetVerboseLevel(level)\r
+\r
+\r
+def _SetCountingStyle(level):\r
+ """Sets the module's counting options."""\r
+ _cpplint_state.SetCountingStyle(level)\r
+\r
+\r
+def _Filters():\r
+ """Returns the module's list of output filters, as a list."""\r
+ return _cpplint_state.filters\r
+\r
+\r
+def _SetFilters(filters):\r
+ """Sets the module's error-message filters.\r
+\r
+ These filters are applied when deciding whether to emit a given\r
+ error message.\r
+\r
+ Args:\r
+ filters: A string of comma-separated filters (eg "whitespace/indent").\r
+ Each filter should start with + or -; else we die.\r
+ """\r
+ _cpplint_state.SetFilters(filters)\r
+\r
+def _AddFilters(filters):\r
+ """Adds more filter overrides.\r
+\r
+ Unlike _SetFilters, this function does not reset the current list of filters\r
+ available.\r
+\r
+ Args:\r
+ filters: A string of comma-separated filters (eg "whitespace/indent").\r
+ Each filter should start with + or -; else we die.\r
+ """\r
+ _cpplint_state.AddFilters(filters)\r
+\r
+def _BackupFilters():\r
+ """ Saves the current filter list to backup storage."""\r
+ _cpplint_state.BackupFilters()\r
+\r
+def _RestoreFilters():\r
+ """ Restores filters previously backed up."""\r
+ _cpplint_state.RestoreFilters()\r
+\r
+class _FunctionState(object):\r
+ """Tracks current function name and the number of lines in its body."""\r
+\r
+ _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.\r
+ _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER.\r
+\r
+ def __init__(self):\r
+ self.in_a_function = False\r
+ self.lines_in_function = 0\r
+ self.current_function = ''\r
+\r
+ def Begin(self, function_name):\r
+ """Start analyzing function body.\r
+\r
+ Args:\r
+ function_name: The name of the function being tracked.\r
+ """\r
+ self.in_a_function = True\r
+ self.lines_in_function = 0\r
+ self.current_function = function_name\r
+\r
+ def Count(self):\r
+ """Count line in current function body."""\r
+ if self.in_a_function:\r
+ self.lines_in_function += 1\r
+\r
+ def Check(self, error, filename, linenum):\r
+ """Report if too many lines in function body.\r
+\r
+ Args:\r
+ error: The function to call with any errors found.\r
+ filename: The name of the current file.\r
+ linenum: The number of the line to check.\r
+ """\r
+ if not self.in_a_function:\r
+ return\r
+\r
+ if Match(r'T(EST|est)', self.current_function):\r
+ base_trigger = self._TEST_TRIGGER\r
+ else:\r
+ base_trigger = self._NORMAL_TRIGGER\r
+ trigger = base_trigger * 2**_VerboseLevel()\r
+\r
+ if self.lines_in_function > trigger:\r
+ error_level = int(math.log(self.lines_in_function / base_trigger, 2))\r
+ # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...\r
+ if error_level > 5:\r
+ error_level = 5\r
+ error(filename, linenum, 'readability/fn_size', error_level,\r
+ 'Small and focused functions are preferred:'\r
+ ' %s has %d non-comment lines'\r
+ ' (error triggered by exceeding %d lines).' % (\r
+ self.current_function, self.lines_in_function, trigger))\r
+\r
+ def End(self):\r
+ """Stop analyzing function body."""\r
+ self.in_a_function = False\r
+\r
+\r
+class _IncludeError(Exception):\r
+ """Indicates a problem with the include order in a file."""\r
+ pass\r
+\r
+\r
+class FileInfo(object):\r
+ """Provides utility functions for filenames.\r
+\r
+ FileInfo provides easy access to the components of a file's path\r
+ relative to the project root.\r
+ """\r
+\r
+ def __init__(self, filename):\r
+ self._filename = filename\r
+\r
+ def FullName(self):\r
+ """Make Windows paths like Unix."""\r
+ return os.path.abspath(self._filename).replace('\\', '/')\r
+\r
+ def RepositoryName(self):\r
+ """FullName after removing the local path to the repository.\r
+\r
+ If we have a real absolute path name here we can try to do something smart:\r
+ detecting the root of the checkout and truncating /path/to/checkout from\r
+ the name so that we get header guards that don't include things like\r
+ "C:\Documents and Settings\..." or "/home/username/..." in them and thus\r
+ people on different computers who have checked the source out to different\r
+ locations won't see bogus errors.\r
+ """\r
+ fullname = self.FullName()\r
+\r
+ if os.path.exists(fullname):\r
+ project_dir = os.path.dirname(fullname)\r
+\r
+ if os.path.exists(os.path.join(project_dir, ".svn")):\r
+ # If there's a .svn file in the current directory, we recursively look\r
+ # up the directory tree for the top of the SVN checkout\r
+ root_dir = project_dir\r
+ one_up_dir = os.path.dirname(root_dir)\r
+ while os.path.exists(os.path.join(one_up_dir, ".svn")):\r
+ root_dir = os.path.dirname(root_dir)\r
+ one_up_dir = os.path.dirname(one_up_dir)\r
+\r
+ prefix = os.path.commonprefix([root_dir, project_dir])\r
+ return fullname[len(prefix) + 1:]\r
+\r
+ # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by\r
+ # searching up from the current path.\r
+ root_dir = current_dir = os.path.dirname(fullname)\r
+ while current_dir != os.path.dirname(current_dir):\r
+ if (os.path.exists(os.path.join(current_dir, ".git")) or\r
+ os.path.exists(os.path.join(current_dir, ".hg")) or\r
+ os.path.exists(os.path.join(current_dir, ".svn"))):\r
+ root_dir = current_dir\r
+ current_dir = os.path.dirname(current_dir)\r
+\r
+ if (os.path.exists(os.path.join(root_dir, ".git")) or\r
+ os.path.exists(os.path.join(root_dir, ".hg")) or\r
+ os.path.exists(os.path.join(root_dir, ".svn"))):\r
+ prefix = os.path.commonprefix([root_dir, project_dir])\r
+ return fullname[len(prefix) + 1:]\r
+\r
+ # Don't know what to do; header guard warnings may be wrong...\r
+ return fullname\r
+\r
+ def Split(self):\r
+ """Splits the file into the directory, basename, and extension.\r
+\r
+ For 'chrome/browser/browser.cc', Split() would\r
+ return ('chrome/browser', 'browser', '.cc')\r
+\r
+ Returns:\r
+ A tuple of (directory, basename, extension).\r
+ """\r
+\r
+ googlename = self.RepositoryName()\r
+ project, rest = os.path.split(googlename)\r
+ return (project,) + os.path.splitext(rest)\r
+\r
+ def BaseName(self):\r
+ """File base name - text after the final slash, before the final period."""\r
+ return self.Split()[1]\r
+\r
+ def Extension(self):\r
+ """File extension - text following the final period."""\r
+ return self.Split()[2]\r
+\r
+ def NoExtension(self):\r
+ """File has no source file extension."""\r
+ return '/'.join(self.Split()[0:2])\r
+\r
+ def IsSource(self):\r
+ """File has a source file extension."""\r
+ return _IsSourceExtension(self.Extension()[1:])\r
+\r
+\r
+def _ShouldPrintError(category, confidence, linenum):\r
+ """If confidence >= verbose, category passes filter and is not suppressed."""\r
+\r
+ # There are three ways we might decide not to print an error message:\r
+ # a "NOLINT(category)" comment appears in the source,\r
+ # the verbosity level isn't high enough, or the filters filter it out.\r
+ if IsErrorSuppressedByNolint(category, linenum):\r
+ return False\r
+\r
+ if confidence < _cpplint_state.verbose_level:\r
+ return False\r
+\r
+ is_filtered = False\r
+ for one_filter in _Filters():\r
+ if one_filter.startswith('-'):\r
+ if category.startswith(one_filter[1:]):\r
+ is_filtered = True\r
+ elif one_filter.startswith('+'):\r
+ if category.startswith(one_filter[1:]):\r
+ is_filtered = False\r
+ else:\r
+ assert False # should have been checked for in SetFilter.\r
+ if is_filtered:\r
+ return False\r
+\r
+ return True\r
+\r
+\r
+def Error(filename, linenum, category, confidence, message):\r
+ """Logs the fact we've found a lint error.\r
+\r
+ We log where the error was found, and also our confidence in the error,\r
+ that is, how certain we are this is a legitimate style regression, and\r
+ not a misidentification or a use that's sometimes justified.\r
+\r
+ False positives can be suppressed by the use of\r
+ "cpplint(category)" comments on the offending line. These are\r
+ parsed into _error_suppressions.\r
+\r
+ Args:\r
+ filename: The name of the file containing the error.\r
+ linenum: The number of the line containing the error.\r
+ category: A string used to describe the "category" this bug\r
+ falls under: "whitespace", say, or "runtime". Categories\r
+ may have a hierarchy separated by slashes: "whitespace/indent".\r
+ confidence: A number from 1-5 representing a confidence score for\r
+ the error, with 5 meaning that we are certain of the problem,\r
+ and 1 meaning that it could be a legitimate construct.\r
+ message: The error message.\r
+ """\r
+ if _ShouldPrintError(category, confidence, linenum):\r
+ _cpplint_state.IncrementErrorCount(category)\r
+ if _cpplint_state.output_format == 'vs7':\r
+ sys.stderr.write('%s(%s): error cpplint: [%s] %s [%d]\n' % (\r
+ filename, linenum, category, message, confidence))\r
+ elif _cpplint_state.output_format == 'eclipse':\r
+ sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % (\r
+ filename, linenum, message, category, confidence))\r
+ else:\r
+ sys.stderr.write('%s:%s: %s [%s] [%d]\n' % (\r
+ filename, linenum, message, category, confidence))\r
+\r
+\r
+# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard.\r
+_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(\r
+ r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')\r
+# Match a single C style comment on the same line.\r
+_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/'\r
+# Matches multi-line C style comments.\r
+# This RE is a little bit more complicated than one might expect, because we\r
+# have to take care of space removals tools so we can handle comments inside\r
+# statements better.\r
+# The current rule is: We only clear spaces from both sides when we're at the\r
+# end of the line. Otherwise, we try to remove spaces from the right side,\r
+# if this doesn't work we try on left side but only if there's a non-character\r
+# on the right.\r
+_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile(\r
+ r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' +\r
+ _RE_PATTERN_C_COMMENTS + r'\s+|' +\r
+ r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' +\r
+ _RE_PATTERN_C_COMMENTS + r')')\r
+\r
+\r
+def IsCppString(line):\r
+ """Does line terminate so, that the next symbol is in string constant.\r
+\r
+ This function does not consider single-line nor multi-line comments.\r
+\r
+ Args:\r
+ line: is a partial line of code starting from the 0..n.\r
+\r
+ Returns:\r
+ True, if next character appended to 'line' is inside a\r
+ string constant.\r
+ """\r
+\r
+ line = line.replace(r'\\', 'XX') # after this, \\" does not match to \"\r
+ return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1\r
+\r
+\r
+def CleanseRawStrings(raw_lines):\r
+ """Removes C++11 raw strings from lines.\r
+\r
+ Before:\r
+ static const char kData[] = R"(\r
+ multi-line string\r
+ )";\r
+\r
+ After:\r
+ static const char kData[] = ""\r
+ (replaced by blank line)\r
+ "";\r
+\r
+ Args:\r
+ raw_lines: list of raw lines.\r
+\r
+ Returns:\r
+ list of lines with C++11 raw strings replaced by empty strings.\r
+ """\r
+\r
+ delimiter = None\r
+ lines_without_raw_strings = []\r
+ for line in raw_lines:\r
+ if delimiter:\r
+ # Inside a raw string, look for the end\r
+ end = line.find(delimiter)\r
+ if end >= 0:\r
+ # Found the end of the string, match leading space for this\r
+ # line and resume copying the original lines, and also insert\r
+ # a "" on the last line.\r
+ leading_space = Match(r'^(\s*)\S', line)\r
+ line = leading_space.group(1) + '""' + line[end + len(delimiter):]\r
+ delimiter = None\r
+ else:\r
+ # Haven't found the end yet, append a blank line.\r
+ line = '""'\r
+\r
+ # Look for beginning of a raw string, and replace them with\r
+ # empty strings. This is done in a loop to handle multiple raw\r
+ # strings on the same line.\r
+ while delimiter is None:\r
+ # Look for beginning of a raw string.\r
+ # See 2.14.15 [lex.string] for syntax.\r
+ #\r
+ # Once we have matched a raw string, we check the prefix of the\r
+ # line to make sure that the line is not part of a single line\r
+ # comment. It's done this way because we remove raw strings\r
+ # before removing comments as opposed to removing comments\r
+ # before removing raw strings. This is because there are some\r
+ # cpplint checks that requires the comments to be preserved, but\r
+ # we don't want to check comments that are inside raw strings.\r
+ matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line)\r
+ if (matched and\r
+ not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//',\r
+ matched.group(1))):\r
+ delimiter = ')' + matched.group(2) + '"'\r
+\r
+ end = matched.group(3).find(delimiter)\r
+ if end >= 0:\r
+ # Raw string ended on same line\r
+ line = (matched.group(1) + '""' +\r
+ matched.group(3)[end + len(delimiter):])\r
+ delimiter = None\r
+ else:\r
+ # Start of a multi-line raw string\r
+ line = matched.group(1) + '""'\r
+ else:\r
+ break\r
+\r
+ lines_without_raw_strings.append(line)\r
+\r
+ # TODO(unknown): if delimiter is not None here, we might want to\r
+ # emit a warning for unterminated string.\r
+ return lines_without_raw_strings\r
+\r
+\r
+def FindNextMultiLineCommentStart(lines, lineix):\r
+ """Find the beginning marker for a multiline comment."""\r
+ while lineix < len(lines):\r
+ if lines[lineix].strip().startswith('/*'):\r
+ # Only return this marker if the comment goes beyond this line\r
+ if lines[lineix].strip().find('*/', 2) < 0:\r
+ return lineix\r
+ lineix += 1\r
+ return len(lines)\r
+\r
+\r
+def FindNextMultiLineCommentEnd(lines, lineix):\r
+ """We are inside a comment, find the end marker."""\r
+ while lineix < len(lines):\r
+ if lines[lineix].strip().endswith('*/'):\r
+ return lineix\r
+ lineix += 1\r
+ return len(lines)\r
+\r
+\r
+def RemoveMultiLineCommentsFromRange(lines, begin, end):\r
+ """Clears a range of lines for multi-line comments."""\r
+ # Having // dummy comments makes the lines non-empty, so we will not get\r
+ # unnecessary blank line warnings later in the code.\r
+ for i in range(begin, end):\r
+ lines[i] = '/**/'\r
+\r
+\r
+def RemoveMultiLineComments(filename, lines, error):\r
+ """Removes multiline (c-style) comments from lines."""\r
+ lineix = 0\r
+ while lineix < len(lines):\r
+ lineix_begin = FindNextMultiLineCommentStart(lines, lineix)\r
+ if lineix_begin >= len(lines):\r
+ return\r
+ lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin)\r
+ if lineix_end >= len(lines):\r
+ error(filename, lineix_begin + 1, 'readability/multiline_comment', 5,\r
+ 'Could not find end of multi-line comment')\r
+ return\r
+ RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)\r
+ lineix = lineix_end + 1\r
+\r
+\r
+def CleanseComments(line):\r
+ """Removes //-comments and single-line C-style /* */ comments.\r
+\r
+ Args:\r
+ line: A line of C++ source.\r
+\r
+ Returns:\r
+ The line with single-line comments removed.\r
+ """\r
+ commentpos = line.find('//')\r
+ if commentpos != -1 and not IsCppString(line[:commentpos]):\r
+ line = line[:commentpos].rstrip()\r
+ # get rid of /* ... */\r
+ return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line)\r
+\r
+\r
+class CleansedLines(object):\r
+ """Holds 4 copies of all lines with different preprocessing applied to them.\r
+\r
+ 1) elided member contains lines without strings and comments.\r
+ 2) lines member contains lines without comments.\r
+ 3) raw_lines member contains all the lines without processing.\r
+ 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw\r
+ strings removed.\r
+ All these members are of <type 'list'>, and of the same length.\r
+ """\r
+\r
+ def __init__(self, lines):\r
+ self.elided = []\r
+ self.lines = []\r
+ self.raw_lines = lines\r
+ self.num_lines = len(lines)\r
+ self.lines_without_raw_strings = CleanseRawStrings(lines)\r
+ for linenum in range(len(self.lines_without_raw_strings)):\r
+ self.lines.append(CleanseComments(\r
+ self.lines_without_raw_strings[linenum]))\r
+ elided = self._CollapseStrings(self.lines_without_raw_strings[linenum])\r
+ self.elided.append(CleanseComments(elided))\r
+\r
+ def NumLines(self):\r
+ """Returns the number of lines represented."""\r
+ return self.num_lines\r
+\r
+ @staticmethod\r
+ def _CollapseStrings(elided):\r
+ """Collapses strings and chars on a line to simple "" or '' blocks.\r
+\r
+ We nix strings first so we're not fooled by text like '"http://"'\r
+\r
+ Args:\r
+ elided: The line being processed.\r
+\r
+ Returns:\r
+ The line with collapsed strings.\r
+ """\r
+ if _RE_PATTERN_INCLUDE.match(elided):\r
+ return elided\r
+\r
+ # Remove escaped characters first to make quote/single quote collapsing\r
+ # basic. Things that look like escaped characters shouldn't occur\r
+ # outside of strings and chars.\r
+ elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided)\r
+\r
+ # Replace quoted strings and digit separators. Both single quotes\r
+ # and double quotes are processed in the same loop, otherwise\r
+ # nested quotes wouldn't work.\r
+ collapsed = ''\r
+ while True:\r
+ # Find the first quote character\r
+ match = Match(r'^([^\'"]*)([\'"])(.*)$', elided)\r
+ if not match:\r
+ collapsed += elided\r
+ break\r
+ head, quote, tail = match.groups()\r
+\r
+ if quote == '"':\r
+ # Collapse double quoted strings\r
+ second_quote = tail.find('"')\r
+ if second_quote >= 0:\r
+ collapsed += head + '""'\r
+ elided = tail[second_quote + 1:]\r
+ else:\r
+ # Unmatched double quote, don't bother processing the rest\r
+ # of the line since this is probably a multiline string.\r
+ collapsed += elided\r
+ break\r
+ else:\r
+ # Found single quote, check nearby text to eliminate digit separators.\r
+ #\r
+ # There is no special handling for floating point here, because\r
+ # the integer/fractional/exponent parts would all be parsed\r
+ # correctly as long as there are digits on both sides of the\r
+ # separator. So we are fine as long as we don't see something\r
+ # like "0.'3" (gcc 4.9.0 will not allow this literal).\r
+ if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head):\r
+ match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail)\r
+ collapsed += head + match_literal.group(1).replace("'", '')\r
+ elided = match_literal.group(2)\r
+ else:\r
+ second_quote = tail.find('\'')\r
+ if second_quote >= 0:\r
+ collapsed += head + "''"\r
+ elided = tail[second_quote + 1:]\r
+ else:\r
+ # Unmatched single quote\r
+ collapsed += elided\r
+ break\r
+\r
+ return collapsed\r
+\r
+\r
+def FindEndOfExpressionInLine(line, startpos, stack):\r
+ """Find the position just after the end of current parenthesized expression.\r
+\r
+ Args:\r
+ line: a CleansedLines line.\r
+ startpos: start searching at this position.\r
+ stack: nesting stack at startpos.\r
+\r
+ Returns:\r
+ On finding matching end: (index just after matching end, None)\r
+ On finding an unclosed expression: (-1, None)\r
+ Otherwise: (-1, new stack at end of this line)\r
+ """\r
+ for i in xrange(startpos, len(line)):\r
+ char = line[i]\r
+ if char in '([{':\r
+ # Found start of parenthesized expression, push to expression stack\r
+ stack.append(char)\r
+ elif char == '<':\r
+ # Found potential start of template argument list\r
+ if i > 0 and line[i - 1] == '<':\r
+ # Left shift operator\r
+ if stack and stack[-1] == '<':\r
+ stack.pop()\r
+ if not stack:\r
+ return (-1, None)\r
+ elif i > 0 and Search(r'\boperator\s*$', line[0:i]):\r
+ # operator<, don't add to stack\r
+ continue\r
+ else:\r
+ # Tentative start of template argument list\r
+ stack.append('<')\r
+ elif char in ')]}':\r
+ # Found end of parenthesized expression.\r
+ #\r
+ # If we are currently expecting a matching '>', the pending '<'\r
+ # must have been an operator. Remove them from expression stack.\r
+ while stack and stack[-1] == '<':\r
+ stack.pop()\r
+ if not stack:\r
+ return (-1, None)\r
+ if ((stack[-1] == '(' and char == ')') or\r
+ (stack[-1] == '[' and char == ']') or\r
+ (stack[-1] == '{' and char == '}')):\r
+ stack.pop()\r
+ if not stack:\r
+ return (i + 1, None)\r
+ else:\r
+ # Mismatched parentheses\r
+ return (-1, None)\r
+ elif char == '>':\r
+ # Found potential end of template argument list.\r
+\r
+ # Ignore "->" and operator functions\r
+ if (i > 0 and\r
+ (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))):\r
+ continue\r
+\r
+ # Pop the stack if there is a matching '<'. Otherwise, ignore\r
+ # this '>' since it must be an operator.\r
+ if stack:\r
+ if stack[-1] == '<':\r
+ stack.pop()\r
+ if not stack:\r
+ return (i + 1, None)\r
+ elif char == ';':\r
+ # Found something that look like end of statements. If we are currently\r
+ # expecting a '>', the matching '<' must have been an operator, since\r
+ # template argument list should not contain statements.\r
+ while stack and stack[-1] == '<':\r
+ stack.pop()\r
+ if not stack:\r
+ return (-1, None)\r
+\r
+ # Did not find end of expression or unbalanced parentheses on this line\r
+ return (-1, stack)\r
+\r
+\r
+def CloseExpression(clean_lines, linenum, pos):\r
+ """If input points to ( or { or [ or <, finds the position that closes it.\r
+\r
+ If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the\r
+ linenum/pos that correspond to the closing of the expression.\r
+\r
+ TODO(unknown): cpplint spends a fair bit of time matching parentheses.\r
+ Ideally we would want to index all opening and closing parentheses once\r
+ and have CloseExpression be just a simple lookup, but due to preprocessor\r
+ tricks, this is not so easy.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ pos: A position on the line.\r
+\r
+ Returns:\r
+ A tuple (line, linenum, pos) pointer *past* the closing brace, or\r
+ (line, len(lines), -1) if we never find a close. Note we ignore\r
+ strings and comments when matching; and the line we return is the\r
+ 'cleansed' line at linenum.\r
+ """\r
+\r
+ line = clean_lines.elided[linenum]\r
+ if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]):\r
+ return (line, clean_lines.NumLines(), -1)\r
+\r
+ # Check first line\r
+ (end_pos, stack) = FindEndOfExpressionInLine(line, pos, [])\r
+ if end_pos > -1:\r
+ return (line, linenum, end_pos)\r
+\r
+ # Continue scanning forward\r
+ while stack and linenum < clean_lines.NumLines() - 1:\r
+ linenum += 1\r
+ line = clean_lines.elided[linenum]\r
+ (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack)\r
+ if end_pos > -1:\r
+ return (line, linenum, end_pos)\r
+\r
+ # Did not find end of expression before end of file, give up\r
+ return (line, clean_lines.NumLines(), -1)\r
+\r
+\r
+def FindStartOfExpressionInLine(line, endpos, stack):\r
+ """Find position at the matching start of current expression.\r
+\r
+ This is almost the reverse of FindEndOfExpressionInLine, but note\r
+ that the input position and returned position differs by 1.\r
+\r
+ Args:\r
+ line: a CleansedLines line.\r
+ endpos: start searching at this position.\r
+ stack: nesting stack at endpos.\r
+\r
+ Returns:\r
+ On finding matching start: (index at matching start, None)\r
+ On finding an unclosed expression: (-1, None)\r
+ Otherwise: (-1, new stack at beginning of this line)\r
+ """\r
+ i = endpos\r
+ while i >= 0:\r
+ char = line[i]\r
+ if char in ')]}':\r
+ # Found end of expression, push to expression stack\r
+ stack.append(char)\r
+ elif char == '>':\r
+ # Found potential end of template argument list.\r
+ #\r
+ # Ignore it if it's a "->" or ">=" or "operator>"\r
+ if (i > 0 and\r
+ (line[i - 1] == '-' or\r
+ Match(r'\s>=\s', line[i - 1:]) or\r
+ Search(r'\boperator\s*$', line[0:i]))):\r
+ i -= 1\r
+ else:\r
+ stack.append('>')\r
+ elif char == '<':\r
+ # Found potential start of template argument list\r
+ if i > 0 and line[i - 1] == '<':\r
+ # Left shift operator\r
+ i -= 1\r
+ else:\r
+ # If there is a matching '>', we can pop the expression stack.\r
+ # Otherwise, ignore this '<' since it must be an operator.\r
+ if stack and stack[-1] == '>':\r
+ stack.pop()\r
+ if not stack:\r
+ return (i, None)\r
+ elif char in '([{':\r
+ # Found start of expression.\r
+ #\r
+ # If there are any unmatched '>' on the stack, they must be\r
+ # operators. Remove those.\r
+ while stack and stack[-1] == '>':\r
+ stack.pop()\r
+ if not stack:\r
+ return (-1, None)\r
+ if ((char == '(' and stack[-1] == ')') or\r
+ (char == '[' and stack[-1] == ']') or\r
+ (char == '{' and stack[-1] == '}')):\r
+ stack.pop()\r
+ if not stack:\r
+ return (i, None)\r
+ else:\r
+ # Mismatched parentheses\r
+ return (-1, None)\r
+ elif char == ';':\r
+ # Found something that look like end of statements. If we are currently\r
+ # expecting a '<', the matching '>' must have been an operator, since\r
+ # template argument list should not contain statements.\r
+ while stack and stack[-1] == '>':\r
+ stack.pop()\r
+ if not stack:\r
+ return (-1, None)\r
+\r
+ i -= 1\r
+\r
+ return (-1, stack)\r
+\r
+\r
+def ReverseCloseExpression(clean_lines, linenum, pos):\r
+ """If input points to ) or } or ] or >, finds the position that opens it.\r
+\r
+ If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the\r
+ linenum/pos that correspond to the opening of the expression.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ pos: A position on the line.\r
+\r
+ Returns:\r
+ A tuple (line, linenum, pos) pointer *at* the opening brace, or\r
+ (line, 0, -1) if we never find the matching opening brace. Note\r
+ we ignore strings and comments when matching; and the line we\r
+ return is the 'cleansed' line at linenum.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+ if line[pos] not in ')}]>':\r
+ return (line, 0, -1)\r
+\r
+ # Check last line\r
+ (start_pos, stack) = FindStartOfExpressionInLine(line, pos, [])\r
+ if start_pos > -1:\r
+ return (line, linenum, start_pos)\r
+\r
+ # Continue scanning backward\r
+ while stack and linenum > 0:\r
+ linenum -= 1\r
+ line = clean_lines.elided[linenum]\r
+ (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack)\r
+ if start_pos > -1:\r
+ return (line, linenum, start_pos)\r
+\r
+ # Did not find start of expression before beginning of file, give up\r
+ return (line, 0, -1)\r
+\r
+\r
+def CheckForCopyright(filename, lines, error):\r
+ """Logs an error if no Copyright message appears at the top of the file."""\r
+\r
+ # We'll say it should occur by line 10. Don't forget there's a\r
+ # dummy line at the front.\r
+ for line in xrange(1, min(len(lines), 11)):\r
+ if re.search(r'Copyright', lines[line], re.I): break\r
+ else: # means no copyright line was found\r
+ error(filename, 0, 'legal/copyright', 5,\r
+ 'No copyright message found. '\r
+ 'You should have a line: "Copyright [year] <Copyright Owner>"')\r
+\r
+\r
+def GetIndentLevel(line):\r
+ """Return the number of leading spaces in line.\r
+\r
+ Args:\r
+ line: A string to check.\r
+\r
+ Returns:\r
+ An integer count of leading spaces, possibly zero.\r
+ """\r
+ indent = Match(r'^( *)\S', line)\r
+ if indent:\r
+ return len(indent.group(1))\r
+ else:\r
+ return 0\r
+\r
+\r
+def GetHeaderGuardCPPVariable(filename):\r
+ """Returns the CPP variable that should be used as a header guard.\r
+\r
+ Args:\r
+ filename: The name of a C++ header file.\r
+\r
+ Returns:\r
+ The CPP variable that should be used as a header guard in the\r
+ named file.\r
+\r
+ """\r
+\r
+ # Restores original filename in case that cpplint is invoked from Emacs's\r
+ # flymake.\r
+ filename = re.sub(r'_flymake\.h$', '.h', filename)\r
+ filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename)\r
+ # Replace 'c++' with 'cpp'.\r
+ filename = filename.replace('C++', 'cpp').replace('c++', 'cpp')\r
+\r
+ fileinfo = FileInfo(filename)\r
+ file_path_from_root = fileinfo.RepositoryName()\r
+ if _root:\r
+ suffix = os.sep\r
+ # On Windows using directory separator will leave us with\r
+ # "bogus escape error" unless we properly escape regex.\r
+ if suffix == '\\':\r
+ suffix += '\\'\r
+ file_path_from_root = re.sub('^' + _root + suffix, '', file_path_from_root)\r
+ return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_'\r
+\r
+\r
+def CheckForHeaderGuard(filename, clean_lines, error):\r
+ """Checks that the file contains a header guard.\r
+\r
+ Logs an error if no #ifndef header guard is present. For other\r
+ headers, checks that the full pathname is used.\r
+\r
+ Args:\r
+ filename: The name of the C++ header file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ error: The function to call with any errors found.\r
+ """\r
+\r
+ # Don't check for header guards if there are error suppression\r
+ # comments somewhere in this file.\r
+ #\r
+ # Because this is silencing a warning for a nonexistent line, we\r
+ # only support the very specific NOLINT(build/header_guard) syntax,\r
+ # and not the general NOLINT or NOLINT(*) syntax.\r
+ raw_lines = clean_lines.lines_without_raw_strings\r
+ for i in raw_lines:\r
+ if Search(r'//\s*NOLINT\(build/header_guard\)', i):\r
+ return\r
+\r
+ cppvar = GetHeaderGuardCPPVariable(filename)\r
+\r
+ ifndef = ''\r
+ ifndef_linenum = 0\r
+ define = ''\r
+ endif = ''\r
+ endif_linenum = 0\r
+ for linenum, line in enumerate(raw_lines):\r
+ linesplit = line.split()\r
+ if len(linesplit) >= 2:\r
+ # find the first occurrence of #ifndef and #define, save arg\r
+ if not ifndef and linesplit[0] == '#ifndef':\r
+ # set ifndef to the header guard presented on the #ifndef line.\r
+ ifndef = linesplit[1]\r
+ ifndef_linenum = linenum\r
+ if not define and linesplit[0] == '#define':\r
+ define = linesplit[1]\r
+ # find the last occurrence of #endif, save entire line\r
+ if line.startswith('#endif'):\r
+ endif = line\r
+ endif_linenum = linenum\r
+\r
+ if not ifndef or not define or ifndef != define:\r
+ error(filename, 0, 'build/header_guard', 5,\r
+ 'No #ifndef header guard found, suggested CPP variable is: %s' %\r
+ cppvar)\r
+ return\r
+\r
+ # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__\r
+ # for backward compatibility.\r
+ if ifndef != cppvar:\r
+ error_level = 0\r
+ if ifndef != cppvar + '_':\r
+ error_level = 5\r
+\r
+ ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum,\r
+ error)\r
+ error(filename, ifndef_linenum, 'build/header_guard', error_level,\r
+ '#ifndef header guard has wrong style, please use: %s' % cppvar)\r
+\r
+ # Check for "//" comments on endif line.\r
+ ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum,\r
+ error)\r
+ match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif)\r
+ if match:\r
+ if match.group(1) == '_':\r
+ # Issue low severity warning for deprecated double trailing underscore\r
+ error(filename, endif_linenum, 'build/header_guard', 0,\r
+ '#endif line should be "#endif // %s"' % cppvar)\r
+ return\r
+\r
+ # Didn't find the corresponding "//" comment. If this file does not\r
+ # contain any "//" comments at all, it could be that the compiler\r
+ # only wants "/**/" comments, look for those instead.\r
+ no_single_line_comments = True\r
+ for i in xrange(1, len(raw_lines) - 1):\r
+ line = raw_lines[i]\r
+ if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line):\r
+ no_single_line_comments = False\r
+ break\r
+\r
+ if no_single_line_comments:\r
+ match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif)\r
+ if match:\r
+ if match.group(1) == '_':\r
+ # Low severity warning for double trailing underscore\r
+ error(filename, endif_linenum, 'build/header_guard', 0,\r
+ '#endif line should be "#endif /* %s */"' % cppvar)\r
+ return\r
+\r
+ # Didn't find anything\r
+ error(filename, endif_linenum, 'build/header_guard', 5,\r
+ '#endif line should be "#endif // %s"' % cppvar)\r
+\r
+\r
+def CheckHeaderFileIncluded(filename, include_state, error):\r
+ """Logs an error if a .cc file does not include its header."""\r
+\r
+ # Do not check test files\r
+ fileinfo = FileInfo(filename)\r
+ if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()):\r
+ return\r
+\r
+ headerfile = filename[0:len(filename) - len(fileinfo.Extension())] + '.h'\r
+ if not os.path.exists(headerfile):\r
+ return\r
+ headername = FileInfo(headerfile).RepositoryName()\r
+ first_include = 0\r
+ for section_list in include_state.include_list:\r
+ for f in section_list:\r
+ if headername in f[0] or f[0] in headername:\r
+ return\r
+ if not first_include:\r
+ first_include = f[1]\r
+\r
+ error(filename, first_include, 'build/include', 5,\r
+ '%s should include its header file %s' % (fileinfo.RepositoryName(),\r
+ headername))\r
+\r
+\r
+def CheckForBadCharacters(filename, lines, error):\r
+ """Logs an error for each line containing bad characters.\r
+\r
+ Two kinds of bad characters:\r
+\r
+ 1. Unicode replacement characters: These indicate that either the file\r
+ contained invalid UTF-8 (likely) or Unicode replacement characters (which\r
+ it shouldn't). Note that it's possible for this to throw off line\r
+ numbering if the invalid UTF-8 occurred adjacent to a newline.\r
+\r
+ 2. NUL bytes. These are problematic for some tools.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ lines: An array of strings, each representing a line of the file.\r
+ error: The function to call with any errors found.\r
+ """\r
+ for linenum, line in enumerate(lines):\r
+ if u'\ufffd' in line:\r
+ error(filename, linenum, 'readability/utf8', 5,\r
+ 'Line contains invalid UTF-8 (or Unicode replacement character).')\r
+ if '\0' in line:\r
+ error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.')\r
+\r
+\r
+def CheckForNewlineAtEOF(filename, lines, error):\r
+ """Logs an error if there is no newline char at the end of the file.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ lines: An array of strings, each representing a line of the file.\r
+ error: The function to call with any errors found.\r
+ """\r
+\r
+ # The array lines() was created by adding two newlines to the\r
+ # original file (go figure), then splitting on \n.\r
+ # To verify that the file ends in \n, we just have to make sure the\r
+ # last-but-two element of lines() exists and is empty.\r
+ if len(lines) < 3 or lines[-2]:\r
+ error(filename, len(lines) - 2, 'whitespace/ending_newline', 5,\r
+ 'Could not find a newline character at the end of the file.')\r
+\r
+\r
+def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):\r
+ """Logs an error if we see /* ... */ or "..." that extend past one line.\r
+\r
+ /* ... */ comments are legit inside macros, for one line.\r
+ Otherwise, we prefer // comments, so it's ok to warn about the\r
+ other. Likewise, it's ok for strings to extend across multiple\r
+ lines, as long as a line continuation character (backslash)\r
+ terminates each line. Although not currently prohibited by the C++\r
+ style guide, it's ugly and unnecessary. We don't do well with either\r
+ in this lint program, so we warn about both.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # Remove all \\ (escaped backslashes) from the line. They are OK, and the\r
+ # second (escaped) slash may trigger later \" detection erroneously.\r
+ line = line.replace('\\\\', '')\r
+\r
+ if line.count('/*') > line.count('*/'):\r
+ error(filename, linenum, 'readability/multiline_comment', 5,\r
+ 'Complex multi-line /*...*/-style comment found. '\r
+ 'Lint may give bogus warnings. '\r
+ 'Consider replacing these with //-style comments, '\r
+ 'with #if 0...#endif, '\r
+ 'or with more clearly structured multi-line comments.')\r
+\r
+ if (line.count('"') - line.count('\\"')) % 2:\r
+ error(filename, linenum, 'readability/multiline_string', 5,\r
+ 'Multi-line string ("...") found. This lint script doesn\'t '\r
+ 'do well with such strings, and may give bogus warnings. '\r
+ 'Use C++11 raw strings or concatenation instead.')\r
+\r
+\r
+# (non-threadsafe name, thread-safe alternative, validation pattern)\r
+#\r
+# The validation pattern is used to eliminate false positives such as:\r
+# _rand(); // false positive due to substring match.\r
+# ->rand(); // some member function rand().\r
+# ACMRandom rand(seed); // some variable named rand.\r
+# ISAACRandom rand(); // another variable named rand.\r
+#\r
+# Basically we require the return value of these functions to be used\r
+# in some expression context on the same line by matching on some\r
+# operator before the function name. This eliminates constructors and\r
+# member function calls.\r
+_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)'\r
+_THREADING_LIST = (\r
+ ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'),\r
+ ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'),\r
+ ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'),\r
+ ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'),\r
+ ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'),\r
+ ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'),\r
+ ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'),\r
+ ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'),\r
+ ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'),\r
+ ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'),\r
+ ('strtok(', 'strtok_r(',\r
+ _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'),\r
+ ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'),\r
+ )\r
+\r
+\r
+def CheckPosixThreading(filename, clean_lines, linenum, error):\r
+ """Checks for calls to thread-unsafe functions.\r
+\r
+ Much code has been originally written without consideration of\r
+ multi-threading. Also, engineers are relying on their old experience;\r
+ they have learned posix before threading extensions were added. These\r
+ tests guide the engineers to use thread-safe functions (when using\r
+ posix directly).\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+ for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST:\r
+ # Additional pattern matching check to confirm that this is the\r
+ # function we are looking for\r
+ if Search(pattern, line):\r
+ error(filename, linenum, 'runtime/threadsafe_fn', 2,\r
+ 'Consider using ' + multithread_safe_func +\r
+ '...) instead of ' + single_thread_func +\r
+ '...) for improved thread safety.')\r
+\r
+\r
+def CheckVlogArguments(filename, clean_lines, linenum, error):\r
+ """Checks that VLOG() is only used for defining a logging level.\r
+\r
+ For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and\r
+ VLOG(FATAL) are not.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+ if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line):\r
+ error(filename, linenum, 'runtime/vlog', 5,\r
+ 'VLOG() should be used with numeric verbosity level. '\r
+ 'Use LOG() if you want symbolic severity levels.')\r
+\r
+# Matches invalid increment: *count++, which moves pointer instead of\r
+# incrementing a value.\r
+_RE_PATTERN_INVALID_INCREMENT = re.compile(\r
+ r'^\s*\*\w+(\+\+|--);')\r
+\r
+\r
+def CheckInvalidIncrement(filename, clean_lines, linenum, error):\r
+ """Checks for invalid increment *count++.\r
+\r
+ For example following function:\r
+ void increment_counter(int* count) {\r
+ *count++;\r
+ }\r
+ is invalid, because it effectively does count++, moving pointer, and should\r
+ be replaced with ++*count, (*count)++ or *count += 1.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+ if _RE_PATTERN_INVALID_INCREMENT.match(line):\r
+ error(filename, linenum, 'runtime/invalid_increment', 5,\r
+ 'Changing pointer instead of value (or unused value of operator*).')\r
+\r
+\r
+def IsMacroDefinition(clean_lines, linenum):\r
+ if Search(r'^#define', clean_lines[linenum]):\r
+ return True\r
+\r
+ if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]):\r
+ return True\r
+\r
+ return False\r
+\r
+\r
+def IsForwardClassDeclaration(clean_lines, linenum):\r
+ return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum])\r
+\r
+\r
+class _BlockInfo(object):\r
+ """Stores information about a generic block of code."""\r
+\r
+ def __init__(self, linenum, seen_open_brace):\r
+ self.starting_linenum = linenum\r
+ self.seen_open_brace = seen_open_brace\r
+ self.open_parentheses = 0\r
+ self.inline_asm = _NO_ASM\r
+ self.check_namespace_indentation = False\r
+\r
+ def CheckBegin(self, filename, clean_lines, linenum, error):\r
+ """Run checks that applies to text up to the opening brace.\r
+\r
+ This is mostly for checking the text after the class identifier\r
+ and the "{", usually where the base class is specified. For other\r
+ blocks, there isn't much to check, so we always pass.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ pass\r
+\r
+ def CheckEnd(self, filename, clean_lines, linenum, error):\r
+ """Run checks that applies to text after the closing brace.\r
+\r
+ This is mostly used for checking end of namespace comments.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ pass\r
+\r
+ def IsBlockInfo(self):\r
+ """Returns true if this block is a _BlockInfo.\r
+\r
+ This is convenient for verifying that an object is an instance of\r
+ a _BlockInfo, but not an instance of any of the derived classes.\r
+\r
+ Returns:\r
+ True for this class, False for derived classes.\r
+ """\r
+ return self.__class__ == _BlockInfo\r
+\r
+\r
+class _ExternCInfo(_BlockInfo):\r
+ """Stores information about an 'extern "C"' block."""\r
+\r
+ def __init__(self, linenum):\r
+ _BlockInfo.__init__(self, linenum, True)\r
+\r
+\r
+class _ClassInfo(_BlockInfo):\r
+ """Stores information about a class."""\r
+\r
+ def __init__(self, name, class_or_struct, clean_lines, linenum):\r
+ _BlockInfo.__init__(self, linenum, False)\r
+ self.name = name\r
+ self.is_derived = False\r
+ self.check_namespace_indentation = True\r
+ if class_or_struct == 'struct':\r
+ self.access = 'public'\r
+ self.is_struct = True\r
+ else:\r
+ self.access = 'private'\r
+ self.is_struct = False\r
+\r
+ # Remember initial indentation level for this class. Using raw_lines here\r
+ # instead of elided to account for leading comments.\r
+ self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum])\r
+\r
+ # Try to find the end of the class. This will be confused by things like:\r
+ # class A {\r
+ # } *x = { ...\r
+ #\r
+ # But it's still good enough for CheckSectionSpacing.\r
+ self.last_line = 0\r
+ depth = 0\r
+ for i in range(linenum, clean_lines.NumLines()):\r
+ line = clean_lines.elided[i]\r
+ depth += line.count('{') - line.count('}')\r
+ if not depth:\r
+ self.last_line = i\r
+ break\r
+\r
+ def CheckBegin(self, filename, clean_lines, linenum, error):\r
+ # Look for a bare ':'\r
+ if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]):\r
+ self.is_derived = True\r
+\r
+ def CheckEnd(self, filename, clean_lines, linenum, error):\r
+ # If there is a DISALLOW macro, it should appear near the end of\r
+ # the class.\r
+ seen_last_thing_in_class = False\r
+ for i in xrange(linenum - 1, self.starting_linenum, -1):\r
+ match = Search(\r
+ r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' +\r
+ self.name + r'\)',\r
+ clean_lines.elided[i])\r
+ if match:\r
+ if seen_last_thing_in_class:\r
+ error(filename, i, 'readability/constructors', 3,\r
+ match.group(1) + ' should be the last thing in the class')\r
+ break\r
+\r
+ if not Match(r'^\s*$', clean_lines.elided[i]):\r
+ seen_last_thing_in_class = True\r
+\r
+ # Check that closing brace is aligned with beginning of the class.\r
+ # Only do this if the closing brace is indented by only whitespaces.\r
+ # This means we will not check single-line class definitions.\r
+ indent = Match(r'^( *)\}', clean_lines.elided[linenum])\r
+ if indent and len(indent.group(1)) != self.class_indent:\r
+ if self.is_struct:\r
+ parent = 'struct ' + self.name\r
+ else:\r
+ parent = 'class ' + self.name\r
+ error(filename, linenum, 'whitespace/indent', 3,\r
+ 'Closing brace should be aligned with beginning of %s' % parent)\r
+\r
+\r
+class _NamespaceInfo(_BlockInfo):\r
+ """Stores information about a namespace."""\r
+\r
+ def __init__(self, name, linenum):\r
+ _BlockInfo.__init__(self, linenum, False)\r
+ self.name = name or ''\r
+ self.check_namespace_indentation = True\r
+\r
+ def CheckEnd(self, filename, clean_lines, linenum, error):\r
+ """Check end of namespace comments."""\r
+ line = clean_lines.raw_lines[linenum]\r
+\r
+ # Check how many lines is enclosed in this namespace. Don't issue\r
+ # warning for missing namespace comments if there aren't enough\r
+ # lines. However, do apply checks if there is already an end of\r
+ # namespace comment and it's incorrect.\r
+ #\r
+ # TODO(unknown): We always want to check end of namespace comments\r
+ # if a namespace is large, but sometimes we also want to apply the\r
+ # check if a short namespace contained nontrivial things (something\r
+ # other than forward declarations). There is currently no logic on\r
+ # deciding what these nontrivial things are, so this check is\r
+ # triggered by namespace size only, which works most of the time.\r
+ if (linenum - self.starting_linenum < 10\r
+ and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)):\r
+ return\r
+\r
+ # Look for matching comment at end of namespace.\r
+ #\r
+ # Note that we accept C style "/* */" comments for terminating\r
+ # namespaces, so that code that terminate namespaces inside\r
+ # preprocessor macros can be cpplint clean.\r
+ #\r
+ # We also accept stuff like "// end of namespace <name>." with the\r
+ # period at the end.\r
+ #\r
+ # Besides these, we don't accept anything else, otherwise we might\r
+ # get false negatives when existing comment is a substring of the\r
+ # expected namespace.\r
+ if self.name:\r
+ # Named namespace\r
+ if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' +\r
+ re.escape(self.name) + r'[\*/\.\\\s]*$'),\r
+ line):\r
+ error(filename, linenum, 'readability/namespace', 5,\r
+ 'Namespace should be terminated with "// namespace %s"' %\r
+ self.name)\r
+ else:\r
+ # Anonymous namespace\r
+ if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line):\r
+ # If "// namespace anonymous" or "// anonymous namespace (more text)",\r
+ # mention "// anonymous namespace" as an acceptable form\r
+ if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line):\r
+ error(filename, linenum, 'readability/namespace', 5,\r
+ 'Anonymous namespace should be terminated with "// namespace"'\r
+ ' or "// anonymous namespace"')\r
+ else:\r
+ error(filename, linenum, 'readability/namespace', 5,\r
+ 'Anonymous namespace should be terminated with "// namespace"')\r
+\r
+\r
+class _PreprocessorInfo(object):\r
+ """Stores checkpoints of nesting stacks when #if/#else is seen."""\r
+\r
+ def __init__(self, stack_before_if):\r
+ # The entire nesting stack before #if\r
+ self.stack_before_if = stack_before_if\r
+\r
+ # The entire nesting stack up to #else\r
+ self.stack_before_else = []\r
+\r
+ # Whether we have already seen #else or #elif\r
+ self.seen_else = False\r
+\r
+\r
+class NestingState(object):\r
+ """Holds states related to parsing braces."""\r
+\r
+ def __init__(self):\r
+ # Stack for tracking all braces. An object is pushed whenever we\r
+ # see a "{", and popped when we see a "}". Only 3 types of\r
+ # objects are possible:\r
+ # - _ClassInfo: a class or struct.\r
+ # - _NamespaceInfo: a namespace.\r
+ # - _BlockInfo: some other type of block.\r
+ self.stack = []\r
+\r
+ # Top of the previous stack before each Update().\r
+ #\r
+ # Because the nesting_stack is updated at the end of each line, we\r
+ # had to do some convoluted checks to find out what is the current\r
+ # scope at the beginning of the line. This check is simplified by\r
+ # saving the previous top of nesting stack.\r
+ #\r
+ # We could save the full stack, but we only need the top. Copying\r
+ # the full nesting stack would slow down cpplint by ~10%.\r
+ self.previous_stack_top = []\r
+\r
+ # Stack of _PreprocessorInfo objects.\r
+ self.pp_stack = []\r
+\r
+ def SeenOpenBrace(self):\r
+ """Check if we have seen the opening brace for the innermost block.\r
+\r
+ Returns:\r
+ True if we have seen the opening brace, False if the innermost\r
+ block is still expecting an opening brace.\r
+ """\r
+ return (not self.stack) or self.stack[-1].seen_open_brace\r
+\r
+ def InNamespaceBody(self):\r
+ """Check if we are currently one level inside a namespace body.\r
+\r
+ Returns:\r
+ True if top of the stack is a namespace block, False otherwise.\r
+ """\r
+ return self.stack and isinstance(self.stack[-1], _NamespaceInfo)\r
+\r
+ def InExternC(self):\r
+ """Check if we are currently one level inside an 'extern "C"' block.\r
+\r
+ Returns:\r
+ True if top of the stack is an extern block, False otherwise.\r
+ """\r
+ return self.stack and isinstance(self.stack[-1], _ExternCInfo)\r
+\r
+ def InClassDeclaration(self):\r
+ """Check if we are currently one level inside a class or struct declaration.\r
+\r
+ Returns:\r
+ True if top of the stack is a class/struct, False otherwise.\r
+ """\r
+ return self.stack and isinstance(self.stack[-1], _ClassInfo)\r
+\r
+ def InAsmBlock(self):\r
+ """Check if we are currently one level inside an inline ASM block.\r
+\r
+ Returns:\r
+ True if the top of the stack is a block containing inline ASM.\r
+ """\r
+ return self.stack and self.stack[-1].inline_asm != _NO_ASM\r
+\r
+ def InTemplateArgumentList(self, clean_lines, linenum, pos):\r
+ """Check if current position is inside template argument list.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ pos: position just after the suspected template argument.\r
+ Returns:\r
+ True if (linenum, pos) is inside template arguments.\r
+ """\r
+ while linenum < clean_lines.NumLines():\r
+ # Find the earliest character that might indicate a template argument\r
+ line = clean_lines.elided[linenum]\r
+ match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:])\r
+ if not match:\r
+ linenum += 1\r
+ pos = 0\r
+ continue\r
+ token = match.group(1)\r
+ pos += len(match.group(0))\r
+\r
+ # These things do not look like template argument list:\r
+ # class Suspect {\r
+ # class Suspect x; }\r
+ if token in ('{', '}', ';'): return False\r
+\r
+ # These things look like template argument list:\r
+ # template <class Suspect>\r
+ # template <class Suspect = default_value>\r
+ # template <class Suspect[]>\r
+ # template <class Suspect...>\r
+ if token in ('>', '=', '[', ']', '.'): return True\r
+\r
+ # Check if token is an unmatched '<'.\r
+ # If not, move on to the next character.\r
+ if token != '<':\r
+ pos += 1\r
+ if pos >= len(line):\r
+ linenum += 1\r
+ pos = 0\r
+ continue\r
+\r
+ # We can't be sure if we just find a single '<', and need to\r
+ # find the matching '>'.\r
+ (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1)\r
+ if end_pos < 0:\r
+ # Not sure if template argument list or syntax error in file\r
+ return False\r
+ linenum = end_line\r
+ pos = end_pos\r
+ return False\r
+\r
+ def UpdatePreprocessor(self, line):\r
+ """Update preprocessor stack.\r
+\r
+ We need to handle preprocessors due to classes like this:\r
+ #ifdef SWIG\r
+ struct ResultDetailsPageElementExtensionPoint {\r
+ #else\r
+ struct ResultDetailsPageElementExtensionPoint : public Extension {\r
+ #endif\r
+\r
+ We make the following assumptions (good enough for most files):\r
+ - Preprocessor condition evaluates to true from #if up to first\r
+ #else/#elif/#endif.\r
+\r
+ - Preprocessor condition evaluates to false from #else/#elif up\r
+ to #endif. We still perform lint checks on these lines, but\r
+ these do not affect nesting stack.\r
+\r
+ Args:\r
+ line: current line to check.\r
+ """\r
+ if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line):\r
+ # Beginning of #if block, save the nesting stack here. The saved\r
+ # stack will allow us to restore the parsing state in the #else case.\r
+ self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack)))\r
+ elif Match(r'^\s*#\s*(else|elif)\b', line):\r
+ # Beginning of #else block\r
+ if self.pp_stack:\r
+ if not self.pp_stack[-1].seen_else:\r
+ # This is the first #else or #elif block. Remember the\r
+ # whole nesting stack up to this point. This is what we\r
+ # keep after the #endif.\r
+ self.pp_stack[-1].seen_else = True\r
+ self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack)\r
+\r
+ # Restore the stack to how it was before the #if\r
+ self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if)\r
+ else:\r
+ # TODO(unknown): unexpected #else, issue warning?\r
+ pass\r
+ elif Match(r'^\s*#\s*endif\b', line):\r
+ # End of #if or #else blocks.\r
+ if self.pp_stack:\r
+ # If we saw an #else, we will need to restore the nesting\r
+ # stack to its former state before the #else, otherwise we\r
+ # will just continue from where we left off.\r
+ if self.pp_stack[-1].seen_else:\r
+ # Here we can just use a shallow copy since we are the last\r
+ # reference to it.\r
+ self.stack = self.pp_stack[-1].stack_before_else\r
+ # Drop the corresponding #if\r
+ self.pp_stack.pop()\r
+ else:\r
+ # TODO(unknown): unexpected #endif, issue warning?\r
+ pass\r
+\r
+ # TODO(unknown): Update() is too long, but we will refactor later.\r
+ def Update(self, filename, clean_lines, linenum, error):\r
+ """Update nesting state with current line.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # Remember top of the previous nesting stack.\r
+ #\r
+ # The stack is always pushed/popped and not modified in place, so\r
+ # we can just do a shallow copy instead of copy.deepcopy. Using\r
+ # deepcopy would slow down cpplint by ~28%.\r
+ if self.stack:\r
+ self.previous_stack_top = self.stack[-1]\r
+ else:\r
+ self.previous_stack_top = None\r
+\r
+ # Update pp_stack\r
+ self.UpdatePreprocessor(line)\r
+\r
+ # Count parentheses. This is to avoid adding struct arguments to\r
+ # the nesting stack.\r
+ if self.stack:\r
+ inner_block = self.stack[-1]\r
+ depth_change = line.count('(') - line.count(')')\r
+ inner_block.open_parentheses += depth_change\r
+\r
+ # Also check if we are starting or ending an inline assembly block.\r
+ if inner_block.inline_asm in (_NO_ASM, _END_ASM):\r
+ if (depth_change != 0 and\r
+ inner_block.open_parentheses == 1 and\r
+ _MATCH_ASM.match(line)):\r
+ # Enter assembly block\r
+ inner_block.inline_asm = _INSIDE_ASM\r
+ else:\r
+ # Not entering assembly block. If previous line was _END_ASM,\r
+ # we will now shift to _NO_ASM state.\r
+ inner_block.inline_asm = _NO_ASM\r
+ elif (inner_block.inline_asm == _INSIDE_ASM and\r
+ inner_block.open_parentheses == 0):\r
+ # Exit assembly block\r
+ inner_block.inline_asm = _END_ASM\r
+\r
+ # Consume namespace declaration at the beginning of the line. Do\r
+ # this in a loop so that we catch same line declarations like this:\r
+ # namespace proto2 { namespace bridge { class MessageSet; } }\r
+ while True:\r
+ # Match start of namespace. The "\b\s*" below catches namespace\r
+ # declarations even if it weren't followed by a whitespace, this\r
+ # is so that we don't confuse our namespace checker. The\r
+ # missing spaces will be flagged by CheckSpacing.\r
+ namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line)\r
+ if not namespace_decl_match:\r
+ break\r
+\r
+ new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum)\r
+ self.stack.append(new_namespace)\r
+\r
+ line = namespace_decl_match.group(2)\r
+ if line.find('{') != -1:\r
+ new_namespace.seen_open_brace = True\r
+ line = line[line.find('{') + 1:]\r
+\r
+ # Look for a class declaration in whatever is left of the line\r
+ # after parsing namespaces. The regexp accounts for decorated classes\r
+ # such as in:\r
+ # class LOCKABLE API Object {\r
+ # };\r
+ class_decl_match = Match(\r
+ r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?'\r
+ r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))'\r
+ r'(.*)$', line)\r
+ if (class_decl_match and\r
+ (not self.stack or self.stack[-1].open_parentheses == 0)):\r
+ # We do not want to accept classes that are actually template arguments:\r
+ # template <class Ignore1,\r
+ # class Ignore2 = Default<Args>,\r
+ # template <Args> class Ignore3>\r
+ # void Function() {};\r
+ #\r
+ # To avoid template argument cases, we scan forward and look for\r
+ # an unmatched '>'. If we see one, assume we are inside a\r
+ # template argument list.\r
+ end_declaration = len(class_decl_match.group(1))\r
+ if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration):\r
+ self.stack.append(_ClassInfo(\r
+ class_decl_match.group(3), class_decl_match.group(2),\r
+ clean_lines, linenum))\r
+ line = class_decl_match.group(4)\r
+\r
+ # If we have not yet seen the opening brace for the innermost block,\r
+ # run checks here.\r
+ if not self.SeenOpenBrace():\r
+ self.stack[-1].CheckBegin(filename, clean_lines, linenum, error)\r
+\r
+ # Update access control if we are inside a class/struct\r
+ if self.stack and isinstance(self.stack[-1], _ClassInfo):\r
+ classinfo = self.stack[-1]\r
+ access_match = Match(\r
+ r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?'\r
+ r':(?:[^:]|$)',\r
+ line)\r
+ if access_match:\r
+ classinfo.access = access_match.group(2)\r
+\r
+ # Check that access keywords are indented +1 space. Skip this\r
+ # check if the keywords are not preceded by whitespaces.\r
+ indent = access_match.group(1)\r
+ if (len(indent) != classinfo.class_indent + 1 and\r
+ Match(r'^\s*$', indent)):\r
+ if classinfo.is_struct:\r
+ parent = 'struct ' + classinfo.name\r
+ else:\r
+ parent = 'class ' + classinfo.name\r
+ slots = ''\r
+ if access_match.group(3):\r
+ slots = access_match.group(3)\r
+ error(filename, linenum, 'whitespace/indent', 3,\r
+ '%s%s: should be indented +1 space inside %s' % (\r
+ access_match.group(2), slots, parent))\r
+\r
+ # Consume braces or semicolons from what's left of the line\r
+ while True:\r
+ # Match first brace, semicolon, or closed parenthesis.\r
+ matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line)\r
+ if not matched:\r
+ break\r
+\r
+ token = matched.group(1)\r
+ if token == '{':\r
+ # If namespace or class hasn't seen a opening brace yet, mark\r
+ # namespace/class head as complete. Push a new block onto the\r
+ # stack otherwise.\r
+ if not self.SeenOpenBrace():\r
+ self.stack[-1].seen_open_brace = True\r
+ elif Match(r'^extern\s*"[^"]*"\s*\{', line):\r
+ self.stack.append(_ExternCInfo(linenum))\r
+ else:\r
+ self.stack.append(_BlockInfo(linenum, True))\r
+ if _MATCH_ASM.match(line):\r
+ self.stack[-1].inline_asm = _BLOCK_ASM\r
+\r
+ elif token == ';' or token == ')':\r
+ # If we haven't seen an opening brace yet, but we already saw\r
+ # a semicolon, this is probably a forward declaration. Pop\r
+ # the stack for these.\r
+ #\r
+ # Similarly, if we haven't seen an opening brace yet, but we\r
+ # already saw a closing parenthesis, then these are probably\r
+ # function arguments with extra "class" or "struct" keywords.\r
+ # Also pop these stack for these.\r
+ if not self.SeenOpenBrace():\r
+ self.stack.pop()\r
+ else: # token == '}'\r
+ # Perform end of block checks and pop the stack.\r
+ if self.stack:\r
+ self.stack[-1].CheckEnd(filename, clean_lines, linenum, error)\r
+ self.stack.pop()\r
+ line = matched.group(2)\r
+\r
+ def InnermostClass(self):\r
+ """Get class info on the top of the stack.\r
+\r
+ Returns:\r
+ A _ClassInfo object if we are inside a class, or None otherwise.\r
+ """\r
+ for i in range(len(self.stack), 0, -1):\r
+ classinfo = self.stack[i - 1]\r
+ if isinstance(classinfo, _ClassInfo):\r
+ return classinfo\r
+ return None\r
+\r
+ def CheckCompletedBlocks(self, filename, error):\r
+ """Checks that all classes and namespaces have been completely parsed.\r
+\r
+ Call this when all lines in a file have been processed.\r
+ Args:\r
+ filename: The name of the current file.\r
+ error: The function to call with any errors found.\r
+ """\r
+ # Note: This test can result in false positives if #ifdef constructs\r
+ # get in the way of brace matching. See the testBuildClass test in\r
+ # cpplint_unittest.py for an example of this.\r
+ for obj in self.stack:\r
+ if isinstance(obj, _ClassInfo):\r
+ error(filename, obj.starting_linenum, 'build/class', 5,\r
+ 'Failed to find complete declaration of class %s' %\r
+ obj.name)\r
+ elif isinstance(obj, _NamespaceInfo):\r
+ error(filename, obj.starting_linenum, 'build/namespaces', 5,\r
+ 'Failed to find complete declaration of namespace %s' %\r
+ obj.name)\r
+\r
+\r
+def CheckForNonStandardConstructs(filename, clean_lines, linenum,\r
+ nesting_state, error):\r
+ r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.\r
+\r
+ Complain about several constructs which gcc-2 accepts, but which are\r
+ not standard C++. Warning about these in lint is one way to ease the\r
+ transition to new compilers.\r
+ - put storage class first (e.g. "static const" instead of "const static").\r
+ - "%lld" instead of %qd" in printf-type functions.\r
+ - "%1$d" is non-standard in printf-type functions.\r
+ - "\%" is an undefined character escape sequence.\r
+ - text after #endif is not allowed.\r
+ - invalid inner-style forward declaration.\r
+ - >? and <? operators, and their >?= and <?= cousins.\r
+\r
+ Additionally, check for constructor/destructor style violations and reference\r
+ members, as it is very convenient to do so while checking for\r
+ gcc-2 compliance.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ nesting_state: A NestingState instance which maintains information about\r
+ the current stack of nested blocks being parsed.\r
+ error: A callable to which errors are reported, which takes 4 arguments:\r
+ filename, line number, error level, and message\r
+ """\r
+\r
+ # Remove comments from the line, but leave in strings for now.\r
+ line = clean_lines.lines[linenum]\r
+\r
+ if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line):\r
+ error(filename, linenum, 'runtime/printf_format', 3,\r
+ '%q in format strings is deprecated. Use %ll instead.')\r
+\r
+ if Search(r'printf\s*\(.*".*%\d+\$', line):\r
+ error(filename, linenum, 'runtime/printf_format', 2,\r
+ '%N$ formats are unconventional. Try rewriting to avoid them.')\r
+\r
+ # Remove escaped backslashes before looking for undefined escapes.\r
+ line = line.replace('\\\\', '')\r
+\r
+ if Search(r'("|\').*\\(%|\[|\(|{)', line):\r
+ error(filename, linenum, 'build/printf_format', 3,\r
+ '%, [, (, and { are undefined character escapes. Unescape them.')\r
+\r
+ # For the rest, work with both comments and strings removed.\r
+ line = clean_lines.elided[linenum]\r
+\r
+ if Search(r'\b(const|volatile|void|char|short|int|long'\r
+ r'|float|double|signed|unsigned'\r
+ r'|schar|u?int8|u?int16|u?int32|u?int64)'\r
+ r'\s+(register|static|extern|typedef)\b',\r
+ line):\r
+ error(filename, linenum, 'build/storage_class', 5,\r
+ 'Storage-class specifier (static, extern, typedef, etc) should be '\r
+ 'at the beginning of the declaration.')\r
+\r
+ if Match(r'\s*#\s*endif\s*[^/\s]+', line):\r
+ error(filename, linenum, 'build/endif_comment', 5,\r
+ 'Uncommented text after #endif is non-standard. Use a comment.')\r
+\r
+ if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line):\r
+ error(filename, linenum, 'build/forward_decl', 5,\r
+ 'Inner-style forward declarations are invalid. Remove this line.')\r
+\r
+ if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?',\r
+ line):\r
+ error(filename, linenum, 'build/deprecated', 3,\r
+ '>? and <? (max and min) operators are non-standard and deprecated.')\r
+\r
+ if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line):\r
+ # TODO(unknown): Could it be expanded safely to arbitrary references,\r
+ # without triggering too many false positives? The first\r
+ # attempt triggered 5 warnings for mostly benign code in the regtest, hence\r
+ # the restriction.\r
+ # Here's the original regexp, for the reference:\r
+ # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?'\r
+ # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;'\r
+ error(filename, linenum, 'runtime/member_string_references', 2,\r
+ 'const string& members are dangerous. It is much better to use '\r
+ 'alternatives, such as pointers or simple constants.')\r
+\r
+ # Everything else in this function operates on class declarations.\r
+ # Return early if the top of the nesting stack is not a class, or if\r
+ # the class head is not completed yet.\r
+ classinfo = nesting_state.InnermostClass()\r
+ if not classinfo or not classinfo.seen_open_brace:\r
+ return\r
+\r
+ # The class may have been declared with namespace or classname qualifiers.\r
+ # The constructor and destructor will not have those qualifiers.\r
+ base_classname = classinfo.name.split('::')[-1]\r
+\r
+ # Look for single-argument constructors that aren't marked explicit.\r
+ # Technically a valid construct, but against style.\r
+ explicit_constructor_match = Match(\r
+ r'\s+(?:(?:inline|constexpr)\s+)*(explicit\s+)?'\r
+ r'(?:(?:inline|constexpr)\s+)*%s\s*'\r
+ r'\(((?:[^()]|\([^()]*\))*)\)'\r
+ % re.escape(base_classname),\r
+ line)\r
+\r
+ if explicit_constructor_match:\r
+ is_marked_explicit = explicit_constructor_match.group(1)\r
+\r
+ if not explicit_constructor_match.group(2):\r
+ constructor_args = []\r
+ else:\r
+ constructor_args = explicit_constructor_match.group(2).split(',')\r
+\r
+ # collapse arguments so that commas in template parameter lists and function\r
+ # argument parameter lists don't split arguments in two\r
+ i = 0\r
+ while i < len(constructor_args):\r
+ constructor_arg = constructor_args[i]\r
+ while (constructor_arg.count('<') > constructor_arg.count('>') or\r
+ constructor_arg.count('(') > constructor_arg.count(')')):\r
+ constructor_arg += ',' + constructor_args[i + 1]\r
+ del constructor_args[i + 1]\r
+ constructor_args[i] = constructor_arg\r
+ i += 1\r
+\r
+ defaulted_args = [arg for arg in constructor_args if '=' in arg]\r
+ noarg_constructor = (not constructor_args or # empty arg list\r
+ # 'void' arg specifier\r
+ (len(constructor_args) == 1 and\r
+ constructor_args[0].strip() == 'void'))\r
+ onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg\r
+ not noarg_constructor) or\r
+ # all but at most one arg defaulted\r
+ (len(constructor_args) >= 1 and\r
+ not noarg_constructor and\r
+ len(defaulted_args) >= len(constructor_args) - 1))\r
+ initializer_list_constructor = bool(\r
+ onearg_constructor and\r
+ Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0]))\r
+ copy_constructor = bool(\r
+ onearg_constructor and\r
+ Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&'\r
+ % re.escape(base_classname), constructor_args[0].strip()))\r
+\r
+ if (not is_marked_explicit and\r
+ onearg_constructor and\r
+ not initializer_list_constructor and\r
+ not copy_constructor):\r
+ if defaulted_args:\r
+ error(filename, linenum, 'runtime/explicit', 5,\r
+ 'Constructors callable with one argument '\r
+ 'should be marked explicit.')\r
+ else:\r
+ error(filename, linenum, 'runtime/explicit', 5,\r
+ 'Single-parameter constructors should be marked explicit.')\r
+ elif is_marked_explicit and not onearg_constructor:\r
+ if noarg_constructor:\r
+ error(filename, linenum, 'runtime/explicit', 5,\r
+ 'Zero-parameter constructors should not be marked explicit.')\r
+\r
+\r
+def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error):\r
+ """Checks for the correctness of various spacing around function calls.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # Since function calls often occur inside if/for/while/switch\r
+ # expressions - which have their own, more liberal conventions - we\r
+ # first see if we should be looking inside such an expression for a\r
+ # function call, to which we can apply more strict standards.\r
+ fncall = line # if there's no control flow construct, look at whole line\r
+ for pattern in (r'\bif\s*\((.*)\)\s*{',\r
+ r'\bfor\s*\((.*)\)\s*{',\r
+ r'\bwhile\s*\((.*)\)\s*[{;]',\r
+ r'\bswitch\s*\((.*)\)\s*{'):\r
+ match = Search(pattern, line)\r
+ if match:\r
+ fncall = match.group(1) # look inside the parens for function calls\r
+ break\r
+\r
+ # Except in if/for/while/switch, there should never be space\r
+ # immediately inside parens (eg "f( 3, 4 )"). We make an exception\r
+ # for nested parens ( (a+b) + c ). Likewise, there should never be\r
+ # a space before a ( when it's a function argument. I assume it's a\r
+ # function argument when the char before the whitespace is legal in\r
+ # a function name (alnum + _) and we're not starting a macro. Also ignore\r
+ # pointers and references to arrays and functions coz they're too tricky:\r
+ # we use a very simple way to recognize these:\r
+ # " (something)(maybe-something)" or\r
+ # " (something)(maybe-something," or\r
+ # " (something)[something]"\r
+ # Note that we assume the contents of [] to be short enough that\r
+ # they'll never need to wrap.\r
+ if ( # Ignore control structures.\r
+ not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b',\r
+ fncall) and\r
+ # Ignore pointers/references to functions.\r
+ not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and\r
+ # Ignore pointers/references to arrays.\r
+ not Search(r' \([^)]+\)\[[^\]]+\]', fncall)):\r
+ if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call\r
+ error(filename, linenum, 'whitespace/parens', 4,\r
+ 'Extra space after ( in function call')\r
+ elif Search(r'\(\s+(?!(\s*\\)|\()', fncall):\r
+ error(filename, linenum, 'whitespace/parens', 2,\r
+ 'Extra space after (')\r
+ if (Search(r'\w\s+\(', fncall) and\r
+ not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and\r
+ not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and\r
+ not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and\r
+ not Search(r'\bcase\s+\(', fncall)):\r
+ # TODO(unknown): Space after an operator function seem to be a common\r
+ # error, silence those for now by restricting them to highest verbosity.\r
+ if Search(r'\boperator_*\b', line):\r
+ error(filename, linenum, 'whitespace/parens', 0,\r
+ 'Extra space before ( in function call')\r
+ else:\r
+ error(filename, linenum, 'whitespace/parens', 4,\r
+ 'Extra space before ( in function call')\r
+ # If the ) is followed only by a newline or a { + newline, assume it's\r
+ # part of a control statement (if/while/etc), and don't complain\r
+ if Search(r'[^)]\s+\)\s*[^{\s]', fncall):\r
+ # If the closing parenthesis is preceded by only whitespaces,\r
+ # try to give a more descriptive error message.\r
+ if Search(r'^\s+\)', fncall):\r
+ error(filename, linenum, 'whitespace/parens', 2,\r
+ 'Closing ) should be moved to the previous line')\r
+ else:\r
+ error(filename, linenum, 'whitespace/parens', 2,\r
+ 'Extra space before )')\r
+\r
+\r
+def IsBlankLine(line):\r
+ """Returns true if the given line is blank.\r
+\r
+ We consider a line to be blank if the line is empty or consists of\r
+ only white spaces.\r
+\r
+ Args:\r
+ line: A line of a string.\r
+\r
+ Returns:\r
+ True, if the given line is blank.\r
+ """\r
+ return not line or line.isspace()\r
+\r
+\r
+def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line,\r
+ error):\r
+ is_namespace_indent_item = (\r
+ len(nesting_state.stack) > 1 and\r
+ nesting_state.stack[-1].check_namespace_indentation and\r
+ isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and\r
+ nesting_state.previous_stack_top == nesting_state.stack[-2])\r
+\r
+ if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item,\r
+ clean_lines.elided, line):\r
+ CheckItemIndentationInNamespace(filename, clean_lines.elided,\r
+ line, error)\r
+\r
+\r
+def CheckForFunctionLengths(filename, clean_lines, linenum,\r
+ function_state, error):\r
+ """Reports for long function bodies.\r
+\r
+ For an overview why this is done, see:\r
+ https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions\r
+\r
+ Uses a simplistic algorithm assuming other style guidelines\r
+ (especially spacing) are followed.\r
+ Only checks unindented functions, so class members are unchecked.\r
+ Trivial bodies are unchecked, so constructors with huge initializer lists\r
+ may be missed.\r
+ Blank/comment lines are not counted so as to avoid encouraging the removal\r
+ of vertical space and comments just to get through a lint check.\r
+ NOLINT *on the last line of a function* disables this check.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ function_state: Current function name and lines in body so far.\r
+ error: The function to call with any errors found.\r
+ """\r
+ lines = clean_lines.lines\r
+ line = lines[linenum]\r
+ joined_line = ''\r
+\r
+ starting_func = False\r
+ regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ...\r
+ match_result = Match(regexp, line)\r
+ if match_result:\r
+ # If the name is all caps and underscores, figure it's a macro and\r
+ # ignore it, unless it's TEST or TEST_F.\r
+ function_name = match_result.group(1).split()[-1]\r
+ if function_name == 'TEST' or function_name == 'TEST_F' or (\r
+ not Match(r'[A-Z_]+$', function_name)):\r
+ starting_func = True\r
+\r
+ if starting_func:\r
+ body_found = False\r
+ for start_linenum in xrange(linenum, clean_lines.NumLines()):\r
+ start_line = lines[start_linenum]\r
+ joined_line += ' ' + start_line.lstrip()\r
+ if Search(r'(;|})', start_line): # Declarations and trivial functions\r
+ body_found = True\r
+ break # ... ignore\r
+ elif Search(r'{', start_line):\r
+ body_found = True\r
+ function = Search(r'((\w|:)*)\(', line).group(1)\r
+ if Match(r'TEST', function): # Handle TEST... macros\r
+ parameter_regexp = Search(r'(\(.*\))', joined_line)\r
+ if parameter_regexp: # Ignore bad syntax\r
+ function += parameter_regexp.group(1)\r
+ else:\r
+ function += '()'\r
+ function_state.Begin(function)\r
+ break\r
+ if not body_found:\r
+ # No body for the function (or evidence of a non-function) was found.\r
+ error(filename, linenum, 'readability/fn_size', 5,\r
+ 'Lint failed to find start of function body.')\r
+ elif Match(r'^\}\s*$', line): # function end\r
+ function_state.Check(error, filename, linenum)\r
+ function_state.End()\r
+ elif not Match(r'^\s*$', line):\r
+ function_state.Count() # Count non-blank/non-comment lines.\r
+\r
+\r
+_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?')\r
+\r
+\r
+def CheckComment(line, filename, linenum, next_line_start, error):\r
+ """Checks for common mistakes in comments.\r
+\r
+ Args:\r
+ line: The line in question.\r
+ filename: The name of the current file.\r
+ linenum: The number of the line to check.\r
+ next_line_start: The first non-whitespace column of the next line.\r
+ error: The function to call with any errors found.\r
+ """\r
+ commentpos = line.find('//')\r
+ if commentpos != -1:\r
+ # Check if the // may be in quotes. If so, ignore it\r
+ if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0:\r
+ # Allow one space for new scopes, two spaces otherwise:\r
+ if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and\r
+ ((commentpos >= 1 and\r
+ line[commentpos-1] not in string.whitespace) or\r
+ (commentpos >= 2 and\r
+ line[commentpos-2] not in string.whitespace))):\r
+ error(filename, linenum, 'whitespace/comments', 2,\r
+ 'At least two spaces is best between code and comments')\r
+\r
+ # Checks for common mistakes in TODO comments.\r
+ comment = line[commentpos:]\r
+ match = _RE_PATTERN_TODO.match(comment)\r
+ if match:\r
+ # One whitespace is correct; zero whitespace is handled elsewhere.\r
+ leading_whitespace = match.group(1)\r
+ if len(leading_whitespace) > 1:\r
+ error(filename, linenum, 'whitespace/todo', 2,\r
+ 'Too many spaces before TODO')\r
+\r
+ username = match.group(2)\r
+ if not username:\r
+ error(filename, linenum, 'readability/todo', 2,\r
+ 'Missing username in TODO; it should look like '\r
+ '"// TODO(my_username): Stuff."')\r
+\r
+ middle_whitespace = match.group(3)\r
+ # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison\r
+ if middle_whitespace != ' ' and middle_whitespace != '':\r
+ error(filename, linenum, 'whitespace/todo', 2,\r
+ 'TODO(my_username) should be followed by a space')\r
+\r
+ # If the comment contains an alphanumeric character, there\r
+ # should be a space somewhere between it and the // unless\r
+ # it's a /// or //! Doxygen comment.\r
+ if (Match(r'//[^ ]*\w', comment) and\r
+ not Match(r'(///|//\!)(\s+|$)', comment)):\r
+ error(filename, linenum, 'whitespace/comments', 4,\r
+ 'Should have a space between // and comment')\r
+\r
+\r
+def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):\r
+ """Checks for the correctness of various spacing issues in the code.\r
+\r
+ Things we check for: spaces around operators, spaces after\r
+ if/for/while/switch, no spaces around parens in function calls, two\r
+ spaces between code and comment, don't start a block with a blank\r
+ line, don't end a function with a blank line, don't add a blank line\r
+ after public/protected/private, don't have too many blank lines in a row.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ nesting_state: A NestingState instance which maintains information about\r
+ the current stack of nested blocks being parsed.\r
+ error: The function to call with any errors found.\r
+ """\r
+\r
+ # Don't use "elided" lines here, otherwise we can't check commented lines.\r
+ # Don't want to use "raw" either, because we don't want to check inside C++11\r
+ # raw strings,\r
+ raw = clean_lines.lines_without_raw_strings\r
+ line = raw[linenum]\r
+\r
+ # Before nixing comments, check if the line is blank for no good\r
+ # reason. This includes the first line after a block is opened, and\r
+ # blank lines at the end of a function (ie, right before a line like '}'\r
+ #\r
+ # Skip all the blank line checks if we are immediately inside a\r
+ # namespace body. In other words, don't issue blank line warnings\r
+ # for this block:\r
+ # namespace {\r
+ #\r
+ # }\r
+ #\r
+ # A warning about missing end of namespace comments will be issued instead.\r
+ #\r
+ # Also skip blank line checks for 'extern "C"' blocks, which are formatted\r
+ # like namespaces.\r
+ if (IsBlankLine(line) and\r
+ not nesting_state.InNamespaceBody() and\r
+ not nesting_state.InExternC()):\r
+ elided = clean_lines.elided\r
+ prev_line = elided[linenum - 1]\r
+ prevbrace = prev_line.rfind('{')\r
+ # TODO(unknown): Don't complain if line before blank line, and line after,\r
+ # both start with alnums and are indented the same amount.\r
+ # This ignores whitespace at the start of a namespace block\r
+ # because those are not usually indented.\r
+ if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1:\r
+ # OK, we have a blank line at the start of a code block. Before we\r
+ # complain, we check if it is an exception to the rule: The previous\r
+ # non-empty line has the parameters of a function header that are indented\r
+ # 4 spaces (because they did not fit in a 80 column line when placed on\r
+ # the same line as the function name). We also check for the case where\r
+ # the previous line is indented 6 spaces, which may happen when the\r
+ # initializers of a constructor do not fit into a 80 column line.\r
+ exception = False\r
+ if Match(r' {6}\w', prev_line): # Initializer list?\r
+ # We are looking for the opening column of initializer list, which\r
+ # should be indented 4 spaces to cause 6 space indentation afterwards.\r
+ search_position = linenum-2\r
+ while (search_position >= 0\r
+ and Match(r' {6}\w', elided[search_position])):\r
+ search_position -= 1\r
+ exception = (search_position >= 0\r
+ and elided[search_position][:5] == ' :')\r
+ else:\r
+ # Search for the function arguments or an initializer list. We use a\r
+ # simple heuristic here: If the line is indented 4 spaces; and we have a\r
+ # closing paren, without the opening paren, followed by an opening brace\r
+ # or colon (for initializer lists) we assume that it is the last line of\r
+ # a function header. If we have a colon indented 4 spaces, it is an\r
+ # initializer list.\r
+ exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',\r
+ prev_line)\r
+ or Match(r' {4}:', prev_line))\r
+\r
+ if not exception:\r
+ error(filename, linenum, 'whitespace/blank_line', 2,\r
+ 'Redundant blank line at the start of a code block '\r
+ 'should be deleted.')\r
+ # Ignore blank lines at the end of a block in a long if-else\r
+ # chain, like this:\r
+ # if (condition1) {\r
+ # // Something followed by a blank line\r
+ #\r
+ # } else if (condition2) {\r
+ # // Something else\r
+ # }\r
+ if linenum + 1 < clean_lines.NumLines():\r
+ next_line = raw[linenum + 1]\r
+ if (next_line\r
+ and Match(r'\s*}', next_line)\r
+ and next_line.find('} else ') == -1):\r
+ error(filename, linenum, 'whitespace/blank_line', 3,\r
+ 'Redundant blank line at the end of a code block '\r
+ 'should be deleted.')\r
+\r
+ matched = Match(r'\s*(public|protected|private):', prev_line)\r
+ if matched:\r
+ error(filename, linenum, 'whitespace/blank_line', 3,\r
+ 'Do not leave a blank line after "%s:"' % matched.group(1))\r
+\r
+ # Next, check comments\r
+ next_line_start = 0\r
+ if linenum + 1 < clean_lines.NumLines():\r
+ next_line = raw[linenum + 1]\r
+ next_line_start = len(next_line) - len(next_line.lstrip())\r
+ CheckComment(line, filename, linenum, next_line_start, error)\r
+\r
+ # get rid of comments and strings\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # You shouldn't have spaces before your brackets, except maybe after\r
+ # 'delete []' or 'return []() {};'\r
+ if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line):\r
+ error(filename, linenum, 'whitespace/braces', 5,\r
+ 'Extra space before [')\r
+\r
+ # In range-based for, we wanted spaces before and after the colon, but\r
+ # not around "::" tokens that might appear.\r
+ if (Search(r'for *\(.*[^:]:[^: ]', line) or\r
+ Search(r'for *\(.*[^: ]:[^:]', line)):\r
+ error(filename, linenum, 'whitespace/forcolon', 2,\r
+ 'Missing space around colon in range-based for loop')\r
+\r
+\r
+def CheckOperatorSpacing(filename, clean_lines, linenum, error):\r
+ """Checks for horizontal spacing around operators.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # Don't try to do spacing checks for operator methods. Do this by\r
+ # replacing the troublesome characters with something else,\r
+ # preserving column position for all other characters.\r
+ #\r
+ # The replacement is done repeatedly to avoid false positives from\r
+ # operators that call operators.\r
+ while True:\r
+ match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line)\r
+ if match:\r
+ line = match.group(1) + ('_' * len(match.group(2))) + match.group(3)\r
+ else:\r
+ break\r
+\r
+ # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )".\r
+ # Otherwise not. Note we only check for non-spaces on *both* sides;\r
+ # sometimes people put non-spaces on one side when aligning ='s among\r
+ # many lines (not that this is behavior that I approve of...)\r
+ if ((Search(r'[\w.]=', line) or\r
+ Search(r'=[\w.]', line))\r
+ and not Search(r'\b(if|while|for) ', line)\r
+ # Operators taken from [lex.operators] in C++11 standard.\r
+ and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line)\r
+ and not Search(r'operator=', line)):\r
+ error(filename, linenum, 'whitespace/operators', 4,\r
+ 'Missing spaces around =')\r
+\r
+ # It's ok not to have spaces around binary operators like + - * /, but if\r
+ # there's too little whitespace, we get concerned. It's hard to tell,\r
+ # though, so we punt on this one for now. TODO.\r
+\r
+ # You should always have whitespace around binary operators.\r
+ #\r
+ # Check <= and >= first to avoid false positives with < and >, then\r
+ # check non-include lines for spacing around < and >.\r
+ #\r
+ # If the operator is followed by a comma, assume it's be used in a\r
+ # macro context and don't do any checks. This avoids false\r
+ # positives.\r
+ #\r
+ # Note that && is not included here. This is because there are too\r
+ # many false positives due to RValue references.\r
+ match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line)\r
+ if match:\r
+ error(filename, linenum, 'whitespace/operators', 3,\r
+ 'Missing spaces around %s' % match.group(1))\r
+ elif not Match(r'#.*include', line):\r
+ # Look for < that is not surrounded by spaces. This is only\r
+ # triggered if both sides are missing spaces, even though\r
+ # technically should should flag if at least one side is missing a\r
+ # space. This is done to avoid some false positives with shifts.\r
+ match = Match(r'^(.*[^\s<])<[^\s=<,]', line)\r
+ if match:\r
+ (_, _, end_pos) = CloseExpression(\r
+ clean_lines, linenum, len(match.group(1)))\r
+ if end_pos <= -1:\r
+ error(filename, linenum, 'whitespace/operators', 3,\r
+ 'Missing spaces around <')\r
+\r
+ # Look for > that is not surrounded by spaces. Similar to the\r
+ # above, we only trigger if both sides are missing spaces to avoid\r
+ # false positives with shifts.\r
+ match = Match(r'^(.*[^-\s>])>[^\s=>,]', line)\r
+ if match:\r
+ (_, _, start_pos) = ReverseCloseExpression(\r
+ clean_lines, linenum, len(match.group(1)))\r
+ if start_pos <= -1:\r
+ error(filename, linenum, 'whitespace/operators', 3,\r
+ 'Missing spaces around >')\r
+\r
+ # We allow no-spaces around << when used like this: 10<<20, but\r
+ # not otherwise (particularly, not when used as streams)\r
+ #\r
+ # We also allow operators following an opening parenthesis, since\r
+ # those tend to be macros that deal with operators.\r
+ match = Search(r'(operator|[^\s(<])(?:L|UL|LL|ULL|l|ul|ll|ull)?<<([^\s,=<])', line)\r
+ if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and\r
+ not (match.group(1) == 'operator' and match.group(2) == ';')):\r
+ error(filename, linenum, 'whitespace/operators', 3,\r
+ 'Missing spaces around <<')\r
+\r
+ # We allow no-spaces around >> for almost anything. This is because\r
+ # C++11 allows ">>" to close nested templates, which accounts for\r
+ # most cases when ">>" is not followed by a space.\r
+ #\r
+ # We still warn on ">>" followed by alpha character, because that is\r
+ # likely due to ">>" being used for right shifts, e.g.:\r
+ # value >> alpha\r
+ #\r
+ # When ">>" is used to close templates, the alphanumeric letter that\r
+ # follows would be part of an identifier, and there should still be\r
+ # a space separating the template type and the identifier.\r
+ # type<type<type>> alpha\r
+ match = Search(r'>>[a-zA-Z_]', line)\r
+ if match:\r
+ error(filename, linenum, 'whitespace/operators', 3,\r
+ 'Missing spaces around >>')\r
+\r
+ # There shouldn't be space around unary operators\r
+ match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line)\r
+ if match:\r
+ error(filename, linenum, 'whitespace/operators', 4,\r
+ 'Extra space for operator %s' % match.group(1))\r
+\r
+\r
+def CheckParenthesisSpacing(filename, clean_lines, linenum, error):\r
+ """Checks for horizontal spacing around parentheses.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # No spaces after an if, while, switch, or for\r
+ match = Search(r' (if\(|for\(|while\(|switch\()', line)\r
+ if match:\r
+ error(filename, linenum, 'whitespace/parens', 5,\r
+ 'Missing space before ( in %s' % match.group(1))\r
+\r
+ # For if/for/while/switch, the left and right parens should be\r
+ # consistent about how many spaces are inside the parens, and\r
+ # there should either be zero or one spaces inside the parens.\r
+ # We don't want: "if ( foo)" or "if ( foo )".\r
+ # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed.\r
+ match = Search(r'\b(if|for|while|switch)\s*'\r
+ r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$',\r
+ line)\r
+ if match:\r
+ if len(match.group(2)) != len(match.group(4)):\r
+ if not (match.group(3) == ';' and\r
+ len(match.group(2)) == 1 + len(match.group(4)) or\r
+ not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)):\r
+ error(filename, linenum, 'whitespace/parens', 5,\r
+ 'Mismatching spaces inside () in %s' % match.group(1))\r
+ if len(match.group(2)) not in [0, 1]:\r
+ error(filename, linenum, 'whitespace/parens', 5,\r
+ 'Should have zero or one spaces inside ( and ) in %s' %\r
+ match.group(1))\r
+\r
+\r
+def CheckCommaSpacing(filename, clean_lines, linenum, error):\r
+ """Checks for horizontal spacing near commas and semicolons.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ raw = clean_lines.lines_without_raw_strings\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # You should always have a space after a comma (either as fn arg or operator)\r
+ #\r
+ # This does not apply when the non-space character following the\r
+ # comma is another comma, since the only time when that happens is\r
+ # for empty macro arguments.\r
+ #\r
+ # We run this check in two passes: first pass on elided lines to\r
+ # verify that lines contain missing whitespaces, second pass on raw\r
+ # lines to confirm that those missing whitespaces are not due to\r
+ # elided comments.\r
+ if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and\r
+ Search(r',[^,\s]', raw[linenum])):\r
+ error(filename, linenum, 'whitespace/comma', 3,\r
+ 'Missing space after ,')\r
+\r
+ # You should always have a space after a semicolon\r
+ # except for few corner cases\r
+ # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more\r
+ # space after ;\r
+ if Search(r';[^\s};\\)/]', line):\r
+ error(filename, linenum, 'whitespace/semicolon', 3,\r
+ 'Missing space after ;')\r
+\r
+\r
+def _IsType(clean_lines, nesting_state, expr):\r
+ """Check if expression looks like a type name, returns true if so.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ nesting_state: A NestingState instance which maintains information about\r
+ the current stack of nested blocks being parsed.\r
+ expr: The expression to check.\r
+ Returns:\r
+ True, if token looks like a type.\r
+ """\r
+ # Keep only the last token in the expression\r
+ last_word = Match(r'^.*(\b\S+)$', expr)\r
+ if last_word:\r
+ token = last_word.group(1)\r
+ else:\r
+ token = expr\r
+\r
+ # Match native types and stdint types\r
+ if _TYPES.match(token):\r
+ return True\r
+\r
+ # Try a bit harder to match templated types. Walk up the nesting\r
+ # stack until we find something that resembles a typename\r
+ # declaration for what we are looking for.\r
+ typename_pattern = (r'\b(?:typename|class|struct)\s+' + re.escape(token) +\r
+ r'\b')\r
+ block_index = len(nesting_state.stack) - 1\r
+ while block_index >= 0:\r
+ if isinstance(nesting_state.stack[block_index], _NamespaceInfo):\r
+ return False\r
+\r
+ # Found where the opening brace is. We want to scan from this\r
+ # line up to the beginning of the function, minus a few lines.\r
+ # template <typename Type1, // stop scanning here\r
+ # ...>\r
+ # class C\r
+ # : public ... { // start scanning here\r
+ last_line = nesting_state.stack[block_index].starting_linenum\r
+\r
+ next_block_start = 0\r
+ if block_index > 0:\r
+ next_block_start = nesting_state.stack[block_index - 1].starting_linenum\r
+ first_line = last_line\r
+ while first_line >= next_block_start:\r
+ if clean_lines.elided[first_line].find('template') >= 0:\r
+ break\r
+ first_line -= 1\r
+ if first_line < next_block_start:\r
+ # Didn't find any "template" keyword before reaching the next block,\r
+ # there are probably no template things to check for this block\r
+ block_index -= 1\r
+ continue\r
+\r
+ # Look for typename in the specified range\r
+ for i in xrange(first_line, last_line + 1, 1):\r
+ if Search(typename_pattern, clean_lines.elided[i]):\r
+ return True\r
+ block_index -= 1\r
+\r
+ return False\r
+\r
+\r
+def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error):\r
+ """Checks for horizontal spacing near commas.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ nesting_state: A NestingState instance which maintains information about\r
+ the current stack of nested blocks being parsed.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # Except after an opening paren, or after another opening brace (in case of\r
+ # an initializer list, for instance), you should have spaces before your\r
+ # braces when they are delimiting blocks, classes, namespaces etc.\r
+ # And since you should never have braces at the beginning of a line,\r
+ # this is an easy test. Except that braces used for initialization don't\r
+ # follow the same rule; we often don't want spaces before those.\r
+ match = Match(r'^(.*[^ ({>]){', line)\r
+\r
+ if match:\r
+ # Try a bit harder to check for brace initialization. This\r
+ # happens in one of the following forms:\r
+ # Constructor() : initializer_list_{} { ... }\r
+ # Constructor{}.MemberFunction()\r
+ # Type variable{};\r
+ # FunctionCall(type{}, ...);\r
+ # LastArgument(..., type{});\r
+ # LOG(INFO) << type{} << " ...";\r
+ # map_of_type[{...}] = ...;\r
+ # ternary = expr ? new type{} : nullptr;\r
+ # OuterTemplate<InnerTemplateConstructor<Type>{}>\r
+ #\r
+ # We check for the character following the closing brace, and\r
+ # silence the warning if it's one of those listed above, i.e.\r
+ # "{.;,)<>]:".\r
+ #\r
+ # To account for nested initializer list, we allow any number of\r
+ # closing braces up to "{;,)<". We can't simply silence the\r
+ # warning on first sight of closing brace, because that would\r
+ # cause false negatives for things that are not initializer lists.\r
+ # Silence this: But not this:\r
+ # Outer{ if (...) {\r
+ # Inner{...} if (...){ // Missing space before {\r
+ # }; }\r
+ #\r
+ # There is a false negative with this approach if people inserted\r
+ # spurious semicolons, e.g. "if (cond){};", but we will catch the\r
+ # spurious semicolon with a separate check.\r
+ leading_text = match.group(1)\r
+ (endline, endlinenum, endpos) = CloseExpression(\r
+ clean_lines, linenum, len(match.group(1)))\r
+ trailing_text = ''\r
+ if endpos > -1:\r
+ trailing_text = endline[endpos:]\r
+ for offset in xrange(endlinenum + 1,\r
+ min(endlinenum + 3, clean_lines.NumLines() - 1)):\r
+ trailing_text += clean_lines.elided[offset]\r
+ # We also suppress warnings for `uint64_t{expression}` etc., as the style\r
+ # guide recommends brace initialization for integral types to avoid\r
+ # overflow/truncation.\r
+ if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text)\r
+ and not _IsType(clean_lines, nesting_state, leading_text)):\r
+ error(filename, linenum, 'whitespace/braces', 5,\r
+ 'Missing space before {')\r
+\r
+ # Make sure '} else {' has spaces.\r
+ if Search(r'}else', line):\r
+ error(filename, linenum, 'whitespace/braces', 5,\r
+ 'Missing space before else')\r
+\r
+ # You shouldn't have a space before a semicolon at the end of the line.\r
+ # There's a special case for "for" since the style guide allows space before\r
+ # the semicolon there.\r
+ if Search(r':\s*;\s*$', line):\r
+ error(filename, linenum, 'whitespace/semicolon', 5,\r
+ 'Semicolon defining empty statement. Use {} instead.')\r
+ elif Search(r'^\s*;\s*$', line):\r
+ error(filename, linenum, 'whitespace/semicolon', 5,\r
+ 'Line contains only semicolon. If this should be an empty statement, '\r
+ 'use {} instead.')\r
+ elif (Search(r'\s+;\s*$', line) and\r
+ not Search(r'\bfor\b', line)):\r
+ error(filename, linenum, 'whitespace/semicolon', 5,\r
+ 'Extra space before last semicolon. If this should be an empty '\r
+ 'statement, use {} instead.')\r
+\r
+\r
+def IsDecltype(clean_lines, linenum, column):\r
+ """Check if the token ending on (linenum, column) is decltype().\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: the number of the line to check.\r
+ column: end column of the token to check.\r
+ Returns:\r
+ True if this token is decltype() expression, False otherwise.\r
+ """\r
+ (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column)\r
+ if start_col < 0:\r
+ return False\r
+ if Search(r'\bdecltype\s*$', text[0:start_col]):\r
+ return True\r
+ return False\r
+\r
+\r
+def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error):\r
+ """Checks for additional blank line issues related to sections.\r
+\r
+ Currently the only thing checked here is blank line before protected/private.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ class_info: A _ClassInfo objects.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ # Skip checks if the class is small, where small means 25 lines or less.\r
+ # 25 lines seems like a good cutoff since that's the usual height of\r
+ # terminals, and any class that can't fit in one screen can't really\r
+ # be considered "small".\r
+ #\r
+ # Also skip checks if we are on the first line. This accounts for\r
+ # classes that look like\r
+ # class Foo { public: ... };\r
+ #\r
+ # If we didn't find the end of the class, last_line would be zero,\r
+ # and the check will be skipped by the first condition.\r
+ if (class_info.last_line - class_info.starting_linenum <= 24 or\r
+ linenum <= class_info.starting_linenum):\r
+ return\r
+\r
+ matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum])\r
+ if matched:\r
+ # Issue warning if the line before public/protected/private was\r
+ # not a blank line, but don't do this if the previous line contains\r
+ # "class" or "struct". This can happen two ways:\r
+ # - We are at the beginning of the class.\r
+ # - We are forward-declaring an inner class that is semantically\r
+ # private, but needed to be public for implementation reasons.\r
+ # Also ignores cases where the previous line ends with a backslash as can be\r
+ # common when defining classes in C macros.\r
+ prev_line = clean_lines.lines[linenum - 1]\r
+ if (not IsBlankLine(prev_line) and\r
+ not Search(r'\b(class|struct)\b', prev_line) and\r
+ not Search(r'\\$', prev_line)):\r
+ # Try a bit harder to find the beginning of the class. This is to\r
+ # account for multi-line base-specifier lists, e.g.:\r
+ # class Derived\r
+ # : public Base {\r
+ end_class_head = class_info.starting_linenum\r
+ for i in range(class_info.starting_linenum, linenum):\r
+ if Search(r'\{\s*$', clean_lines.lines[i]):\r
+ end_class_head = i\r
+ break\r
+ if end_class_head < linenum - 1:\r
+ error(filename, linenum, 'whitespace/blank_line', 3,\r
+ '"%s:" should be preceded by a blank line' % matched.group(1))\r
+\r
+\r
+def GetPreviousNonBlankLine(clean_lines, linenum):\r
+ """Return the most recent non-blank line and its line number.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file contents.\r
+ linenum: The number of the line to check.\r
+\r
+ Returns:\r
+ A tuple with two elements. The first element is the contents of the last\r
+ non-blank line before the current line, or the empty string if this is the\r
+ first non-blank line. The second is the line number of that line, or -1\r
+ if this is the first non-blank line.\r
+ """\r
+\r
+ prevlinenum = linenum - 1\r
+ while prevlinenum >= 0:\r
+ prevline = clean_lines.elided[prevlinenum]\r
+ if not IsBlankLine(prevline): # if not a blank line...\r
+ return (prevline, prevlinenum)\r
+ prevlinenum -= 1\r
+ return ('', -1)\r
+\r
+\r
+def CheckBraces(filename, clean_lines, linenum, error):\r
+ """Looks for misplaced braces (e.g. at the end of line).\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+\r
+ line = clean_lines.elided[linenum] # get rid of comments and strings\r
+\r
+ if Match(r'\s*{\s*$', line):\r
+ # We allow an open brace to start a line in the case where someone is using\r
+ # braces in a block to explicitly create a new scope, which is commonly used\r
+ # to control the lifetime of stack-allocated variables. Braces are also\r
+ # used for brace initializers inside function calls. We don't detect this\r
+ # perfectly: we just don't complain if the last non-whitespace character on\r
+ # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the\r
+ # previous line starts a preprocessor block. We also allow a brace on the\r
+ # following line if it is part of an array initialization and would not fit\r
+ # within the 80 character limit of the preceding line.\r
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]\r
+ if (not Search(r'[,;:}{(]\s*$', prevline) and\r
+ not Match(r'\s*#', prevline) and\r
+ not (GetLineWidth(prevline) > _line_length - 2 and '[]' in prevline)):\r
+ error(filename, linenum, 'whitespace/braces', 4,\r
+ '{ should almost always be at the end of the previous line')\r
+\r
+ # An else clause should be on the same line as the preceding closing brace.\r
+ if Match(r'\s*else\b\s*(?:if\b|\{|$)', line):\r
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]\r
+ if Match(r'\s*}\s*$', prevline):\r
+ error(filename, linenum, 'whitespace/newline', 4,\r
+ 'An else should appear on the same line as the preceding }')\r
+\r
+ # If braces come on one side of an else, they should be on both.\r
+ # However, we have to worry about "else if" that spans multiple lines!\r
+ if Search(r'else if\s*\(', line): # could be multi-line if\r
+ brace_on_left = bool(Search(r'}\s*else if\s*\(', line))\r
+ # find the ( after the if\r
+ pos = line.find('else if')\r
+ pos = line.find('(', pos)\r
+ if pos > 0:\r
+ (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos)\r
+ brace_on_right = endline[endpos:].find('{') != -1\r
+ if brace_on_left != brace_on_right: # must be brace after if\r
+ error(filename, linenum, 'readability/braces', 5,\r
+ 'If an else has a brace on one side, it should have it on both')\r
+ elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line):\r
+ error(filename, linenum, 'readability/braces', 5,\r
+ 'If an else has a brace on one side, it should have it on both')\r
+\r
+ # Likewise, an else should never have the else clause on the same line\r
+ if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line):\r
+ error(filename, linenum, 'whitespace/newline', 4,\r
+ 'Else clause should never be on same line as else (use 2 lines)')\r
+\r
+ # In the same way, a do/while should never be on one line\r
+ if Match(r'\s*do [^\s{]', line):\r
+ error(filename, linenum, 'whitespace/newline', 4,\r
+ 'do/while clauses should not be on a single line')\r
+\r
+ # Check single-line if/else bodies. The style guide says 'curly braces are not\r
+ # required for single-line statements'. We additionally allow multi-line,\r
+ # single statements, but we reject anything with more than one semicolon in\r
+ # it. This means that the first semicolon after the if should be at the end of\r
+ # its line, and the line after that should have an indent level equal to or\r
+ # lower than the if. We also check for ambiguous if/else nesting without\r
+ # braces.\r
+ if_else_match = Search(r'\b(if\s*\(|else\b)', line)\r
+ if if_else_match and not Match(r'\s*#', line):\r
+ if_indent = GetIndentLevel(line)\r
+ endline, endlinenum, endpos = line, linenum, if_else_match.end()\r
+ if_match = Search(r'\bif\s*\(', line)\r
+ if if_match:\r
+ # This could be a multiline if condition, so find the end first.\r
+ pos = if_match.end() - 1\r
+ (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos)\r
+ # Check for an opening brace, either directly after the if or on the next\r
+ # line. If found, this isn't a single-statement conditional.\r
+ if (not Match(r'\s*{', endline[endpos:])\r
+ and not (Match(r'\s*$', endline[endpos:])\r
+ and endlinenum < (len(clean_lines.elided) - 1)\r
+ and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))):\r
+ while (endlinenum < len(clean_lines.elided)\r
+ and ';' not in clean_lines.elided[endlinenum][endpos:]):\r
+ endlinenum += 1\r
+ endpos = 0\r
+ if endlinenum < len(clean_lines.elided):\r
+ endline = clean_lines.elided[endlinenum]\r
+ # We allow a mix of whitespace and closing braces (e.g. for one-liner\r
+ # methods) and a single \ after the semicolon (for macros)\r
+ endpos = endline.find(';')\r
+ if not Match(r';[\s}]*(\\?)$', endline[endpos:]):\r
+ # Semicolon isn't the last character, there's something trailing.\r
+ # Output a warning if the semicolon is not contained inside\r
+ # a lambda expression.\r
+ if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$',\r
+ endline):\r
+ error(filename, linenum, 'readability/braces', 4,\r
+ 'If/else bodies with multiple statements require braces')\r
+ elif endlinenum < len(clean_lines.elided) - 1:\r
+ # Make sure the next line is dedented\r
+ next_line = clean_lines.elided[endlinenum + 1]\r
+ next_indent = GetIndentLevel(next_line)\r
+ # With ambiguous nested if statements, this will error out on the\r
+ # if that *doesn't* match the else, regardless of whether it's the\r
+ # inner one or outer one.\r
+ if (if_match and Match(r'\s*else\b', next_line)\r
+ and next_indent != if_indent):\r
+ error(filename, linenum, 'readability/braces', 4,\r
+ 'Else clause should be indented at the same level as if. '\r
+ 'Ambiguous nested if/else chains require braces.')\r
+ elif next_indent > if_indent:\r
+ error(filename, linenum, 'readability/braces', 4,\r
+ 'If/else bodies with multiple statements require braces')\r
+\r
+\r
+def CheckTrailingSemicolon(filename, clean_lines, linenum, error):\r
+ """Looks for redundant trailing semicolon.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # Block bodies should not be followed by a semicolon. Due to C++11\r
+ # brace initialization, there are more places where semicolons are\r
+ # required than not, so we use a whitelist approach to check these\r
+ # rather than a blacklist. These are the places where "};" should\r
+ # be replaced by just "}":\r
+ # 1. Some flavor of block following closing parenthesis:\r
+ # for (;;) {};\r
+ # while (...) {};\r
+ # switch (...) {};\r
+ # Function(...) {};\r
+ # if (...) {};\r
+ # if (...) else if (...) {};\r
+ #\r
+ # 2. else block:\r
+ # if (...) else {};\r
+ #\r
+ # 3. const member function:\r
+ # Function(...) const {};\r
+ #\r
+ # 4. Block following some statement:\r
+ # x = 42;\r
+ # {};\r
+ #\r
+ # 5. Block at the beginning of a function:\r
+ # Function(...) {\r
+ # {};\r
+ # }\r
+ #\r
+ # Note that naively checking for the preceding "{" will also match\r
+ # braces inside multi-dimensional arrays, but this is fine since\r
+ # that expression will not contain semicolons.\r
+ #\r
+ # 6. Block following another block:\r
+ # while (true) {}\r
+ # {};\r
+ #\r
+ # 7. End of namespaces:\r
+ # namespace {};\r
+ #\r
+ # These semicolons seems far more common than other kinds of\r
+ # redundant semicolons, possibly due to people converting classes\r
+ # to namespaces. For now we do not warn for this case.\r
+ #\r
+ # Try matching case 1 first.\r
+ match = Match(r'^(.*\)\s*)\{', line)\r
+ if match:\r
+ # Matched closing parenthesis (case 1). Check the token before the\r
+ # matching opening parenthesis, and don't warn if it looks like a\r
+ # macro. This avoids these false positives:\r
+ # - macro that defines a base class\r
+ # - multi-line macro that defines a base class\r
+ # - macro that defines the whole class-head\r
+ #\r
+ # But we still issue warnings for macros that we know are safe to\r
+ # warn, specifically:\r
+ # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P\r
+ # - TYPED_TEST\r
+ # - INTERFACE_DEF\r
+ # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED:\r
+ #\r
+ # We implement a whitelist of safe macros instead of a blacklist of\r
+ # unsafe macros, even though the latter appears less frequently in\r
+ # google code and would have been easier to implement. This is because\r
+ # the downside for getting the whitelist wrong means some extra\r
+ # semicolons, while the downside for getting the blacklist wrong\r
+ # would result in compile errors.\r
+ #\r
+ # In addition to macros, we also don't want to warn on\r
+ # - Compound literals\r
+ # - Lambdas\r
+ # - alignas specifier with anonymous structs\r
+ # - decltype\r
+ closing_brace_pos = match.group(1).rfind(')')\r
+ opening_parenthesis = ReverseCloseExpression(\r
+ clean_lines, linenum, closing_brace_pos)\r
+ if opening_parenthesis[2] > -1:\r
+ line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]]\r
+ macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix)\r
+ func = Match(r'^(.*\])\s*$', line_prefix)\r
+ if ((macro and\r
+ macro.group(1) not in (\r
+ 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST',\r
+ 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED',\r
+ 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or\r
+ (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or\r
+ Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or\r
+ Search(r'\bdecltype$', line_prefix) or\r
+ Search(r'\s+=\s*$', line_prefix)):\r
+ match = None\r
+ if (match and\r
+ opening_parenthesis[1] > 1 and\r
+ Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])):\r
+ # Multi-line lambda-expression\r
+ match = None\r
+\r
+ else:\r
+ # Try matching cases 2-3.\r
+ match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line)\r
+ if not match:\r
+ # Try matching cases 4-6. These are always matched on separate lines.\r
+ #\r
+ # Note that we can't simply concatenate the previous line to the\r
+ # current line and do a single match, otherwise we may output\r
+ # duplicate warnings for the blank line case:\r
+ # if (cond) {\r
+ # // blank line\r
+ # }\r
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]\r
+ if prevline and Search(r'[;{}]\s*$', prevline):\r
+ match = Match(r'^(\s*)\{', line)\r
+\r
+ # Check matching closing brace\r
+ if match:\r
+ (endline, endlinenum, endpos) = CloseExpression(\r
+ clean_lines, linenum, len(match.group(1)))\r
+ if endpos > -1 and Match(r'^\s*;', endline[endpos:]):\r
+ # Current {} pair is eligible for semicolon check, and we have found\r
+ # the redundant semicolon, output warning here.\r
+ #\r
+ # Note: because we are scanning forward for opening braces, and\r
+ # outputting warnings for the matching closing brace, if there are\r
+ # nested blocks with trailing semicolons, we will get the error\r
+ # messages in reversed order.\r
+\r
+ # We need to check the line forward for NOLINT\r
+ raw_lines = clean_lines.raw_lines\r
+ ParseNolintSuppressions(filename, raw_lines[endlinenum-1], endlinenum-1,\r
+ error)\r
+ ParseNolintSuppressions(filename, raw_lines[endlinenum], endlinenum,\r
+ error)\r
+\r
+ error(filename, endlinenum, 'readability/braces', 4,\r
+ "You don't need a ; after a }")\r
+\r
+\r
+def CheckEmptyBlockBody(filename, clean_lines, linenum, error):\r
+ """Look for empty loop/conditional body with only a single semicolon.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+\r
+ # Search for loop keywords at the beginning of the line. Because only\r
+ # whitespaces are allowed before the keywords, this will also ignore most\r
+ # do-while-loops, since those lines should start with closing brace.\r
+ #\r
+ # We also check "if" blocks here, since an empty conditional block\r
+ # is likely an error.\r
+ line = clean_lines.elided[linenum]\r
+ matched = Match(r'\s*(for|while|if)\s*\(', line)\r
+ if matched:\r
+ # Find the end of the conditional expression.\r
+ (end_line, end_linenum, end_pos) = CloseExpression(\r
+ clean_lines, linenum, line.find('('))\r
+\r
+ # Output warning if what follows the condition expression is a semicolon.\r
+ # No warning for all other cases, including whitespace or newline, since we\r
+ # have a separate check for semicolons preceded by whitespace.\r
+ if end_pos >= 0 and Match(r';', end_line[end_pos:]):\r
+ if matched.group(1) == 'if':\r
+ error(filename, end_linenum, 'whitespace/empty_conditional_body', 5,\r
+ 'Empty conditional bodies should use {}')\r
+ else:\r
+ error(filename, end_linenum, 'whitespace/empty_loop_body', 5,\r
+ 'Empty loop bodies should use {} or continue')\r
+\r
+ # Check for if statements that have completely empty bodies (no comments)\r
+ # and no else clauses.\r
+ if end_pos >= 0 and matched.group(1) == 'if':\r
+ # Find the position of the opening { for the if statement.\r
+ # Return without logging an error if it has no brackets.\r
+ opening_linenum = end_linenum\r
+ opening_line_fragment = end_line[end_pos:]\r
+ # Loop until EOF or find anything that's not whitespace or opening {.\r
+ while not Search(r'^\s*\{', opening_line_fragment):\r
+ if Search(r'^(?!\s*$)', opening_line_fragment):\r
+ # Conditional has no brackets.\r
+ return\r
+ opening_linenum += 1\r
+ if opening_linenum == len(clean_lines.elided):\r
+ # Couldn't find conditional's opening { or any code before EOF.\r
+ return\r
+ opening_line_fragment = clean_lines.elided[opening_linenum]\r
+ # Set opening_line (opening_line_fragment may not be entire opening line).\r
+ opening_line = clean_lines.elided[opening_linenum]\r
+\r
+ # Find the position of the closing }.\r
+ opening_pos = opening_line_fragment.find('{')\r
+ if opening_linenum == end_linenum:\r
+ # We need to make opening_pos relative to the start of the entire line.\r
+ opening_pos += end_pos\r
+ (closing_line, closing_linenum, closing_pos) = CloseExpression(\r
+ clean_lines, opening_linenum, opening_pos)\r
+ if closing_pos < 0:\r
+ return\r
+\r
+ # Now construct the body of the conditional. This consists of the portion\r
+ # of the opening line after the {, all lines until the closing line,\r
+ # and the portion of the closing line before the }.\r
+ if (clean_lines.raw_lines[opening_linenum] !=\r
+ CleanseComments(clean_lines.raw_lines[opening_linenum])):\r
+ # Opening line ends with a comment, so conditional isn't empty.\r
+ return\r
+ if closing_linenum > opening_linenum:\r
+ # Opening line after the {. Ignore comments here since we checked above.\r
+ body = list(opening_line[opening_pos+1:])\r
+ # All lines until closing line, excluding closing line, with comments.\r
+ body.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum])\r
+ # Closing line before the }. Won't (and can't) have comments.\r
+ body.append(clean_lines.elided[closing_linenum][:closing_pos-1])\r
+ body = '\n'.join(body)\r
+ else:\r
+ # If statement has brackets and fits on a single line.\r
+ body = opening_line[opening_pos+1:closing_pos-1]\r
+\r
+ # Check if the body is empty\r
+ if not _EMPTY_CONDITIONAL_BODY_PATTERN.search(body):\r
+ return\r
+ # The body is empty. Now make sure there's not an else clause.\r
+ current_linenum = closing_linenum\r
+ current_line_fragment = closing_line[closing_pos:]\r
+ # Loop until EOF or find anything that's not whitespace or else clause.\r
+ while Search(r'^\s*$|^(?=\s*else)', current_line_fragment):\r
+ if Search(r'^(?=\s*else)', current_line_fragment):\r
+ # Found an else clause, so don't log an error.\r
+ return\r
+ current_linenum += 1\r
+ if current_linenum == len(clean_lines.elided):\r
+ break\r
+ current_line_fragment = clean_lines.elided[current_linenum]\r
+\r
+ # The body is empty and there's no else clause until EOF or other code.\r
+ error(filename, end_linenum, 'whitespace/empty_if_body', 4,\r
+ ('If statement had no body and no else clause'))\r
+\r
+\r
+def FindCheckMacro(line):\r
+ """Find a replaceable CHECK-like macro.\r
+\r
+ Args:\r
+ line: line to search on.\r
+ Returns:\r
+ (macro name, start position), or (None, -1) if no replaceable\r
+ macro is found.\r
+ """\r
+ for macro in _CHECK_MACROS:\r
+ i = line.find(macro)\r
+ if i >= 0:\r
+ # Find opening parenthesis. Do a regular expression match here\r
+ # to make sure that we are matching the expected CHECK macro, as\r
+ # opposed to some other macro that happens to contain the CHECK\r
+ # substring.\r
+ matched = Match(r'^(.*\b' + macro + r'\s*)\(', line)\r
+ if not matched:\r
+ continue\r
+ return (macro, len(matched.group(1)))\r
+ return (None, -1)\r
+\r
+\r
+def CheckCheck(filename, clean_lines, linenum, error):\r
+ """Checks the use of CHECK and EXPECT macros.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+\r
+ # Decide the set of replacement macros that should be suggested\r
+ lines = clean_lines.elided\r
+ (check_macro, start_pos) = FindCheckMacro(lines[linenum])\r
+ if not check_macro:\r
+ return\r
+\r
+ # Find end of the boolean expression by matching parentheses\r
+ (last_line, end_line, end_pos) = CloseExpression(\r
+ clean_lines, linenum, start_pos)\r
+ if end_pos < 0:\r
+ return\r
+\r
+ # If the check macro is followed by something other than a\r
+ # semicolon, assume users will log their own custom error messages\r
+ # and don't suggest any replacements.\r
+ if not Match(r'\s*;', last_line[end_pos:]):\r
+ return\r
+\r
+ if linenum == end_line:\r
+ expression = lines[linenum][start_pos + 1:end_pos - 1]\r
+ else:\r
+ expression = lines[linenum][start_pos + 1:]\r
+ for i in xrange(linenum + 1, end_line):\r
+ expression += lines[i]\r
+ expression += last_line[0:end_pos - 1]\r
+\r
+ # Parse expression so that we can take parentheses into account.\r
+ # This avoids false positives for inputs like "CHECK((a < 4) == b)",\r
+ # which is not replaceable by CHECK_LE.\r
+ lhs = ''\r
+ rhs = ''\r
+ operator = None\r
+ while expression:\r
+ matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||'\r
+ r'==|!=|>=|>|<=|<|\()(.*)$', expression)\r
+ if matched:\r
+ token = matched.group(1)\r
+ if token == '(':\r
+ # Parenthesized operand\r
+ expression = matched.group(2)\r
+ (end, _) = FindEndOfExpressionInLine(expression, 0, ['('])\r
+ if end < 0:\r
+ return # Unmatched parenthesis\r
+ lhs += '(' + expression[0:end]\r
+ expression = expression[end:]\r
+ elif token in ('&&', '||'):\r
+ # Logical and/or operators. This means the expression\r
+ # contains more than one term, for example:\r
+ # CHECK(42 < a && a < b);\r
+ #\r
+ # These are not replaceable with CHECK_LE, so bail out early.\r
+ return\r
+ elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'):\r
+ # Non-relational operator\r
+ lhs += token\r
+ expression = matched.group(2)\r
+ else:\r
+ # Relational operator\r
+ operator = token\r
+ rhs = matched.group(2)\r
+ break\r
+ else:\r
+ # Unparenthesized operand. Instead of appending to lhs one character\r
+ # at a time, we do another regular expression match to consume several\r
+ # characters at once if possible. Trivial benchmark shows that this\r
+ # is more efficient when the operands are longer than a single\r
+ # character, which is generally the case.\r
+ matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression)\r
+ if not matched:\r
+ matched = Match(r'^(\s*\S)(.*)$', expression)\r
+ if not matched:\r
+ break\r
+ lhs += matched.group(1)\r
+ expression = matched.group(2)\r
+\r
+ # Only apply checks if we got all parts of the boolean expression\r
+ if not (lhs and operator and rhs):\r
+ return\r
+\r
+ # Check that rhs do not contain logical operators. We already know\r
+ # that lhs is fine since the loop above parses out && and ||.\r
+ if rhs.find('&&') > -1 or rhs.find('||') > -1:\r
+ return\r
+\r
+ # At least one of the operands must be a constant literal. This is\r
+ # to avoid suggesting replacements for unprintable things like\r
+ # CHECK(variable != iterator)\r
+ #\r
+ # The following pattern matches decimal, hex integers, strings, and\r
+ # characters (in that order).\r
+ lhs = lhs.strip()\r
+ rhs = rhs.strip()\r
+ match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$'\r
+ if Match(match_constant, lhs) or Match(match_constant, rhs):\r
+ # Note: since we know both lhs and rhs, we can provide a more\r
+ # descriptive error message like:\r
+ # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42)\r
+ # Instead of:\r
+ # Consider using CHECK_EQ instead of CHECK(a == b)\r
+ #\r
+ # We are still keeping the less descriptive message because if lhs\r
+ # or rhs gets long, the error message might become unreadable.\r
+ error(filename, linenum, 'readability/check', 2,\r
+ 'Consider using %s instead of %s(a %s b)' % (\r
+ _CHECK_REPLACEMENT[check_macro][operator],\r
+ check_macro, operator))\r
+\r
+\r
+def CheckAltTokens(filename, clean_lines, linenum, error):\r
+ """Check alternative keywords being used in boolean expressions.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # Avoid preprocessor lines\r
+ if Match(r'^\s*#', line):\r
+ return\r
+\r
+ # Last ditch effort to avoid multi-line comments. This will not help\r
+ # if the comment started before the current line or ended after the\r
+ # current line, but it catches most of the false positives. At least,\r
+ # it provides a way to workaround this warning for people who use\r
+ # multi-line comments in preprocessor macros.\r
+ #\r
+ # TODO(unknown): remove this once cpplint has better support for\r
+ # multi-line comments.\r
+ if line.find('/*') >= 0 or line.find('*/') >= 0:\r
+ return\r
+\r
+ for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line):\r
+ error(filename, linenum, 'readability/alt_tokens', 2,\r
+ 'Use operator %s instead of %s' % (\r
+ _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1)))\r
+\r
+\r
+def GetLineWidth(line):\r
+ """Determines the width of the line in column positions.\r
+\r
+ Args:\r
+ line: A string, which may be a Unicode string.\r
+\r
+ Returns:\r
+ The width of the line in column positions, accounting for Unicode\r
+ combining characters and wide characters.\r
+ """\r
+ if isinstance(line, unicode):\r
+ width = 0\r
+ for uc in unicodedata.normalize('NFC', line):\r
+ if unicodedata.east_asian_width(uc) in ('W', 'F'):\r
+ width += 2\r
+ elif not unicodedata.combining(uc):\r
+ width += 1\r
+ return width\r
+ else:\r
+ return len(line)\r
+\r
+\r
+def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,\r
+ error):\r
+ """Checks rules from the 'C++ style rules' section of cppguide.html.\r
+\r
+ Most of these rules are hard to test (naming, comment style), but we\r
+ do what we can. In particular we check for 2-space indents, line lengths,\r
+ tab usage, spaces inside code, etc.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ file_extension: The extension (without the dot) of the filename.\r
+ nesting_state: A NestingState instance which maintains information about\r
+ the current stack of nested blocks being parsed.\r
+ error: The function to call with any errors found.\r
+ """\r
+\r
+ # Don't use "elided" lines here, otherwise we can't check commented lines.\r
+ # Don't want to use "raw" either, because we don't want to check inside C++11\r
+ # raw strings,\r
+ raw_lines = clean_lines.lines_without_raw_strings\r
+ line = raw_lines[linenum]\r
+ prev = raw_lines[linenum - 1] if linenum > 0 else ''\r
+\r
+ if line.find('\t') != -1:\r
+ error(filename, linenum, 'whitespace/tab', 1,\r
+ 'Tab found; better to use spaces')\r
+\r
+ # One or three blank spaces at the beginning of the line is weird; it's\r
+ # hard to reconcile that with 2-space indents.\r
+ # NOTE: here are the conditions rob pike used for his tests. Mine aren't\r
+ # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces\r
+ # if(RLENGTH > 20) complain = 0;\r
+ # if(match($0, " +(error|private|public|protected):")) complain = 0;\r
+ # if(match(prev, "&& *$")) complain = 0;\r
+ # if(match(prev, "\\|\\| *$")) complain = 0;\r
+ # if(match(prev, "[\",=><] *$")) complain = 0;\r
+ # if(match($0, " <<")) complain = 0;\r
+ # if(match(prev, " +for \\(")) complain = 0;\r
+ # if(prevodd && match(prevprev, " +for \\(")) complain = 0;\r
+ scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$'\r
+ classinfo = nesting_state.InnermostClass()\r
+ initial_spaces = 0\r
+ cleansed_line = clean_lines.elided[linenum]\r
+ while initial_spaces < len(line) and line[initial_spaces] == ' ':\r
+ initial_spaces += 1\r
+ # There are certain situations we allow one space, notably for\r
+ # section labels, and also lines containing multi-line raw strings.\r
+ # We also don't check for lines that look like continuation lines\r
+ # (of lines ending in double quotes, commas, equals, or angle brackets)\r
+ # because the rules for how to indent those are non-trivial.\r
+ if (not Search(r'[",=><] *$', prev) and\r
+ (initial_spaces == 1 or initial_spaces == 3) and\r
+ not Match(scope_or_label_pattern, cleansed_line) and\r
+ not (clean_lines.raw_lines[linenum] != line and\r
+ Match(r'^\s*""', line))):\r
+ error(filename, linenum, 'whitespace/indent', 3,\r
+ 'Weird number of spaces at line-start. '\r
+ 'Are you using a 2-space indent?')\r
+\r
+ if line and line[-1].isspace():\r
+ error(filename, linenum, 'whitespace/end_of_line', 4,\r
+ 'Line ends in whitespace. Consider deleting these extra spaces.')\r
+\r
+ # Check if the line is a header guard.\r
+ is_header_guard = False\r
+ if IsHeaderExtension(file_extension):\r
+ cppvar = GetHeaderGuardCPPVariable(filename)\r
+ if (line.startswith('#ifndef %s' % cppvar) or\r
+ line.startswith('#define %s' % cppvar) or\r
+ line.startswith('#endif // %s' % cppvar)):\r
+ is_header_guard = True\r
+ # #include lines and header guards can be long, since there's no clean way to\r
+ # split them.\r
+ #\r
+ # URLs can be long too. It's possible to split these, but it makes them\r
+ # harder to cut&paste.\r
+ #\r
+ # The "$Id:...$" comment may also get very long without it being the\r
+ # developers fault.\r
+ if (not line.startswith('#include') and not is_header_guard and\r
+ not Match(r'^\s*//.*http(s?)://\S*$', line) and\r
+ not Match(r'^\s*//\s*[^\s]*$', line) and\r
+ not Match(r'^// \$Id:.*#[0-9]+ \$$', line)):\r
+ line_width = GetLineWidth(line)\r
+ if line_width > _line_length:\r
+ error(filename, linenum, 'whitespace/line_length', 2,\r
+ 'Lines should be <= %i characters long' % _line_length)\r
+\r
+ if (cleansed_line.count(';') > 1 and\r
+ # for loops are allowed two ;'s (and may run over two lines).\r
+ cleansed_line.find('for') == -1 and\r
+ (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or\r
+ GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and\r
+ # It's ok to have many commands in a switch case that fits in 1 line\r
+ not ((cleansed_line.find('case ') != -1 or\r
+ cleansed_line.find('default:') != -1) and\r
+ cleansed_line.find('break;') != -1)):\r
+ error(filename, linenum, 'whitespace/newline', 0,\r
+ 'More than one command on the same line')\r
+\r
+ # Some more style checks\r
+ CheckBraces(filename, clean_lines, linenum, error)\r
+ CheckTrailingSemicolon(filename, clean_lines, linenum, error)\r
+ CheckEmptyBlockBody(filename, clean_lines, linenum, error)\r
+ CheckSpacing(filename, clean_lines, linenum, nesting_state, error)\r
+ CheckOperatorSpacing(filename, clean_lines, linenum, error)\r
+ CheckParenthesisSpacing(filename, clean_lines, linenum, error)\r
+ CheckCommaSpacing(filename, clean_lines, linenum, error)\r
+ CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error)\r
+ CheckSpacingForFunctionCall(filename, clean_lines, linenum, error)\r
+ CheckCheck(filename, clean_lines, linenum, error)\r
+ CheckAltTokens(filename, clean_lines, linenum, error)\r
+ classinfo = nesting_state.InnermostClass()\r
+ if classinfo:\r
+ CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error)\r
+\r
+\r
+_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$')\r
+# Matches the first component of a filename delimited by -s and _s. That is:\r
+# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo'\r
+# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo'\r
+# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo'\r
+# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo'\r
+_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+')\r
+\r
+\r
+def _DropCommonSuffixes(filename):\r
+ """Drops common suffixes like _test.cc or -inl.h from filename.\r
+\r
+ For example:\r
+ >>> _DropCommonSuffixes('foo/foo-inl.h')\r
+ 'foo/foo'\r
+ >>> _DropCommonSuffixes('foo/bar/foo.cc')\r
+ 'foo/bar/foo'\r
+ >>> _DropCommonSuffixes('foo/foo_internal.h')\r
+ 'foo/foo'\r
+ >>> _DropCommonSuffixes('foo/foo_unusualinternal.h')\r
+ 'foo/foo_unusualinternal'\r
+\r
+ Args:\r
+ filename: The input filename.\r
+\r
+ Returns:\r
+ The filename with the common suffix removed.\r
+ """\r
+ for suffix in ('test.cc', 'regtest.cc', 'unittest.cc',\r
+ 'inl.h', 'impl.h', 'internal.h'):\r
+ if (filename.endswith(suffix) and len(filename) > len(suffix) and\r
+ filename[-len(suffix) - 1] in ('-', '_')):\r
+ return filename[:-len(suffix) - 1]\r
+ return os.path.splitext(filename)[0]\r
+\r
+\r
+def _ClassifyInclude(fileinfo, include, is_system):\r
+ """Figures out what kind of header 'include' is.\r
+\r
+ Args:\r
+ fileinfo: The current file cpplint is running over. A FileInfo instance.\r
+ include: The path to a #included file.\r
+ is_system: True if the #include used <> rather than "".\r
+\r
+ Returns:\r
+ One of the _XXX_HEADER constants.\r
+\r
+ For example:\r
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True)\r
+ _C_SYS_HEADER\r
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True)\r
+ _CPP_SYS_HEADER\r
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False)\r
+ _LIKELY_MY_HEADER\r
+ >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'),\r
+ ... 'bar/foo_other_ext.h', False)\r
+ _POSSIBLE_MY_HEADER\r
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False)\r
+ _OTHER_HEADER\r
+ """\r
+ # This is a list of all standard c++ header files, except\r
+ # those already checked for above.\r
+ is_cpp_h = include in _CPP_HEADERS\r
+\r
+ if is_system:\r
+ if is_cpp_h:\r
+ return _CPP_SYS_HEADER\r
+ else:\r
+ return _C_SYS_HEADER\r
+\r
+ # If the target file and the include we're checking share a\r
+ # basename when we drop common extensions, and the include\r
+ # lives in . , then it's likely to be owned by the target file.\r
+ target_dir, target_base = (\r
+ os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName())))\r
+ include_dir, include_base = os.path.split(_DropCommonSuffixes(include))\r
+ if target_base == include_base and (\r
+ include_dir == target_dir or\r
+ include_dir == os.path.normpath(target_dir + '/../public')):\r
+ return _LIKELY_MY_HEADER\r
+\r
+ # If the target and include share some initial basename\r
+ # component, it's possible the target is implementing the\r
+ # include, so it's allowed to be first, but we'll never\r
+ # complain if it's not there.\r
+ target_first_component = _RE_FIRST_COMPONENT.match(target_base)\r
+ include_first_component = _RE_FIRST_COMPONENT.match(include_base)\r
+ if (target_first_component and include_first_component and\r
+ target_first_component.group(0) ==\r
+ include_first_component.group(0)):\r
+ return _POSSIBLE_MY_HEADER\r
+\r
+ return _OTHER_HEADER\r
+\r
+\r
+\r
+def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):\r
+ """Check rules that are applicable to #include lines.\r
+\r
+ Strings on #include lines are NOT removed from elided line, to make\r
+ certain tasks easier. However, to prevent false positives, checks\r
+ applicable to #include lines in CheckLanguage must be put here.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ include_state: An _IncludeState instance in which the headers are inserted.\r
+ error: The function to call with any errors found.\r
+ """\r
+ fileinfo = FileInfo(filename)\r
+ line = clean_lines.lines[linenum]\r
+\r
+ # "include" should use the new style "foo/bar.h" instead of just "bar.h"\r
+ # Only do this check if the included header follows google naming\r
+ # conventions. If not, assume that it's a 3rd party API that\r
+ # requires special include conventions.\r
+ #\r
+ # We also make an exception for Lua headers, which follow google\r
+ # naming convention but not the include convention.\r
+ match = Match(r'#include\s*"([^/]+\.h)"', line)\r
+ if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)):\r
+ error(filename, linenum, 'build/include', 4,\r
+ 'Include the directory when naming .h files')\r
+\r
+ # we shouldn't include a file more than once. actually, there are a\r
+ # handful of instances where doing so is okay, but in general it's\r
+ # not.\r
+ match = _RE_PATTERN_INCLUDE.search(line)\r
+ if match:\r
+ include = match.group(2)\r
+ is_system = (match.group(1) == '<')\r
+ duplicate_line = include_state.FindHeader(include)\r
+ if duplicate_line >= 0:\r
+ error(filename, linenum, 'build/include', 4,\r
+ '"%s" already included at %s:%s' %\r
+ (include, filename, duplicate_line))\r
+ elif (include.endswith('.cc') and\r
+ os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)):\r
+ error(filename, linenum, 'build/include', 4,\r
+ 'Do not include .cc files from other packages')\r
+ elif not _THIRD_PARTY_HEADERS_PATTERN.match(include):\r
+ include_state.include_list[-1].append((include, linenum))\r
+\r
+ # We want to ensure that headers appear in the right order:\r
+ # 1) for foo.cc, foo.h (preferred location)\r
+ # 2) c system files\r
+ # 3) cpp system files\r
+ # 4) for foo.cc, foo.h (deprecated location)\r
+ # 5) other google headers\r
+ #\r
+ # We classify each include statement as one of those 5 types\r
+ # using a number of techniques. The include_state object keeps\r
+ # track of the highest type seen, and complains if we see a\r
+ # lower type after that.\r
+ error_message = include_state.CheckNextIncludeOrder(\r
+ _ClassifyInclude(fileinfo, include, is_system))\r
+ if error_message:\r
+ error(filename, linenum, 'build/include_order', 4,\r
+ '%s. Should be: %s.h, c system, c++ system, other.' %\r
+ (error_message, fileinfo.BaseName()))\r
+ canonical_include = include_state.CanonicalizeAlphabeticalOrder(include)\r
+ if not include_state.IsInAlphabeticalOrder(\r
+ clean_lines, linenum, canonical_include):\r
+ error(filename, linenum, 'build/include_alpha', 4,\r
+ 'Include "%s" not in alphabetical order' % include)\r
+ include_state.SetLastHeader(canonical_include)\r
+\r
+\r
+\r
+def _GetTextInside(text, start_pattern):\r
+ r"""Retrieves all the text between matching open and close parentheses.\r
+\r
+ Given a string of lines and a regular expression string, retrieve all the text\r
+ following the expression and between opening punctuation symbols like\r
+ (, [, or {, and the matching close-punctuation symbol. This properly nested\r
+ occurrences of the punctuations, so for the text like\r
+ printf(a(), b(c()));\r
+ a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'.\r
+ start_pattern must match string having an open punctuation symbol at the end.\r
+\r
+ Args:\r
+ text: The lines to extract text. Its comments and strings must be elided.\r
+ It can be single line and can span multiple lines.\r
+ start_pattern: The regexp string indicating where to start extracting\r
+ the text.\r
+ Returns:\r
+ The extracted text.\r
+ None if either the opening string or ending punctuation could not be found.\r
+ """\r
+ # TODO(unknown): Audit cpplint.py to see what places could be profitably\r
+ # rewritten to use _GetTextInside (and use inferior regexp matching today).\r
+\r
+ # Give opening punctuations to get the matching close-punctuations.\r
+ matching_punctuation = {'(': ')', '{': '}', '[': ']'}\r
+ closing_punctuation = set(matching_punctuation.itervalues())\r
+\r
+ # Find the position to start extracting text.\r
+ match = re.search(start_pattern, text, re.M)\r
+ if not match: # start_pattern not found in text.\r
+ return None\r
+ start_position = match.end(0)\r
+\r
+ assert start_position > 0, (\r
+ 'start_pattern must ends with an opening punctuation.')\r
+ assert text[start_position - 1] in matching_punctuation, (\r
+ 'start_pattern must ends with an opening punctuation.')\r
+ # Stack of closing punctuations we expect to have in text after position.\r
+ punctuation_stack = [matching_punctuation[text[start_position - 1]]]\r
+ position = start_position\r
+ while punctuation_stack and position < len(text):\r
+ if text[position] == punctuation_stack[-1]:\r
+ punctuation_stack.pop()\r
+ elif text[position] in closing_punctuation:\r
+ # A closing punctuation without matching opening punctuations.\r
+ return None\r
+ elif text[position] in matching_punctuation:\r
+ punctuation_stack.append(matching_punctuation[text[position]])\r
+ position += 1\r
+ if punctuation_stack:\r
+ # Opening punctuations left without matching close-punctuations.\r
+ return None\r
+ # punctuations match.\r
+ return text[start_position:position - 1]\r
+\r
+\r
+# Patterns for matching call-by-reference parameters.\r
+#\r
+# Supports nested templates up to 2 levels deep using this messy pattern:\r
+# < (?: < (?: < [^<>]*\r
+# >\r
+# | [^<>] )*\r
+# >\r
+# | [^<>] )*\r
+# >\r
+_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]*\r
+_RE_PATTERN_TYPE = (\r
+ r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?'\r
+ r'(?:\w|'\r
+ r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|'\r
+ r'::)+')\r
+# A call-by-reference parameter ends with '& identifier'.\r
+_RE_PATTERN_REF_PARAM = re.compile(\r
+ r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*'\r
+ r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]')\r
+# A call-by-const-reference parameter either ends with 'const& identifier'\r
+# or looks like 'const type& identifier' when 'type' is atomic.\r
+_RE_PATTERN_CONST_REF_PARAM = (\r
+ r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT +\r
+ r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')')\r
+# Stream types.\r
+_RE_PATTERN_REF_STREAM_PARAM = (\r
+ r'(?:.*stream\s*&\s*' + _RE_PATTERN_IDENT + r')')\r
+\r
+\r
+def CheckLanguage(filename, clean_lines, linenum, file_extension,\r
+ include_state, nesting_state, error):\r
+ """Checks rules from the 'C++ language rules' section of cppguide.html.\r
+\r
+ Some of these rules are hard to test (function overloading, using\r
+ uint32 inappropriately), but we do the best we can.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ file_extension: The extension (without the dot) of the filename.\r
+ include_state: An _IncludeState instance in which the headers are inserted.\r
+ nesting_state: A NestingState instance which maintains information about\r
+ the current stack of nested blocks being parsed.\r
+ error: The function to call with any errors found.\r
+ """\r
+ # If the line is empty or consists of entirely a comment, no need to\r
+ # check it.\r
+ line = clean_lines.elided[linenum]\r
+ if not line:\r
+ return\r
+\r
+ match = _RE_PATTERN_INCLUDE.search(line)\r
+ if match:\r
+ CheckIncludeLine(filename, clean_lines, linenum, include_state, error)\r
+ return\r
+\r
+ # Reset include state across preprocessor directives. This is meant\r
+ # to silence warnings for conditional includes.\r
+ match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line)\r
+ if match:\r
+ include_state.ResetSection(match.group(1))\r
+\r
+ # Make Windows paths like Unix.\r
+ fullname = os.path.abspath(filename).replace('\\', '/')\r
+\r
+ # Perform other checks now that we are sure that this is not an include line\r
+ CheckCasts(filename, clean_lines, linenum, error)\r
+ CheckGlobalStatic(filename, clean_lines, linenum, error)\r
+ CheckPrintf(filename, clean_lines, linenum, error)\r
+\r
+ if IsHeaderExtension(file_extension):\r
+ # TODO(unknown): check that 1-arg constructors are explicit.\r
+ # How to tell it's a constructor?\r
+ # (handled in CheckForNonStandardConstructs for now)\r
+ # TODO(unknown): check that classes declare or disable copy/assign\r
+ # (level 1 error)\r
+ pass\r
+\r
+ # Check if people are using the verboten C basic types. The only exception\r
+ # we regularly allow is "unsigned short port" for port.\r
+ if Search(r'\bshort port\b', line):\r
+ if not Search(r'\bunsigned short port\b', line):\r
+ error(filename, linenum, 'runtime/int', 4,\r
+ 'Use "unsigned short" for ports, not "short"')\r
+ else:\r
+ match = Search(r'\b(short|long(?! +double)|long long)\b', line)\r
+ if match:\r
+ error(filename, linenum, 'runtime/int', 4,\r
+ 'Use int16/int64/etc, rather than the C type %s' % match.group(1))\r
+\r
+ # Check if some verboten operator overloading is going on\r
+ # TODO(unknown): catch out-of-line unary operator&:\r
+ # class X {};\r
+ # int operator&(const X& x) { return 42; } // unary operator&\r
+ # The trick is it's hard to tell apart from binary operator&:\r
+ # class Y { int operator&(const Y& x) { return 23; } }; // binary operator&\r
+ if Search(r'\boperator\s*&\s*\(\s*\)', line):\r
+ error(filename, linenum, 'runtime/operator', 4,\r
+ 'Unary operator& is dangerous. Do not use it.')\r
+\r
+ # Check for suspicious usage of "if" like\r
+ # } if (a == b) {\r
+ if Search(r'\}\s*if\s*\(', line):\r
+ error(filename, linenum, 'readability/braces', 4,\r
+ 'Did you mean "else if"? If not, start a new line for "if".')\r
+\r
+ # Check for potential format string bugs like printf(foo).\r
+ # We constrain the pattern not to pick things like DocidForPrintf(foo).\r
+ # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str())\r
+ # TODO(unknown): Catch the following case. Need to change the calling\r
+ # convention of the whole function to process multiple line to handle it.\r
+ # printf(\r
+ # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line);\r
+ printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(')\r
+ if printf_args:\r
+ match = Match(r'([\w.\->()]+)$', printf_args)\r
+ if match and match.group(1) != '__VA_ARGS__':\r
+ function_name = re.search(r'\b((?:string)?printf)\s*\(',\r
+ line, re.I).group(1)\r
+ error(filename, linenum, 'runtime/printf', 4,\r
+ 'Potential format string bug. Do %s("%%s", %s) instead.'\r
+ % (function_name, match.group(1)))\r
+\r
+ # Check for potential memset bugs like memset(buf, sizeof(buf), 0).\r
+ match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line)\r
+ if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)):\r
+ error(filename, linenum, 'runtime/memset', 4,\r
+ 'Did you mean "memset(%s, 0, %s)"?'\r
+ % (match.group(1), match.group(2)))\r
+\r
+ if Search(r'\busing namespace\b', line):\r
+ error(filename, linenum, 'build/namespaces', 5,\r
+ 'Do not use namespace using-directives. '\r
+ 'Use using-declarations instead.')\r
+\r
+ # Detect variable-length arrays.\r
+ match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line)\r
+ if (match and match.group(2) != 'return' and match.group(2) != 'delete' and\r
+ match.group(3).find(']') == -1):\r
+ # Split the size using space and arithmetic operators as delimiters.\r
+ # If any of the resulting tokens are not compile time constants then\r
+ # report the error.\r
+ tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3))\r
+ is_const = True\r
+ skip_next = False\r
+ for tok in tokens:\r
+ if skip_next:\r
+ skip_next = False\r
+ continue\r
+\r
+ if Search(r'sizeof\(.+\)', tok): continue\r
+ if Search(r'arraysize\(\w+\)', tok): continue\r
+\r
+ tok = tok.lstrip('(')\r
+ tok = tok.rstrip(')')\r
+ if not tok: continue\r
+ if Match(r'\d+', tok): continue\r
+ if Match(r'0[xX][0-9a-fA-F]+', tok): continue\r
+ if Match(r'k[A-Z0-9]\w*', tok): continue\r
+ if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue\r
+ if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue\r
+ # A catch all for tricky sizeof cases, including 'sizeof expression',\r
+ # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)'\r
+ # requires skipping the next token because we split on ' ' and '*'.\r
+ if tok.startswith('sizeof'):\r
+ skip_next = True\r
+ continue\r
+ is_const = False\r
+ break\r
+ if not is_const:\r
+ error(filename, linenum, 'runtime/arrays', 1,\r
+ 'Do not use variable-length arrays. Use an appropriately named '\r
+ "('k' followed by CamelCase) compile-time constant for the size.")\r
+\r
+ # Check for use of unnamed namespaces in header files. Registration\r
+ # macros are typically OK, so we allow use of "namespace {" on lines\r
+ # that end with backslashes.\r
+ if (IsHeaderExtension(file_extension)\r
+ and Search(r'\bnamespace\s*{', line)\r
+ and line[-1] != '\\'):\r
+ error(filename, linenum, 'build/namespaces', 4,\r
+ 'Do not use unnamed namespaces in header files. See '\r
+ 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'\r
+ ' for more information.')\r
+\r
+\r
+def CheckGlobalStatic(filename, clean_lines, linenum, error):\r
+ """Check for unsafe global or static objects.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # Match two lines at a time to support multiline declarations\r
+ if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line):\r
+ line += clean_lines.elided[linenum + 1].strip()\r
+\r
+ # Check for people declaring static/global STL strings at the top level.\r
+ # This is dangerous because the C++ language does not guarantee that\r
+ # globals with constructors are initialized before the first access, and\r
+ # also because globals can be destroyed when some threads are still running.\r
+ # TODO(unknown): Generalize this to also find static unique_ptr instances.\r
+ # TODO(unknown): File bugs for clang-tidy to find these.\r
+ match = Match(\r
+ r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +'\r
+ r'([a-zA-Z0-9_:]+)\b(.*)',\r
+ line)\r
+\r
+ # Remove false positives:\r
+ # - String pointers (as opposed to values).\r
+ # string *pointer\r
+ # const string *pointer\r
+ # string const *pointer\r
+ # string *const pointer\r
+ #\r
+ # - Functions and template specializations.\r
+ # string Function<Type>(...\r
+ # string Class<Type>::Method(...\r
+ #\r
+ # - Operators. These are matched separately because operator names\r
+ # cross non-word boundaries, and trying to match both operators\r
+ # and functions at the same time would decrease accuracy of\r
+ # matching identifiers.\r
+ # string Class::operator*()\r
+ if (match and\r
+ not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and\r
+ not Search(r'\boperator\W', line) and\r
+ not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))):\r
+ if Search(r'\bconst\b', line):\r
+ error(filename, linenum, 'runtime/string', 4,\r
+ 'For a static/global string constant, use a C style string '\r
+ 'instead: "%schar%s %s[]".' %\r
+ (match.group(1), match.group(2) or '', match.group(3)))\r
+ else:\r
+ error(filename, linenum, 'runtime/string', 4,\r
+ 'Static/global string variables are not permitted.')\r
+\r
+ if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or\r
+ Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)):\r
+ error(filename, linenum, 'runtime/init', 4,\r
+ 'You seem to be initializing a member variable with itself.')\r
+\r
+\r
+def CheckPrintf(filename, clean_lines, linenum, error):\r
+ """Check for printf related issues.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # When snprintf is used, the second argument shouldn't be a literal.\r
+ match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line)\r
+ if match and match.group(2) != '0':\r
+ # If 2nd arg is zero, snprintf is used to calculate size.\r
+ error(filename, linenum, 'runtime/printf', 3,\r
+ 'If you can, use sizeof(%s) instead of %s as the 2nd arg '\r
+ 'to snprintf.' % (match.group(1), match.group(2)))\r
+\r
+ # Check if some verboten C functions are being used.\r
+ if Search(r'\bsprintf\s*\(', line):\r
+ error(filename, linenum, 'runtime/printf', 5,\r
+ 'Never use sprintf. Use snprintf instead.')\r
+ match = Search(r'\b(strcpy|strcat)\s*\(', line)\r
+ if match:\r
+ error(filename, linenum, 'runtime/printf', 4,\r
+ 'Almost always, snprintf is better than %s' % match.group(1))\r
+\r
+\r
+def IsDerivedFunction(clean_lines, linenum):\r
+ """Check if current line contains an inherited function.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ Returns:\r
+ True if current line contains a function with "override"\r
+ virt-specifier.\r
+ """\r
+ # Scan back a few lines for start of current function\r
+ for i in xrange(linenum, max(-1, linenum - 10), -1):\r
+ match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i])\r
+ if match:\r
+ # Look for "override" after the matching closing parenthesis\r
+ line, _, closing_paren = CloseExpression(\r
+ clean_lines, i, len(match.group(1)))\r
+ return (closing_paren >= 0 and\r
+ Search(r'\boverride\b', line[closing_paren:]))\r
+ return False\r
+\r
+\r
+def IsOutOfLineMethodDefinition(clean_lines, linenum):\r
+ """Check if current line contains an out-of-line method definition.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ Returns:\r
+ True if current line contains an out-of-line method definition.\r
+ """\r
+ # Scan back a few lines for start of current function\r
+ for i in xrange(linenum, max(-1, linenum - 10), -1):\r
+ if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]):\r
+ return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None\r
+ return False\r
+\r
+\r
+def IsInitializerList(clean_lines, linenum):\r
+ """Check if current line is inside constructor initializer list.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ Returns:\r
+ True if current line appears to be inside constructor initializer\r
+ list, False otherwise.\r
+ """\r
+ for i in xrange(linenum, 1, -1):\r
+ line = clean_lines.elided[i]\r
+ if i == linenum:\r
+ remove_function_body = Match(r'^(.*)\{\s*$', line)\r
+ if remove_function_body:\r
+ line = remove_function_body.group(1)\r
+\r
+ if Search(r'\s:\s*\w+[({]', line):\r
+ # A lone colon tend to indicate the start of a constructor\r
+ # initializer list. It could also be a ternary operator, which\r
+ # also tend to appear in constructor initializer lists as\r
+ # opposed to parameter lists.\r
+ return True\r
+ if Search(r'\}\s*,\s*$', line):\r
+ # A closing brace followed by a comma is probably the end of a\r
+ # brace-initialized member in constructor initializer list.\r
+ return True\r
+ if Search(r'[{};]\s*$', line):\r
+ # Found one of the following:\r
+ # - A closing brace or semicolon, probably the end of the previous\r
+ # function.\r
+ # - An opening brace, probably the start of current class or namespace.\r
+ #\r
+ # Current line is probably not inside an initializer list since\r
+ # we saw one of those things without seeing the starting colon.\r
+ return False\r
+\r
+ # Got to the beginning of the file without seeing the start of\r
+ # constructor initializer list.\r
+ return False\r
+\r
+\r
+def CheckForNonConstReference(filename, clean_lines, linenum,\r
+ nesting_state, error):\r
+ """Check for non-const references.\r
+\r
+ Separate from CheckLanguage since it scans backwards from current\r
+ line, instead of scanning forward.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ nesting_state: A NestingState instance which maintains information about\r
+ the current stack of nested blocks being parsed.\r
+ error: The function to call with any errors found.\r
+ """\r
+ # Do nothing if there is no '&' on current line.\r
+ line = clean_lines.elided[linenum]\r
+ if '&' not in line:\r
+ return\r
+\r
+ # If a function is inherited, current function doesn't have much of\r
+ # a choice, so any non-const references should not be blamed on\r
+ # derived function.\r
+ if IsDerivedFunction(clean_lines, linenum):\r
+ return\r
+\r
+ # Don't warn on out-of-line method definitions, as we would warn on the\r
+ # in-line declaration, if it isn't marked with 'override'.\r
+ if IsOutOfLineMethodDefinition(clean_lines, linenum):\r
+ return\r
+\r
+ # Long type names may be broken across multiple lines, usually in one\r
+ # of these forms:\r
+ # LongType\r
+ # ::LongTypeContinued &identifier\r
+ # LongType::\r
+ # LongTypeContinued &identifier\r
+ # LongType<\r
+ # ...>::LongTypeContinued &identifier\r
+ #\r
+ # If we detected a type split across two lines, join the previous\r
+ # line to current line so that we can match const references\r
+ # accordingly.\r
+ #\r
+ # Note that this only scans back one line, since scanning back\r
+ # arbitrary number of lines would be expensive. If you have a type\r
+ # that spans more than 2 lines, please use a typedef.\r
+ if linenum > 1:\r
+ previous = None\r
+ if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line):\r
+ # previous_line\n + ::current_line\r
+ previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$',\r
+ clean_lines.elided[linenum - 1])\r
+ elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line):\r
+ # previous_line::\n + current_line\r
+ previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$',\r
+ clean_lines.elided[linenum - 1])\r
+ if previous:\r
+ line = previous.group(1) + line.lstrip()\r
+ else:\r
+ # Check for templated parameter that is split across multiple lines\r
+ endpos = line.rfind('>')\r
+ if endpos > -1:\r
+ (_, startline, startpos) = ReverseCloseExpression(\r
+ clean_lines, linenum, endpos)\r
+ if startpos > -1 and startline < linenum:\r
+ # Found the matching < on an earlier line, collect all\r
+ # pieces up to current line.\r
+ line = ''\r
+ for i in xrange(startline, linenum + 1):\r
+ line += clean_lines.elided[i].strip()\r
+\r
+ # Check for non-const references in function parameters. A single '&' may\r
+ # found in the following places:\r
+ # inside expression: binary & for bitwise AND\r
+ # inside expression: unary & for taking the address of something\r
+ # inside declarators: reference parameter\r
+ # We will exclude the first two cases by checking that we are not inside a\r
+ # function body, including one that was just introduced by a trailing '{'.\r
+ # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare].\r
+ if (nesting_state.previous_stack_top and\r
+ not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or\r
+ isinstance(nesting_state.previous_stack_top, _NamespaceInfo))):\r
+ # Not at toplevel, not within a class, and not within a namespace\r
+ return\r
+\r
+ # Avoid initializer lists. We only need to scan back from the\r
+ # current line for something that starts with ':'.\r
+ #\r
+ # We don't need to check the current line, since the '&' would\r
+ # appear inside the second set of parentheses on the current line as\r
+ # opposed to the first set.\r
+ if linenum > 0:\r
+ for i in xrange(linenum - 1, max(0, linenum - 10), -1):\r
+ previous_line = clean_lines.elided[i]\r
+ if not Search(r'[),]\s*$', previous_line):\r
+ break\r
+ if Match(r'^\s*:\s+\S', previous_line):\r
+ return\r
+\r
+ # Avoid preprocessors\r
+ if Search(r'\\\s*$', line):\r
+ return\r
+\r
+ # Avoid constructor initializer lists\r
+ if IsInitializerList(clean_lines, linenum):\r
+ return\r
+\r
+ # We allow non-const references in a few standard places, like functions\r
+ # called "swap()" or iostream operators like "<<" or ">>". Do not check\r
+ # those function parameters.\r
+ #\r
+ # We also accept & in static_assert, which looks like a function but\r
+ # it's actually a declaration expression.\r
+ whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|'\r
+ r'operator\s*[<>][<>]|'\r
+ r'static_assert|COMPILE_ASSERT'\r
+ r')\s*\(')\r
+ if Search(whitelisted_functions, line):\r
+ return\r
+ elif not Search(r'\S+\([^)]*$', line):\r
+ # Don't see a whitelisted function on this line. Actually we\r
+ # didn't see any function name on this line, so this is likely a\r
+ # multi-line parameter list. Try a bit harder to catch this case.\r
+ for i in xrange(2):\r
+ if (linenum > i and\r
+ Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])):\r
+ return\r
+\r
+ decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body\r
+ for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls):\r
+ if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and\r
+ not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)):\r
+ error(filename, linenum, 'runtime/references', 2,\r
+ 'Is this a non-const reference? '\r
+ 'If so, make const or use a pointer: ' +\r
+ ReplaceAll(' *<', '<', parameter))\r
+\r
+\r
+def CheckCasts(filename, clean_lines, linenum, error):\r
+ """Various cast related checks.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ # Check to see if they're using an conversion function cast.\r
+ # I just try to capture the most common basic types, though there are more.\r
+ # Parameterless conversion functions, such as bool(), are allowed as they are\r
+ # probably a member operator declaration or default constructor.\r
+ match = Search(\r
+ r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b'\r
+ r'(int|float|double|bool|char|int32|uint32|int64|uint64)'\r
+ r'(\([^)].*)', line)\r
+ expecting_function = ExpectingFunctionArgs(clean_lines, linenum)\r
+ if match and not expecting_function:\r
+ matched_type = match.group(2)\r
+\r
+ # matched_new_or_template is used to silence two false positives:\r
+ # - New operators\r
+ # - Template arguments with function types\r
+ #\r
+ # For template arguments, we match on types immediately following\r
+ # an opening bracket without any spaces. This is a fast way to\r
+ # silence the common case where the function type is the first\r
+ # template argument. False negative with less-than comparison is\r
+ # avoided because those operators are usually followed by a space.\r
+ #\r
+ # function<double(double)> // bracket + no space = false positive\r
+ # value < double(42) // bracket + space = true positive\r
+ matched_new_or_template = match.group(1)\r
+\r
+ # Avoid arrays by looking for brackets that come after the closing\r
+ # parenthesis.\r
+ if Match(r'\([^()]+\)\s*\[', match.group(3)):\r
+ return\r
+\r
+ # Other things to ignore:\r
+ # - Function pointers\r
+ # - Casts to pointer types\r
+ # - Placement new\r
+ # - Alias declarations\r
+ matched_funcptr = match.group(3)\r
+ if (matched_new_or_template is None and\r
+ not (matched_funcptr and\r
+ (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(',\r
+ matched_funcptr) or\r
+ matched_funcptr.startswith('(*)'))) and\r
+ not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and\r
+ not Search(r'new\(\S+\)\s*' + matched_type, line)):\r
+ error(filename, linenum, 'readability/casting', 4,\r
+ 'Using deprecated casting style. '\r
+ 'Use static_cast<%s>(...) instead' %\r
+ matched_type)\r
+\r
+ if not expecting_function:\r
+ CheckCStyleCast(filename, clean_lines, linenum, 'static_cast',\r
+ r'\((int|float|double|bool|char|u?int(16|32|64))\)', error)\r
+\r
+ # This doesn't catch all cases. Consider (const char * const)"hello".\r
+ #\r
+ # (char *) "foo" should always be a const_cast (reinterpret_cast won't\r
+ # compile).\r
+ if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast',\r
+ r'\((char\s?\*+\s?)\)\s*"', error):\r
+ pass\r
+ else:\r
+ # Check pointer casts for other than string constants\r
+ CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast',\r
+ r'\((\w+\s?\*+\s?)\)', error)\r
+\r
+ # In addition, we look for people taking the address of a cast. This\r
+ # is dangerous -- casts can assign to temporaries, so the pointer doesn't\r
+ # point where you think.\r
+ #\r
+ # Some non-identifier character is required before the '&' for the\r
+ # expression to be recognized as a cast. These are casts:\r
+ # expression = &static_cast<int*>(temporary());\r
+ # function(&(int*)(temporary()));\r
+ #\r
+ # This is not a cast:\r
+ # reference_type&(int* function_param);\r
+ match = Search(\r
+ r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|'\r
+ r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line)\r
+ if match:\r
+ # Try a better error message when the & is bound to something\r
+ # dereferenced by the casted pointer, as opposed to the casted\r
+ # pointer itself.\r
+ parenthesis_error = False\r
+ match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line)\r
+ if match:\r
+ _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1)))\r
+ if x1 >= 0 and clean_lines.elided[y1][x1] == '(':\r
+ _, y2, x2 = CloseExpression(clean_lines, y1, x1)\r
+ if x2 >= 0:\r
+ extended_line = clean_lines.elided[y2][x2:]\r
+ if y2 < clean_lines.NumLines() - 1:\r
+ extended_line += clean_lines.elided[y2 + 1]\r
+ if Match(r'\s*(?:->|\[)', extended_line):\r
+ parenthesis_error = True\r
+\r
+ if parenthesis_error:\r
+ error(filename, linenum, 'readability/casting', 4,\r
+ ('Are you taking an address of something dereferenced '\r
+ 'from a cast? Wrapping the dereferenced expression in '\r
+ 'parentheses will make the binding more obvious'))\r
+ else:\r
+ error(filename, linenum, 'runtime/casting', 4,\r
+ ('Are you taking an address of a cast? '\r
+ 'This is dangerous: could be a temp var. '\r
+ 'Take the address before doing the cast, rather than after'))\r
+\r
+\r
+def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error):\r
+ """Checks for a C-style cast by looking for the pattern.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ cast_type: The string for the C++ cast to recommend. This is either\r
+ reinterpret_cast, static_cast, or const_cast, depending.\r
+ pattern: The regular expression used to find C-style casts.\r
+ error: The function to call with any errors found.\r
+\r
+ Returns:\r
+ True if an error was emitted.\r
+ False otherwise.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+ match = Search(pattern, line)\r
+ if not match:\r
+ return False\r
+\r
+ # Exclude lines with keywords that tend to look like casts\r
+ context = line[0:match.start(1) - 1]\r
+ if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context):\r
+ return False\r
+\r
+ # Try expanding current context to see if we one level of\r
+ # parentheses inside a macro.\r
+ if linenum > 0:\r
+ for i in xrange(linenum - 1, max(0, linenum - 5), -1):\r
+ context = clean_lines.elided[i] + context\r
+ if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context):\r
+ return False\r
+\r
+ # operator++(int) and operator--(int)\r
+ if context.endswith(' operator++') or context.endswith(' operator--'):\r
+ return False\r
+\r
+ # A single unnamed argument for a function tends to look like old style cast.\r
+ # If we see those, don't issue warnings for deprecated casts.\r
+ remainder = line[match.end(0):]\r
+ if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)',\r
+ remainder):\r
+ return False\r
+\r
+ # At this point, all that should be left is actual casts.\r
+ error(filename, linenum, 'readability/casting', 4,\r
+ 'Using C-style cast. Use %s<%s>(...) instead' %\r
+ (cast_type, match.group(1)))\r
+\r
+ return True\r
+\r
+\r
+def ExpectingFunctionArgs(clean_lines, linenum):\r
+ """Checks whether where function type arguments are expected.\r
+\r
+ Args:\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+\r
+ Returns:\r
+ True if the line at 'linenum' is inside something that expects arguments\r
+ of function types.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+ return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or\r
+ (linenum >= 2 and\r
+ (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$',\r
+ clean_lines.elided[linenum - 1]) or\r
+ Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$',\r
+ clean_lines.elided[linenum - 2]) or\r
+ Search(r'\bstd::m?function\s*\<\s*$',\r
+ clean_lines.elided[linenum - 1]))))\r
+\r
+\r
+_HEADERS_CONTAINING_TEMPLATES = (\r
+ ('<deque>', ('deque',)),\r
+ ('<functional>', ('unary_function', 'binary_function',\r
+ 'plus', 'minus', 'multiplies', 'divides', 'modulus',\r
+ 'negate',\r
+ 'equal_to', 'not_equal_to', 'greater', 'less',\r
+ 'greater_equal', 'less_equal',\r
+ 'logical_and', 'logical_or', 'logical_not',\r
+ 'unary_negate', 'not1', 'binary_negate', 'not2',\r
+ 'bind1st', 'bind2nd',\r
+ 'pointer_to_unary_function',\r
+ 'pointer_to_binary_function',\r
+ 'ptr_fun',\r
+ 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t',\r
+ 'mem_fun_ref_t',\r
+ 'const_mem_fun_t', 'const_mem_fun1_t',\r
+ 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t',\r
+ 'mem_fun_ref',\r
+ )),\r
+ ('<limits>', ('numeric_limits',)),\r
+ ('<list>', ('list',)),\r
+ ('<map>', ('map', 'multimap',)),\r
+ ('<memory>', ('allocator', 'make_shared', 'make_unique', 'shared_ptr',\r
+ 'unique_ptr', 'weak_ptr')),\r
+ ('<queue>', ('queue', 'priority_queue',)),\r
+ ('<set>', ('set', 'multiset',)),\r
+ ('<stack>', ('stack',)),\r
+ ('<string>', ('char_traits', 'basic_string',)),\r
+ ('<tuple>', ('tuple',)),\r
+ ('<unordered_map>', ('unordered_map', 'unordered_multimap')),\r
+ ('<unordered_set>', ('unordered_set', 'unordered_multiset')),\r
+ ('<utility>', ('pair',)),\r
+ ('<vector>', ('vector',)),\r
+\r
+ # gcc extensions.\r
+ # Note: std::hash is their hash, ::hash is our hash\r
+ ('<hash_map>', ('hash_map', 'hash_multimap',)),\r
+ ('<hash_set>', ('hash_set', 'hash_multiset',)),\r
+ ('<slist>', ('slist',)),\r
+ )\r
+\r
+_HEADERS_MAYBE_TEMPLATES = (\r
+ ('<algorithm>', ('copy', 'max', 'min', 'min_element', 'sort',\r
+ 'transform',\r
+ )),\r
+ ('<utility>', ('forward', 'make_pair', 'move', 'swap')),\r
+ )\r
+\r
+_RE_PATTERN_STRING = re.compile(r'\bstring\b')\r
+\r
+_re_pattern_headers_maybe_templates = []\r
+for _header, _templates in _HEADERS_MAYBE_TEMPLATES:\r
+ for _template in _templates:\r
+ # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or\r
+ # type::max().\r
+ _re_pattern_headers_maybe_templates.append(\r
+ (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'),\r
+ _template,\r
+ _header))\r
+\r
+# Other scripts may reach in and modify this pattern.\r
+_re_pattern_templates = []\r
+for _header, _templates in _HEADERS_CONTAINING_TEMPLATES:\r
+ for _template in _templates:\r
+ _re_pattern_templates.append(\r
+ (re.compile(r'(\<|\b)' + _template + r'\s*\<'),\r
+ _template + '<>',\r
+ _header))\r
+\r
+\r
+def FilesBelongToSameModule(filename_cc, filename_h):\r
+ """Check if these two filenames belong to the same module.\r
+\r
+ The concept of a 'module' here is a as follows:\r
+ foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the\r
+ same 'module' if they are in the same directory.\r
+ some/path/public/xyzzy and some/path/internal/xyzzy are also considered\r
+ to belong to the same module here.\r
+\r
+ If the filename_cc contains a longer path than the filename_h, for example,\r
+ '/absolute/path/to/base/sysinfo.cc', and this file would include\r
+ 'base/sysinfo.h', this function also produces the prefix needed to open the\r
+ header. This is used by the caller of this function to more robustly open the\r
+ header file. We don't have access to the real include paths in this context,\r
+ so we need this guesswork here.\r
+\r
+ Known bugs: tools/base/bar.cc and base/bar.h belong to the same module\r
+ according to this implementation. Because of this, this function gives\r
+ some false positives. This should be sufficiently rare in practice.\r
+\r
+ Args:\r
+ filename_cc: is the path for the .cc file\r
+ filename_h: is the path for the header path\r
+\r
+ Returns:\r
+ Tuple with a bool and a string:\r
+ bool: True if filename_cc and filename_h belong to the same module.\r
+ string: the additional prefix needed to open the header file.\r
+ """\r
+\r
+ fileinfo = FileInfo(filename_cc)\r
+ if not fileinfo.IsSource():\r
+ return (False, '')\r
+ filename_cc = filename_cc[:-len(fileinfo.Extension())]\r
+ matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo.BaseName())\r
+ if matched_test_suffix:\r
+ filename_cc = filename_cc[:-len(matched_test_suffix.group(1))]\r
+ filename_cc = filename_cc.replace('/public/', '/')\r
+ filename_cc = filename_cc.replace('/internal/', '/')\r
+\r
+ if not filename_h.endswith('.h'):\r
+ return (False, '')\r
+ filename_h = filename_h[:-len('.h')]\r
+ if filename_h.endswith('-inl'):\r
+ filename_h = filename_h[:-len('-inl')]\r
+ filename_h = filename_h.replace('/public/', '/')\r
+ filename_h = filename_h.replace('/internal/', '/')\r
+\r
+ files_belong_to_same_module = filename_cc.endswith(filename_h)\r
+ common_path = ''\r
+ if files_belong_to_same_module:\r
+ common_path = filename_cc[:-len(filename_h)]\r
+ return files_belong_to_same_module, common_path\r
+\r
+\r
+def UpdateIncludeState(filename, include_dict, io=codecs):\r
+ """Fill up the include_dict with new includes found from the file.\r
+\r
+ Args:\r
+ filename: the name of the header to read.\r
+ include_dict: a dictionary in which the headers are inserted.\r
+ io: The io factory to use to read the file. Provided for testability.\r
+\r
+ Returns:\r
+ True if a header was successfully added. False otherwise.\r
+ """\r
+ headerfile = None\r
+ try:\r
+ headerfile = io.open(filename, 'r', 'utf8', 'replace')\r
+ except IOError:\r
+ return False\r
+ linenum = 0\r
+ for line in headerfile:\r
+ linenum += 1\r
+ clean_line = CleanseComments(line)\r
+ match = _RE_PATTERN_INCLUDE.search(clean_line)\r
+ if match:\r
+ include = match.group(2)\r
+ include_dict.setdefault(include, linenum)\r
+ return True\r
+\r
+\r
+def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,\r
+ io=codecs):\r
+ """Reports for missing stl includes.\r
+\r
+ This function will output warnings to make sure you are including the headers\r
+ necessary for the stl containers and functions that you use. We only give one\r
+ reason to include a header. For example, if you use both equal_to<> and\r
+ less<> in a .h file, only one (the latter in the file) of these will be\r
+ reported as a reason to include the <functional>.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ include_state: An _IncludeState instance.\r
+ error: The function to call with any errors found.\r
+ io: The IO factory to use to read the header file. Provided for unittest\r
+ injection.\r
+ """\r
+ required = {} # A map of header name to linenumber and the template entity.\r
+ # Example of required: { '<functional>': (1219, 'less<>') }\r
+\r
+ for linenum in xrange(clean_lines.NumLines()):\r
+ line = clean_lines.elided[linenum]\r
+ if not line or line[0] == '#':\r
+ continue\r
+\r
+ # String is special -- it is a non-templatized type in STL.\r
+ matched = _RE_PATTERN_STRING.search(line)\r
+ if matched:\r
+ # Don't warn about strings in non-STL namespaces:\r
+ # (We check only the first match per line; good enough.)\r
+ prefix = line[:matched.start()]\r
+ if prefix.endswith('std::') or not prefix.endswith('::'):\r
+ required['<string>'] = (linenum, 'string')\r
+\r
+ for pattern, template, header in _re_pattern_headers_maybe_templates:\r
+ if pattern.search(line):\r
+ required[header] = (linenum, template)\r
+\r
+ # The following function is just a speed up, no semantics are changed.\r
+ if not '<' in line: # Reduces the cpu time usage by skipping lines.\r
+ continue\r
+\r
+ for pattern, template, header in _re_pattern_templates:\r
+ matched = pattern.search(line)\r
+ if matched:\r
+ # Don't warn about IWYU in non-STL namespaces:\r
+ # (We check only the first match per line; good enough.)\r
+ prefix = line[:matched.start()]\r
+ if prefix.endswith('std::') or not prefix.endswith('::'):\r
+ required[header] = (linenum, template)\r
+\r
+ # The policy is that if you #include something in foo.h you don't need to\r
+ # include it again in foo.cc. Here, we will look at possible includes.\r
+ # Let's flatten the include_state include_list and copy it into a dictionary.\r
+ include_dict = dict([item for sublist in include_state.include_list\r
+ for item in sublist])\r
+\r
+ # Did we find the header for this file (if any) and successfully load it?\r
+ header_found = False\r
+\r
+ # Use the absolute path so that matching works properly.\r
+ abs_filename = FileInfo(filename).FullName()\r
+\r
+ # For Emacs's flymake.\r
+ # If cpplint is invoked from Emacs's flymake, a temporary file is generated\r
+ # by flymake and that file name might end with '_flymake.cc'. In that case,\r
+ # restore original file name here so that the corresponding header file can be\r
+ # found.\r
+ # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h'\r
+ # instead of 'foo_flymake.h'\r
+ abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename)\r
+\r
+ # include_dict is modified during iteration, so we iterate over a copy of\r
+ # the keys.\r
+ header_keys = include_dict.keys()\r
+ for header in header_keys:\r
+ (same_module, common_path) = FilesBelongToSameModule(abs_filename, header)\r
+ fullpath = common_path + header\r
+ if same_module and UpdateIncludeState(fullpath, include_dict, io):\r
+ header_found = True\r
+\r
+ # If we can't find the header file for a .cc, assume it's because we don't\r
+ # know where to look. In that case we'll give up as we're not sure they\r
+ # didn't include it in the .h file.\r
+ # TODO(unknown): Do a better job of finding .h files so we are confident that\r
+ # not having the .h file means there isn't one.\r
+ if filename.endswith('.cc') and not header_found:\r
+ return\r
+\r
+ # All the lines have been processed, report the errors found.\r
+ for required_header_unstripped in required:\r
+ template = required[required_header_unstripped][1]\r
+ if required_header_unstripped.strip('<>"') not in include_dict:\r
+ error(filename, required[required_header_unstripped][0],\r
+ 'build/include_what_you_use', 4,\r
+ 'Add #include ' + required_header_unstripped + ' for ' + template)\r
+\r
+\r
+_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<')\r
+\r
+\r
+def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):\r
+ """Check that make_pair's template arguments are deduced.\r
+\r
+ G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are\r
+ specified explicitly, and such use isn't intended in any case.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+ match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line)\r
+ if match:\r
+ error(filename, linenum, 'build/explicit_make_pair',\r
+ 4, # 4 = high confidence\r
+ 'For C++11-compatibility, omit template arguments from make_pair'\r
+ ' OR use pair directly OR if appropriate, construct a pair directly')\r
+\r
+\r
+def CheckRedundantVirtual(filename, clean_lines, linenum, error):\r
+ """Check if line contains a redundant "virtual" function-specifier.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ # Look for "virtual" on current line.\r
+ line = clean_lines.elided[linenum]\r
+ virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line)\r
+ if not virtual: return\r
+\r
+ # Ignore "virtual" keywords that are near access-specifiers. These\r
+ # are only used in class base-specifier and do not apply to member\r
+ # functions.\r
+ if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or\r
+ Match(r'^\s+(public|protected|private)\b', virtual.group(3))):\r
+ return\r
+\r
+ # Ignore the "virtual" keyword from virtual base classes. Usually\r
+ # there is a column on the same line in these cases (virtual base\r
+ # classes are rare in google3 because multiple inheritance is rare).\r
+ if Match(r'^.*[^:]:[^:].*$', line): return\r
+\r
+ # Look for the next opening parenthesis. This is the start of the\r
+ # parameter list (possibly on the next line shortly after virtual).\r
+ # TODO(unknown): doesn't work if there are virtual functions with\r
+ # decltype() or other things that use parentheses, but csearch suggests\r
+ # that this is rare.\r
+ end_col = -1\r
+ end_line = -1\r
+ start_col = len(virtual.group(2))\r
+ for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())):\r
+ line = clean_lines.elided[start_line][start_col:]\r
+ parameter_list = Match(r'^([^(]*)\(', line)\r
+ if parameter_list:\r
+ # Match parentheses to find the end of the parameter list\r
+ (_, end_line, end_col) = CloseExpression(\r
+ clean_lines, start_line, start_col + len(parameter_list.group(1)))\r
+ break\r
+ start_col = 0\r
+\r
+ if end_col < 0:\r
+ return # Couldn't find end of parameter list, give up\r
+\r
+ # Look for "override" or "final" after the parameter list\r
+ # (possibly on the next few lines).\r
+ for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())):\r
+ line = clean_lines.elided[i][end_col:]\r
+ match = Search(r'\b(override|final)\b', line)\r
+ if match:\r
+ error(filename, linenum, 'readability/inheritance', 4,\r
+ ('"virtual" is redundant since function is '\r
+ 'already declared as "%s"' % match.group(1)))\r
+\r
+ # Set end_col to check whole lines after we are done with the\r
+ # first line.\r
+ end_col = 0\r
+ if Search(r'[^\w]\s*$', line):\r
+ break\r
+\r
+\r
+def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error):\r
+ """Check if line contains a redundant "override" or "final" virt-specifier.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ # Look for closing parenthesis nearby. We need one to confirm where\r
+ # the declarator ends and where the virt-specifier starts to avoid\r
+ # false positives.\r
+ line = clean_lines.elided[linenum]\r
+ declarator_end = line.rfind(')')\r
+ if declarator_end >= 0:\r
+ fragment = line[declarator_end:]\r
+ else:\r
+ if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0:\r
+ fragment = line\r
+ else:\r
+ return\r
+\r
+ # Check that at most one of "override" or "final" is present, not both\r
+ if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment):\r
+ error(filename, linenum, 'readability/inheritance', 4,\r
+ ('"override" is redundant since function is '\r
+ 'already declared as "final"'))\r
+\r
+\r
+\r
+\r
+# Returns true if we are at a new block, and it is directly\r
+# inside of a namespace.\r
+def IsBlockInNameSpace(nesting_state, is_forward_declaration):\r
+ """Checks that the new block is directly in a namespace.\r
+\r
+ Args:\r
+ nesting_state: The _NestingState object that contains info about our state.\r
+ is_forward_declaration: If the class is a forward declared class.\r
+ Returns:\r
+ Whether or not the new block is directly in a namespace.\r
+ """\r
+ if is_forward_declaration:\r
+ if len(nesting_state.stack) >= 1 and (\r
+ isinstance(nesting_state.stack[-1], _NamespaceInfo)):\r
+ return True\r
+ else:\r
+ return False\r
+\r
+ return (len(nesting_state.stack) > 1 and\r
+ nesting_state.stack[-1].check_namespace_indentation and\r
+ isinstance(nesting_state.stack[-2], _NamespaceInfo))\r
+\r
+\r
+def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item,\r
+ raw_lines_no_comments, linenum):\r
+ """This method determines if we should apply our namespace indentation check.\r
+\r
+ Args:\r
+ nesting_state: The current nesting state.\r
+ is_namespace_indent_item: If we just put a new class on the stack, True.\r
+ If the top of the stack is not a class, or we did not recently\r
+ add the class, False.\r
+ raw_lines_no_comments: The lines without the comments.\r
+ linenum: The current line number we are processing.\r
+\r
+ Returns:\r
+ True if we should apply our namespace indentation check. Currently, it\r
+ only works for classes and namespaces inside of a namespace.\r
+ """\r
+\r
+ is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments,\r
+ linenum)\r
+\r
+ if not (is_namespace_indent_item or is_forward_declaration):\r
+ return False\r
+\r
+ # If we are in a macro, we do not want to check the namespace indentation.\r
+ if IsMacroDefinition(raw_lines_no_comments, linenum):\r
+ return False\r
+\r
+ return IsBlockInNameSpace(nesting_state, is_forward_declaration)\r
+\r
+\r
+# Call this method if the line is directly inside of a namespace.\r
+# If the line above is blank (excluding comments) or the start of\r
+# an inner namespace, it cannot be indented.\r
+def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum,\r
+ error):\r
+ line = raw_lines_no_comments[linenum]\r
+ if Match(r'^\s+', line):\r
+ error(filename, linenum, 'runtime/indentation_namespace', 4,\r
+ 'Do not indent within a namespace')\r
+\r
+\r
+def ProcessLine(filename, file_extension, clean_lines, line,\r
+ include_state, function_state, nesting_state, error,\r
+ extra_check_functions=[]):\r
+ """Processes a single line in the file.\r
+\r
+ Args:\r
+ filename: Filename of the file that is being processed.\r
+ file_extension: The extension (dot not included) of the file.\r
+ clean_lines: An array of strings, each representing a line of the file,\r
+ with comments stripped.\r
+ line: Number of line being processed.\r
+ include_state: An _IncludeState instance in which the headers are inserted.\r
+ function_state: A _FunctionState instance which counts function lines, etc.\r
+ nesting_state: A NestingState instance which maintains information about\r
+ the current stack of nested blocks being parsed.\r
+ error: A callable to which errors are reported, which takes 4 arguments:\r
+ filename, line number, error level, and message\r
+ extra_check_functions: An array of additional check functions that will be\r
+ run on each source line. Each function takes 4\r
+ arguments: filename, clean_lines, line, error\r
+ """\r
+ raw_lines = clean_lines.raw_lines\r
+ ParseNolintSuppressions(filename, raw_lines[line], line, error)\r
+ nesting_state.Update(filename, clean_lines, line, error)\r
+ CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line,\r
+ error)\r
+ if nesting_state.InAsmBlock(): return\r
+ CheckForFunctionLengths(filename, clean_lines, line, function_state, error)\r
+ CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)\r
+ CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error)\r
+ CheckLanguage(filename, clean_lines, line, file_extension, include_state,\r
+ nesting_state, error)\r
+ CheckForNonConstReference(filename, clean_lines, line, nesting_state, error)\r
+ CheckForNonStandardConstructs(filename, clean_lines, line,\r
+ nesting_state, error)\r
+ CheckVlogArguments(filename, clean_lines, line, error)\r
+ CheckPosixThreading(filename, clean_lines, line, error)\r
+ CheckInvalidIncrement(filename, clean_lines, line, error)\r
+ CheckMakePairUsesDeduction(filename, clean_lines, line, error)\r
+ CheckRedundantVirtual(filename, clean_lines, line, error)\r
+ CheckRedundantOverrideOrFinal(filename, clean_lines, line, error)\r
+ for check_fn in extra_check_functions:\r
+ check_fn(filename, clean_lines, line, error)\r
+\r
+def FlagCxx11Features(filename, clean_lines, linenum, error):\r
+ """Flag those c++11 features that we only allow in certain places.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line)\r
+\r
+ # Flag unapproved C++ TR1 headers.\r
+ if include and include.group(1).startswith('tr1/'):\r
+ error(filename, linenum, 'build/c++tr1', 5,\r
+ ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1))\r
+\r
+ # Flag unapproved C++11 headers.\r
+ if include and include.group(1) in ('cfenv',\r
+ 'condition_variable',\r
+ 'fenv.h',\r
+ 'future',\r
+ 'mutex',\r
+ 'thread',\r
+ 'chrono',\r
+ 'ratio',\r
+ 'regex',\r
+ 'system_error',\r
+ ):\r
+ error(filename, linenum, 'build/c++11', 5,\r
+ ('<%s> is an unapproved C++11 header.') % include.group(1))\r
+\r
+ # The only place where we need to worry about C++11 keywords and library\r
+ # features in preprocessor directives is in macro definitions.\r
+ if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return\r
+\r
+ # These are classes and free functions. The classes are always\r
+ # mentioned as std::*, but we only catch the free functions if\r
+ # they're not found by ADL. They're alphabetical by header.\r
+ for top_name in (\r
+ # type_traits\r
+ 'alignment_of',\r
+ 'aligned_union',\r
+ ):\r
+ if Search(r'\bstd::%s\b' % top_name, line):\r
+ error(filename, linenum, 'build/c++11', 5,\r
+ ('std::%s is an unapproved C++11 class or function. Send c-style '\r
+ 'an example of where it would make your code more readable, and '\r
+ 'they may let you use it.') % top_name)\r
+\r
+\r
+def FlagCxx14Features(filename, clean_lines, linenum, error):\r
+ """Flag those C++14 features that we restrict.\r
+\r
+ Args:\r
+ filename: The name of the current file.\r
+ clean_lines: A CleansedLines instance containing the file.\r
+ linenum: The number of the line to check.\r
+ error: The function to call with any errors found.\r
+ """\r
+ line = clean_lines.elided[linenum]\r
+\r
+ include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line)\r
+\r
+ # Flag unapproved C++14 headers.\r
+ if include and include.group(1) in ('scoped_allocator', 'shared_mutex'):\r
+ error(filename, linenum, 'build/c++14', 5,\r
+ ('<%s> is an unapproved C++14 header.') % include.group(1))\r
+\r
+\r
+def ProcessFileData(filename, file_extension, lines, error,\r
+ extra_check_functions=[]):\r
+ """Performs lint checks and reports any errors to the given error function.\r
+\r
+ Args:\r
+ filename: Filename of the file that is being processed.\r
+ file_extension: The extension (dot not included) of the file.\r
+ lines: An array of strings, each representing a line of the file, with the\r
+ last element being empty if the file is terminated with a newline.\r
+ error: A callable to which errors are reported, which takes 4 arguments:\r
+ filename, line number, error level, and message\r
+ extra_check_functions: An array of additional check functions that will be\r
+ run on each source line. Each function takes 4\r
+ arguments: filename, clean_lines, line, error\r
+ """\r
+ lines = (['// marker so line numbers and indices both start at 1'] + lines +\r
+ ['// marker so line numbers end in a known way'])\r
+\r
+ include_state = _IncludeState()\r
+ function_state = _FunctionState()\r
+ nesting_state = NestingState()\r
+\r
+ ResetNolintSuppressions()\r
+\r
+ CheckForCopyright(filename, lines, error)\r
+ ProcessGlobalSuppresions(lines)\r
+ RemoveMultiLineComments(filename, lines, error)\r
+ clean_lines = CleansedLines(lines)\r
+\r
+ if IsHeaderExtension(file_extension):\r
+ CheckForHeaderGuard(filename, clean_lines, error)\r
+\r
+ for line in xrange(clean_lines.NumLines()):\r
+ ProcessLine(filename, file_extension, clean_lines, line,\r
+ include_state, function_state, nesting_state, error,\r
+ extra_check_functions)\r
+ FlagCxx11Features(filename, clean_lines, line, error)\r
+ nesting_state.CheckCompletedBlocks(filename, error)\r
+\r
+ CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)\r
+\r
+ # Check that the .cc file has included its header if it exists.\r
+ if _IsSourceExtension(file_extension):\r
+ CheckHeaderFileIncluded(filename, include_state, error)\r
+\r
+ # We check here rather than inside ProcessLine so that we see raw\r
+ # lines rather than "cleaned" lines.\r
+ CheckForBadCharacters(filename, lines, error)\r
+\r
+ CheckForNewlineAtEOF(filename, lines, error)\r
+\r
+def ProcessConfigOverrides(filename):\r
+ """ Loads the configuration files and processes the config overrides.\r
+\r
+ Args:\r
+ filename: The name of the file being processed by the linter.\r
+\r
+ Returns:\r
+ False if the current |filename| should not be processed further.\r
+ """\r
+\r
+ abs_filename = os.path.abspath(filename)\r
+ cfg_filters = []\r
+ keep_looking = True\r
+ while keep_looking:\r
+ abs_path, base_name = os.path.split(abs_filename)\r
+ if not base_name:\r
+ break # Reached the root directory.\r
+\r
+ cfg_file = os.path.join(abs_path, "CPPLINT.cfg")\r
+ abs_filename = abs_path\r
+ if not os.path.isfile(cfg_file):\r
+ continue\r
+\r
+ try:\r
+ with open(cfg_file) as file_handle:\r
+ for line in file_handle:\r
+ line, _, _ = line.partition('#') # Remove comments.\r
+ if not line.strip():\r
+ continue\r
+\r
+ name, _, val = line.partition('=')\r
+ name = name.strip()\r
+ val = val.strip()\r
+ if name == 'set noparent':\r
+ keep_looking = False\r
+ elif name == 'filter':\r
+ cfg_filters.append(val)\r
+ elif name == 'exclude_files':\r
+ # When matching exclude_files pattern, use the base_name of\r
+ # the current file name or the directory name we are processing.\r
+ # For example, if we are checking for lint errors in /foo/bar/baz.cc\r
+ # and we found the .cfg file at /foo/CPPLINT.cfg, then the config\r
+ # file's "exclude_files" filter is meant to be checked against "bar"\r
+ # and not "baz" nor "bar/baz.cc".\r
+ if base_name:\r
+ pattern = re.compile(val)\r
+ if pattern.match(base_name):\r
+ sys.stderr.write('Ignoring "%s": file excluded by "%s". '\r
+ 'File path component "%s" matches '\r
+ 'pattern "%s"\n' %\r
+ (filename, cfg_file, base_name, val))\r
+ return False\r
+ elif name == 'linelength':\r
+ global _line_length\r
+ try:\r
+ _line_length = int(val)\r
+ except ValueError:\r
+ sys.stderr.write('Line length must be numeric.')\r
+ elif name == 'root':\r
+ global _root\r
+ _root = val\r
+ elif name == 'headers':\r
+ ProcessHppHeadersOption(val)\r
+ else:\r
+ sys.stderr.write(\r
+ 'Invalid configuration option (%s) in file %s\n' %\r
+ (name, cfg_file))\r
+\r
+ except IOError:\r
+ sys.stderr.write(\r
+ "Skipping config file '%s': Can't open for reading\n" % cfg_file)\r
+ keep_looking = False\r
+\r
+ # Apply all the accumulated filters in reverse order (top-level directory\r
+ # config options having the least priority).\r
+ for filter in reversed(cfg_filters):\r
+ _AddFilters(filter)\r
+\r
+ return True\r
+\r
+\r
+def ProcessFile(filename, vlevel, extra_check_functions=[]):\r
+ """Does google-lint on a single file.\r
+\r
+ Args:\r
+ filename: The name of the file to parse.\r
+\r
+ vlevel: The level of errors to report. Every error of confidence\r
+ >= verbose_level will be reported. 0 is a good default.\r
+\r
+ extra_check_functions: An array of additional check functions that will be\r
+ run on each source line. Each function takes 4\r
+ arguments: filename, clean_lines, line, error\r
+ """\r
+\r
+ _SetVerboseLevel(vlevel)\r
+ _BackupFilters()\r
+\r
+ if not ProcessConfigOverrides(filename):\r
+ _RestoreFilters()\r
+ return\r
+\r
+ lf_lines = []\r
+ crlf_lines = []\r
+ try:\r
+ # Support the UNIX convention of using "-" for stdin. Note that\r
+ # we are not opening the file with universal newline support\r
+ # (which codecs doesn't support anyway), so the resulting lines do\r
+ # contain trailing '\r' characters if we are reading a file that\r
+ # has CRLF endings.\r
+ # If after the split a trailing '\r' is present, it is removed\r
+ # below.\r
+ if filename == '-':\r
+ lines = codecs.StreamReaderWriter(sys.stdin,\r
+ codecs.getreader('utf8'),\r
+ codecs.getwriter('utf8'),\r
+ 'replace').read().split('\n')\r
+ else:\r
+ lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')\r
+\r
+ # Remove trailing '\r'.\r
+ # The -1 accounts for the extra trailing blank line we get from split()\r
+ for linenum in range(len(lines) - 1):\r
+ if lines[linenum].endswith('\r'):\r
+ lines[linenum] = lines[linenum].rstrip('\r')\r
+ crlf_lines.append(linenum + 1)\r
+ else:\r
+ lf_lines.append(linenum + 1)\r
+\r
+ except IOError:\r
+ sys.stderr.write(\r
+ "Skipping input '%s': Can't open for reading\n" % filename)\r
+ _RestoreFilters()\r
+ return\r
+\r
+ # Note, if no dot is found, this will give the entire filename as the ext.\r
+ file_extension = filename[filename.rfind('.') + 1:]\r
+\r
+ # When reading from stdin, the extension is unknown, so no cpplint tests\r
+ # should rely on the extension.\r
+ if filename != '-' and file_extension not in _valid_extensions:\r
+ sys.stderr.write('Ignoring %s; not a valid file name '\r
+ '(%s)\n' % (filename, ', '.join(_valid_extensions)))\r
+ else:\r
+ ProcessFileData(filename, file_extension, lines, Error,\r
+ extra_check_functions)\r
+\r
+ # If end-of-line sequences are a mix of LF and CR-LF, issue\r
+ # warnings on the lines with CR.\r
+ #\r
+ # Don't issue any warnings if all lines are uniformly LF or CR-LF,\r
+ # since critique can handle these just fine, and the style guide\r
+ # doesn't dictate a particular end of line sequence.\r
+ #\r
+ # We can't depend on os.linesep to determine what the desired\r
+ # end-of-line sequence should be, since that will return the\r
+ # server-side end-of-line sequence.\r
+ if lf_lines and crlf_lines:\r
+ # Warn on every line with CR. An alternative approach might be to\r
+ # check whether the file is mostly CRLF or just LF, and warn on the\r
+ # minority, we bias toward LF here since most tools prefer LF.\r
+ for linenum in crlf_lines:\r
+ Error(filename, linenum, 'whitespace/newline', 1,\r
+ 'Unexpected \\r (^M) found; better to use only \\n')\r
+\r
+ sys.stdout.write('Done processing %s\n' % filename)\r
+ _RestoreFilters()\r
+\r
+\r
+def PrintUsage(message):\r
+ """Prints a brief usage string and exits, optionally with an error message.\r
+\r
+ Args:\r
+ message: The optional error message.\r
+ """\r
+ sys.stderr.write(_USAGE)\r
+ if message:\r
+ sys.exit('\nFATAL ERROR: ' + message)\r
+ else:\r
+ sys.exit(1)\r
+\r
+\r
+def PrintCategories():\r
+ """Prints a list of all the error-categories used by error messages.\r
+\r
+ These are the categories used to filter messages via --filter.\r
+ """\r
+ sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES))\r
+ sys.exit(0)\r
+\r
+\r
+def ParseArguments(args):\r
+ """Parses the command line arguments.\r
+\r
+ This may set the output format and verbosity level as side-effects.\r
+\r
+ Args:\r
+ args: The command line arguments:\r
+\r
+ Returns:\r
+ The list of filenames to lint.\r
+ """\r
+ try:\r
+ (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',\r
+ 'counting=',\r
+ 'filter=',\r
+ 'root=',\r
+ 'linelength=',\r
+ 'extensions=',\r
+ 'headers='])\r
+ except getopt.GetoptError:\r
+ PrintUsage('Invalid arguments.')\r
+\r
+ verbosity = _VerboseLevel()\r
+ output_format = _OutputFormat()\r
+ filters = ''\r
+ counting_style = ''\r
+\r
+ for (opt, val) in opts:\r
+ if opt == '--help':\r
+ PrintUsage(None)\r
+ elif opt == '--output':\r
+ if val not in ('emacs', 'vs7', 'eclipse'):\r
+ PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.')\r
+ output_format = val\r
+ elif opt == '--verbose':\r
+ verbosity = int(val)\r
+ elif opt == '--filter':\r
+ filters = val\r
+ if not filters:\r
+ PrintCategories()\r
+ elif opt == '--counting':\r
+ if val not in ('total', 'toplevel', 'detailed'):\r
+ PrintUsage('Valid counting options are total, toplevel, and detailed')\r
+ counting_style = val\r
+ elif opt == '--root':\r
+ global _root\r
+ _root = val\r
+ elif opt == '--linelength':\r
+ global _line_length\r
+ try:\r
+ _line_length = int(val)\r
+ except ValueError:\r
+ PrintUsage('Line length must be digits.')\r
+ elif opt == '--extensions':\r
+ global _valid_extensions\r
+ try:\r
+ _valid_extensions = set(val.split(','))\r
+ except ValueError:\r
+ PrintUsage('Extensions must be comma seperated list.')\r
+ elif opt == '--headers':\r
+ ProcessHppHeadersOption(val)\r
+\r
+ if not filenames:\r
+ PrintUsage('No files were specified.')\r
+\r
+ _SetOutputFormat(output_format)\r
+ _SetVerboseLevel(verbosity)\r
+ _SetFilters(filters)\r
+ _SetCountingStyle(counting_style)\r
+\r
+ return filenames\r
+\r
+\r
+def main():\r
+ filenames = ParseArguments(sys.argv[1:])\r
+\r
+ # Change stderr to write with replacement characters so we don't die\r
+ # if we try to print something containing non-ASCII characters.\r
+ sys.stderr = codecs.StreamReaderWriter(sys.stderr,\r
+ codecs.getreader('utf8'),\r
+ codecs.getwriter('utf8'),\r
+ 'replace')\r
+\r
+ _cpplint_state.ResetErrorCounts()\r
+ for filename in filenames:\r
+ ProcessFile(filename, _cpplint_state.verbose_level)\r
+ _cpplint_state.PrintErrorCounts()\r
+\r
+ sys.exit(_cpplint_state.error_count > 0)\r
+\r
+\r
+if __name__ == '__main__':\r
+ main()\r
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __PLUSPLAYER_UT_INCLUDE_APPWINDOW_H__
+#define __PLUSPLAYER_UT_INCLUDE_APPWINDOW_H__
+
+#define ECORE_WAYLAND_DISPLAY_TEST 1
+
+#include <chrono>
+#include <thread>
+
+#include "Ecore.h"
+#include "Elementary.h"
+#if ECORE_WAYLAND_DISPLAY_TEST
+#include "Ecore_Wl2.h"
+#endif
+
+namespace plusplayer_ut {
+
+class AppWindow {
+ public:
+ struct Window {
+ Evas_Object* obj = nullptr;
+ int x = 0, y = 0;
+ int w = 0, h = 0; // width , height
+ };
+
+ AppWindow(int x, int y, int width, int height) : thread_init_done_(false) {
+ window_.x = x, window_.y = y, window_.w = width, window_.h = height;
+ thread_ = std::thread(AppWindow::WindowThread, this);
+ while (!thread_init_done_) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ }
+
+ ~AppWindow() {
+ ecore_thread_main_loop_begin();
+ elm_exit();
+ ecore_thread_main_loop_end();
+ thread_.join();
+ }
+
+ static void WindowThread(AppWindow* appwindow) {
+ elm_init(1, NULL);
+ appwindow->CreateWindow_();
+ appwindow->thread_init_done_ = true;
+ elm_run();
+ elm_shutdown();
+ }
+
+ Window GetWindow() { return window_; }
+
+#if ECORE_WAYLAND_DISPLAY_TEST
+ Ecore_Wl2_Window* GetEcoreWL2Window() {
+ return ecore_wl2_window_;
+ }
+#endif
+
+ private:
+ void CreateWindow_() {
+ ecore_thread_main_loop_begin();
+
+ window_.obj = elm_win_add(NULL, "player", ELM_WIN_BASIC);
+
+ assert(window_.obj && "window is NULL.");
+
+ elm_win_title_set(window_.obj, "Plusplayer");
+
+ elm_win_autodel_set(window_.obj, EINA_TRUE);
+ elm_win_aux_hint_add(window_.obj, "wm.policy.win.user.geometry", "1");
+ evas_object_move(window_.obj, window_.x, window_.y);
+ evas_object_resize(window_.obj, window_.w, window_.h);
+ evas_object_show(window_.obj);
+
+#if ECORE_WAYLAND_DISPLAY_TEST
+ Ecore_Evas *ee =
+ ecore_evas_ecore_evas_get(evas_object_evas_get(window_.obj));
+ ecore_wl2_window_ = ecore_evas_wayland2_window_get(ee);
+#endif
+ ecore_thread_main_loop_end();
+ }
+
+ private:
+ bool thread_init_done_;
+ Window window_;
+#if ECORE_WAYLAND_DISPLAY_TEST
+ Ecore_Wl2_Window* ecore_wl2_window_;
+#endif
+ std::thread thread_;
+};
+
+} // namespace plusplayer_ut
+
+#endif // __PLUSPLAYER_UT_INCLUDE_APPWINDOW_H__
--- /dev/null
+//\r
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>\r
+//\r
+\r
+#ifndef __CAPI_TRACKRENDERER_TV_UT_INCLUDE_ES_EVENT_LISTENER_H__\r
+#define __CAPI_TRACKRENDERER_TV_UT_INCLUDE_ES_EVENT_LISTENER_H__\r
+\r
+#include <string>\r
+#include <thread>\r
+#include <chrono>\r
+\r
+#include "ut/include/esplusplayer/esreader.hpp"\r
+#include "esplusplayer_capi/esplusplayer_capi.h"\r
+\r
+class EsPlayerEventCallback {
+ public:
+ EsPlayerEventCallback(esplusplayer_handle handle,\r
+ EsStreamReader* video_reader,\r
+ EsStreamReader* audio_reader)\r
+ : handle_(handle), video_reader_(video_reader),\r
+ audio_reader_(audio_reader) {}\r
+ ~EsPlayerEventCallback() {\r
+ if (audio_feeding_task_.joinable())\r
+ audio_feeding_task_.join();\r
+ if (video_feeding_task_.joinable())\r
+ video_feeding_task_.join();\r
+ }\r
+ void SetCallback(void) {
+ esplusplayer_set_error_cb(handle_, OnError, this);
+ esplusplayer_set_buffer_status_cb(handle_, OnBufferStatus, this);
+ esplusplayer_set_resource_conflicted_cb(handle_, OnResourceConflicted,
+ this);
+ esplusplayer_set_eos_cb(handle_, OnEos, this);
+ esplusplayer_set_ready_to_prepare_cb(handle_, OnReadyToPrepare, this);
+ esplusplayer_set_prepare_async_done_cb(handle_, OnPrepareDone, this);
+ }
+\r
+ static void DataFeedingTask(EsStreamReader* stream, esplusplayer_handle esplayer) {\r
+ esplusplayer_es_packet pkt;\r
+ while (true) {\r
+ if (!stream->ReadNextPacket(pkt)) break;\r
+ esplusplayer_submit_packet(esplayer, &pkt);\r
+ delete pkt.buffer;\r
+ }\r
+ }\r
+ static void OnError(const esplusplayer_error_type err_code, void* userdata) {
+ std::cout << "OnError" << std::endl;
+ }
+ static void OnResourceConflicted(void* userdata) {
+ std::cout << "OnResourceConflicted" << std::endl;
+ }
+ static void OnSeekDone(void* userdata) {
+ std::cout << "OnSeekDone" << std::endl;
+ }
+ static void OnEos(void* userdata) {
+ EsPlayerEventCallback* cb = static_cast<EsPlayerEventCallback*>(userdata);
+ std::unique_lock<std::mutex> lk(cb->eos_m_);
+ std::cout << "OnEos" << std::endl;
+ cb->eos_ = true;
+ lk.unlock();
+ cb->eos_cv_.notify_all();
+ }
+ static void OnPrepareDone(bool ret, void* userdata) {
+ EsPlayerEventCallback* cb = static_cast<EsPlayerEventCallback*>(userdata);
+ std::unique_lock<std::mutex> lk(cb->prepare_done_m_);
+ std::cout << "OnPrepareDone" << std::endl;
+ cb->prepare_done_ = true;
+ lk.unlock();
+ cb->prepare_done_cv_.notify_all();
+ }
+ static void OnBufferStatus(const esplusplayer_stream_type type,
+ const esplusplayer_buffer_status status,
+ void* userdata) {
+ auto buffer_status =
+ status == ESPLUSPLAYER_BUFFER_STATUS_UNDERRUN ? "underrun" : "overrun";
+ std::cout << "OnBufferStatus " << buffer_status << std::endl;
+ }
+ static void OnReadyToPrepare(const esplusplayer_stream_type type,
+ void* userdata) {
+ EsPlayerEventCallback* cb = static_cast<EsPlayerEventCallback*>(userdata);
+ std::cout << "OnReadyToPrepare" << std::endl;
+ std::unique_lock<std::mutex> lk(cb->data_m_);
+ if (type == ESPLUSPLAYER_STREAM_TYPE_AUDIO) {
+ cb->ready_audio_data_ = true;\r
+ if (cb->audio_reader_ != nullptr) {\r
+ cb->audio_feeding_task_ = std::thread(DataFeedingTask,\r
+ std::ref(cb->audio_reader_),\r
+ std::ref(cb->handle_));\r
+ }\r
+ std::cout << "Audio ready to prepare" << std::endl;
+ } else if (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {
+ cb->ready_video_data_ = true;\r
+ if (cb->video_reader_ != nullptr) {\r
+ cb->video_feeding_task_ = std::thread(DataFeedingTask,\r
+ std::ref(cb->video_reader_),\r
+ std::ref(cb->handle_));\r
+ }\r
+ std::cout << "Video ready to prepare" << std::endl;
+ }
+ lk.unlock();
+ cb->data_cv_.notify_all();
+ }
+ static void OnReadyToSeek(const esplusplayer_stream_type type,
+ const uint64_t offset, void* userdata) {
+ EsPlayerEventCallback* cb = static_cast<EsPlayerEventCallback*>(userdata);
+ std::cout << "OnReadyToSeek" << std::endl;
+ std::unique_lock<std::mutex> lk(cb->data_m_);
+ if (type == ESPLUSPLAYER_STREAM_TYPE_AUDIO)
+ cb->ready_audio_data_ = true;
+ else if (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO)
+ cb->ready_video_data_ = true;
+ lk.unlock();
+ cb->data_cv_.notify_all();
+ }
+ void WaitAllStreamData() {
+ std::unique_lock<std::mutex> lk(data_m_);
+ std::cout << "WaitAllStreamData start" << std::endl;
+ data_cv_.wait_for(lk, std::chrono::seconds(1), [this]() -> bool {
+ return ready_audio_data_ && ready_video_data_;
+ });\r
+ std::cout << "WaitAllStreamData stop" << std::endl;
+ lk.unlock();
+ }
+ void WaitAudioStreamData() {
+ std::unique_lock<std::mutex> lk(data_m_);
+ std::cout << "WaitAudioStreamData start" << std::endl;
+ data_cv_.wait_for(lk, std::chrono::seconds(1),
+ [this]() -> bool { return ready_audio_data_; });
+ std::cout << "WaitAudioStreamData stop" << std::endl;
+ lk.unlock();
+ }
+ void WaitVideoStreamData() {
+ std::unique_lock<std::mutex> lk(data_m_);
+ std::cout << "WaitVideoStreamData start" << std::endl;
+ data_cv_.wait_for(lk, std::chrono::seconds(1),
+ [this]() -> bool { return ready_audio_data_; });
+ std::cout << "WaitVideoStreamData stop" << std::endl;
+ lk.unlock();
+ }
+ void WaitForEos() {
+ std::cout << "WaitForEos start" << std::endl;
+ std::unique_lock<std::mutex> lk(eos_m_);
+ eos_cv_.wait_for(lk, std::chrono::minutes(1),
+ [this]() -> bool { return eos_; });\r
+ std::cout << "WaitForEos stop" << std::endl;
+ lk.unlock();
+ }
+ void WaitForPrepareDone() {
+ std::cout << "WaitForPrepareDone start" << std::endl;
+ std::unique_lock<std::mutex> lk(prepare_done_m_);
+ prepare_done_cv_.wait_for(lk, std::chrono::seconds(10),\r
+ [this]() -> bool { return prepare_done_; });\r
+ std::cout << "WaitForPrepareDone stop" << std::endl;
+ lk.unlock();
+ }
+
+ private:
+ esplusplayer_handle handle_ = nullptr;\r
+ EsStreamReader* video_reader_ = nullptr;\r
+ EsStreamReader* audio_reader_ = nullptr;\r
+ std::thread video_feeding_task_;\r
+ std::thread audio_feeding_task_;\r
+\r
+ bool eos_ = false;
+ std::mutex eos_m_;
+ std::condition_variable eos_cv_;
+ bool ready_audio_data_ = false;
+ bool ready_video_data_ = false;
+ std::mutex data_m_;
+ std::condition_variable data_cv_;
+ bool prepare_done_ = false;
+ std::mutex prepare_done_m_;
+ std::condition_variable prepare_done_cv_;
+};\r
+\r
+#endif // __CAPI_TRACKRENDERER_TV_UT_INCLUDE_ES_EVENT_LISTENER_H__
\ No newline at end of file
--- /dev/null
+//\r
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>\r
+//\r
+\r
+#ifndef __CAPI_TRACKRENDERER_TV_UT_INCLUDE_ES_READER_H__\r
+#define __CAPI_TRACKRENDERER_TV_UT_INCLUDE_ES_READER_H__\r
+\r
+\r
+#include <string>\r
+#include <fstream>\r
+\r
+#include "esplusplayer_capi/esplusplayer_capi.h"\r
+\r
+static const std::string tc_root_dir = "/tmp/esdata/";\r
+\r
+class EsStreamReader {\r
+ public:\r
+ explicit EsStreamReader(const std::string dirpath,\r
+ esplusplayer_stream_type type) {\r
+ dir_path_ = tc_root_dir + dirpath;\r
+ es_data_file_ = dir_path_ + "ESP.es";\r
+ es_info_file_ = dir_path_ + "ESP.info";\r
+ extra_codec_file_ = dir_path_ + "ESP.codec_extradata";\r
+ type_ = type;\r
+ std::cout << "ES data file " << es_data_file_ << std::endl;\r
+ }\r
+ ~EsStreamReader() {\r
+ if (stream_.is_open()) {\r
+ stream_.close();\r
+ }\r
+ }\r
+\r
+ bool SetStreamInfo(esplusplayer_handle& esplayer) {\r
+ if (type_ == ESPLUSPLAYER_STREAM_TYPE_AUDIO) {\r
+ esplusplayer_audio_stream_info audio_stream;\r
+ audio_stream.codec_data = nullptr;\r
+ audio_stream.codec_data_length = 0;\r
+ GetExtraData_(audio_stream.codec_data, audio_stream.codec_data_length);\r
+ if (!GetMediaInfo_(audio_stream)) {\r
+ if (audio_stream.codec_data != nullptr)\r
+ delete audio_stream.codec_data;\r
+ return false;\r
+ }\r
+\r
+ esplusplayer_set_audio_stream_info(esplayer, &audio_stream);\r
+ if (audio_stream.codec_data != nullptr)\r
+ delete audio_stream.codec_data;\r
+ } else if (type_ == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {\r
+ esplusplayer_video_stream_info video_stream;\r
+ video_stream.codec_data = nullptr;\r
+ video_stream.codec_data_length = 0;\r
+ GetExtraData_(video_stream.codec_data, video_stream.codec_data_length);\r
+ if (!GetMediaInfo_(video_stream)) {\r
+ if (video_stream.codec_data != nullptr)\r
+ delete video_stream.codec_data;\r
+ return false;\r
+ }\r
+ esplusplayer_set_video_stream_info(esplayer, &video_stream);\r
+ if (video_stream.codec_data != nullptr)\r
+ delete video_stream.codec_data;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ bool ReadNextPacket(esplusplayer_es_packet& pkt) {\r
+ if (!stream_.is_open()) {\r
+ stream_ = std::ifstream(es_data_file_, std::ifstream::binary);\r
+ if (!stream_.is_open()) return false;\r
+ }\r
+ if (stream_.eof() || GetFileLeftSize_() < 24) {\r
+ std::cout << type_ << " stream EOF" << std::endl;\r
+ return false;\r
+ }\r
+ pkt.type = type_;\r
+ std::uint64_t size;\r
+ stream_.read(reinterpret_cast<char*>(&pkt.pts), sizeof(pkt.pts));\r
+ pkt.pts = pkt.pts / 1000000; //ns -> ms\r
+ stream_.read(reinterpret_cast<char*>(&pkt.duration), sizeof(pkt.duration));\r
+ pkt.duration = pkt.duration / 1000000; //ns -> ms\r
+ stream_.read(reinterpret_cast<char*>(&size), sizeof(size));\r
+ pkt.buffer_size = static_cast<uint32_t>(size);\r
+ if (pkt.buffer_size == 0) return false;\r
+ pkt.buffer = new char[pkt.buffer_size];\r
+ stream_.read(reinterpret_cast<char*>(pkt.buffer), pkt.buffer_size);\r
+ pkt.matroska_color_info = nullptr;\r
+ // std::cout << "Read audio/video buffer: " << type_ << std::endl;\r
+ // std::cout << "Type: " << type_ << "Pts: " << pkt.pts << "duration: " <<\r
+ // pkt.duration << "size: " << size << std::endl;\r
+ return true;\r
+ }\r
+\r
+ void ResetReader() {\r
+ stream_.seekg(0,std::ios::beg);\r
+ }\r
+\r
+private:\r
+ int GetFileLeftSize_(void) {\r
+ if (!stream_.is_open()) return 0;\r
+ int cur = stream_.tellg();\r
+ stream_.seekg(0, stream_.end);\r
+ int total = stream_.tellg();\r
+ stream_.seekg(cur);\r
+ return total - cur;\r
+ }\r
+ bool GetExtraData_(char*& data, unsigned int& size) {\r
+ auto stream = std::ifstream(extra_codec_file_, std::ifstream::binary);\r
+ if (!stream.is_open()) return false;\r
+ stream.read(reinterpret_cast<char*>(&size), sizeof(size));\r
+ if (size == 0) return false;\r
+ data = new char[size];\r
+ stream.read(data, size);\r
+ stream.close();\r
+ return true;\r
+ }\r
+\r
+ bool GetMediaInfo_(esplusplayer_video_stream_info& info) {\r
+ auto stream = std::ifstream(es_info_file_, std::ifstream::in);\r
+ if (!stream.is_open()) {\r
+ std::cout << "No video es file: " << es_info_file_ << std::endl;\r
+ return false;\r
+ }\r
+\r
+ uint32_t mime_type;\r
+ stream >> mime_type >> info.width >> info.height >> info.max_width >> info.max_height >>\r
+ info.framerate_num >> info.framerate_den;\r
+ info.mime_type = static_cast<esplusplayer_video_mime_type>(mime_type);\r
+ std::cout << "mime_type: " << info.mime_type << std::endl;\r
+ std::cout << "info.width: " << info.width << std::endl;\r
+ stream.close();\r
+ return true;\r
+ }\r
+\r
+ bool GetMediaInfo_(esplusplayer_audio_stream_info& info) {\r
+ auto stream = std::ifstream(es_info_file_, std::ifstream::in);\r
+ if (!stream.is_open()) {\r
+ std::cout << "No audio es file: " << es_info_file_ << std::endl;\r
+ return false;\r
+ }\r
+ uint32_t mime_type;\r
+ stream >> mime_type >> info.sample_rate >> info.channels;\r
+ info.mime_type = static_cast<esplusplayer_audio_mime_type>(mime_type);\r
+ std::cout << "mime_type: " << info.mime_type << std::endl;\r
+ std::cout << "info.sample_rate: " << info.sample_rate << std::endl;\r
+ stream.close();\r
+ return true;\r
+ }\r
+\r
+\r
+\r
+ private:\r
+ std::string dir_path_;\r
+ std::string es_data_file_;\r
+ std::string es_info_file_;\r
+ std::string extra_codec_file_;\r
+ std::ifstream stream_;\r
+ esplusplayer_stream_type type_;\r
+};\r
+\r
+#endif // __CAPI_TRACKRENDERER_TV_UT_INCLUDE_ES_READER_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __CAPI_TRACKRENDERER_TV_UT_INCLUDE_PLUSPLAYER_TCLIST_H__
+#define __CAPI_TRACKRENDERER_TV_UT_INCLUDE_PLUSPLAYER_TCLIST_H__
+
+#include <map>
+#include <string>
+#include <tuple>
+#include <vector>
+#include <assert.h>
+
+namespace tc
+{
+ enum class tc_type {
+ kDefault,
+ kCosmos,
+ };
+
+ enum class stream_state_type {
+ kNormal,
+ k4k,
+ k8k,
+ kVideoOnly,
+ kAudioOnly,
+ kNotSupportStream,
+ k360,
+ kNonSeekable,
+ kDualmono,
+ kNoIFrameListInHls,
+ // add more state
+ kSize,
+ };
+
+ enum class contents_type {
+ kDash,
+ kHls,
+ kHttp,
+ kSize
+ };
+
+ enum class container_type {
+ kMp4,
+ kMp3,
+ kOgg,
+ kAvi,
+ kWebM,
+ kAsf,
+ kWmv,
+ kWma,
+ kMkv,
+ kMka,
+ kMk3d,
+ kMkms,
+ kTs,
+ kDivx,
+ kEs,
+ kMov,
+ // more container
+ kSize
+ };
+
+ enum class drm_type {
+ kClean,
+ kPlayready,
+ kVerimatrix,
+ kWidevineModular,
+ // more drm type
+ kSize
+ };
+
+ enum class video_codec_type {
+ kNone,
+ kH261,
+ kH262,
+ kH263,
+ kH264, // avc
+ kH265,
+ kHevc,
+ kVc1,
+ kVp9,
+ kMp4_Visual,
+ kMpeg1,
+ kMpeg2,
+ // more codec type
+ kSize
+ };
+
+ enum class audio_codec_type {
+ kNone,
+ kMp3,
+ kAc3,
+ kAac,
+ kE_ac3,
+ kWma,
+ // more codec type
+ kSize
+ };
+
+ enum class audio_track_type {
+ kSingle,
+ kMulti,
+ kSize
+ };
+
+ using url = std::string;
+ using contents_tag = std::tuple<stream_state_type, contents_type, container_type, drm_type, video_codec_type, audio_codec_type, audio_track_type, unsigned int>;
+ using tc_map = std::map<url, contents_tag>;
+ using tc_list = std::vector<url>;
+
+ class TCList {
+ public:
+ static TCList& Instance();
+ static void InitializeTC(tc_type type);
+
+ static void Kill();
+
+ tc_map GetTCMap() const;
+ tc_list GetTCURIList() const;
+
+ // return uri tag from args
+ static inline contents_tag MakeContentsTag(stream_state_type t0, contents_type t1, container_type t2, drm_type t3, video_codec_type t4, audio_codec_type t5, audio_track_type t6) {
+ return std::make_tuple(t0, t1, t2, t3, t4, t5, t6, 0);
+ }
+
+ static inline contents_tag MakeUniqueContentsTag(stream_state_type t0, contents_type t1, container_type t2, drm_type t3, video_codec_type t4, audio_codec_type t5, audio_track_type t6, unsigned int unique_id) {
+ assert(unique_id != 0); // because 0 is default id for MakeContentTag()
+ return std::make_tuple(t0, t1, t2, t3, t4, t5, t6, unique_id);
+ }
+
+ static inline stream_state_type GetStreamStateType(contents_tag tag) { return std::get<0>(tag); }
+ static inline contents_type GetContentsType(contents_tag tag) { return std::get<1>(tag); }
+ static inline container_type GetContainerType(contents_tag tag) { return std::get<2>(tag); }
+ static inline drm_type GetDrmType(contents_tag tag) { return std::get<3>(tag); }
+ static inline video_codec_type GetVideoCodecType(contents_tag tag) { return std::get<4>(tag); }
+ static inline audio_codec_type GetAudioCodecType(contents_tag tag) { return std::get<5>(tag); }
+ static inline audio_track_type GetAudioTrackType(contents_tag tag) { return std::get<6>(tag); }
+
+ virtual ~TCList();
+ private:
+ TCList(tc_type type);
+
+#ifdef VD_UNIT_TEST
+ void MountCIFS();
+ void UnmountCIFS();
+#endif
+
+ private:
+ tc_type type_;
+ tc_map map_;
+ tc_list list_;
+ };
+
+ namespace hls_tc {
+ static const url hls_normal_clean_1 = "";
+ static const url hls_normal_clean_2 = "";
+ static const url hls_normal_clean_3 = "";
+
+ static const url hls_normal_clean_multiaudio_subtitle_1 = "";
+ static const url hls_normal_clean_discontinuity_1 = "";
+ }
+
+ namespace hls_abnormal_tc {
+
+ }
+
+ namespace dash_tc {
+ static const url dash_normal_clean_1 = "";
+ static const url dash_normal_clean_2 = "";
+ static const url dash_normal_clean_3 = "";
+
+ static const url dash_normal_clean_uhd_1 = "";
+ static const url dash_normal_clean_uhd_2 = "";
+
+ }
+
+ namespace http_tc {
+ static const url avc_aac_mp4 = "";
+ static const url avc_mp3_mp4 = "";
+ static const url hevc_aac_4k_mp4 = "";
+ static const url avc_aac_4k_mp4 = "";
+ static const url hevc_aac_4k_ts = "";
+
+ static const url aac_audioonly_es = "";
+ static const url avc_videoonly_es = "";
+
+ static const url avc_aac_360_mp4 = "";
+ static const url hevc_aac_360_mp4 = "";
+ static const url avc_aac_360_4k_mp4 = "";
+
+ static const url mp3_mp3 = "";
+
+ static const url mpeg2video_ac3_ac3_tp = "";
+ }
+
+ namespace track_tc {
+ static const url http_1video_2audio = http_tc::mpeg2video_ac3_ac3_tp;
+ static const url hls_1video_2audio_4text = "";
+ static const url hls_1video_2audio_3text = hls_tc::hls_normal_clean_multiaudio_subtitle_1;
+ static const url hls_1video_1audio_0text = hls_tc::hls_normal_clean_3;
+ static const url dash_2video_1audio_0text = "";
+ static const url dash_2video_2audio_0text = "";
+ static const url dash_1video_1audio_0text = dash_tc::dash_normal_clean_3;
+ static const url dash_1video_0audio_0text = "";
+ static const url dash_1video_1audio_2text_ttml = "";
+ static const url dash_1video_1audio_2text_xml = "";
+ }
+}
+
+#endif // __CAPI_TRACKRENDERER_TV_UT_INCLUDE_PLUSPLAYER_TCLIST_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#ifndef __CAPI_TRACKRENDERER_TV_UT_INCLUDE_PLUSPLAYER_UTILITY_H__
+#define __CAPI_TRACKRENDERER_TV_UT_INCLUDE_PLUSPLAYER_UTILITY_H__
+
+#include <Ecore_Evas.h>
+
+#include "core/utils/plusplayer_log.h"
+#include "plusplayer/plusplayer.h"
+#include "ut/include/appwindow.h"
+
+namespace utils {
+class Utility;
+using evas_h = Evas_Object*;
+
+class Utility {
+ public:
+ static Utility& Instance();
+ static void ThreadSleep(long ms);
+ static void Kill();
+ virtual ~Utility();
+
+ const char* GetCurrentTestName(void);
+
+ plusplayer::PlusPlayer::Ptr GetCreatedPlusPlayer(std::string& uri);
+
+ plusplayer::PlusPlayer::Ptr GetPreparedPlusPlayer(std::string& uri);
+
+ void DestroyPlayer(plusplayer::PlusPlayer::Ptr& player);
+
+ bool IsPaused(plusplayer::PlusPlayer::Ptr& player, int monitoring_time_msec);
+
+ evas_h GetWindow() const;
+
+ Utility(const Utility& rhs) = delete;
+ Utility& operator=(const Utility& rhs) = delete;
+
+ private:
+ explicit Utility();
+
+ private:
+ std::unique_ptr<plusplayer_ut::AppWindow> appwindow_;
+};
+
+} // namespace utils
+
+#endif // __CAPI_TRACKRENDERER_TV_UT_INCLUDE_PLUSPLAYER_UTILITY_H__
\ No newline at end of file
--- /dev/null
+//\r
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>\r
+//\r
+\r
+#ifndef __CAPI_TRACKRENDERER_TV_UT_INCLUDE_ESCOMMON_H__\r
+#define __CAPI_TRACKRENDERER_TV_UT_INCLUDE_ESCOMMON_H__\r
+\r
+#include <chrono>\r
+#include <cstdlib>\r
+#include <fstream>\r
+#include <memory>\r
+#include <string>\r
+#include <thread>\r
+#include <vector>\r
+\r
+#include "gtest/gtest.h"\r
+\r
+#include "plusplayer/espacket.h"\r
+#include "trackrenderer/core/decoderinputbuffer.h"\r
+#include "ut/include/appwindow.h"\r
+\r
+namespace pp = plusplayer;\r
+\r
+namespace utils {\r
+inline bool Exists(const std::string &path) {\r
+ std::ifstream ifile(path);\r
+ return static_cast<bool>(ifile);\r
+}\r
+} // namespace utils\r
+\r
+namespace {\r
+class Environment {\r
+ public:\r
+ Environment() {\r
+ appwindow_.reset(new plusplayer_ut::AppWindow(0, 0, 1920, 1080));\r
+ }\r
+ void *Window() { return appwindow_->GetWindow().obj; }\r
+ void *EcoreWindow() { return appwindow_->GetEcoreWL2Window(); }\r
+\r
+ private:\r
+ std::shared_ptr<plusplayer_ut::AppWindow> appwindow_;\r
+};\r
+\r
+class ESPacketDownloader {\r
+ public:\r
+ static void Init() {\r
+ if (utils::Exists("/tmp/esdata")) {\r
+ return;\r
+ }\r
+ std::cout << "Download ESData via http - path : /tmp/esdata" << std::endl;\r
+ int ret = std::system(\r
+ "/usr/bin/wget "\r
+ "--recursive --directory-prefix=/tmp/esdata --force-directories "\r
+ "--no-host-directories --cut-dirs=2 --no-parent --reject=index.html "\r
+ "");\r
+ assert(ret == 0 && "wget is failed, please retry");\r
+ }\r
+};\r
+} // namespace\r
+\r
+namespace es {\r
+inline std::uint64_t ConvertNsToMs(std::uint64_t ms) {\r
+ constexpr std::uint64_t ns_unit = 1000000;\r
+ return ms / ns_unit;\r
+}\r
+struct Packet {\r
+ std::uint64_t pts;\r
+ std::uint64_t duration;\r
+ std::uint64_t size;\r
+ std::shared_ptr<char> data;\r
+\r
+ pp::trackrenderer::DecoderInputBufferPtr MakeDecoderInputBuffer(\r
+ pp::trackrenderer::TrackType type) {\r
+ GstMapInfo map;\r
+ auto gstbuffer = gst_buffer_new_and_alloc(size);\r
+ gst_buffer_map(gstbuffer, &map, GST_MAP_WRITE);\r
+ memcpy(map.data, data.get(), size);\r
+ gst_buffer_unmap(gstbuffer, &map);\r
+ GST_BUFFER_PTS(gstbuffer) = pts;\r
+ GST_BUFFER_DURATION(gstbuffer) = duration;\r
+ auto buffer =\r
+ pp::trackrenderer::DecoderInputBuffer::Create(type, 0, gstbuffer);\r
+ gst_buffer_unref(gstbuffer);\r
+ return std::move(buffer);\r
+ }\r
+\r
+ static pp::trackrenderer::DecoderInputBufferPtr MakeEosBuffer(\r
+ pp::trackrenderer::TrackType type) {\r
+ return std::move(pp::trackrenderer::DecoderInputBuffer::Create(type));\r
+ }\r
+\r
+ pp::EsPacketPtr MakeEsPacket(pp::StreamType type) {\r
+ return std::move(\r
+ pp::EsPacket::Create(type, data, static_cast<std::uint32_t>(size),\r
+ // pts/1000000,duration/1000000));\r
+ ConvertNsToMs(pts), ConvertNsToMs(duration)));\r
+ }\r
+\r
+ static pp::EsPacketPtr MakeEosPacket(pp::StreamType type) {\r
+ return std::move(pp::EsPacket::CreateEos(type));\r
+ }\r
+};\r
+\r
+struct CodecExtraData {\r
+ std::uint32_t size;\r
+ std::shared_ptr<char> data;\r
+};\r
+using PacketPtr = std::shared_ptr<Packet>;\r
+using CodecExtraDataPtr = std::shared_ptr<CodecExtraData>;\r
+\r
+template <typename TRACK, typename TRACKTYPE>\r
+struct VideoInfo {\r
+ std::string mimetype;\r
+ int width, height;\r
+ int maxwidth, maxheight;\r
+ int framerate_num, framerate_den;\r
+\r
+ void ExtractMediaInfoFrom(std::ifstream &stream) {\r
+ stream >> mimetype >> width >> height >> maxwidth >> maxheight >>\r
+ framerate_num >> framerate_den;\r
+ }\r
+\r
+ TRACK GetTrack() {\r
+ TRACK t;\r
+ t.active = true;\r
+ t.index = 0;\r
+ t.type = TRACKTYPE::kTrackTypeVideo;\r
+ t.mimetype = mimetype;\r
+ t.width = width;\r
+ t.height = height;\r
+ t.maxwidth = maxwidth;\r
+ t.maxheight = maxheight;\r
+ t.framerate_num = framerate_num;\r
+ t.framerate_den = framerate_den;\r
+ return t;\r
+ }\r
+};\r
+\r
+template <typename TRACK, typename TRACKTYPE>\r
+struct AudioInfo {\r
+ std::string mimetype;\r
+ int samplerate;\r
+ int channels;\r
+ int version;\r
+\r
+ void ExtractMediaInfoFrom(std::ifstream &stream) {\r
+ stream >> mimetype >> samplerate >> channels >> version;\r
+ }\r
+ TRACK GetTrack() {\r
+ TRACK t;\r
+ t.active = true;\r
+ t.index = 0;\r
+ t.type = TRACKTYPE::kTrackTypeAudio;\r
+ t.mimetype = mimetype;\r
+ t.sample_rate = samplerate;\r
+ t.channels = channels;\r
+ t.version = version;\r
+ return t;\r
+ }\r
+};\r
+\r
+template <typename TRACKTYPE>\r
+class StreamReader {\r
+ public:\r
+ using Ptr = std::shared_ptr<StreamReader<TRACKTYPE>>;\r
+ friend Ptr;\r
+ static Ptr Create(const std::string &dirpath, const TRACKTYPE &type) {\r
+ return Ptr(new StreamReader(dirpath, type));\r
+ }\r
+ virtual ~StreamReader() {\r
+ if (stream_.is_open()) {\r
+ stream_.close();\r
+ }\r
+ }\r
+\r
+ private:\r
+ explicit StreamReader(const std::string dirpath, const TRACKTYPE &type)\r
+ : type_(type) {\r
+ dirpath_ = "/tmp/esdata/" + dirpath;\r
+ if (utils::Exists(dirpath_) == false) {\r
+ throw std::runtime_error(dirpath_ + "doesn't exist");\r
+ }\r
+ stream_ = std::ifstream(dirpath_ + "/ESP.es", std::ifstream::binary);\r
+ }\r
+\r
+ public:\r
+ CodecExtraDataPtr GetExtraData() {\r
+ std::string path = dirpath_ + "/ESP.codec_extradata";\r
+ auto stream = std::ifstream(path, std::ifstream::binary);\r
+ auto extradata = CodecExtraDataPtr(new CodecExtraData);\r
+ stream.read(reinterpret_cast<char *>(&extradata->size),\r
+ sizeof extradata->size);\r
+ using DataPtr = std::shared_ptr<char>;\r
+ extradata->data = DataPtr(new char[extradata->size]);\r
+ stream.read(reinterpret_cast<char *>(extradata->data.get()),\r
+ extradata->size);\r
+ stream.close();\r
+ return extradata;\r
+ }\r
+\r
+ PacketPtr ReadNextPacket() {\r
+ if (stream_.eof()) {\r
+ return nullptr;\r
+ }\r
+ auto pkt = PacketPtr(new Packet);\r
+ stream_.read(reinterpret_cast<char *>(&pkt->pts), sizeof pkt->pts);\r
+ stream_.read(reinterpret_cast<char *>(&pkt->duration),\r
+ sizeof pkt->duration);\r
+ stream_.read(reinterpret_cast<char *>(&pkt->size), sizeof pkt->size);\r
+ using DataPtr = std::unique_ptr<char>;\r
+ pkt->data = DataPtr(new char[pkt->size]);\r
+ stream_.read(reinterpret_cast<char *>(pkt->data.get()), pkt->size);\r
+ return pkt;\r
+ }\r
+\r
+ template <typename T>\r
+ T GetMediaInfo() {\r
+ std::string path = dirpath_ + "/ESP.info";\r
+ T media;\r
+ auto stream = std::ifstream(path, std::ifstream::in);\r
+ media.ExtractMediaInfoFrom(stream);\r
+ stream.close();\r
+ return media;\r
+ }\r
+\r
+ TRACKTYPE GetTrackType() { return type_; }\r
+\r
+ private:\r
+ std::string dirpath_;\r
+ std::ifstream stream_;\r
+ TRACKTYPE type_;\r
+};\r
+\r
+} // namespace es\r
+#endif // __CAPI_TRACKRENDERER_TV_UT_INCLUDE_ESCOMMON_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include "ut/include/plusplayer/tclist.h"
+
+#include <sys/mount.h>
+#include <cassert>
+#include <chrono>
+#include <initializer_list>
+#include <memory>
+
+
+using tclist_ptr = std::unique_ptr<TCList>;
+static tclist_ptr ptr = nullptr;
+
+TCList& TCList::Instance() { return *(ptr.get()); }
+
+void TCList::InitializeTC(tc_type type) {
+ if (ptr.get() == nullptr) ptr.reset(new TCList(type));
+}
+
+void TCList::Kill() { ptr.reset(); }
+
+tc_map TCList::GetTCMap() const { return this->map_; }
+
+tc_list TCList::GetTCURIList() const {
+ std::vector<url> l;
+ for (const auto& p : this->map_) {
+ l.emplace_back(p.first);
+ }
+ return l;
+}
+
+TCList::TCList(tc_type type) : type_(type) {
+ this->map_.insert(http_tc::tc_map_.begin(), http_tc::tc_map_.end());
+ this->map_.insert(hls_tc::tc_map_.begin(), hls_tc::tc_map_.end());
+ this->map_.insert(dash_tc::tc_map_.begin(), dash_tc::tc_map_.end());
+#ifdef VD_UNIT_TEST
+ this->MountCIFS();
+#endif
+}
+
+TCList::~TCList() {
+#ifdef VD_UNIT_TEST
+ this->UnmountCIFS();
+#endif
+}
+
+#ifdef VD_UNIT_TEST
+void TCList::MountCIFS() {
+ LOG_DEBUG("system result : %d",
+ system("/usr/bin/chmod +x /usr/bin/mount-server.sh"));
+ LOG_DEBUG("system result : %d", system("/usr/bin/mount-server.sh"));
+}
+
+void TCList::UnmountCIFS() {
+ LOG_DEBUG(
+ "system result : %d",
+ system("/usr/bin/umount -f -l -a -t cifs")); // -l : lazy umount for
+ // mount to windows
+}
+#endif
+} // namespace tc
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "ut/include/plusplayer/tclist.h"
+
+using tc::tc_type;
+using tc::TCList;
+
+int main(int argc, char *argv[]) {
+ //TCList::InitializeTC(tc_type::kDefault);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::InitGoogleMock(&argc, argv);
+
+ auto ret = -1;
+ ret = RUN_ALL_TESTS();
+
+ //TCList::Kill();
+
+ return ret;
+}
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <chrono> // std::chrono::seconds()
+#include <iostream>
+#include <memory>
+#include <string>
+#include <thread> // std::thread
+
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+
+#include "core/track_util.h"
+#include "player/defaultplayer.h"
+#include "plusplayer/plusplayer.h"
+#include "plusplayer/track.h"
+#include "trackrenderer/display.h"
+#include "trackrenderer/trackrenderer.h"
+#include "tracksource/tracksource.h"
+#include "ut/include/appwindow.h"
+
+class TrackRendererTest : public ::testing::Test {
+ public:
+ TrackRendererTest() {}
+
+ void SetUp() override {
+ gst_init_check(nullptr, nullptr, nullptr);
+
+ // basic , clean stream
+ std::string url = "";
+
+ // multi-track with closed caption data
+ // std::string url =
+ // "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8";
+
+ // single-track with closed caption data
+ // std::string url =
+ // "http://mediadelivery.mlbcontrol.net/2015/hls_testbeds/ads_302/master_wired_fwv2.m3u8";
+ TypeFinder typefinder(url);
+ typefinder.Probe();
+ StreamingProperty property;
+ tracksource_ = TrackSource::CreateCompositor();
+ tracksource_->AddSource(&typefinder, property);
+ ASSERT_TRUE(tracksource_.get());
+
+ trackrenderer_ = TrackRenderer::Create();
+ ASSERT_TRUE(trackrenderer_.get());
+
+ appwindow_.reset(new plusplayer_ut::AppWindow(0, 0, 1920, 1080));
+ ASSERT_TRUE(appwindow_.get());
+
+ trackrenderer_->SetDisplayMode(plusplayer::DisplayMode::kDstRoi);
+ trackrenderer_->SetDisplay(plusplayer::DisplayType::kOverlay,
+ appwindow_->GetWindow().obj);
+ plusplayer::Geometry roi;
+ roi.x = 600, roi.y = 300, roi.w = 720, roi.h = 480;
+ trackrenderer_->SetDisplayRoi(roi);
+ }
+
+ void TearDown() override {
+ trackrenderer_->Stop();
+ tracksource_->Stop();
+ }
+
+ std::shared_ptr<TrackSource> GetTrackSource() { return tracksource_; }
+ std::shared_ptr<TrackRenderer> GetTrackRenderer() { return trackrenderer_; }
+
+ private:
+ std::shared_ptr<TrackSource> tracksource_;
+ std::shared_ptr<TrackRenderer> trackrenderer_;
+ std::unique_ptr<plusplayer_ut::AppWindow> appwindow_;
+};
+
+Track SetVideoInfo(std::string mimetype, int w, int h, int framerate_num,
+ int framerate_den) {
+ Track trackinfo;
+ trackinfo.mimetype = mimetype;
+ trackinfo.width = w;
+ trackinfo.height = h;
+ trackinfo.framerate_num = framerate_num;
+ trackinfo.framerate_den = framerate_den;
+ trackinfo.active = true;
+
+ return trackinfo;
+}
+
+Track SetAudioInfo(std::string mimetype, int samplerate, int sampleformat,
+ int channels, int version) {
+ Track trackinfo;
+ trackinfo.mimetype = mimetype;
+ trackinfo.sample_rate = samplerate;
+ trackinfo.sample_format = sampleformat;
+ trackinfo.channels = channels;
+ trackinfo.version = version;
+ trackinfo.active = true;
+ return trackinfo;
+}
+
+TEST_F(TrackRendererTest, DISABLED_Create) {}
+
+TEST_F(TrackRendererTest, DISABLED_Prepare) { // 기존 Prepare와 합쳐도 됨
+ auto tracksource = GetTrackSource();
+ auto trackrenderer = GetTrackRenderer();
+
+ tracksource->Prepare(); // READY to PAUSE
+ std::vector<Track> track = tracksource->GetTrackInfo();
+
+ // for ut test. will be remove is after tracksource implement GetTrackInfo().
+ Track trackvideoinfo = SetVideoInfo("video/x-h264", 640, 352, 30, 1);
+ Track trackaudioinfo = SetAudioInfo("audio/mpeg", 44100, 0, 2, 4);
+ std::vector<Track> track_vector = {trackvideoinfo, trackaudioinfo};
+ bool ret =
+ trackrenderer->SetTrack(track_vector); // in PlusPlayer or in Wrapper
+ ASSERT_TRUE(ret);
+ // for ut test. will be remove is after tracksource implement GetTrackInfo()
+ // end
+
+ trackrenderer->Prepare(); // NULL to READY
+}
+
+TEST_F(TrackRendererTest, DISABLED_CreateAndSetWindow) {
+ auto tracksource = GetTrackSource();
+ auto trackrenderer = GetTrackRenderer();
+}
+
+TEST_F(TrackRendererTest, DISABLED_Start) {
+ auto tracksource = GetTrackSource();
+ auto trackrenderer = GetTrackRenderer();
+
+ tracksource->Prepare(); // READY to PAUSE , track parsing done
+
+ std::vector<Track> track = tracksource->GetTrackInfo();
+
+ // for ut test. will be remove is after tracksource implement GetTrackInfo().
+ Track vtrack = SetVideoInfo("video/x-h264", 640, 352, 30, 1);
+ Track atrack = SetAudioInfo("audio/mpeg", 44100, 0, 2, 4);
+ std::vector<Track> selectedtrack = {vtrack, atrack};
+ bool ret = trackrenderer->SetTrack(selectedtrack);
+
+ track_util::ShowTrackInfo(selectedtrack);
+ ASSERT_TRUE(ret);
+ // for ut test. will be remove is after tracksource implement GetTrackInfo()
+ // end
+
+ trackrenderer->Prepare(); // NULL to READY
+
+ trackrenderer->Start(); // READY to PAUSE to PLAYING
+ // PAUSE to PLAYING , Hands-off -> Listener -> SubmitPacket
+ tracksource->Start();
+
+ std::this_thread::sleep_for(std::chrono::seconds(60));
+ std::cout << "wait for 10 secs done ... terminate the test" << std::endl;
+}
+
+TEST_F(TrackRendererTest, DISABLED_SetTrack) {
+ auto tracksource = GetTrackSource();
+ auto trackrenderer = GetTrackRenderer();
+
+ Track trackvideoinfo = SetVideoInfo("video/x-h264", 640, 352, 30, 1);
+ Track trackaudioinfo = SetAudioInfo("audio/mpeg", 44100, 0, 2, 4);
+ std::vector<Track> track_vector = {trackvideoinfo, trackaudioinfo};
+ bool ret = trackrenderer->SetTrack(track_vector);
+ ASSERT_TRUE(ret);
+}
+
+TEST_F(TrackRendererTest, DISABLED_ChangeTrack) {
+}
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <boost/scope_exit.hpp>
+#include <chrono> // std::chrono::seconds()
+#include <iostream>
+#include <memory>
+#include <queue>
+#include <string>
+#include <thread> // std::thread
+
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+
+#include "libavformat/avformat.h"
+#include "plusplayer/track.h"
+#include "trackrenderer/core/utils/log.h"
+#include "trackrenderer/display.h"
+#include "trackrenderer/trackrenderer.h"
+#include "tracksource/tracksource.h"
+
+using namespace plusplayer;
+using namespace plusplayer::trackrenderer;
+
+namespace {
+
+class ExternalDemuxer {
+ public:
+ explicit ExternalDemuxer(const std::string& url) : url_(url) {
+// Init_();
+ }
+
+ private:
+ struct Context {
+ bool stop = false;
+ std::shared_ptr<TrackRenderer> trackrenderer;
+ };
+
+ bool Init_() {
+ int ret = 0;
+ av_register_all();
+ if(av_open_input_file(&ic_, url_.c_str() , nullptr , 0 , nullptr) < 0) {
+ return false;
+ }
+ ret = av_find_stream_info(ic_);
+ BOOST_SCOPE_EXIT(&ic_, &ret) {
+ if (ic_ && ret < 0) {
+ av_close_input_file(ic_);
+ }
+ } BOOST_SCOPE_EXIT_END;
+
+ if (ret < 0) {
+ return false;
+ }
+
+ ret = av_find_stream_info(ic_);
+ if(ret < 0) {
+ return false;
+ }
+ return true;
+ }
+
+ void InputTask_(Context* ctx) {
+ while (!ctx->stop) {
+ int ret = 0;
+ AVPacket packet;
+ av_init_packet(&packet);
+ if(ctx->stop) break;
+ ret = av_read_frame(ic_, &packet);
+ if(ret < 0) {
+ TRACKRENDERER_ERROR("av_read_frame error!!");
+ break;
+ }
+ MakeGstBuffer_(&packet);
+ }
+ }
+ plusplayer::DecoderInputBuffer* MakeGstBuffer_(AVPacket *packet) {
+ return nullptr;
+ }
+ private:
+ std::string url_;
+ AVFormatContext* ic_ = nullptr;
+ Context ctx;
+};
+#if 0
+ void DecoderInputTask(DataCtx* ctx) {
+ while (!ctx->stop) {
+ char* buf = read_es(frame_cnt, ctx->type, &size);
+ push_media_data(buf, size, pts, ctx->appsrc, send_segment);
+ }
+ private:
+
+#endif
+} // end unname space
+
+class TrackRendererTest : public ::testing::Test {
+ public:
+ TrackRendererTest() {}
+
+ void SetUp() override {
+
+ appwindow_.reset(new AppWindow(0, 0, 1920, 1080));
+ ASSERT_TRUE(appwindow_.get());
+
+ //basic , clean stream
+ std::string url = "/opt/media/USBDriveA1/test.mp4";
+ ::ExternalDemuxer external_demuxer(url);
+
+ // multi-track with closed caption data
+ // std::string url =
+ // "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8";
+
+ // single-track with closed caption data
+ // std::string url =
+ // "http://mediadelivery.mlbcontrol.net/2015/hls_testbeds/ads_302/master_wired_fwv2.m3u8";
+ trackrenderer_ = TrackRenderer::Create();
+ ASSERT_TRUE(trackrenderer_.get());
+
+ trackrenderer_->SetDisplayMode(DisplayMode::kDstRoi);
+ trackrenderer_->SetDisplay(DisplayType::kOverlay,appwindow_->GetWindow().obj);
+ Geometry roi;
+ roi.x = 600, roi.y = 300, roi.w = 720, roi.h = 480;
+ trackrenderer_->SetDisplayRoi(roi);
+ }
+
+ void TearDown() override {
+ trackrenderer_->Stop();
+ }
+
+ std::shared_ptr<TrackRenderer> GetTrackRenderer() { return trackrenderer_; }
+ SourceType& GetSourceType() const { return type_; }
+
+ private:
+ SourceType type_;
+ std::shared_ptr<TrackRenderer> trackrenderer_;
+ std::unique_ptr<AppWindow> appwindow_;
+};
+
+
+Track SetVideoInfo(std::string mimetype, int w, int h, int framerate_num, int framerate_den) {
+ Track trackinfo;
+ trackinfo.mimetype = mimetype;
+ trackinfo.width = w;
+ trackinfo.height = h;
+ trackinfo.framerate_num = framerate_num;
+ trackinfo.framerate_den = framerate_den;
+ trackinfo.active = true;
+
+ return trackinfo;
+}
+
+Track SetAudioInfo(std::string mimetype, int samplerate, int sampleformat, int channels, int version) {
+ Track trackinfo;
+ trackinfo.mimetype = mimetype;
+ trackinfo.sample_rate = samplerate;
+ trackinfo.sample_format = sampleformat;
+ trackinfo.channels = channels;
+ trackinfo.version = version;
+ trackinfo.active = true;
+ return trackinfo;
+}
+
+TEST_F(TrackRendererTest, DISABLED_Create) {}
+
+TEST_F(TrackRendererTest, DISABLED_Prepare) {
+ auto trackrenderer = GetTrackRenderer();
+
+ Track trackvideoinfo = SetVideoInfo("video/x-h264", 640, 352, 30, 1);
+ Track trackaudioinfo = SetAudioInfo("audio/mpeg", 44100, 0, 2, 4);
+ std::vector<Track> track_vector = {trackvideoinfo, trackaudioinfo};
+ bool ret = trackrenderer->SetTrack(track_vector); // in PlusPlayer or in Wrapper
+ ASSERT_TRUE(ret);
+ trackrenderer->Prepare(); // NULL to READY
+}
+
+TEST_F(TrackRendererTest, DISABLED_SetDisplay) {
+}
+
+TEST_F(TrackRendererTest, DISABLED_Start) {
+ auto trackrenderer = GetTrackRenderer();
+ auto start = logutil::performance::Start();
+
+ Track vtrack = SetVideoInfo("video/x-h264", 640, 352, 30, 1);
+ Track atrack = SetAudioInfo("audio/mpeg", 44100, 0, 2, 4);
+ std::vector<Track> selectedtrack = {vtrack, atrack};
+ bool ret = trackrenderer->SetTrack(selectedtrack);
+ track_util::ShowTrackInfo(selectedtrack);
+ ASSERT_TRUE(ret);
+
+ trackrenderer->Prepare(); // NULL to READY
+
+ trackrenderer->Start(); // READY to PAUSE to PLAYING
+ // PAUSE to PLAYING , Hands-off -> Listener -> SubmitPacket
+ logutil::performance::End(start, "[TrackRenderer start::]");
+ std::this_thread::sleep_for(std::chrono::seconds(60));
+ std::cout << "wait for 10 secs done ... terminate the test" << std::endl;
+}
+
+TEST_F(TrackRendererTest, DISABLED_SetTrack) {
+ auto trackrenderer = GetTrackRenderer();
+
+ Track trackvideoinfo = SetVideoInfo("video/x-h264", 640, 352, 30, 1);
+ Track trackaudioinfo = SetAudioInfo("audio/mpeg", 44100, 0, 2, 4);
+ std::vector<Track> track_vector = {trackvideoinfo, trackaudioinfo};
+ bool ret = trackrenderer->SetTrack(track_vector);
+ ASSERT_TRUE(ret);
+}
+
+TEST_F(TrackRendererTest, DISABLED_ChangeTrack) {
+ auto trackrenderer = GetTrackRenderer();
+ Track trackvideoinfo = SetVideoInfo("video/x-h264", 640, 352, 30, 1);
+ Track trackaudioinfo = SetAudioInfo("audio/mpeg", 44100, 0, 2, 4);
+ std::vector<Track> track_vector = {trackvideoinfo, trackaudioinfo};
+ bool ret = trackrenderer->SetTrack(track_vector); // in PlusPlayer or in Wrapper
+ ASSERT_TRUE(ret);
+
+ trackrenderer->Prepare(); // NULL to READY
+
+ start = logutil::performance::Start();
+ trackrenderer->Start(); // READY to PAUSE to PLAYING
+
+ logutil::performance::End(start, "player start");
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+
+ TRACKRENDERER_DEBUG("Start Track change");
+ Track changed_info;
+ changed_info.channels = 2;
+ changed_info.mimetype = "audio/mpeg";
+ changed_info.sample_rate = 48000;
+ changed_info.sample_format = 2;
+ changed_info.version = 4;
+ changed_info.active = true;
+
+ // push thread for appsrc must be stopped first. otherwisw appsrc block mode
+ // makes deadlock
+ trackrenderer->ChangeTrack(changed_info);
+ TRACKRENDERER_DEBUG("ChangeTrack DONE");
+ std::this_thread::sleep_for(std::chrono::seconds(15));
+ TRACKRENDERER_DEBUG("STOP");
+}
--- /dev/null
+/**
+* Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+* PROPRIETARY/CONFIDENTIAL
+* This software is the confidential and proprietary
+* information of SAMSUNG ELECTRONICS ("Confidential Information"). You shall
+* not disclose such Confidential Information and shall use it only in
+* accordance with the terms of the license agreement you entered into with
+* SAMSUNG ELECTRONICS. SAMSUNG make no representations or warranties about the
+* suitability of the software, either express or implied, including but not
+* limited to the implied warranties of merchantability, fitness for a
+* particular purpose, or non-infringement. SAMSUNG shall not be liable for any
+* damages suffered by licensee as a result of using, modifying or distributing
+* this software or its derivatives.
+*/
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+
+#include "trackrenderer/core/utils/log.h"
+#include "trackrenderer/version.h"
+#include "trackrenderer_capi/trackrenderer_capi.h"
+
+TEST(TrackRendererCapiTest, DISABLED_get_version) {
+ std::string version { TRACKRENDERER_STRINGIFY(LIB_TRACKRENDERER_VERSION)};
+ TRACKRENDERER_INFO("version string[%s]", trackrenderer_get_version());
+ ASSERT_STREQ(version.c_str() , trackrenderer_get_version());
+}
+
+TEST(TrackRendererCapiTest, DISABLED_get_version_int) {
+ uint32_t version = LIB_TRACKRENDERER_VERSION_INT;
+ TRACKRENDERER_INFO("version int[%u]", trackrenderer_get_version_int());
+ ASSERT_EQ(version , trackrenderer_get_version_int());
+}