--- /dev/null
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+
+PROJECT(esplusplayer)
+SET(description "new multimedia player, object-oriented model")
+SET(PC_NAME "esplusplayer")
+SET(PC_LDFLAGS "-lesplusplayer -lmixer")
+SET(PC_CFLAGS "-I/usr/include/esplusplayer_capi -I/usr/include/mixer")
+
+SET(INC_DIR ${PROJECT_SOURCE_DIR}/include/)
+INCLUDE_DIRECTORIES(${INC_DIR})
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+CONFIGURE_FILE(esplusplayer.pc.in
+ esplusplayer.pc
+ @ONLY
+)
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/esplusplayer.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+ADD_SUBDIRECTORY(src)
+
+OPTION(ESPLUSPLAYER_BUILD_UT "Build esplusplayer ut codes" OFF)
+IF(ESPLUSPLAYER_BUILD_UT)
+ADD_SUBDIRECTORY(ut)
+ENDIF(ESPLUSPLAYER_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} )
\ No newline at end of file
--- /dev/null
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
--- /dev/null
+{
+ "version" : "0.0.4948390.firmware",
+ "gstparam1" : "--gst-debug=*:2",
+ "gstparam2" : "--gst-disable-segtrap",
+ "gstparam3" : "--gst-plugin-load",
+ "gstparam4" : "--gst-disable-registry-fork",
+ "gstparam5" : "--gst-disable-registry-update",
+ "tz_video_es_dump" : false,
+ "tz_video_dump_insert_vp9_header" : false,
+ "tz_audio_es_dump" : false,
+ "generate_dot" : false
+}
--- /dev/null
+@startuml
+
+package "esplusplayer" {
+
+ interface EsPlusPlayer {
+ }
+
+ EsPlusPlayer <|.. EsPlayer
+
+ class EsEventListener {
+ }
+
+ class AudioStream {
+ }
+
+ class VideoStream {
+ }
+
+ class EsPacket {
+ }
+
+ class TrackRenderer {
+ }
+
+' EsPlayer
+ EsPlayer "1" *-- "1" StateManager
+ EsPlayer "1" *-- "1" TrackRenderer
+ EsPlayer "1" *-- "1" MsgHandler
+ EsPlayer "1" *-- "1" TrackRendererEventListener
+ EsPlayer ..> AudioStream
+ EsPlayer ..> VideoStream
+ EsPlayer ..> EsPacket
+ EsPlayer ..> Track
+ EsPlayer --- DecoderInputBuffer
+
+ AudioStream ..> Track
+ VideoStream ..> Track
+
+' TrackRenderer
+ TrackRenderer "1" o--- "1" Display
+ TrackRenderer *-- ResourceManager
+ TrackRenderer *-- Track
+ TrackRenderer ...> DecoderInputBuffer
+ TrackRenderer *- Pipeline
+
+ Pipeline --> GstObjectGuard
+
+ ResourceManager --> Resource
+ ResourceManager *-- Resource
+}
+
+@enduml
\ No newline at end of file
--- /dev/null
+## **CODING STYLE** ##
+ * All codes follow 'Google C++ Style Guide'
+ - [Korean](http://jongwook.kim/google-styleguide/trunk/cppguide.xml)
+ - [English](https://google.github.io/styleguide/cppguide.html)
+ * some major rules are below
+
+---
+
+### § Naming ###
+ Give as descriptive a name as possible.
+
+#### 1. Namespace/File/Variable/Struct Data member ####
+ * All lower case(mandatory) + between underscore(optional)
+ * e.g) mediaplayer.h , trackrenderer , track_renderer
+
+#### 2. Class Data Members
+ * Private attributes : Variable names + Trailing underscore("_")
+ * e.g) tablename_ , tracksource_ , track_source_
+ * Public attributes - Not Allowed.
+
+#### 3. Type Names - Class,Struct,Type Aliases,Enums and type template parameters ####
+ * Names start with a capital letter and have a capital letter for each new word
+ * No Underscore
+ * e.g) MMPlayer(X) , MmPlayer(O)
+ * e.g) class MediaPlayer {} ...
+ * e.g) struct PlayerContext {} , enum SourceType {...}
+
+#### 4. Macro Names ####
+ * All capitals + Underscore
+
+#### 5. Constant / Enumerator ####
+ * prefix 'k' + TypeNames
+ * e.g
+ const int kDaysInAWeek = 7;
+ enum UrlTableErrors {
+ kErrorOutOfMemory
+ }
+
+### § Formating ###
+
+#### 1. Indentation ####
+ * Default indentation : 2 spaces
+ * We use spaces for indentation. Do not use tabs in your code.
+
+#### 2. Class ####
+ * Ordering
+ * Sections in public - protected - private order
+ * method(public/protected/private) -> attribute order
+ * public, protected and private keywords : **1** space indentation
+
+### § Header Files ###
+
+#### 1. Order of Includes ####
+ * Related header, C library, C++ library, other libraries' .h, your project's .h.
+
+> plz refer to "Google C++ Style Guide" for the rest of detail guide.
--- /dev/null
+**GTest guide**
+===============
+ For unit test for plusplayer
+
+---
+### Reference
+- <https://github.com/google/googletest>
+- <http://stinkfist.egloos.com/2262578>
+
+## Assertion
+* ASSERT_* : 실패시 해당 테스트를 바로 종료 <br>
+* EXPECT_* : 실패하여도 테스트 계속 진행 <br>
+
+#### Basic Assertions ###
+
+These assertions do basic true/false condition testing.
+
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
+|:--------------------|:-----------------------|:-------------|
+| `ASSERT_TRUE(`_condition_`)`; | `EXPECT_TRUE(`_condition_`)`; | _condition_ is true |
+| `ASSERT_FALSE(`_condition_`)`; | `EXPECT_FALSE(`_condition_`)`; | _condition_ is false |
+
+#### Binary Comparison ###
+
+This section describes assertions that compare two values.
+
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
+|:--------------------|:-----------------------|:-------------|
+|`ASSERT_EQ(`_val1_`, `_val2_`);`|`EXPECT_EQ(`_val1_`, `_val2_`);`| _val1_ `==` _val2_ |
+|`ASSERT_NE(`_val1_`, `_val2_`);`|`EXPECT_NE(`_val1_`, `_val2_`);`| _val1_ `!=` _val2_ |
+|`ASSERT_LT(`_val1_`, `_val2_`);`|`EXPECT_LT(`_val1_`, `_val2_`);`| _val1_ `<` _val2_ |
+|`ASSERT_LE(`_val1_`, `_val2_`);`|`EXPECT_LE(`_val1_`, `_val2_`);`| _val1_ `<=` _val2_ |
+|`ASSERT_GT(`_val1_`, `_val2_`);`|`EXPECT_GT(`_val1_`, `_val2_`);`| _val1_ `>` _val2_ |
+|`ASSERT_GE(`_val1_`, `_val2_`);`|`EXPECT_GE(`_val1_`, `_val2_`);`| _val1_ `>=` _val2_ |
+
+
+#### String Comparison ###
+
+The assertions in this group compare two **C strings**.<br>
+If you want to compare two `string` objects, use `EXPECT_EQ`, `EXPECT_NE`, and etc instead.
+
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
+|:--------------------|:-----------------------|:-------------|
+| `ASSERT_STREQ(`_str1_`, `_str2_`);` | `EXPECT_STREQ(`_str1_`, `_str_2`);` | the two C strings have the same content |
+| `ASSERT_STRNE(`_str1_`, `_str2_`);` | `EXPECT_STRNE(`_str1_`, `_str2_`);` | the two C strings have different content |
+| `ASSERT_STRCASEEQ(`_str1_`, `_str2_`);`| `EXPECT_STRCASEEQ(`_str1_`, `_str2_`);` | the two C strings have the same content, ignoring case |
+| `ASSERT_STRCASENE(`_str1_`, `_str2_`);`| `EXPECT_STRCASENE(`_str1_`, `_str2_`);` | the two C strings have different content, ignoring case |
+
+## Text Fixtures : Using the Same Data Configuration for Multiple Tests ##
+
+사용자가 유사한 data를 사용해서 하나 이상의 test를 작성한다면, test fixture를 사용할 수 있다. 이 test fixture를 사용한다는 것은 여러개의 다양한 test를 작성하는 과정에서 같은 object의 configuration을 재사용한다는 것을 의미한다.
+
+Fixture를 작성할 때에는 아래의 내용대로 수행하면 된다.
+
+1. ::testing::Test 로부터 class를 derive한다. Sub-class 에서 fixture member에 접근해야 하기 때문에 protected 혹은 public 으로 작성해야 한다.
+2. Class 내부에서 사용자가 원하는대로 object들을 선언해 사용한다.
+3. 필요하다면, 생성자나 SetUp() function을 작성해둔다.
+4. 생성자나 SetUp() function을 정의해서 사용하고 있다면, 해당 function에서 사용했던 resource를 반환하기 위해 소멸자나 TearDown() function을 작성한다.
+5. Subroutine 들을 작성한다.
+
+Fixture를 사용하기 위해서는 TEST() 대신에 TEST_F()를 사용해야만 한다.
+TEST()에서는 첫번째 argument가 testcase의 이름이었지만 TEST_F()를 사용할 때는 첫번째 argument로 test fixture class의 이름을 사용해야만 한다.
+
+**Fixture class 기본 구현 Form**
+* 관례에 따라 테스트할 클래스가 Foo라면 이름을 FooTest라고 하는게 좋다.
+~~~
+class PlusPlayerTest : public ::testing::Test {
+public:
+ PlusPlayerTest(std::string url)
+ : plusplayer_(nullptr), url_(url)
+ {
+ }
+
+ void SetUp() override
+ {
+ plusplayer_ = new PlusPlayer();
+ create(url_);
+ }
+
+ void TearDown() override
+ {
+ destory(plusplayer_);
+ }
+
+private:
+ std::string url_;
+ PlusPlayer* plusplayer_;
+
+}
+~~~
+
+**실행 순서**
+1. 모든 구글 테스트 플래그 상태를 저장한다.
+2. 첫 번째 테스트에 대해 테스트 fixture 객체를 생성한다.
+3. 만든 객체를 SetUp()에서 초기화한다.
+4. 픽스처 객체에 대해 테스트를 실행한다.
+5. TearDown()에서 해당 픽스처를 정리한다.
+6. 해당 픽스처를 삭제한다.
+7. 모든 구글 테스트 플래그 상태를 복원한다.
+8. 모든 테스트를 마칠 때까지 다음 테스트에 대해 위 과정을 반복한다.
+
+---
+
+## Arguments
+reference <http://m.blog.naver.com/disturb000/130171100719> <br>
+
+1. test selection
+ * --gtest_list_tests <br>
+ > 테스트할 항목을 보여준다. (테스트 실시 X)
+ * --gtest_also_run_disabled_tests <br>
+ > DISABLED_ 로 막아둔 test case 를 일시적으로 실행
+ * --gtest_filter <br>
+ > 특정 case 들만 실행 가능<br>
+ Ex) --gtest_filter="*.create*" : 모든 TC중에서 create 로 시작하는 모든 TC 실행. <br>
+
+2. test Execution
+ * --gtest_repeat <br>
+ > test 반복 가능. -1일 경우 무한히 반복<br>
+ --gtest_break_on_failure와 함께 사용하면 실패할 경우 멈춤.
+ * --gtest_shuffle <br>
+ > 무작위로 실행 가능 (test case 간 dependency 가 없어야 하기 때문이다)<br>
+ gtest는 기본적으로 현재시간을 랜덤함수의 시드값으로 사용하나, --gtest_random_seed로 조절가능하다
+ * --gtest_random_seed
+ > 1 ~ 99999까지의 값을 --gtest_shuffle에서 사용할 랜덤함수의 시드로 사용.
+
+---
+## Reference
+ * Gtest Primer(KR)<br> <https://surpreem.com/%EC%9E%85%EB%AC%B8-%EA%B5%AC%EA%B8%80-c-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0/>
+ * Gtest Primer(EN)<br> <https://github.com/google/googletest/blob/master/googletest/docs/Primer.md>
+ * Unit Test Planning & How to write Unit Test Code(KR)<br> <http://168.219.243.246:8090/pages/viewpage.action?pageId=9112244>
+ * Unit Test Planning & How to write Unit Test Code(EN)<br> <http://168.219.243.246:8090/pages/viewpage.action?pageId=9113344>
--- /dev/null
+
+prefix = @PREFIX@
+exec_prefix = /usr
+libdir = @LIB_INSTALL_DIR@
+
+Name: @PC_NAME@
+Description: @PACKAGE_DESCRYPTION@
+Version: @VERSION@
+Libs: -L${libdir} @PC_LDFLAGS@
+Cflags : @PC_CFLAGS@
--- /dev/null
+/**
+ * @file attribute.h
+ * @interfacetype module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ *
+ * 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 __ESPLUSPLAYER_APPINFO_H__
+#define __ESPLUSPLAYER_APPINFO_H__
+
+#include <string>
+
+namespace esplusplayer {
+
+/**
+* @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.. */
+};
+
+/**
+* @brief Player app information.
+*/
+struct PlayerAppInfoEx {
+ std::string id; /**< App id */
+ std::string version; /**< App version */
+ std::string type; /**< App type. ex)"MSE", "HTML5", etc.. */
+ std::string runtitle; /**< App runtitle */
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_PLAYER_APPINFO_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file attribute.h
+ * @interfacetype module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ *
+ * 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 __ESPLUSPLAYER_ATTRIBUTE_H__
+#define __ESPLUSPLAYER_ATTRIBUTE_H__
+
+namespace esplusplayer {
+
+/**
+ * @brief Enumeration for plusplayer attribute
+ * If there is new attribute, please write details in below documents.
+ * http://wiki.vd.sec.samsung.net/display/plusplayer/TrackRenderer+Attribute
+ */
+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
+ kMax,
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_ATTRIBUTE_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file audioeasinginfo.h
+ * @interfacetype module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 3.0
+ * @SDK_Support N
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_AUDIOEASINGINFO_H__
+#define __ESPLUSPLAYER_AUDIOEASINGINFO_H__
+
+namespace esplusplayer {
+
+enum class AudioEasingType {
+ kAudioEasingLinear = 0,
+ kAudioEasingIncubic,
+ kAudioEasingOutcubic,
+ kAudioEasingNone
+};
+
+/**
+ * @brief audio easing information struct
+ */
+struct AudioEasingInfo {
+ uint32_t target_volume; /**< Audio easing target volume (0 ~ 100)*/
+ uint32_t duration; /**< Audio easing duration, in millisecond */
+ AudioEasingType type; /**< Audio easing type */
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_AUDIOEASINGINFO_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file
+ * @brief the decoded video packet for playback
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_DECODED_VIDEO_PACKET_EX_H__
+#define __ESPLUSPLAYER_DECODED_VIDEO_PACKET_EX_H__
+
+#include <boost/core/noncopyable.hpp>
+#include <memory>
+#include <utility>
+
+#include "tbm_surface.h"
+#include "tbm_type.h"
+
+namespace esplusplayer {
+
+class DecodedVideoPacketEx : private boost::noncopyable {
+ public:
+ using Ptr = std::unique_ptr<DecodedVideoPacketEx>;
+
+ static Ptr Create(const uint64_t pts = 0, const uint64_t duration = 0,
+ tbm_surface_h surface_data = nullptr,
+ const void* scaler_index = nullptr);
+
+ DecodedVideoPacketEx() = delete;
+
+ virtual ~DecodedVideoPacketEx();
+
+ uint64_t GetPts() const { return pts_; }
+ uint64_t GetDuration() const { return duration_; }
+ const tbm_surface_h GetTbmSurface() const { return surface_data_; }
+ const void* GetScalerIndex() const { return scaler_index_; }
+
+ protected:
+ explicit DecodedVideoPacketEx(const uint64_t pts, const uint64_t duration,
+ tbm_surface_h surface_data,
+ const void* scaler_index)
+ : pts_(pts),
+ duration_(duration),
+ surface_data_(surface_data),
+ scaler_index_(scaler_index) {}
+
+ private:
+ const uint64_t pts_ = 0;
+ const uint64_t duration_ = 0;
+ tbm_surface_h surface_data_ = nullptr; // tbm_surface
+ const void* scaler_index_ = nullptr;
+};
+
+using DecodedVideoPacketExPtr = DecodedVideoPacketEx::Ptr;
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_DECODED_VIDEO_PACKET_EX_H__
--- /dev/null
+/**
+* @file
+* @interfacetype module
+* @privlevel None-privilege
+* @privilege None
+* @product TV, AV, B2B
+* @version 1.0
+* @SDK_Support N
+*
+* 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 __ESPLUSPLAYER_DRM_H__
+#define __ESPLUSPLAYER_DRM_H__
+
+namespace esplusplayer {
+
+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 esplusplayer
+
+#endif // __ESPLUSPLAYER_DRM_H__
--- /dev/null
+/**
+ * @file elementary_stream.h
+ * @brief the contents information for elementary stream
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ * @remark You must add contents information of elementary streams for
+ * esplusplayer::EsPlusPlayer
+ * @see esplusplayer::EsPlusPlayer class
+ *
+ * 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_ELEMENTARY_STREAM_H_
+#define PLUSPLAYER_ELEMENTARY_STREAM_H_
+
+#include <boost/core/noncopyable.hpp>
+#include <memory>
+
+#include "esplusplayer/track.h"
+#include "esplusplayer/types/stream.h"
+
+namespace esplusplayer {
+
+/**
+ * @brief Enumerations for audio mime type
+ */
+enum class AudioMimeType {
+ kUnKnown,
+ kAAC,
+ kMP2,
+ kMP3,
+ kAC3,
+ kEAC3,
+ kVORBIS,
+ kOPUS,
+ kPCM_S16LE,
+ kPCM_S16BE,
+ kPCM_U16LE,
+ kPCM_U16BE,
+ kPCM_S24LE,
+ kPCM_S24BE,
+ kPCM_U24LE,
+ kPCM_U24BE,
+ kPCM_S32LE,
+ kPCM_S32BE,
+ kPCM_U32LE,
+ kPCM_U32BE,
+ kG711_MULAW,
+ kAC4,
+ kMpegH
+};
+/**
+ * @brief Enumerations for video mime type
+ */
+enum class VideoMimeType {
+ kUnKnown,
+ kH263,
+ kH264,
+ kHEVC,
+ kMPEG1,
+ kMPEG2,
+ kMPEG4,
+ kVP8,
+ kVP9,
+ kWMV3,
+ kAV1,
+ kMJPEG
+};
+
+/**
+ * @brief the interface of the contents information class for audio stream
+ * @remark You must add contents information of at least one audio or video
+ * stream
+ */
+class AudioStream : private boost::noncopyable {
+ public:
+ using Ptr = std::unique_ptr<AudioStream>;
+ /**
+ * @brief Create a audio stream object
+ * @remarks You must use this to get audio stream object
+ * @return audio stream object (unique_ptr<AudioStream>)
+ */
+ static Ptr Create() { return Ptr(new AudioStream); }
+
+ AudioStream() noexcept;
+
+ ~AudioStream() {}
+ /**
+ * @brief Set mime type for the associated audio stream
+ * @param [in] mimetype : the mime type of stream
+ * @return void
+ * @see AudioStream::GetMimeType()
+ */
+ void SetMimeType(AudioMimeType mimetype);
+ /**
+ * @brief Get mime type for the associated audio stream
+ * @return the mime type of stream (AudioMimeType)
+ * @see AudioStream::SetMimeType()
+ */
+ AudioMimeType GetMimeType() const { return mimetype_; }
+ /**
+ * @brief Set samplerate for the associated audio stream
+ * @param [in] sample_rate : the samplerate of stream
+ * @return void
+ * @see AudioStream::GetSamplerate()
+ */
+ void SetSamplerate(uint32_t sample_rate) { track_.sample_rate = sample_rate; }
+ /**
+ * @brief Get samplerate for the associated audio stream
+ * @return samplerate : the samplerate of stream
+ * @see AudioStream::SetSamplerate()
+ */
+ uint32_t GetSamplerate() const { return track_.sample_rate; }
+ /**
+ * @brief Set channels for the associated audio stream
+ * @param [in] channels : the channels of stream
+ * @return void
+ * @see AudioStream::GetChannels()
+ */
+ void SetChannels(uint32_t channels) { track_.channels = channels; }
+ /**
+ * @brief Get channels for the associated audio stream
+ * @return the channels of stream
+ * @see AudioStream::SetChannels()
+ */
+ uint32_t GetChannels() const { return track_.channels; }
+ /**
+ * @brief Set bitrate for the associated audio stream
+ * @param [in] bitrate : the bitrate of stream
+ * @return void
+ * @see AudioStream::GetBitrate()
+ */
+ void SetBitrate(uint32_t bitrate) { track_.bitrate = bitrate; }
+ /**
+ * @brief Get bitrate for the associated audio stream
+ * @return the bitrate of stream
+ * @see AudioStream::SetBitrate()
+ */
+ uint32_t GetBitrate() const { return track_.bitrate; }
+ /**
+ * @brief Set codec data for the associated audio stream
+ * @param [in] data : the codec data of stream
+ * @param [in] data_size : the size of codec data
+ * @return void
+ * @see AudioStream::GetCodecData(), AudioStream::GetCodecDataSize()
+ */
+ void SetCodecData(std::shared_ptr<char> data, uint32_t data_size) {
+ track_.codec_data = data;
+ track_.codec_data_len = data_size;
+ }
+ /**
+ * @brief Get codec data for the associated audio stream
+ * @return std::shared_ptr<char> of codec data
+ * @see AudioStream::SetCodecData(), AudioStream::GetCodecDataSize()
+ */
+ std::shared_ptr<char> GetCodecData() const { return track_.codec_data; }
+ /**
+ * @brief Get codec data for the associated audio stream
+ * @return the size of codec data
+ * @see AudioStream::SetCodecData(), AudioStream::GetCodecData()
+ */
+ uint32_t GetCodecDataSize() const { return track_.codec_data_len; }
+ /**
+ * @brief Get whether to use the sw decoder forcibly
+ * @return @c True if the sw decoder is use, otherwise @c False.
+ */
+ bool GetForceSwDecoderUse() { return force_swdecoder_use_; }
+
+ private:
+ void SetMimeType_(AudioMimeType mimetype);
+
+ Track GetTrack_() const { return track_; }
+
+ friend class EsPlayer;
+
+ private:
+ Track track_;
+ AudioMimeType mimetype_ = AudioMimeType::kUnKnown;
+ bool force_swdecoder_use_ = false;
+};
+
+using AudioStreamPtr = AudioStream::Ptr;
+
+/**
+ * @brief the interface of the contents information class for video stream
+ * @remark You must add contents information of at least one audio or video
+ * stream
+ */
+class VideoStream : private boost::noncopyable {
+ public:
+ using Ptr = std::unique_ptr<VideoStream>;
+ /**
+ * @brief Create a video stream object
+ * @remarks You must use this to get video stream object
+ * @return video stream object (unique_ptr<VideoStream>)
+ */
+ static Ptr Create() { return Ptr(new VideoStream); }
+
+ VideoStream() noexcept;
+
+ ~VideoStream() {}
+
+ /**
+ * @brief Set mime type for the associated video stream
+ * @param [in] mimetype : the mime type of stream
+ * @return void
+ * @see VideoStream::GetMimeType()
+ */
+ void SetMimeType(VideoMimeType mimetype);
+ /**
+ * @brief Get mime type for the associated video stream
+ * @return the mime type (VideoMimeType)
+ * @see VideoStream::SetMimeType()
+ */
+ VideoMimeType GetMimeType() const { return mimetype_; }
+ /**
+ * @brief Set width for the associated video stream
+ * @param [in] width : the width of stream
+ * @return void
+ * @see VideoStream::GetWidth()
+ */
+ void SetWidth(uint32_t width) { track_.width = width; }
+ /**
+ * @brief Get width for the associated video stream
+ * @return the width of stream
+ * @see VideoStream::SetWidth()
+ */
+ uint32_t GetWidth() const { return track_.width; }
+ /**
+ * @brief Set height for the associated video stream
+ * @param [in] height : the height of stream
+ * @return void
+ * @see VideoStream::GetHeight()
+ */
+ void SetHeight(uint32_t height) { track_.height = height; }
+ /**
+ * @brief Get height for the associated video stream
+ * @return the height of stream
+ * @see VideoStream::SetHeight()
+ */
+ uint32_t GetHeight() const { return track_.height; }
+ /**
+ * @brief Set maximum width for the associated video stream
+ * @param [in] maxwidth : the maximum width of stream
+ * @return void
+ * @see VideoStream::GetMaxWidth()
+ */
+ void SetMaxWidth(uint32_t maxwidth) { track_.maxwidth = maxwidth; }
+ /**
+ * @brief Get maximum width for the associated video stream
+ * @return the maximum width of stream
+ * @see VideoStream::SetMaxWidth()
+ */
+ uint32_t GetMaxWidth() const { return track_.maxwidth; }
+ /**
+ * @brief Set maximum height for the associated video stream
+ * @param [in] maxheight : the maximum height of stream
+ * @return void
+ * @see VideoStream::GetMaxHeight()
+ */
+ void SetMaxHeight(uint32_t maxheight) { track_.maxheight = maxheight; }
+ /**
+ * @brief Get maximum height for the associated video stream
+ * @return the maximum height of stream
+ * @see VideoStream::SetMaxHeight()
+ */
+ uint32_t GetMaxHeight() const { return track_.maxheight; }
+ /**
+ * @brief Set framerate for the associated video stream
+ * @param [in] num : the numerator of framerate
+ * @param [in] den : the denominator of framerate
+ * @return void
+ * @see VideoStream::GetFramerate()
+ */
+ void SetFramerate(uint32_t num, uint32_t den) {
+ track_.framerate_num = num;
+ track_.framerate_den = den;
+ }
+ /**
+ * @brief Get framerate for the associated video stream
+ * @param [out] num : the numerator of framerate
+ * @param [out] den : the denominator of framerate
+ * @return void
+ * @see VideoStream::SetFramerate()
+ */
+ void GetFramerate(uint32_t* num, uint32_t* den) {
+ *num = track_.framerate_num;
+ *den = track_.framerate_den;
+ }
+ /**
+ * @brief Set codec data for the associated video stream
+ * @param [in] data : the codec data of stream
+ * @param [in] data_size : the size of codec data
+ * @return void
+ * @see VideoStream::GetCodecData(), VideoStream::GetCodecDataSize()
+ */
+ void SetCodecData(std::shared_ptr<char> data, uint32_t data_size) {
+ track_.codec_data = data;
+ track_.codec_data_len = data_size;
+ }
+ /**
+ * @brief Get codec data for the associated video stream
+ * @return std::shared_ptr<char> of codec data
+ * @see VideoStream::SetCodecData(), VideoStream::GetCodecDataSize()
+ */
+ std::shared_ptr<char> GetCodecData() const { return track_.codec_data; }
+ /**
+ * @brief Get codec data for the associated video stream
+ * @return the size of codec data
+ * @see VideoStream::SetCodecData(), VideoStream::GetCodecData()
+ */
+ uint32_t GetCodecDataSize() const { return track_.codec_data_len; }
+
+ private:
+ void SetMimeType_(VideoMimeType mimetype);
+ Track GetTrack_() const { return track_; }
+
+ friend class EsPlayer;
+
+ private:
+ Track track_;
+ VideoMimeType mimetype_ = VideoMimeType::kUnKnown;
+};
+
+using VideoStreamPtr = VideoStream::Ptr;
+
+} // namespace esplusplayer
+
+#endif // PLUSPLAYER_ELEMENTARY_STREAM_H_
--- /dev/null
+/**
+ * @file es_eventlistener.h
+ * @brief the event listener for esplusplayer::EsPlusPlayer
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ * @see esplusplayer::EsPlusPlayer class
+ *
+ * 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 __ESPLUSPLAYER_ES_EVENTLISTENER__H__
+#define __ESPLUSPLAYER_ES_EVENTLISTENER__H__
+
+#include "esplusplayer/drm.h"
+#include "esplusplayer/types/buffer.h"
+#include "esplusplayer/types/error.h"
+#include "esplusplayer/types/event.h"
+#include "esplusplayer/types/latency.h"
+#include "esplusplayer/types/stream.h"
+
+namespace esplusplayer {
+
+/**
+ * @brief the interface of es player EventListener class
+ * @remark You must implement concrete class and register it to get es player
+ * events.
+ * @see EsPlusPlayer::RegisterListener()
+ */
+class EsEventListener {
+ public:
+ EsEventListener() noexcept {}
+ using UserData = void*;
+ virtual ~EsEventListener() {}
+// LCOV_EXCL_START
+ /**
+ * @brief It will be invoked when error has happened
+ * @param [in] error_code : #ErrorType
+ * @param [in] userdata : user data
+ * @see #ErrorType
+ * @see OnErrorMsg() if a detailed error message is required
+ */
+ virtual void OnError(const ErrorType& error_code, UserData userdata) {}
+ /**
+ * @brief It will be invoked when error has happened
+ * @param [in] error_code : #ErrorType
+ * @param [in] error_msg : detailed error message including info related to
+ * codec,demuxer,network status, etc.
+ * @see #ErrorType
+ * @see OnError() if only simple error code is required
+ */
+ virtual void OnErrorMsg(const ErrorType& error_code, const char* error_msg,
+ UserData userdata) {}
+ /**
+ * @brief It will be invoked when buffer overrun or underrun is detected
+ * @param [in] type : the type of target stream
+ * @param [in] status : current buffer status
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ */
+ virtual void OnBufferStatus(const StreamType& type,
+ const BufferStatus& status,
+ const uint64_t byte_size,
+ const uint64_t time_size, UserData userdata) {}
+ /**
+ * @brief It will be invoked when H/W resource is conflicted
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ * @remarks Player state will be changed to #EsState::kPaused
+ */
+ virtual void OnResourceConflicted(UserData userdata) {}
+ /**
+ * @brief It will be invoked when player has reached the end of the stream
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ */
+ virtual void OnEos(UserData userdata) {}
+ /**
+ * @brief It will be invoked when player is prepared to be started
+ * @details This will be invoked when user calls
+ * EsPlusPlayer::PrepareAsync()
+ * @param [in] ret : statue of prepare (@c true = success, @c false =
+ * fail)
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ * @see EsPlusPlayer::PrepareAsync()
+ */
+ virtual void OnPrepareDone(bool ret, UserData userdata) {}
+ /**
+ * @brief It will be invoked when ready to receive es packets after
+ * prepare
+ * @details This will be invoked when user calls
+ * EsPlusPlayer::PrepareAsync()
+ * @param [in] type : the stream type
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ * @see EsPlusPlayer::PrepareAsync() \n
+ * @see EsPlusPlayer::SubmitPacket()
+ */
+ virtual void OnReadyToPrepare(const StreamType& type, UserData userdata) {}
+ /**
+ * @brief It will be invoked when the seek operation is completed
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ * @remarks OnSeekDone() will be called once seek operation is finished
+ * @see EsPlusPlayer::Seek()
+ */
+ virtual void OnSeekDone(UserData userdata) {}
+ /**
+ * @brief It will be invoked when ready to receive es packets after seek
+ * @details This will be invoked when user calls EsPlusPlayer::Seek()
+ * @param [in] type : the stream type
+ * @param [in] offset : the new position to seek in milliseconds
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ * @see EsPlusPlayer::Seek() \n
+ * @see EsPlusPlayer::SubmitPacket()
+ */
+ virtual void OnReadyToSeek(const StreamType& type, const uint64_t offset,
+ UserData userdata) {}
+
+ /**
+ * @brief Set a callback function to be invoked when trackrender side need
+ * to get an useful tbm surface.
+ * @param [in] ptr : pointer which set to get tbm address.
+ * @param [in] is_scale_change : parameter which indicate whether the
+ * scale resolution changed.
+ * @remark SetVideoFrameBufferType()
+ */
+ virtual void OnMediaPacketGetTbmBufPtr(void** ptr, bool is_scale_change) {}
+
+ /**
+ * @brief Set a callback function to be invoked when player decoded video
+ * frame. A video frame can be retrieved
+ * @param [in] packet : decoded video packet
+ * @param [in] userdata : the user data passed from the event listener
+ * @remark SetVideoFrameBufferType()
+ */
+ virtual void OnMediaPacketVideoDecoded(const DecodedVideoPacket& packet) {}
+
+ /**
+ * @brief It will be invoked when player gets closed caption data from
+ * decoder
+ * @param [in] data : closed caption data
+ * @param [in] size : size of closed caption data
+ */
+ virtual void OnClosedCaptionData(std::unique_ptr<char[]> data, const int size,
+ UserData userdata) {}
+
+ /**
+ * @brief It will be invoked when the flush operation is completed
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ * @remarks OnFlushDone() will be called once flush operation is finished
+ * @see EsPlusPlayer::Flush()
+ */
+ virtual void OnFlushDone(UserData userdata) {}
+
+ virtual void OnEvent(const EventType& event_type, const EventMsg& msg_data,
+ UserData userdata) {}
+
+ virtual void OnFirstDecodingDone(UserData userdata) {}
+
+ /**
+ * @brief It will be invoked when buffer underrun is detected from a video
+ * decoder.
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ */
+ virtual void OnVideoDecoderUnderrun(UserData userdata) {}
+
+ /**
+ * @brief It will be invoked when the latency status of the video stream
+ * changes.
+ * @param [in] latency_status : the latency status
+ * [in] userdata : the user data passed from the event listener
+ * registration function
+ */
+ virtual void OnVideoLatencyStatus(const LatencyStatus& latency_status,
+ UserData userdata) {}
+
+ /**
+ * @brief It will be invoked when the latency status of the audio stream
+ * changes.
+ * @param [in] latency_status : the latency status
+ * [in] userdata : the user data passed from the event listener
+ * registration function
+ */
+ virtual void OnAudioLatencyStatus(const LatencyStatus& latency_status,
+ UserData userdata) {}
+ /**
+ * @brief It will be invoked when video high latency occurs.
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ */
+ virtual void OnVideoHighLatency(UserData userdata) {}
+
+ /**
+ * @brief It will be invoked when audio high latency occurs.
+ * @param [in] userdata : the user data passed from the event listener
+ * registration function
+ */
+ virtual void OnAudioHighLatency(UserData userdata) {}
+
+ /**
+ * @brief It will be invoked when the frame dropped in SoC
+ * changes.
+ * @param [in] count : the frame dropped count
+ * [in] userdata : the user data passed from the event listener
+ * registration function
+ */
+ virtual void OnVideoFrameDropped(const uint64_t& count,
+ UserData userdata) {}
+
+ /**
+ * @brief Set a callback function to be invoked when input buffer is received in decoder
+ * @param [in] type : the stream type
+ * @param [in] time : buffer time info(pts and system time)
+ * @param [in] userdata : the user data passed from the event listener
+ */
+ virtual void OnDecoderInputBufferTime(const StreamType& type, const DecoderBufferTime& time) {}
+
+ /**
+ * @brief Set a callback function to be invoked when get one output buffer from decoder
+ * @param [in] type : the stream type
+ * @param [in] time : buffer time info(pts and system time)
+ * @param [in] userdata : the user data passed from the event listener
+ */
+ virtual void OnDecoderOutputBufferTime(const StreamType& type, const DecoderBufferTime& time) {}
+};
+// LCOV_EXCL_STOP
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_ES_EVENTLISTENER__H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file espacket.h
+ * @brief the packet for elementary stream
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ * @see esplusplayer::EsPlusPlayer class
+ *
+ * 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 __ESPLUSPLAYER_ESPACKET_H__
+#define __ESPLUSPLAYER_ESPACKET_H__
+
+#include <boost/core/noncopyable.hpp>
+#include <memory>
+
+#include "esplusplayer/types/stream.h"
+
+namespace esplusplayer {
+
+/**
+ * @brief Structure of matroska meta data
+ */
+struct MatroskaMetaData {
+ double primary_r_chromaticity_x;
+ double primary_r_chromaticity_y;
+ double primary_g_chromaticity_x;
+ double primary_g_chromaticity_y;
+ double primary_b_chromaticity_x;
+ double primary_b_chromaticity_y;
+ double white_point_chromaticity_x;
+ double white_point_chromaticity_y;
+ double luminance_max;
+ double luminance_min;
+};
+/**
+ * @brief Structure of matroska color information
+ */
+struct MatroskaColor {
+ uint32_t matrix_coefficients;
+ uint32_t bits_per_channel;
+ uint32_t chroma_subsampling_horizontal;
+ uint32_t chroma_subsampling_vertical;
+ uint32_t cb_subsampling_horizontal;
+ uint32_t cb_subsampling_vertical;
+ uint32_t chroma_siting_horizontal;
+ uint32_t chroma_siting_vertical;
+ uint32_t range;
+ uint32_t transfer_characteristics;
+ uint32_t primaries;
+ uint32_t max_cll;
+ uint32_t max_fall;
+ MatroskaMetaData metadata;
+ uint32_t is_hdr_10p;
+};
+
+/**
+ * @brief the interface of the packet class for elementary stream
+ * @see esplusplayer::EsPlusPlayer class
+ */
+class EsPacket : private boost::noncopyable {
+ public:
+ using Ptr = std::unique_ptr<EsPacket>;
+ /**
+ * @brief Create a espacket object
+ * @remarks You must use this to get espacket object
+ * @return espacket object (unique_ptr<EsPacket>)
+ */
+ static Ptr Create(const StreamType type = StreamType::kMax,
+ std::shared_ptr<char> buffer = nullptr,
+ const uint32_t buffer_size = 0, const uint64_t pts = 0,
+ const uint64_t duration = 0, const uint32_t hdr10p_size = 0,
+ std::shared_ptr<char> hdr10p_data = nullptr);
+ /**
+ * @brief Create an eos espacket object
+ * @remarks You must use this to get espacket object
+ * @return espacket object (unique_ptr<EsPacket>)
+ */
+ static Ptr CreateEos(const StreamType type = StreamType::kMax);
+ EsPacket() = delete;
+
+ ~EsPacket() {}
+ /**
+ * @brief Get the stream type for the associated packet
+ * @return the stream type to set (StreamType)
+ */
+ StreamType GetType() const { return type_; }
+ /**
+ * @brief Get the size of the buffer for the associated packet
+ * @return the size of the buffer to set (StreamType)
+ */
+ uint32_t GetSize() const { return buffer_size_; }
+ /**
+ * @brief Get the size of the hdr10+ metadata size for the associated
+ * packet
+ * @return the size of the hdr10+ metadata to set (StreamType)
+ */
+ uint32_t GetHdr10pSize() const { return hdr10p_metadata_size_; }
+ /**
+ * @brief Get the pts of buffer for the associated packet
+ * @return the pts to set
+ */
+ uint64_t GetPts() const { return pts_; }
+ /**
+ * @brief Get the pts of buffer for the associated packet
+ * @return the duration to set
+ */
+ uint64_t GetDuration() const { return duration_; }
+ /**
+ * @brief Get the buffer for the associated packet
+ * @return the buffer to set
+ */
+ std::shared_ptr<char> GetBuffer() const { return buffer_; }
+ /**
+ * @brief Get the Hdr10p Metadata for the associated packet
+ * @return the Hdr10p Metadata to set
+ */
+ std::shared_ptr<char> GetHdr10pData() const { return hdr10p_metadata_; }
+ /**
+ * @brief Set the matroska color information for the associated packet
+ * @return @c False if the streamtype of this EsPacket isn't
+ * StreamType::kVideo, otherwise @c True
+ * @see MatroskaColor
+ */
+ bool SetMatroskaColorInfo(const MatroskaColor& color_info) {
+ if (type_ != StreamType::kVideo) return false;
+ matroska_color_info_.reset(new MatroskaColor(color_info));
+ return true;
+ }
+ /**
+ * @brief Get the matroska color information for the associated packet
+ * @return the matroska color information to set
+ * @see MatroskaColor
+ */
+ const MatroskaColor& GetMatroskaColorInfo() const {
+ return *matroska_color_info_.get();
+ }
+ /**
+ * @brief Inform whether the matroska color information for the associated
+ * packet is set
+ * @return @c True if matroska color information is set, otherwise @c False
+ * @see SetMatroskaColorInfo(), GetMatroskaColorInfo()
+ */
+ bool HasMatroskaColorInfo() const { return matroska_color_info_ != nullptr; }
+ /**
+ * @brief Inform whether this EsPacket is for EndOfStream(EOS)
+ * @return @c True if this EsPacket is EndOfStream(EOS) Packet, otherwise
+ * @c False
+ * @see CreateEos()
+ */
+ bool IsEosPacket() const { return buffer_size_ == 0 && buffer_ == nullptr; }
+
+ private:
+ explicit EsPacket(const StreamType type, std::shared_ptr<char> buffer,
+ const uint32_t buffer_size, const uint64_t pts,
+ const uint64_t duration, const uint32_t hdr10p_size,
+ std::shared_ptr<char> hdr10p_data);
+
+ private:
+ const StreamType type_ = StreamType::kMax;
+ std::shared_ptr<char> buffer_ = nullptr;
+ const uint32_t buffer_size_ = 0;
+ const uint64_t pts_ = 0;
+ const uint64_t duration_ = 0;
+ const uint32_t hdr10p_metadata_size_ = 0;
+ std::shared_ptr<char> hdr10p_metadata_ = nullptr;
+ std::unique_ptr<MatroskaColor> matroska_color_info_ = nullptr;
+};
+
+using EsPacketPtr = EsPacket::Ptr;
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_ESPACKET_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file esplusplayer.h
+ * @brief the playback for elementary stream
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * 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 __ESPLUSPLAYER_ESPLUSPLAYER__H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER__H__
+
+#include <boost/core/noncopyable.hpp>
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#ifndef IS_AUDIO_PRODUCT
+#include "mixer/mixer.h"
+#endif
+
+#include "esplusplayer/appinfo.h"
+#include "esplusplayer/audioeasinginfo.h"
+#include "esplusplayer/drm.h"
+#include "esplusplayer/elementary_stream.h"
+#include "esplusplayer/es_eventlistener.h"
+#include "esplusplayer/espacket.h"
+#include "esplusplayer/external_drm.h"
+#include "esplusplayer/types/buffer.h"
+#include "esplusplayer/types/display.h"
+#include "esplusplayer/types/error.h"
+#include "esplusplayer/types/latency.h"
+#include "esplusplayer/types/picturequality.h"
+#include "esplusplayer/types/resource.h"
+#include "esplusplayer/types/submitdata.h"
+
+namespace esplusplayer {
+
+/**
+ * @brief Enumerations for es player state.
+ */
+enum class EsState {
+ kNone, // Player is created, but not opened
+ kIdle, // Player is opened, but not prepared or player is stopped
+ kReady, // Player is ready to play(start)
+ kPlaying, // Player is playing media
+ kPaused, // Player is paused while playing media
+};
+/**
+ * @brief Enumerations for espacket submit status.
+ */
+enum class PacketSubmitStatus {
+ kNotPrepared, // not prepared to get packet
+ kInvalidPacket, // invalid packet
+ kOutOfMemory, // out of memory on device
+ kFull, // buffer already full
+ kSuccess // submit succeeded
+};
+/**
+ * @brief Enumerations for adaptive info type.
+ */
+enum class PlayerAdaptiveInfo {
+ kMinType,
+ kVideoDroppedFrames,
+ kDroppedVideoFramesForCatchup,
+ kDroppedAudioFramesForCatchup,
+ kMaxType,
+};
+/**
+ * @brief Enumerations for low latency mode
+ */
+enum PlayerLowLatencyMode {
+ kLowLatencyModeNone = 0x0000,
+ /**
+ * @description to support audio fast decoding/rendering
+ */
+ kLowLatencyModeAudio = 0x0001,
+ /**
+ * @description to support video fast decoding/rendering
+ * Video stream should be composed only of P and I frames.
+ * The mode support seamless resolution change since tizen 6.5
+ */
+ kLowLatencyModeVideo = 0x0010,
+ /**
+ * @description to support video fast decoding/rendering and video
+ distortion concealment.
+ Video stream should be composed only of P and I frames.
+ For applications using the UDP protocol, packet loss can
+ occur. when video distortion by video packet loss is
+ detected, it is a function to conceal distortion by showing
+ previous vido frame. It is supported only in h.264 codec &
+ FHD or lower resolution.
+ */
+ kLowLatencyModeVideoDistortionConcealment = kLowLatencyModeVideo | 0x0020,
+ /**
+ * @description to disable clock sync and a/v sync when rendering. it
+ * includes #kLowLatencyModeDisablePreroll.
+ */
+ kLowLatencyModeDisableAVSync = 0x0100,
+ /**
+ * @deprecated Deprecated since tizen 6.5
+ * @description to disable preroll which means player doesn't wait for
+ * first buffer when state is changed to
+ * #EsState::kReady from #EsState::kIdle.
+ * It changes the state immediately.
+ * It's usually used for sparse stream. (e.g. video packet
+ * arrives but audio packet does't yet.)
+ */
+ kLowLatencyModeDisablePreroll = 0x0200,
+ /**
+ * @deprecated Deprecated since tizen 6.5
+ * @description to set lower video quality
+ */
+ kLowLatencyModeDisableVideoQuality = 0x1000,
+ /**
+ * @description to set game mode for latency
+ * Video stream should be composed only of P and I frames.
+ * It must use this value with kLowLatencyModeVideo.
+ * It must not be used together with
+ * kLowLatencyModeDisableVideoQuality.
+ * If use this value, It can expect better latency performance
+ * than kLowLatencyModeDisableVideoQuality, it can use enhanced
+ * game_mode.
+ */
+ kLowLatencyModeEnableGameMode =
+ kLowLatencyModeAudio | kLowLatencyModeVideo | 0x2000,
+ /**
+ * @description to set game mode for latency
+ * Video stream should be composed only of P and I frames.
+ * Video stream must use fixed resolution.
+ * It must not be used together with
+ * #ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY.
+ * If use this value, It can expect better latency
+ * performance than
+ * #ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY and
+ * #ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE
+ * The mode use lower video quality.
+ */
+ kLowLatencyModeGameModeWithFixedResolution =
+ kLowLatencyModeEnableGameMode | 0x4000
+};
+
+/**
+ * @brief Enumerations for player audio codec type
+ */
+enum PlayerAudioCodecType {
+ /**
+ * @description hardware codec can only be selected, default type
+ */
+ kPlayerAudioCodecTypeHW,
+ /**
+ * @description software codec can only be selected.
+ */
+ kPlayerAudioCodecTypeSW,
+};
+
+/**
+ * @brief Enumerations for player video codec type
+ */
+enum PlayerVideoCodecType {
+ /**
+ * @description hardware codec can only be selected, default type
+ */
+ kPlayerVideoCodecTypeHW,
+ /**
+ * @description software codec can only be selected.
+ */
+ kPlayerVideoCodecTypeSW,
+ /**
+ * @description hardware codec using n-decoding mode can only be selected.
+ It must set display type to mixer type by display setting
+ api.
+ */
+ kPlayerVideoCodecTypeHWNdecoding,
+};
+
+/**
+ * @brief Enumerations for player video scan type
+ */
+enum PlayerVideoScanType {
+ /**
+ * @description progressive, mfd or dvde will be allocated for H.264 2K in normal mode.
+ */
+ kPlayerVideoScanTypeProgressive,
+ /**
+ * @description interlaced, only mfd has been allocated for H.264 2K in normal mode.
+ */
+ kPlayerVideoScanTypeInterlaced,
+};
+
+/**
+ * @brief Enumerations for the time unit
+ */
+enum PlayerTimeUnitType {
+ /**
+ * @description the timeunit will be ms. It is default value.
+ */
+ kPlayerTimeUnitTypeMs,
+ /**
+ * @description the timeunit will be us.
+ */
+ kPlayerTimeUnitTypeUs,
+};
+
+/**
+ * @brief Enumerations for buffer level of simple mix out
+ */
+enum PlayerSimpleMixOutBufferLevel {
+ /**
+ * @description buffer level is low
+ */
+ kPlayerSimpleMixOutBufferLow,
+ /**
+ * @description buffer level is middle, default type
+ */
+ kPlayerSimpleMixOutBufferMid,
+ /**
+ * @description buffer level is high
+ */
+ kPlayerSimpleMixOutBufferHigh,
+};
+
+/**
+ * @brief the interface of the playback class for elementary stream
+ */
+class EsPlusPlayer : private boost::noncopyable {
+ public:
+ using Ptr = std::unique_ptr<EsPlusPlayer>;
+ /**
+ * @brief Create a esplusplayer object
+ * @remarks You must use this to get esplusplayer object
+ * @return Player object (unique_ptr<EsPlusPlayer>)
+ */
+ static Ptr Create();
+
+ public:
+ virtual ~EsPlusPlayer() {}
+ /**
+ * @brief Make player get ready to set playback mode
+ * @remarks General call sequence to start playback:
+ * Open() -> SetStream() -> PrepareAsync() -> Start() -> Stop() ->
+ * Close()
+ * @pre Player state must be kNone
+ * @post The player state will be EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see Close()
+ */
+// LCOV_EXCL_START
+ virtual bool Open() { return false; }
+ /**
+ * @brief Release all the player resources
+ * @pre The player state must be one of
+ * EsState::kIdle or EsState::kNone
+ * @post The player state will be kNone
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::Open()
+ */
+ virtual bool Close() { return false; }
+
+ /**
+ * @brief Flush the specific buffered stream data and release TV resource
+ * to change stream.
+ * @remark To activate, the stream must be set again.
+ * @pre The player must be set to at least #EsState::kReady
+ * @post The player state is same as before calling Deactivate().
+ * @return @c True on success, otherwise @c False.
+ * @see EsPlusPlayer::Deactivate().
+ */
+ virtual bool Deactivate(const StreamType type) { return false; }
+
+ /**
+ * @brief Reprepare for the specific stream playback.
+ * @remark There must be active stream to prepare playback.
+ * @pre The player must be set to at least #EsState::kReady
+ * The StreamType must be deactivated in advance.
+ * Stream should be set in advance.
+ * @return @c True on success, otherwise @c False
+ * @code
+ PrepareAsync();
+ Deactivate(StreamType::kVideo);
+ VideoStreamPtr video_stream = VideoStream::Create();
+ video_stream->SetMimeType(VideoMimeType::kH264);
+ video_stream->SetWidth(640);
+ video_stream->SetHeight(352);
+ video_stream->SetFramerate(30, 1);
+ SetStream(video_stream);
+ Activate(StreamType::kVideo);
+ * @endcode
+ * @see EsPlusPlayer::Activate()
+ */
+ virtual bool Activate(const StreamType type) { return false; }
+
+ /**
+ * @brief Prepare the player for playback, asynchronously.
+ * @remarks EsEventListener::OnPrepareDone() will be called when prepare is
+ * finished
+ *
+ * @pre The player state must be set to #EsState::kIdle
+ * @post The player state will be #EsState::kReady
+ * @return @c true if async task is correctly started
+ * @see EsPlusPlayer::Open() \n
+ * EsPlusPlayer::Stop() \n
+ * EsPlusPlayer::SubmitPacket()
+ */
+ virtual bool PrepareAsync() { return false; }
+ /**
+ * @brief Start playback
+ * @pre The player state should be #EsState::kReady
+ * @post The player state will be #EsState::kPlaying
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::Open() \n
+ * EsPlusPlayer::PrepareAsync() \n
+ * EsPlusPlayer::Stop() \n
+ * EsPlusPlayer::Close()
+ */
+ virtual bool Start() { return false; }
+ /**
+ * @brief Stop playing media content
+ * @remarks EsPlusPlayer::Close() must be called once after player is
+ * stopped
+ * @pre The player state must be all of #EsState except #EsState::kNone
+ * @post The player state will be #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::Open() \n
+ * EsPlusPlayer::PrepareAsync() \n
+ * EsPlusPlayer::Start() \n
+ * EsPlusPlayer::Pause() \n
+ * EsPlusPlayer::Resume() \n
+ * EsPlusPlayer::Close()
+ */
+ virtual bool Stop() { return false; }
+ /**
+ * @brief Pause playing media content
+ * @remarks You can resume playback by using EsPlusPlayer::Resume()
+ * @pre The player state must be one of #EsState::kReady or
+ * #EsState::kPlaying or #EsState::kPaused
+ * @post The player state will be #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::Start() \n
+ * EsPlusPlayer::Resume()
+ */
+ virtual bool Pause() { return false; }
+ /**
+ * @brief Resume a media content
+ * @pre The player state must be one of #EsState::kPlaying or
+ * #EsState::kPaused
+ * @post The player state will be #EsState::kPlaying
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::Start() \n
+ * EsPlusPlayer::Pause()
+ */
+ virtual bool Resume() { return false; }
+ /**
+ * @brief SetAppInfo
+ * @remarks Set app_id to resource manager. Resource manager check the
+ * priority to control resource.
+ * @param [in] app_info : application id, version, type
+ * @pre The player state must be set to #EsState::kIdle
+ * @return None
+ */
+ virtual void SetAppInfo(const PlayerAppInfo& app_info) { return; }
+ /**
+ * @brief SetAppInfoEx
+ * @remarks Set app_id to resource manager. Resource manager check the
+ * priority to control resource.
+ * @param [in] app_info : application id, version, type, runtitle
+ * @pre The player state must be set to #EsState::kIdle
+ * @return None
+ */
+ virtual void SetAppInfoEx(const PlayerAppInfoEx& app_info) { return; }
+ /**
+ * @brief SetPlaybackRate.
+ * @remarks Set playback rate from 0.0 to 2.0.
+ * @param [in] rate : The playback rate from 0.0 to 2.0. EsPlayer isn't
+ * support trick play.
+ * @param [in] audio_mute : The audio is mute on/off,true: mute on, false:
+ * mute off.
+ * @pre The source and feeder have to push the data as fast as playback
+ * rate.
+ * @return @c True if set playback rate is finished without any problem
+ * otherwise @c False
+ */
+ virtual bool SetPlaybackRate(const double rate, bool audio_mute) {
+ return false;
+ }
+ /**
+ * @brief Seek for playback, asynchronously.
+ * @remarks EsEventListener::OnSeekDone() will be called if seek operation
+ * is finished \n Seek result can be succeeded or not at this moment. \n
+ * @param [in] time : the absolute position(playingtime) of
+ * the stream default in milliseconds, according to SetTimeUnitType.
+ * @pre The player state must be one of #EsState::kReady,
+ * #EsState::kPlaying or #EsState::kPaused
+ * @return @c True if seek operation is started without any problem
+ * otherwise @c False
+ * @see EsEventListener::OnSeekDone() \n
+ * EsPlusPlayer::SubmitPacket()
+ */
+ virtual bool Seek(const uint64_t time) { return false; }
+ /**
+ * @brief Set the video display
+ * @remarks We are not supporting changing display.
+ * @remarks This API have to be called before calling the
+ * EsPlusPlayer::PrepareAsync() to reflect the display type.
+ * @param [in] type : display type
+ * @param [in] obj : The handle to display window
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see DisplayType \n
+ * EsPlusPlayer::SetDisplayMode() \n
+ * EsPlusPlayer::SetDisplayRoi() \n
+ * EsPlusPlayer::SetDisplayVisible()
+ */
+ virtual bool SetDisplay(const DisplayType& type, void* obj) { return false; }
+#ifndef IS_AUDIO_PRODUCT
+ /**
+ * @brief Set the video display
+ * @remarks We are not supporting changing display.
+ * @remarks This API have to be called before calling the
+ * PlusPlayer::Prepare()
+ * or PlusPlayer::PrepareAsync() to reflect the display type.
+ * @param [in] type : display type
+ * @param [in] handle : The handle of mixer ticket
+ * @pre The player state must be set to #EsState::kIdle
+ * @post None
+ * @return @c True on success, otherwise @c False
+ * @see DisplayType
+ * PlusPlayer::SetDisplayRoi()
+ * PlusPlayer::SetDisplayVisible()
+ * @exception None
+ */
+ virtual bool SetDisplay(const DisplayType& type, MixerTicket* handle) {
+ return false;
+ }
+#endif
+ /**
+ * @brief Set the video display
+ * @remarks We are not supporting changing display.
+ * @remarks This API have to be called before calling the
+ * EsPlusPlayer::PrepareAsync() to reflect the display type.
+ * @param [in] type : display type
+ * @param [in] ecore_wl2_window : The ecore wayland window handle
+ * @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
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see DisplayType \n
+ * EsPlusPlayer::SetDisplayMode() \n
+ * EsPlusPlayer::SetDisplayRoi() \n
+ * EsPlusPlayer::SetDisplayVisible()
+ */
+ virtual bool SetDisplay(const DisplayType& type, void* ecore_wl2_window,
+ const int x, const int y, const int w, const int h) {
+ return false;
+ }
+ /**
+ * @brief Set the video display
+ * @remarks We are not supporting changing display.
+ * @remarks This API have to be called before calling the
+ * EsPlusPlayer::PrepareAsync() to reflect the display type.
+ * @param [in] type : display type
+ * @param [in] ecore_wl2_subsurface : The ecore wayland subsurface handle
+ * @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
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see DisplayType \n
+ * EsPlusPlayer::SetDisplayMode() \n
+ * EsPlusPlayer::SetDisplayRoi() \n
+ * EsPlusPlayer::SetDisplayVisible()
+ */
+ virtual bool SetDisplaySubsurface(const DisplayType& type,
+ void* ecore_wl2_subsurface, const int x,
+ const int y, const int w, const int h) {
+ return false;
+ }
+ /**
+ * @brief Set the video display
+ * @remarks We are not supporting changing display.
+ * @remarks This API have to be called before calling the
+ * EsPlusPlayer::PrepareAsync() to reflect the display type.
+ * @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
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see DisplayType \n
+ * EsPlusPlayer::SetDisplayMode() \n
+ * EsPlusPlayer::SetDisplayRoi() \n
+ * EsPlusPlayer::SetDisplayVisible()
+ */
+ virtual bool SetDisplay(const DisplayType& type, unsigned int surface_id,
+ const int x, const int y, const int w, const int h) {
+ return false;
+ }
+
+ /**
+ * @brief Set the video stretch mode on 21:9 TV
+ * @param [in] mode : stretch mode
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::SetVideoRoi()
+ * @post None
+ * @exception None
+ * @version 4.8
+ */
+ virtual bool SetStretchMode(const int& mode) { return false; }
+ /**
+ * @brief Set the video display mode
+ * @param [in] mode : display mode
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ * @see #DisplayMode
+ * @see EsPlusPlayer::SetDisplay()
+ * @see EsPlusPlayer::SetDisplayRoi()
+ * @see EsPlusPlayer::SetDisplayVisible()
+ */
+ virtual bool SetDisplayMode(const DisplayMode& mode) { return false; }
+ /**
+ * @brief Set the ROI(Region Of Interest) area of display
+ * @remarks The minimum value of width and height are 1.
+ * @param [in] roi : Roi Geometry
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * \n Before set display ROI, #DisplayMode::kDstRoi must be set with
+ * EsPlusPlayer::SetDisplayMode().
+ * @return @c True on success, otherwise @c False
+ * @see DisplayMode \n
+ * EsPlusPlayer::SetDisplay() \n
+ * EsPlusPlayer::SetDisplayMode() \n
+ * EsPlusPlayer::SetDisplayVisible()
+ */
+ virtual bool SetDisplayRoi(const Geometry& roi) { return false; }
+
+ /**
+ * @brief Set scaled area ratio of display
+ * @param [in] area : Crop ratio of src area
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ * @remark The minimum value of input are 0,maximun is 1.
+ */
+ virtual bool SetVideoRoi(const CropArea& area) { return false; }
+
+ /**
+ * @brief Resize the render rectangle(the max region that video can be
+ * displayed).
+ * @param [in] rect : render rectangle.
+ * @pre Should be called after SetDisplay()
+ * @return @c True on success, otherwise @c False
+ * @remark The minimum value of width and height are 1.
+ */
+ virtual bool ResizeRenderRect(const RenderRect& rect) { return false; }
+
+ /**
+ * @brief Set the rotate angle of display
+ * @remarks The default value is 0.
+ * @param [in] rotate : Rotate angle.
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::SetDisplay()
+ */
+ virtual bool SetDisplayRotate(const DisplayRotation& rotate) { return false; }
+
+ /**
+ * @brief Get the rotate angle of display
+ * @remarks The default value is 0.
+ * @param [out] rotate : Stored rotate angle value.
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::SetDisplayRotate()
+ */
+ virtual bool GetDisplayRotate(DisplayRotation* rotate) { return false; }
+
+ /**
+ * @brief Set the visibility of the video display
+ * @param [in] is_visible : The visibility of the display
+ * (@c true = visible, @c false = non-visible)
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::SetDisplay()
+ */
+ virtual bool SetDisplayVisible(bool is_visible) { return false; }
+
+ /**
+ * @deprecated Use SetSubmitDataType instead.
+ * @brief Set whether to send decrypted es packets in the trust zone
+ * @remarks This API have to be called before calling the
+ * EsPlusPlayer::PrepareAsync() \n If is_using_tz is set to true, use
+ * EsPlusPlayer::SubmitTrustZonePacket() to send decrypted packets \n
+ * @param [in] is_using_tz : whether to use trust zone memory
+ * (@c true = if decrypted packets are sent in trust zone, @c false
+ * = otherwise)
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::SubmitTrustZonePacket()
+ */
+ virtual bool SetTrustZoneUse(bool is_using_tz) { return false; }
+ /**
+ * @brief Set whether to send decrypted es packets in the trust zone or
+ * encrypted es packets
+ * @remarks This API have to be called before calling the
+ * EsPlusPlayer::PrepareAsync() \n
+ * If type is kCleanData, Use SubmitPacket() \n
+ * If type is kTrustZoneData, Use SubmitTrustZonePacket() \n
+ * If type is kEncryptedData, Use SubmitEncryptedPacket()
+ * @param [in] type : whether to use trust zone memory or encrypted data
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see EsPlusPlayer::SubmitPacket() \n
+ * EsPlusPlayer::SubmitTrustZonePacket() \n
+ * EsPlusPlayer::SubmitEncryptedPacket() \\n
+ * SubmitDataType
+ */
+ virtual bool SetSubmitDataType(SubmitDataType type) { return false; }
+ /**
+ * @brief Set audio stream to have contents information
+ * @remarks This API have to be called before calling the
+ * EsPlusPlayer::PrepareAsync()
+ * @param [in] stream : audio stream object
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see AudioStream
+ */
+ virtual bool SetStream(const AudioStreamPtr& stream) { return false; }
+ /**
+ * @brief Set video stream to have contents information
+ * @remarks This API have to be called before calling the
+ * EsPlusPlayer::PrepareAsync()
+ * @param [in] stream : video stream object
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ * @see VideoStream
+ */
+ virtual bool SetStream(const VideoStreamPtr& stream) { return false; }
+ /**
+ * @brief Submit es packet to decode audio or video
+ * @remarks Amount of packets for at least one decoded frame must be
+ * submitted after EsPlusPlayer::PrepareAsync() or EsPlusPlayer::Seek() for
+ * calling the EsPlusPlayer::OnPrepareDone() or EsPlusPlayer::OnSeekDone() \n
+ * User can submit es packets when EsPlusPlayer::OnReadyToPrepare()
+ * or EsPlusPlayer::OnReadyToSeek() is called \n
+ * To use this api, Must set SubmitDataType::kCleanData using
+ * SetSubmitDataType() or Don't set any SubmitDataType \n
+ * This api must be called from a different thread than other apis
+ * @param [in] packet : es packet object
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c PacketSubmitStatus::kSuccess : succeed to submit es packet
+ * @c otherwise : fail to submit es packet
+ * @see SetSubmitDataType() \n
+ * EsPacket \n
+ * OnBufferStatus \n
+ * OnReadyToPrepare \n
+ * OnReadyToSeek \n
+ * PacketSubmitStatus
+ */
+ virtual PacketSubmitStatus SubmitPacket(const EsPacketPtr& packet) {
+ return PacketSubmitStatus::kNotPrepared;
+ }
+ /**
+ * @brief Submit es packet to decode audio or video in the trust zone
+ * @remarks Amount of decrypted packets for at least one decoded frame must
+ * be submitted after EsPlusPlayer::PrepareAsync() or EsPlusPlayer::Seek() for
+ * calling the EsPlusPlayer::OnPrepareDone() or EsPlusPlayer::OnSeekDone() \n
+ * User can submit es packets when EsPlusPlayer::OnReadyToPrepare()
+ * or EsPlusPlayer::OnReadyToSeek() is called \n
+ * EsPlusPlayer::SetTrustZoneUse() must be set to true \n
+ * To use this api, Must set SubmitDataType::kTrustZoneData using
+ * SetSubmitDataType() \n
+ * This api must be called from a different thread than other apis.
+ * @param [in] packet : es packet object
+ * [in] tz_handle : a handle for es packet in the trust zone
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c PacketSubmitStatus::kSuccess : succeed to submit es packet
+ * @c otherwise : fail to submit es packet
+ * @see SetSubmitDataType() \n
+ * EsPacket \n
+ * OnBufferStatus \n
+ * OnReadyToPrepare \n
+ * OnReadyToSeek \n
+ * PacketSubmitStatus
+ */
+ virtual PacketSubmitStatus SubmitTrustZonePacket(const EsPacketPtr& packet,
+ uint32_t tz_handle = 0) {
+ return PacketSubmitStatus::kNotPrepared;
+ }
+ /**
+ * @brief Submit encrypted es packet to decode audio or video
+ * @remarks Amount of encrypted packets for at least one decoded frame must
+ * be submitted after EsPlusPlayer::PrepareAsync() or EsPlusPlayer::Seek() for
+ * calling the EsPlusPlayer::OnPrepareDone() or EsPlusPlayer::OnSeekDone()
+ * User can submit es packets when EsPlusPlayer::OnReadyToPrepare()
+ * or EsPlusPlayer::OnReadyToSeek() is called \n
+ * EsPlusPlayer::SetDrm() must be set to an appropriate drm type \n
+ * To use this api, Must set SubmitDataType::kEncryptedData using
+ * SetSubmitDataType() \n
+ * This api must be called from a different thread than other apis.
+ * @param [in] packet : encrypted es packet object
+ * [in] drm_info : information for decryption
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c PacketSubmitStatus::kSuccess : succeed to submit es packet
+ * @c PacketSubmitStatus::kFull, PacketSubmitStatus::kNotPrepared :
+ * fail to submit es packet
+ * @see SetSubmitDataType() \n
+ * EsPacket \n
+ * OnBufferStatus \n
+ * OnReadyToPrepare \n
+ * OnReadyToSeek \n
+ * PacketSubmitStatus
+ */
+ virtual PacketSubmitStatus SubmitEncryptedPacket(
+ const EsPacketPtr& packet, const drm::EsPlayerEncryptedInfo& drm_info) {
+ return PacketSubmitStatus::kNotPrepared;
+ }
+ /**
+ * @brief Get current state of player
+ * @return current #EsState of player
+ */
+ virtual EsState GetState() { return EsState::kNone; }
+ /**
+ * @brief Get the current playing time of the associated media.
+ * @param [out] time : current playing time default in milliseconds,
+ * can be set by @SetTimeUnitType
+ * @pre The player must be one of #EsState::kPlaying or
+ * #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ * ("time" will be 0)
+ */
+ virtual bool GetPlayingTime(uint64_t* time) { return false; }
+ /**
+ * @brief Get the adaptive info from the plugins
+ * @param [in] adaptive_type : App wanted get info type
+ * @param [out] padaptive_info : return value of requested(such as dropped
+ * frames)
+ * @pre The player must be one of #EsState::kPlaying or
+ * #EsState::kPaused or #EsState::kReady
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool GetAdaptiveInfo(void* padaptive_info,
+ const PlayerAdaptiveInfo& adaptive_type) {
+ return false;
+ }
+ /**
+ * @brief Set on mute of the audio sound
+ * @param [in] is_mute : On mute of the sound
+ * (@c true = mute, @c false = non-mute)
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetAudioMute(bool is_mute) { return false; }
+
+ /**
+ * @brief Set decoded video frame buffer type.
+ * @param [in] type : A type of decoded video frame buffer.
+ * @pre The player state must be set to #EsState::kIdle
+ */
+ virtual bool SetVideoFrameBufferType(DecodedVideoFrameBufferType type) {
+ return false;
+ }
+
+ /**
+ * @brief Set the request frame rate of decoded video
+ * @remarks Only works when decoded video frame buffer type is scale
+ * @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.
+ * @pre The player state must be not #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetDecodedVideoFrameRate(const Rational& request_framerate) {
+ return false;
+ }
+
+ /**
+ * @brief Set the target scale resolution when decoded video frame buffer
+ * type is scale
+ * @param [in] target_width : target width of scale.
+ * @param [in] target_height : target height of scale.
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetVideoFrameBufferScaleResolution(
+ const uint32_t& target_width, const uint32_t& target_height) {
+ return false;
+ }
+
+ /**
+ * @brief Register eventlistener to player
+ * @param [in] listener : listener object
+ * @param [in] userdata : listener object's userdata to be returned via
+ * notification without any modification
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @see EsEventListener
+ */
+ virtual void RegisterListener(EsEventListener* listener,
+ EsEventListener::UserData userdata) {
+ return;
+ }
+ /**
+ * @brief Set volume to player
+ * @param [in] volume : volume level
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetVolume(const int& volume) { return false; }
+ /**
+ * @brief Get volume to player
+ * @param [out] volume : volume ptr
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool GetVolume(int* volume) { return false; }
+ /**
+ * @brief Flush the data in pipeline
+ * @param [in] type : can be kAudio/kVideo
+ * @pre The player state can greater than #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool Flush(const StreamType& type) { return false; }
+ /**
+ * @brief Set the buffer size
+ * @param [in] option : A type of Buffer Option
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ */
+ virtual void SetBufferSize(const BufferOption& option, uint64_t size) {
+ return;
+ }
+ /**
+ * @brief Provided api for setting low latency mode
+ * @remarks This API have to be called before calling the
+ * EsPlusPlayer::PrepareAsync()
+ * @param [in] mode : one of the low latency mode to set.
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetLowLatencyMode(const PlayerLowLatencyMode& mode) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for enabling video frame peek mode
+ * @pre The player state must be set to #EsState::kIdle.
+ * @return @c True on success, otherwise @c False
+ * @see RenderVideoFrame().
+ */
+ virtual bool SetVideoFramePeekMode() { return false; }
+
+ /**
+ * @brief Provided api for rendering a video frame which is holded by
+ * video frame peek mode.
+ * @pre In order to use this api, The player state must be one of
+ * #EsState::kReady or #EsState::kPaused after EsEventListener::OnSeekDone()
+ * or EsEventListener::OnPrepareDone() is called \n
+ * @see SetVideoFramePeekMode().
+ */
+ virtual bool RenderVideoFrame() { return false; }
+ /**
+ * @brief Provided api for setting unlimited max buffer mode
+ * @remarks The player does not limit es packet transmission although in
+ * buffer overrun status.
+ * @pre The player state must be set to #EsState::kIdle
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetUnlimitedMaxBufferMode() { return false; }
+ /**
+ * @brief Provided api to deliver fmm signal and getting fmm auto status
+ * @remarks The player just delivers fmm signal to system. It doesn't
+ * gaurantee activating fmm mode. System refers to fmm signal when it decides
+ * to activate fmm mode or not.
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c ErrorType::kNone = fmm auto mode on @c
+ * ErrorType::kInvalidOperation = fmm audo mode off @c
+ * ErrorType::kInvalidState = internal operation failed
+ */
+ virtual ErrorType SetFmmMode() { return ErrorType::kNone; }
+ /**
+ * @brief Provided api for setting audio codec type
+ * @pre The player state must be set to #EsState::kIdle.
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetAudioCodecType(const PlayerAudioCodecType& type) {
+ return false;
+ }
+ /**
+ * @brief Provided api for setting video codec type
+ * @pre The player state must be set to #EsState::kIdle.
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetVideoCodecType(const PlayerVideoCodecType& type) {
+ return false;
+ }
+ /**
+ * @brief Provided api for setting alternative video resource(sub decoder
+ * and sub scaler)
+ * @param [in] is_set : set alternative video resource
+ * (@c 0 [defualt] = set all video resources(decoder/scaler) to
+ * main resources,
+ * @c 1 = set all video resources(decoder/scaler) to sub
+ * resources,
+ * @c 2 = set only decoder to sub resource,
+ * @c 3 = set only scaler to sub resource)
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetAlternativeVideoResource(unsigned int rsc_type) {
+ return false;
+ }
+ /**
+ * @brief Provided api for setting alternative audio resource(sub decoder
+ * and audio out)
+ * @param [in] is_set : set alternative audio resource
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetAlternativeAudioResource(
+ const PlayerAudioResourceType rsc_type) {
+ return false;
+ }
+ /**
+ * @brief Provided api for switching audio stream between the different
+ * audio codec types on the fly
+ * @param [in] stream : audio stream object
+ * @remark Audio stream can be switched between only #AudioMimeType::kAAC,
+ * #AudioMimeType::kEac3 and #AudioMimeType::kAC3.
+ * if other codec type is set, this api will return false.
+ * @pre The player state must be one of #EsState::kReady,
+ * #EsState::kPlaying, #EsState::kPaused.
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SwitchAudioStreamOnTheFly(const AudioStreamPtr& stream) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for setting aifilter to video pipeline
+ * @pre The player state must be set to #EsState::kIdle.
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetAiFilter(void* aifilter) { return false; }
+ /**
+ * @brief Provided api for setting render time offset
+ * @param [in] type : stream type
+ * @param [in] offset : offset (milisecond).
+ * @pre The player state must be set to #EsState::kReady,
+ * #EsState::kPaused or #EsState::kPlaying.
+ * It have to be set to low latency mode.
+ * @remark esplusplayer_set_low_latency_mode().
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetRenderTimeOffset(const StreamType type, int64_t offset) {
+ return false;
+ }
+ /**
+ * @brief Provided api for getting render time offset
+ * @param [in] type : stream type
+ * @param [in] offset : offset ptr (milisecond).
+ * @pre The player state must be set to #EsState::kReady,
+ * #EsState::kPaused or #EsState::kPlaying.
+ * It have to be set to low latency mode.
+ * @remark esplusplayer_set_low_latency_mode().
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool GetRenderTimeOffset(const StreamType type, int64_t* offset) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for setting catch up speed level
+ * @pre The player state must be set to #EsState::kIdle,
+ * #EsState::kReady, #EsState::kPlaying or #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetCatchUpSpeed(const CatchUpSpeed& level) { return false; }
+ /**
+ * @brief Provided api for getting current video latency status
+ * @pre The player state must be one of #EsState::kReady,
+ * #EsState::kPlaying or #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool GetVideoLatencyStatus(LatencyStatus* status) { return false; }
+ /**
+ * @brief Provided api for getting current audio latency status
+ * @pre The player state must be one of #EsState::kReady,
+ * #EsState::kPlaying or #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool GetAudioLatencyStatus(LatencyStatus* status) { return false; }
+ /**
+ * @brief Provided api for setting video mid latency threshold for low
+ * latency playback
+ * @pre The player state must be set to #EsState::kIdle,
+ * #EsState::kReady, #EsState::kPlaying or #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetVideoMidLatencyThreshold(const unsigned int threshold) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for setting audio mid latency threshold for low
+ * latency playback
+ * @pre The player state must be set to #EsState::kIdle,
+ * #EsState::kReady, #EsState::kPlaying or #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetAudioMidLatencyThreshold(const unsigned int threshold) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for setting video high latency threshold for low
+ * latency playback
+ * @pre The player state must be set to #EsState::kIdle,
+ * #EsState::kReady, #EsState::kPlaying or #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetVideoHighLatencyThreshold(const unsigned int threshold) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for setting audio high latency threshold for low
+ * latency playback
+ * @pre The player state must be set to #EsState::kIdle,
+ * #EsState::kReady, #EsState::kPlaying or #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetAudioHighLatencyThreshold(const unsigned int threshold) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for initializing audio easing information
+ * @param [in] init_volume : initial volume
+ * @param [in] init_elapsed_time : initail elapsed time (milisecond).
+ * @param [in] easing_info : target_volume, duration(milisecond), easing
+ * type
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool InitAudioEasingInfo(const uint32_t init_volume,
+ const uint32_t init_elapsed_time,
+ const AudioEasingInfo& easing_info) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for updating audio easing information
+ * @param [in] easing_info : target_volume, duration(milisecond), easing
+ * type
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @remarks This API have to be called after calling the
+ * EsPlusPlayer::InitAudioEasingInfo()
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool UpdateAudioEasingInfo(const AudioEasingInfo& easing_info) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for getting audio easing information
+ * @param [out] current_volume : current volume
+ * @param [out] elapsed_time : elapsed time (milisecond).
+ * @param [out] easing_info : target_volume, duration(milisecond), easing
+ * type
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @remarks This API have to be called after calling the
+ * EsPlusPlayer::InitAudioEasingInfo()
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool GetAudioEasingInfo(uint32_t* current_volume,
+ uint32_t* elapsed_time,
+ AudioEasingInfo* easing_info) {
+ return false;
+ }
+
+ /**
+ * @brief Provided api for starting audio easing
+ * @pre The player state should be at least #EsState::kReady
+ * @remarks This API have to be called after calling the
+ * EsPlusPlayer::InitAudioEasingInfo()
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool StartAudioEasing() { return false; }
+
+ /**
+ * @brief Provided api for stopping audio easing
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @remarks This API have to be called after calling the
+ * EsPlusPlayer::InitAudioEasingInfo()
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool StopAudioEasing() { return false; }
+
+ /**
+ * @brief Get virtual resource id
+ * @param [in] type : The resource type of virtual id.
+ * @param [out] virtual_id : Stored virtual resource id value.
+ * @pre The player state should be #EsState::kReady, #EsState::kPlaying
+ * or #EsState::kPaused
+ * @post None
+ * @return @c True on success, otherwise @c False ("virtual_id" will be -1)
+ * @exception None
+ */
+ virtual bool GetVirtualRscId(const RscType type, int* virtual_id) {
+ return false;
+ }
+
+ /**
+ * @brief Set advanced picture quality type.
+ * @param [in] type : The picture quality type.
+ * @pre The player state must be set to #EsState::kIdle.
+ * @post None
+ * @return @c True on success, otherwise @c False
+ * @exception None
+ */
+ virtual bool SetAdvancedPictureQualityType(const AdvPictureQualityType type) {
+ return false;
+ }
+
+ /**
+ * @brief Set resource allocate policy.
+ * @param [in] policy : The resource allocate policy.
+ * @pre The player state must be set to #EsState::kIdle.
+ * @post None
+ * @return @c True on success, otherwise @c False
+ * @exception None
+ */
+ virtual bool SetResourceAllocatePolicy(const RscAllocPolicy policy) {
+ return false;
+ }
+
+ /**
+ * @brief Get the decoded video packet.
+ * @param [out] packet
+ * @return GetDecodedVideoFrameStatus
+ */
+ virtual GetDecodedVideoFrameStatus GetDecodedPacket(
+ DecodedVideoPacket& packet) {
+ return GetDecodedVideoFrameStatus::kUnknown;
+ }
+
+ /**
+ * @brief Return the decoded video packet.
+ * @param [in] packet
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool ReturnDecodedPacket(const DecodedVideoPacket& packet) {
+ return false;
+ }
+
+ /**
+ * @brief Set audio preloading mode
+ * @pre The player state must be set to #EsState::kIdle
+ * @post None
+ * @return @c True on success, otherwise @c False
+ * @exception None
+ */
+ virtual bool SetAudioPreloading() { return false; }
+
+ /**
+ * @brief Set resource allocate policy.
+ * @param [in] policy : The resource allocate policy.
+ * @pre The player state must be set to #EsState::kIdle.
+ * @post None
+ * @return @c True on success, otherwise @c False
+ * @exception None
+ */
+ virtual bool SetVideoScanType(const PlayerVideoScanType type) {
+ return false;
+ }
+
+ /**
+ * @brief Get the decoding time of the stream type.
+ * @param [in] type : stream type
+ * @param [out] time_in_milliseconds : decoding time in
+ * milliseconds
+ * @pre The player must be one of #EsState::kPlaying or
+ * #EsState::kPaused
+ * @return @c True on success, otherwise @c False
+ * ("time_in_milliseconds" will be 0)
+ */
+ virtual bool GetDecodingTime(StreamType type, int32_t* time_in_milliseconds) {
+ return false;
+ }
+
+ /**
+ * @brief Set the time unit type, ms or us.
+ * @param [in] type : The type of time unit, defalut is ms.
+ * @pre The player state must be set to #EsState::kIdle.
+ * @post None
+ * @return @c True on success, otherwise @c False
+ * @exception None
+ */
+ virtual bool SetTimeUnitType(const PlayerTimeUnitType type) { return false; }
+
+ /**
+ * @brief Set the rotate angle of video stream
+ * @param [in] rotation : Rotate type.
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @post None
+ * @return @c True on success, otherwise @c False
+ * @exception None
+ */
+ virtual bool SetVideoStreamRotationInfo(const VideoRotation& rotation) { return false; }
+
+ /**
+ * @brief Get the rotate angle of video stream
+ * @param [out] rotation : Rotate type.
+ * @pre The player state can be all of #EsState except #EsState::kNone
+ * @post None
+ * @return @c True on success, otherwise @c False
+ * @exception None
+ */
+ virtual bool GetVideoStreamRotationInfo(VideoRotation* rotation) { return false; }
+
+ /**
+ * @brief Set buffer level of simple mix out
+ * @param [in] level : buffer level of simple mix out
+ * @pre The player state must be set to #EsState::kIdle.
+ * @post None
+ * @return @c True on success, otherwise @c False
+ * @exception None
+ */
+ virtual bool SetSimpleMixOutBufferLevel(const PlayerSimpleMixOutBufferLevel level) { return false; }
+
+// LCOV_EXCL_STOP
+
+
+ protected:
+ EsPlusPlayer() noexcept {};
+}; // class EsPlusPlayer
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER__H__
--- /dev/null
+/**
+ * @file external_drm.h
+ * @brief the extrnal drm information for elementary stream
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * 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 __ESPLUSPLAYER_EXTERNAL_DRM_H__
+#define __ESPLUSPLAYER_EXTERNAL_DRM_H__
+
+#include <array>
+#include <string>
+#include <vector>
+
+namespace esplusplayer {
+
+namespace drm {
+
+using MetaData = void*;
+/**
+ * @brief Enumerations for cipher algorithm for drm
+ */
+enum class DrmbEsCipherAlgorithm : int {
+ kUnknown = -1,
+ kRc4 = 0,
+ kAes128Ctr = 1,
+ kAes128Cbc = 2
+};
+/**
+ * @brief Enumerations for media format for drm
+ */
+enum class DrmbEsMediaFormat : int {
+ kNone = 0,
+ kFragmentedMp4 = 1,
+ kTs = 2,
+ kAsf = 3,
+ kFragmentedMp4Audio = 4,
+ kFragmentedMp4Video = 5,
+ kCleanAudio = 6, // Clean Audio Data
+ kPes = 7, // Packetized ES
+};
+/**
+ * @brief Enumerations for cipher phase for drm
+ */
+enum class DrmbEsCipherPhase : int {
+ kNone = 0,
+ kInit = 1,
+ kUpdate = 2,
+ kFinal = 3
+};
+/**
+ * @brief Structure of subsample information for drm
+ */
+struct DrmbEsSubSampleInfo {
+ explicit DrmbEsSubSampleInfo(const uint32_t _bytes_of_clear_data,
+ const uint32_t _bytes_of_encrypted_data)
+ : bytes_of_clear_data(_bytes_of_clear_data),
+ bytes_of_encrypted_data(_bytes_of_encrypted_data) {}
+ uint32_t bytes_of_clear_data;
+ uint32_t bytes_of_encrypted_data;
+};
+/**
+ * @brief Structure of fragmented mp4 data for drm
+ */
+struct DrmbEsFragmentedMp4Data {
+ std::vector<DrmbEsSubSampleInfo> sub_sample_info_vector;
+};
+/**
+ * @brief Structure of encrypted information for es playback
+ */
+struct EsPlayerEncryptedInfo {
+ int32_t handle = 0;
+
+ DrmbEsCipherAlgorithm algorithm = DrmbEsCipherAlgorithm::kUnknown;
+ DrmbEsMediaFormat format = DrmbEsMediaFormat::kNone;
+ DrmbEsCipherPhase phase = DrmbEsCipherPhase::kNone;
+
+ std::vector<unsigned char> kid;
+ std::vector<unsigned char> initialization_vector;
+
+ MetaData sub_data = nullptr;
+ std::array<int, 15> split_offsets;
+
+ bool use_out_buffer = false;
+ bool use_pattern = false;
+
+ uint32_t crypt_byte_block = 0;
+ uint32_t skip_byte_block = 0;
+};
+
+} // namespace drm
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_EXTERNAL_DRM_H__
--- /dev/null
+/**
+* @file
+* @interfacetype module
+* @privlevel None-privilege
+* @privilege None
+* @product TV, AV, B2B
+* @version 1.0
+* @SDK_Support N
+*
+* 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 __ESPLUSPLAYER_TRACK_H__
+#define __ESPLUSPLAYER_TRACK_H__
+
+#include <boost/any.hpp>
+#include <boost/core/noncopyable.hpp>
+#include <cstdint>
+#include <limits>
+#include <list>
+#include <memory>
+#include <string>
+
+namespace esplusplayer {
+
+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;
+ std::string container_type;
+ TrackType type = kTrackTypeMax;
+ std::shared_ptr<char> codec_data;
+ unsigned int codec_tag = 0;
+ 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;
+ Track() {};
+ Track(int _index, int _id, std::string _mimetype, std::string _streamtype, std::string _container_type,
+ TrackType _type, std::shared_ptr<char> _codec_data, unsigned int _codec_tag, int _codec_data_len,
+ int _width, int _height, int _maxwidth, int _maxheight, int _framerate_num, int _framerate_den,
+ int _sample_rate, int _sample_format, int _channels, int _version, int _layer, int _bits_per_sample,
+ int _block_align, int _bitrate, int _endianness, bool _is_signed, bool _active, bool _use_swdecoder,
+ std::string _language_code, std::string _subtitle_format)
+ : index(_index), id(_id), mimetype(_mimetype), streamtype(_streamtype), container_type(_container_type),
+ type(_type), codec_data(_codec_data), codec_tag(_codec_tag), codec_data_len(_codec_data_len),
+ width(_width), height(_height), maxwidth(_maxwidth), maxheight(_maxheight), framerate_num(_framerate_num), framerate_den(_framerate_den),
+ sample_rate(_sample_rate), sample_format(_sample_format), channels(_channels), version(_version), layer(_layer), bits_per_sample(_bits_per_sample),
+ block_align(_block_align), bitrate(_bitrate), endianness(_endianness), is_signed(_is_signed), active(_active), use_swdecoder(_use_swdecoder),
+ language_code(_language_code), subtitle_format(_subtitle_format) {};
+};
+
+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>;
+struct Rational {
+ int num = 0; // the numerator value
+ int den = 0; // the denominator value
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_TRACK_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file
+ * @brief the buffer for playback
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * 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 __ESPLUSPLAYER_TYPES_BUFFER_H__
+#define __ESPLUSPLAYER_TYPES_BUFFER_H__
+
+#include <cstdint>
+
+#include "tbm_type.h"
+
+namespace esplusplayer {
+
+/**
+ * @brief Enumerations for the buffer status
+ */
+enum class BufferStatus { kUnderrun, kOverrun };
+
+enum class DecodedVideoFrameBufferType {
+ kNone,
+ kCopy,
+ kReference,
+ kScale,
+ kManualCopy,
+};
+
+enum class BufferOption {
+ kBufferAudioMaxTimeSize,
+ kBufferVideoMaxTimeSize,
+ kBufferAudioMinTimeThreshold,
+ kBufferVideoMinTimeThreshold,
+ kBufferAudioMaxByteSize,
+ kBufferVideoMaxByteSize,
+ kBufferAudioMinByteThreshold,
+ kBufferVideoMinByteThreshold,
+ kBufferOptionMax
+};
+
+struct DecodedVideoPacket {
+ uint64_t pts = 0;
+ uint64_t duration = 0;
+ tbm_surface_h surface_data = nullptr; // tbm_surface
+ void* scaler_index = nullptr;
+};
+
+struct DecoderBufferTime{
+ uint64_t pts = 0;
+ uint64_t system_time = 0;
+} ;
+
+/**
+ * @brief Enumerations for the state of getting decoded packet
+ */
+enum class GetDecodedVideoFrameStatus {
+ kSuccess,
+ kNoRemainingBuffer,
+ kNoFilledBuffer,
+ kUnknown,
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_TYPES_BUFFER_H__
--- /dev/null
+/**
+ * @file
+ * @interfacetype module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 1.0
+ * @SDK_Support N
+ *
+ * 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 __ESPLUSPLAYER_TYPES_DISPLAY_H__
+#define __ESPLUSPLAYER_TYPES_DISPLAY_H__
+
+namespace esplusplayer {
+
+enum class DisplayType { kNone, kOverlay, kEvas, kMixer };
+
+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;
+};
+
+enum class VisibleStatus { kHide, kVisible };
+
+struct DisplayInfo {
+ Geometry geometry;
+ CropArea croparea;
+ VisibleStatus visible_status = VisibleStatus::kVisible;
+};
+
+enum class StillMode { kNone, kOff, kOn };
+
+struct DisplayObject {
+ DisplayType type_;
+ int surface_id_;
+ DisplayMode mode_;
+ Geometry geometry_;
+ void* obj_;
+ bool is_obj_ = false;
+};
+
+enum class VideoRotation {
+ kVideoRotateNone,
+ kVideoRotate90,
+ kVideoRotate180,
+ kVideoRotate270,
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_TYPES_DISPLAY_H__
--- /dev/null
+/**
+* @file
+* @interfacetype module
+* @privlevel None-privilege
+* @privilege None
+* @product TV, AV, B2B
+* @version 1.0
+* @SDK_Support N
+*
+* 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 __ESPLUSPLAYER_TYPES_ERROR_H__
+#define __ESPLUSPLAYER_TYPES_ERROR_H__
+
+#include "tizen.h"
+
+namespace esplusplayer {
+
+#define PLUSPLAYER_ERROR_CLASS TIZEN_ERROR_PLAYER | 0x20
+
+/* This is for custom defined player error. */
+#define PLUSPLAYER_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 = PLUSPLAYER_ERROR_CLASS | 0x01, /**< Seek operation failure */
+ kInvalidState = PLUSPLAYER_ERROR_CLASS | 0x02, /**< Invalid state */
+ kNotSupportedFile = PLUSPLAYER_ERROR_CLASS | 0x03, /**< File format not supported */
+ kInvalidUri = PLUSPLAYER_ERROR_CLASS | 0x04, /**< Invalid URI */
+ kSoundPolicy = PLUSPLAYER_ERROR_CLASS | 0x05, /**< Sound policy error */
+ kConnectionFailed = PLUSPLAYER_ERROR_CLASS | 0x06, /**< Streaming connection failed */
+ kVideoCaptureFailed = PLUSPLAYER_ERROR_CLASS | 0x07, /**< Video capture failed */
+ kDrmExpired = PLUSPLAYER_ERROR_CLASS | 0x08, /**< Expired license */
+ kDrmNoLicense = PLUSPLAYER_ERROR_CLASS | 0x09, /**< No license */
+ kDrmFutureUse = PLUSPLAYER_ERROR_CLASS | 0x0a, /**< License for future use */
+ kDrmNotPermitted = PLUSPLAYER_ERROR_CLASS | 0x0b, /**< Format not permitted */
+ kResourceLimit = PLUSPLAYER_ERROR_CLASS | 0x0c, /**< Resource limit */
+ kPermissionDenied = TIZEN_ERROR_PERMISSION_DENIED, /**< Permission denied */
+ kServiceDisconnected = PLUSPLAYER_ERROR_CLASS | 0x0d, /**< Socket connection lost (Since 3.0) */
+ kBufferSpace = TIZEN_ERROR_BUFFER_SPACE, /**< No buffer space available (Since 3.0)*/
+ kNotSupportedAudioCodec = PLUSPLAYER_ERROR_CLASS | 0x0e, /**< Not supported audio codec but video can be played (Since 4.0) */
+ kNotSupportedVideoCodec = PLUSPLAYER_ERROR_CLASS | 0x0f, /**< Not supported video codec but audio can be played (Since 4.0) */
+ kNotSupportedSubtitle = PLUSPLAYER_ERROR_CLASS | 0x10, /**< Not supported subtitle format (Since 4.0) */
+
+ // TODO(euna7.ko) Can be removed. refer to http://168.219.243.246:8090/pages/viewpage.action?pageId=27269511
+ kDrmInfo = PLUSPLAYER_CUSTOM_ERROR_CLASS | 0x05, /**< playready drm error info */
+ kNotSupportedFormat = PLUSPLAYER_CUSTOM_ERROR_CLASS | 0x08,
+ kStreamingPlayer = PLUSPLAYER_CUSTOM_ERROR_CLASS | 0x09,
+ kDtcpFsk = PLUSPLAYER_CUSTOM_ERROR_CLASS | 0x0a,
+ kPreLoadingTimeOut =PLUSPLAYER_CUSTOM_ERROR_CLASS | 0x0b, /**< can't finish preloading in time*/
+ kNetworkError = PLUSPLAYER_CUSTOM_ERROR_CLASS | 0x0c, /**< for network error */
+ kChannelSurfingFailed = PLUSPLAYER_CUSTOM_ERROR_CLASS | 0x0d, /**< for channel surfing error */
+
+ kUnknown
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_TYPES_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 __ESPLUSPLAYER_TYPES_EVENT_H__
+#define __ESPLUSPLAYER_TYPES_EVENT_H__
+
+namespace esplusplayer {
+/**
+ * @brief
+ */
+typedef struct {
+ /**
+ * @description
+ */
+ std::string data;
+ /**
+ * @description
+ */
+ uint64_t len;
+} EventMsg;
+
+/**
+ * @brief
+ */
+enum class EventType {
+ kNone,
+ kResolutionChanged,
+ kRequestedFirstRenderFrame,
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_TYPES_EVENT_H__
--- /dev/null
+/**
+* @file
+* @interfacetype module
+* @privlevel None-privilege
+* @privilege None
+* @product TV, AV, B2B
+* @version 2.7
+* @SDK_Support N
+*
+* Copyright (c) 2020 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 __ESPLUSPLAYER_TYPES_LATENCY_H__
+#define __ESPLUSPLAYER_TYPES_LATENCY_H__
+
+namespace esplusplayer {
+
+enum class CatchUpSpeed {
+ kNone,
+ kSlow,
+ kNormal,
+ kFast
+};
+
+enum class LatencyStatus {
+ kLow,
+ kMid,
+ kHigh
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_TYPES_LATENCY_H__
\ No newline at end of file
--- /dev/null
+/**
+* @file
+* @interfacetype module
+* @privlevel None-privilege
+* @privilege None
+* @product TV, AV, B2B
+* @version 1.0
+* @SDK_Support N
+*
+* 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 __ESPLUSPLAYER_PICTUREQUALITY_H__
+#define __ESPLUSPLAYER_PICTUREQUALITY_H__
+
+namespace esplusplayer {
+
+/**
+* @brief Advanced Picture Quality Type.
+*/
+enum class AdvPictureQualityType {
+ kVideoCall,
+ kUsbCamera,
+ kAirplayMirroring
+};
+
+} // namespace esplusplayer
+#endif // __ESPLUSPLAYER_PICTUREQUALITY_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file
+ * @brief the stream information for playback
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 3.0
+ * @SDK_Support N
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_TYPES_RESOURCE_H__
+#define __ESPLUSPLAYER_TYPES_RESOURCE_H__
+
+namespace esplusplayer {
+
+/**
+ * @brief Enumerations for the resource type
+ */
+enum class RscType { kVideoRenderer };
+
+/**
+ * @brief Enumerations for resource allocate policy
+ */
+enum class RscAllocPolicy {
+ /**
+ * @description exclusive policy, default policy
+ */
+ kRscAllocExclusive,
+ /**
+ * @description conditional policy
+ */
+ kRscAllocConditional,
+ /**
+ * @description exclusive no explicit policy
+ */
+ kRscAllocExclusiveNoExplicit,
+};
+
+/**
+ * @brief Enumerations for audio resource type
+ */
+enum PlayerAudioResourceType {
+ /**
+ * @description all audio resources(decoder/audio out) to main resources
+ */
+ kPlayerAudioResourceTypeMain,
+ /**
+ * @description only audio decoder to sub resource
+ */
+ kPlayerAudioResourceTypeSubDecoder,
+ /**
+ * @description only audio out to simple mix out resource
+ */
+ kPlayerAudioResourceTypeSimpleMixOut,
+};
+
+} // namespace plusplayer
+
+#endif // __ESPLUSPLAYER_TYPES_RESOURCE_H__
\ No newline at end of file
--- /dev/null
+/**
+* @file source.h
+* @brief Types related to TrackSource
+* @interfacetype module
+* @privlevel None-privilege
+* @privilege None
+* @product TV, AV, B2B
+* @version 1.0
+* @SDK_Support N
+*
+* 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 __ESPLUSPLAYER_SRC_TRACKSOURCE_TYPES_H__
+#define __ESPLUSPLAYER_SRC_TRACKSOURCE_TYPES_H__
+
+namespace esplusplayer {
+
+enum class SourceType {
+ kNone,
+ kBase,
+ kHttp,
+ kHls,
+ kDash,
+ kSmooth,
+ kFile,
+ kExternalSubtitle,
+ kNotFound,
+ kMax
+};
+
+enum class ContentFormat {
+ kNone,
+ kMP4Mov,
+ kMpegts,
+ k3GpMov,
+ kAudioMpeg,
+ kAudioMpegAac,
+ kMkv,
+ kAvi,
+ kVideoAsf,
+ kAppXid3,
+ kUnknown
+};
+
+enum class TrickPlayMode {
+ kNone,
+ kDefault,
+ kBySeek
+};
+
+enum class PlayingTimeSupport {
+ kNone,
+ kNeeded
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_TRACKSOURCE_TYPES_H__
--- /dev/null
+/**
+ * @file
+ * @brief the stream information for playback
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * 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 __ESPLUSPLAYER_TYPES_STREAM_H__
+#define __ESPLUSPLAYER_TYPES_STREAM_H__
+
+namespace esplusplayer {
+
+/**
+ * @brief Enumerations for the stream type
+ */
+enum class StreamType { kAudio = 0, kVideo, kMax };
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_TYPES_STREAM_H__
\ No newline at end of file
--- /dev/null
+/**
+* @file streaming_message.h
+* @interfacetype module
+* @privlevel None-privilege
+* @privilege None
+* @product TV, AV, B2B
+* @version 1.0
+* @SDK_Support N
+*
+* 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 __ESPLUSPLAYER_TYPES_STREAMING_MESSAGE_H__
+#define __ESPLUSPLAYER_TYPES_STREAMING_MESSAGE_H__
+
+namespace esplusplayer {
+
+enum class StreamingMessageType {
+ kNone = 0,
+ // kResolutionChanged,
+ // kAdEnd,
+ // kAdStart,
+ // kRenderDone,
+ kBitrateChange,
+ // kFragmentInfo,
+ kSparseTrackDetect,
+ // kStreamingEvent,
+ // kDrmChallengeData,
+ kDrmInitData,
+ // kHttpErrorCode,
+ // kDrmRenewSessionData,
+ kStreamEventType,
+ kStreamEventData,
+ kStreamSyncFlush,
+ kStreamMrsUrlChanged,
+ kDrmKeyRotation,
+ kFragmentDownloadInfo,
+ kDvrLiveLag,
+ kSparseTrackData,
+ kConnectionRetry,
+ kConfigLowLatency,
+ kCurlErrorDebugInfo,
+ kParDarChange
+};
+
+struct MessageParam {
+ std::string data;
+ int size = 0;
+ int code = 0; // Error or warning code
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_TYPES_STREAMING_MESSAGE_H__
\ No newline at end of file
--- /dev/null
+/**
+* @file submitdata.h
+* @brief the data type to submit
+* @interfacetype Module
+* @privlevel None-privilege
+* @privilege None
+* @product TV, AV, B2B
+* @version 0.0.1
+* @SDK_Support N
+*
+* Copyright (c) 2019 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 __ESPLUSPLAYER_TYPES_SUBMITDATA_H__
+#define __ESPLUSPLAYER_TYPES_SUBMITDATA_H__
+
+namespace esplusplayer {
+
+/**
+ * @brief Enumerations for the type of espacket submitted
+ */
+enum class SubmitDataType {
+ kCleanData, // For clean data
+ kEncryptedData, // For encrypted data
+ kTrustZoneData // For trustzone data
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_TYPES_SUBMITDATA_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file
+ * @brief The buffer for playback.
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.0
+ * @SDK_Support N
+ * @remark This is a group of C style buffer related enum.
+ * @see N/A
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_BUFFER_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_BUFFER_H__
+
+#include <cstdint>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the buffer status
+ */
+enum esplusplayer_buffer_status {
+ ESPLUSPLAYER_BUFFER_STATUS_UNDERRUN,
+ ESPLUSPLAYER_BUFFER_STATUS_OVERRUN
+};
+
+/**
+ * @brief Enumerations for video decoded buffer type
+ * ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE:
+ * decoded video frame will be croped and scaled, then sent to
+ * user by media_packet_video_decoded_cb, only support hw decoder now
+*/
+enum esplusplayer_decoded_video_frame_buffer_type {
+ ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_NONE,
+ ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_COPY,
+ ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_REFERENCE,
+ ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE,
+ ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_MANUAL_COPY
+};
+
+/**
+ * @brief Enumerations for buffer size option
+ * MAX_TIME_SIZE: The maximum amount of data to esplusplayer internally(in ms)
+ * MIN_TIME_THRESHOLD : Emit under-run when queued bytes drops below this
+ * percent of max-time-size(size%)
+ * MAX_BYTE_SIZE: The maximum number of bytes to esplusplayer internally
+ * MIN_BYTE_THRESHOLD : Emit under-run when queued bytes drops below this
+ * percent of max-byte-size(size%)
+ */
+enum esplusplayer_buffer_option {
+ ESPLUSPLAYER_BUFFER_AUDIO_MAX_TIME_SIZE,
+ ESPLUSPLAYER_BUFFER_VIDEO_MAX_TIME_SIZE,
+ ESPLUSPLAYER_BUFFER_AUDIO_MIN_TIME_THRESHOLD,
+ ESPLUSPLAYER_BUFFER_VIDEO_MIN_TIME_THRESHOLD,
+ ESPLUSPLAYER_BUFFER_AUDIO_MAX_BYTE_SIZE,
+ ESPLUSPLAYER_BUFFER_VIDEO_MAX_BYTE_SIZE,
+ ESPLUSPLAYER_BUFFER_AUDIO_MIN_BYTE_THRESHOLD,
+ ESPLUSPLAYER_BUFFER_VIDEO_MIN_BYTE_THRESHOLD
+};
+
+/**
+ * @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* private_data;
+} esplusplayer_decoded_video_packet;
+
+/**
+ * @brief decoder input and output buffer time struct
+ */
+typedef struct {
+ /**
+ * @description buffer pts, in millisecond
+ */
+ uint64_t pts;
+ /**
+ * @description system time of decoder input or output buffer, in millisecond
+ */
+ uint64_t system_time;
+} esplusplayer_decoder_buffer_time;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_BUFFER_H__
--- /dev/null
+/**
+ * @file
+ * @brief Display related enums
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.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) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_DISPLAY_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_DISPLAY_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the display mode
+ */
+enum esplusplayer_display_mode {
+ ESPLUSPLAYER_DISPLAY_MODE_LETTER_BOX,
+ ESPLUSPLAYER_DISPLAY_MODE_ORIGIN_SIZE,
+ ESPLUSPLAYER_DISPLAY_MODE_FULL_SCREEN,
+ ESPLUSPLAYER_DISPLAY_MODE_CROPPED_FULL,
+ ESPLUSPLAYER_DISPLAY_MODE_ORIGIN_OR_LETTER,
+ ESPLUSPLAYER_DISPLAY_MODE_DST_ROI
+};
+
+/**
+ * @brief Enumerations for the display type
+ */
+enum esplusplayer_display_type {
+ ESPLUSPLAYER_DISPLAY_TYPE_NONE,
+ ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ ESPLUSPLAYER_DISPLAY_TYPE_EVAS,
+ ESPLUSPLAYER_DISPLAY_TYPE_MIXER
+};
+
+/**
+ * @brief Enumerations for the display rotation type
+ */
+enum esplusplayer_display_rotation_type {
+ ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_NONE,
+ ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_90,
+ ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_180,
+ ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_270
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_DISPLAY_H__
--- /dev/null
+/**
+ * @file
+ * @brief Drm related enums
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.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) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_DRM_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_DRM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/**
+ * @brief Enumerations for the drm type
+ */
+enum esplusplayer_drm_type {
+ ESPLUSPLAYER_DRM_TYPE_NONE,
+ ESPLUSPLAYER_DRM_TYPE_PLAYREADY,
+ ESPLUSPLAYER_DRM_TYPE_MARLIN,
+ ESPLUSPLAYER_DRM_TYPE_VERIMATRIX,
+ ESPLUSPLAYER_DRM_TYPE_WV_MODULAR
+};
+
+/**
+ * @brief Enumerations for the algorithm encrypted
+ */
+enum esplusplayer_drmb_es_cipher_algorithm {
+ ESPLUSPLAYER_DRMB_ES_CIPHER_ALGORITHM_UNKNOWN = -1,
+ ESPLUSPLAYER_DRMB_ES_CIPHER_ALGORITHM_RC4 = 0,
+ ESPLUSPLAYER_DRMB_ES_CIPHER_ALGORITHM_AES128_CTR = 1,
+ ESPLUSPLAYER_DRMB_ES_CIPHER_ALGORITHM_AES128_CBC = 2
+};
+
+/**
+ * @brief Enumerations for the algorithm encrypted
+ */
+enum esplusplayer_drmb_es_media_format {
+ ESPLUSPLAYER_DRMB_ES_MEDIA_FORMAT_NONE = 0,
+ ESPLUSPLAYER_DRMB_ES_MEDIA_FORMAT_FMP4 = 1,
+ ESPLUSPLAYER_DRMB_ES_MEDIA_FORMAT_TS = 2,
+ ESPLUSPLAYER_DRMB_ES_MEDIA_FORMAT_ASF = 3,
+ ESPLUSPLAYER_DRMB_ES_MEDIA_FORMAT_FMP4_AUDIO = 4,
+ ESPLUSPLAYER_DRMB_ES_MEDIA_FORMAT_FMP4_VIDEO = 5,
+ ESPLUSPLAYER_DRMB_ES_MEDIA_FORMAT_CLEAN_AUDIO = 6,
+ ESPLUSPLAYER_DRMB_ES_MEDIA_FORMAT_PES = 7
+};
+
+/**
+ * @brief Enumerations for the phase for cipher
+ */
+enum esplusplayer_drmb_es_cipher_phase {
+ ESPLUSPLAYER_DRMB_ES_CIPHER_PHASE_NONE = 0,
+ ESPLUSPLAYER_DRMB_ES_CIPHER_PHASE_INIT = 1,
+ ESPLUSPLAYER_DRMB_ES_CIPHER_PHASE_UPDATE = 2,
+ ESPLUSPLAYER_DRMB_ES_CIPHER_PHASE_FINAL = 3
+};
+
+/**
+ * @brief Subsample information structure for encrypted es
+ */
+typedef struct {
+ /**
+ * @description The bytes of clear data.
+ */
+ uint32_t bytes_of_clear_data;
+ /**
+ * @description The bytes of encrypted data.
+ */
+ uint32_t bytes_of_encrypted_data;
+} esplusplayer_drmb_es_subsample_info;
+
+/**
+ * @brief Fragmented MP4 data structure for encrypted es
+ */
+typedef struct {
+ /**
+ * @description The count of subsample informations
+ */
+ uint32_t subsample_count;
+ /**
+ * @description The subsample informations
+ */
+ esplusplayer_drmb_es_subsample_info* subsample_infos;
+} esplusplayer_drmb_es_fmp4_data;
+
+/**
+ * @brief The information to decrypt es packet
+ */
+typedef struct {
+ /**
+ * @description The handle to decrypt es packet.
+ */
+ int32_t handle;
+ /**
+ * @description The algorithm encrypted.
+ */
+ esplusplayer_drmb_es_cipher_algorithm algorithm;
+ /**
+ * @description The es media format.
+ */
+ esplusplayer_drmb_es_media_format format;
+ /**
+ * @description The phase to decrypt.
+ */
+ esplusplayer_drmb_es_cipher_phase phase;
+ /**
+ * @description The KID.
+ */
+ unsigned char* kid;
+ /**
+ * @description The length of KID.
+ */
+ uint32_t kid_length;
+ /**
+ * @description The vector for initialization.
+ */
+ unsigned char* iv;
+ /**
+ * @description The length of IV.
+ */
+ uint32_t iv_length;
+ /**
+ * @description The sub data.
+ * @see esplusplayer_drmb_es_fmp4_data
+ */
+ void* sub_data;
+ /**
+ * @description The offset of sample.
+ * It can be NULL.
+ * If used, it have to be -1 terminated.
+ * Max offset is 15.
+ */
+ int* split_offsets;
+ /**
+ * @description It should be 0 when it must be protected with trustzone.
+ */
+ bool use_out_buffer;
+ /**
+ * @description If use 'cbcs' pattern scheme, It should be 1. otherwise 0.
+ */
+ bool use_pattern;
+ /**
+ * @description In case that use_patter is 1,
+ * count of the encrypted blocks in the protection pattern.
+ */
+ uint32_t crypt_byte_block;
+ /**
+ * @description In case that use_patter is 1,
+ * count of the unencrypted blocks in the protection pattern.
+ */
+ uint32_t skip_byte_block;
+} esplusplayer_drm_info;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_DRM_H__
--- /dev/null
+/**
+ * @file
+ * @brief Error related enums
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.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) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ERROR_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ERROR_H__
+
+#include "tizen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ESPLUSPLAYER_ERROR_CLASS TIZEN_ERROR_PLAYER | 0x20
+
+/* This is for custom defined esplusplayer error. */
+#define ESPLUSPLAYER_CUSTOM_ERROR_CLASS TIZEN_ERROR_PLAYER | 0x1000
+
+/**
+ * @brief Enumerations for the error type
+ */
+enum esplusplayer_error_type {
+ ESPLUSPLAYER_ERROR_TYPE_NONE = TIZEN_ERROR_NONE, /**< Successful */
+ ESPLUSPLAYER_ERROR_TYPE_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory */
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION, /**< Invalid operation */
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_STATE = ESPLUSPLAYER_ERROR_CLASS | 0x02, /**< Invalid state */
+ ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_AUDIO_CODEC = ESPLUSPLAYER_ERROR_CLASS | 0x0e, /**< Not supported audio codec but video can be played (Since 4.0)*/
+ ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_VIDEO_CODEC = ESPLUSPLAYER_ERROR_CLASS | 0x0f, /**< Not supported video codec but audio can be played (Since 4.0)*/
+ ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FILE = ESPLUSPLAYER_ERROR_CLASS | 0x03, /**< File format not supported */
+ ESPLUSPLAYER_ERROR_TYPE_CONNECTION_FAILED = ESPLUSPLAYER_ERROR_CLASS | 0x06, /**< Streaming connection failed */
+ ESPLUSPLAYER_ERROR_TYPE_DRM_EXPIRED = ESPLUSPLAYER_ERROR_CLASS | 0x08, /**< Expired license */
+ ESPLUSPLAYER_ERROR_TYPE_DRM_NO_LICENSE = ESPLUSPLAYER_ERROR_CLASS | 0x09, /**< No license */
+ ESPLUSPLAYER_ERROR_TYPE_DRM_FUTURE_USE = ESPLUSPLAYER_ERROR_CLASS | 0x0a, /**< License for future use */
+ ESPLUSPLAYER_ERROR_TYPE_NOT_PERMITTED = ESPLUSPLAYER_ERROR_CLASS | 0x0b, /**< Format not permitted */
+
+ ESPLUSPLAYER_ERROR_TYPE_DRM_DECRYPTION_FAILED = ESPLUSPLAYER_CUSTOM_ERROR_CLASS | 0x05, /**< drm decryption failed */
+ ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FORMAT = ESPLUSPLAYER_CUSTOM_ERROR_CLASS | 0x08,/**< format not supported */
+ ESPLUSPLAYER_ERROR_TYPE_UNKNOWN
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ERROR_H__
--- /dev/null
+/**
+ * @file
+ * @brief The packet for elementary stream
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.0
+ * @SDK_Support N
+ * @see esplusplayer::EsPlusPlayer class
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ESPACKET_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ESPACKET_H__
+
+#include <cstdint>
+
+#include "esplusplayer_capi/matroska_color.h"
+#include "esplusplayer_capi/stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Es packet structure
+ */
+typedef struct {
+ /**
+ * @description The stream type.
+ */
+ esplusplayer_stream_type type;
+ /**
+ * @description The buffer data pointer
+ */
+ char* buffer;
+ /**
+ * @description The buffer size.
+ */
+ uint32_t buffer_size;
+ /**
+ * @description The pts value in milisecond.
+ */
+ uint64_t pts;
+ /**
+ * @description The duration value in milisecond.
+ */
+ uint64_t duration;
+ /**
+ * @description The matroska color information. this value is only for video
+ * packet. If you set this value on a packet of other type, you can see an
+ * error when you submit the packet.
+ */
+ esplusplayer_matroska_color* matroska_color_info;
+ /**
+ * @description The hdr10+ metadata size.
+ */
+ uint32_t hdr10p_metadata_size;
+ /**
+ * @description The hdr10+ metadata.
+ */
+ char* hdr10p_metadata;
+} esplusplayer_es_packet;
+
+/**
+ * @brief Trust zone es packet structure
+ */
+typedef struct {
+ /**
+ * @description The Stream type.
+ */
+ esplusplayer_stream_type type;
+ /**
+ * @description The tz handle.
+ */
+ uint32_t handle;
+ /**
+ * @description The tz handle size.
+ */
+ uint32_t handle_size;
+ /**
+ * @description The pts value in milisecond.
+ */
+ uint64_t pts;
+ /**
+ * @description The duration value in milisecond.
+ */
+ uint64_t duration;
+ /**
+ * @description The matroska color information. this value is only for video
+ * packet. If you set this value on a packet of other type, you can see an
+ * error when you submit the packet.
+ */
+ esplusplayer_matroska_color* matroska_color_info;
+} esplusplayer_es_tz_packet;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ESPACKET_H__
--- /dev/null
+/**
+ * @file esplusplayer_capi.h
+ * @brief EsPlusPlayer api c version
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.0
+ * @SDK_Support N
+ * @remark This is esplusplayer api header implemented as C style to
+ * avoid binary compatibility issues.
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ESPLUSPLAYER_CAPI_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ESPLUSPLAYER_CAPI_H__
+
+#include "esplusplayer_capi/buffer.h"
+#include "esplusplayer_capi/display.h"
+#include "esplusplayer_capi/drm.h"
+#include "esplusplayer_capi/error.h"
+#include "esplusplayer_capi/espacket.h"
+#include "esplusplayer_capi/event.h"
+#include "esplusplayer_capi/latency.h"
+#include "esplusplayer_capi/state.h"
+#include "esplusplayer_capi/stream.h"
+#include "esplusplayer_capi/submitdatatype.h"
+#include "esplusplayer_capi/submitstatus.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+typedef void (*esplusplayer_error_cb)(const esplusplayer_error_type, void*);
+typedef void (*esplusplayer_buffer_status_cb)(const esplusplayer_stream_type,
+ const esplusplayer_buffer_status,
+ void*);
+typedef void (*esplusplayer_buffer_byte_status_cb)(
+ const esplusplayer_stream_type, const esplusplayer_buffer_status, uint64_t,
+ void*);
+typedef void (*esplusplayer_buffer_time_status_cb)(
+ const esplusplayer_stream_type, const esplusplayer_buffer_status, uint64_t,
+ void*);
+typedef void (*esplusplayer_resource_conflicted_cb)(void*);
+typedef void (*esplusplayer_eos_cb)(void*);
+typedef void (*esplusplayer_ready_to_prepare_cb)(const esplusplayer_stream_type,
+ void*);
+typedef void (*esplusplayer_prepare_async_done_cb)(bool, void*);
+typedef void (*esplusplayer_seek_done_cb)(void*);
+typedef void (*esplusplayer_ready_to_seek_cb)(const esplusplayer_stream_type,
+ const uint64_t, void*);
+typedef void (*esplusplayer_media_packet_video_decoded_cb)(
+ const esplusplayer_decoded_video_packet*, void*);
+typedef void (*esplusplayer_closed_caption_cb)(const char* data, const int size,
+ void* userdata);
+typedef void (*esplusplayer_flush_done_cb)(void*);
+typedef void (*esplusplayer_event_cb)(const esplusplayer_event_type,
+ const esplusplayer_event_msg, void*);
+typedef void (*esplusplayer_video_latency_status_cb)(
+ const esplusplayer_latency_status latency_status, void*);
+typedef void (*esplusplayer_audio_latency_status_cb)(
+ const esplusplayer_latency_status latency_status, void*);
+typedef void (*esplusplayer_video_high_latency_cb)(void*);
+typedef void (*esplusplayer_audio_high_latency_cb)(void*);
+typedef void (*esplusplayer_video_frame_dropped_cb)(const uint64_t count,
+ void*);
+typedef void (*esplusplayer_decoder_buffer_time_cb)(
+ const esplusplayer_stream_type, const esplusplayer_decoder_buffer_time, void*);
+
+typedef void* esplusplayer_handle;
+
+/**
+ * @brief Enumerations for the Adaptive info type
+ */
+enum esplusplayer_adaptive_info_type {
+ ESPLUSPLAYER_ADAPT_INFO_TYPE_NONE,
+ ESPLUSPLAYER_ADAPT_INFO_TYPE_DROPPED_FRAMES,
+ ESPLUSPLAYER_ADAPT_INFO_TYPE_DROPPED_VIDEO_FRAMES_FOR_CATCHUP,
+ ESPLUSPLAYER_ADAPT_INFO_TYPE_DROPPED_AUDIO_FRAMES_FOR_CATCHUP,
+};
+
+/**
+ * @brief Enumerations for low latency mode
+ * @version 3.2
+ */
+enum esplusplayer_low_latency_mode {
+ ESPLUSPLAYER_LOW_LATENCY_MODE_NONE = 0x0000,
+ /**
+ * @description to support audio fast decoding/rendering
+ */
+ ESPLUSPLAYER_LOW_LATENCY_MODE_AUDIO = 0x0001,
+ /**
+ * @description to support video fast decoding/rendering
+ * Video stream should be composed only of P and I frames.
+ * The mode support seamless resolution change since tizen 6.5
+ */
+ ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO = 0x0010,
+ /**
+ * @description to support video fast decoding/rendering and video
+ * distortion concealment.
+ * Video stream should be composed only of P and I frames.
+ * For applications using the UDP protocol, packet loss can
+ * occur. when video distortion by video packet loss is
+ * detected, it is a function to conceal distortion by showing
+ * previous vido frame. It is supported only in h.264 codec &
+ * FHD or lower resolution.
+ */
+ ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO_DISTORTION_CONCEALMENT =
+ ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO | 0x0020,
+ /**
+ * @description to disable clock sync and a/v sync when rendering. it
+ * includes #ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_PREROLL.
+ */
+ ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC = 0x0100,
+ /**
+ * @description to disable preroll which means player doesn't wait for
+ * first buffer when state is changed to
+ * #ESPLUSPLAYER_STATE_READY from #ESPLUSPLAYER_STATE_IDLE.
+ * It changes the state immediately.
+ * It's usually used for sparse stream. (e.g. video packet
+ * arrives but audio packet does't yet.)
+ */
+ ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_PREROLL = 0x0200,
+ /**
+ * @deprecated Deprecated since tizen 6.5
+ * @description to set lower video quality
+ * If set this value, it can use original game_mode.
+ * This value will be deprecated from 2022TV.
+ * Please use ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE.
+ */
+ ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY = 0x1000,
+ /**
+ * @description to set game mode for minimum latency
+ * Video stream should be composed only of P and I frames.
+ * It must not be used together with
+ * #ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY.
+ * If use this value, It can expect better latency performance
+ * than #ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY.
+ * The mode support seamless resolution change.
+ * The mode use lower video quality.
+ */
+ ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE =
+ ESPLUSPLAYER_LOW_LATENCY_MODE_AUDIO |
+ ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO | 0x2000,
+ /**
+ * @description to set game mode for latency
+ * Video stream should be composed only of P and I frames.
+ * Video stream must use fixed resolution.
+ * It must not be used together with
+ * #ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY.
+ * If use this value, It can expect better latency
+ * performance than
+ * #ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY and
+ * #ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE
+ * The mode use lower video quality.
+ */
+ ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE_WITH_FIXED_RESOLUTION =
+ ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE | 0x4000,
+};
+
+/**
+ * @brief Enumerations for esplusplayer audio codec type
+ */
+enum esplusplayer_audio_codec_type {
+ /**
+ * @description hardware codec can only be selected, default type
+ */
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_HW,
+ /**
+ * @description sorfware codec can only be selected
+ */
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW
+};
+
+/**
+ * @brief Enumerations for esplusplayer video codec type
+ */
+enum esplusplayer_video_codec_type {
+ /**
+ * @description hardware codec can only be selected, default type
+ */
+ ESPLUSPLAYER_VIDEO_CODEC_TYPE_HW,
+ /**
+ * @description software codec can only be selected
+ */
+ ESPLUSPLAYER_VIDEO_CODEC_TYPE_SW,
+ /**
+ * @description hardware codec using n-decoding mode can only be selected.
+ It must set display type to mixer type by display setting
+ api.
+ esplusplayer_set_display()
+ */
+ ESPLUSPLAYER_VIDEO_CODEC_TYPE_HW_N_DECODING
+
+};
+/**
+ * @brief Enumerations for esplusplayer audio easing type
+ * @version 3.0
+ */
+enum esplusplayer_audio_easing_type {
+ /**
+ * @description audio easing function type is linear
+ */
+ ESPLUSPLAYER_AUDIO_EASING_LINEAR,
+ /**
+ * @description audio easing function type is incubic
+ */
+ ESPLUSPLAYER_AUDIO_EASING_INCUBIC,
+ /**
+ * @description audio easing function type is outcubic
+ */
+ ESPLUSPLAYER_AUDIO_EASING_OUTCUBIC,
+ /**
+ * @description audio easing function type is none
+ */
+ ESPLUSPLAYER_AUDIO_EASING_NONE
+};
+
+/**
+ * @brief Enumerations for esplusplayer resource type
+ * @version 3.0
+ */
+enum esplusplayer_rsc_type {
+ /**
+ * @description video renderer type
+ */
+ ESPLUSPLAYER_RSC_TYPE_VIDEO_RENDERER
+};
+
+/**
+ * @brief Enumerations for advanced video quality type
+ * @version 4.0
+ */
+enum esplusplayer_advanced_picture_quality_type {
+ /**
+ * @description advanced picture quality for video call
+ * @version 3.1
+ */
+ ESPLUSPLAYER_ADVANCED_PICTURE_QUALITY_VIDEO_CALL,
+ /**
+ * @description advanced picture quality for usb camera
+ * @version 3.1
+ */
+ ESPLUSPLAYER_ADVANCED_PICTURE_QUALITY_USB_CAMERA,
+ /**
+ * @description advanced picture quality for airplay screen mirroring
+ * @version 4.0
+ */
+ ESPLUSPLAYER_ADVANCED_PICTURE_QUALITY_AIRPLAY_MIRRORING
+};
+
+/**
+ * @brief Enumerations for audio resource type
+ * @version 3.8
+ */
+enum esplusplayer_audio_resource_type {
+ /**
+ * @description all audio resources(decoder/audio out) to main resources
+ */
+ ESPLUSPLAYER_AUDIO_RESOURCE_MAIN,
+ /**
+ * @description only audio decoder to sub resource
+ */
+ ESPLUSPLAYER_AUDIO_RESOURCE_SUB_DECODER,
+ /**
+ * @description only audio out to simple mix out resource
+ */
+ ESPLUSPLAYER_AUDIO_RESOURCE_SIMPLE_MIX_OUT
+};
+
+/**
+ * @brief Enumerations for buffer level of simple mix out
+ * @version 3.8
+ */
+enum esplusplayer_simple_mix_out_buffer_level {
+ /**
+ * @description buffer level is low
+ */
+ ESPLUSPLAYER_SIMPLE_MIX_OUT_BUFFER_LOW,
+ /**
+ * @description buffer level is middle, default type
+ */
+ ESPLUSPLAYER_SIMPLE_MIX_OUT_BUFFER_MID,
+ /**
+ * @description buffer level is high
+ */
+ ESPLUSPLAYER_SIMPLE_MIX_OUT_BUFFER_HIGH
+};
+
+/**
+ * @brief ESPlusplayer easing target volume, duration, type information
+ * @version 3.0
+ */
+typedef struct {
+ /**
+ * @description audio easing target volume (0 ~ 100)
+ */
+ unsigned int volume;
+ /**
+ * @description audio easing duration, in millisecond
+ */
+ unsigned int duration;
+ /**
+ * @description audio easing function type
+ */
+ esplusplayer_audio_easing_type type;
+} esplusplayer_target_audio_easing_info;
+
+/**
+ * @brief ESPlusplayer 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;
+} esplusplayer_app_info;
+
+/**
+ * @brief ESPlusplayer app id, version, type, runtitle information
+ * @version 5.0
+ */
+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;
+ /**
+ * @description App runtitle.
+ */
+ char* runtitle;
+} esplusplayer_app_info_ex;
+
+typedef struct {
+ /**
+ * @description the minimum frame number in case of mid latency
+ */
+ int mid_latency_threshold;
+ /**
+ * @description the minimum frame number in case of high latency
+ */
+ int high_latency_threshold;
+} esplusplayer_latency_threshold;
+
+/**
+ * @brief rational number numerator/denominator
+ */
+typedef struct {
+ /**
+ * @description the numerator value
+ */
+ int num;
+ /**
+ * @description the denominator value
+ */
+ int den;
+} esplusplayer_rational;
+
+/**
+ * @brief resource allocate policy
+ * @version 3.3
+ */
+enum esplusplayer_rsc_alloc_policy {
+ /**
+ * @description exclusive policy, RM will return the requested resources,
+ * default policy
+ */
+ ESPLUSPLAYER_RSC_ALLOC_EXCLUSIVE = 0,
+ /**
+ * @description conditional policy, when trying to allocate resources and
+ * available resources are not left, RM will return fail.
+ */
+ ESPLUSPLAYER_RSC_ALLOC_EXCLUSIVE_CONDITIONAL,
+ /**
+ * @description exclusive no explicit policy, RM will return available
+ * resources.
+ * @version 6.0
+ */
+ ESPLUSPLAYER_RSC_ALLOC_EXCLUSIVE_NO_EXPLICIT
+};
+
+/**
+ * @brief Enumerations for the status of getting decoded video frame
+ * @version 4.0
+ */
+enum esplusplayer_get_decoded_video_frame_status_type {
+ /** @brief successfully decoded video frame acquired. */
+ ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_SUCCESS,
+ /** @brief it means app has to return the video before getting decoded
+ * video frame frame.
+ */
+ ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_NO_REMAINING_BUFFER,
+ /**
+ * @brief there is no filled video frame yet.
+ */
+ ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_NO_FILLED_BUFFER,
+ /**
+ * @brief internal error
+ */
+ ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_UNKNOWN
+};
+
+/**
+ * @brief Enumerations for the video scan type
+ * @version 4.0
+ */
+enum esplusplayer_video_scan_type {
+ /**
+ * @description progressive, mfd or dvde will be allocated for H.264 2K in
+ * normal mode.
+ */
+ ESPLUSPLAYER_VIDEO_SCAN_TYPE_PROGRESSIVE,
+ /**
+ * @description interlaced, only mfd has been allocated for H.264 2K in normal
+ * mode.
+ */
+ ESPLUSPLAYER_VIDEO_SCAN_TYPE_INTERLACED,
+};
+
+/**
+ * @brief Enumerations for the time unit type
+ * @version 5.0
+ */
+enum esplusplayer_time_unit_type {
+ /**
+ * @description the timeunit will be ms. It is default value.
+ */
+ ESPLUSPLAYER_TIME_UNIT_MS,
+ /**
+ * @description the timeunit will be us.
+ */
+ ESPLUSPLAYER_TIME_UNIT_US,
+};
+
+/**
+ * @brief Enumerations for the video stream rotation type
+ * @version 5.2
+ */
+enum esplusplayer_video_stream_rotation_type {
+ ESPLUSPLAYER_VIDEO_ROTATION_NONE,
+ ESPLUSPLAYER_VIDEO_ROTATION_90,
+ ESPLUSPLAYER_VIDEO_ROTATION_180,
+ ESPLUSPLAYER_VIDEO_ROTATION_270
+};
+
+/**
+ * @brief Create a esplusplayer handle.
+ * @param None
+ * @return return esplusplayer handle pointer.
+ * @code
+ * esplusplayer_handle esplayer = esplusplayer_create();
+ * // ... your codes ...
+ * esplusplayer_destroy(esplayer);
+ * @endcode
+ * @pre None
+ * @post The player state will be #ESPLUSPLAYER_STATE_NONE.
+ * @exception None
+ */
+esplusplayer_handle esplusplayer_create();
+
+/**
+ * @brief Open esplusplayer handle.
+ * @param [in] handle : esplusplayer handle
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_handle esplayer = esplusplayer_create();
+ * esplusplayer_open(esplayer);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * esplusplayer_destroy(esplayer);
+ * @endcode
+ * @pre The player state must be #ESPLUSPLAYER_STATE_NONE.
+ * @post The player state will be #ESPLUSPLAYER_STATE_IDLE.
+ * @exception None
+ * @see esplusplayer_close()
+ */
+int esplusplayer_open(esplusplayer_handle handle);
+
+/**
+ * @brief Release all the player resources and all setting except callback
+ * functions.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @pre The player state must be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post The player state will be #ESPLUSPLAYER_STATE_NONE.
+ * @exception None
+ * @see esplusplayer_open()
+ */
+int esplusplayer_close(esplusplayer_handle handle);
+
+/**
+ * @brief Release player handle.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_create()
+ * @endcode
+ * @pre The player state must be #ESPLUSPLAYER_STATE_NONE
+ * @post player handle will be removed.
+ * @exception None
+ * @see esplusplayer_create()
+ */
+int esplusplayer_destroy(esplusplayer_handle handle);
+
+/**
+ * @brief Flush the specific buffered stream data and release TV resource
+ * to change stream.
+ * @remark To activate, the stream must be set again.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : stream type which user want to deactivate.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ refer to the sample code of esplusplayer_activate()
+ * @endcode
+ * @pre The player state must be at least #ESPLUSPLAYER_STATE_READY
+ * @post The player state is same as before calling
+ * esplusplayer_deactivate(). The deactivated stream will stop
+ * rendering and release the decorer, renderer resources.
+ * @exception None
+ * @see esplusplayer_activate
+ */
+int esplusplayer_deactivate(esplusplayer_handle handle,
+ esplusplayer_stream_type type);
+
+/**
+ * @brief Reprepare for the specific stream playback.
+ * @remark There must be active stream to prepare playback.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : stream type which user want to activate.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_deactivate(esplayer, ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ * esplusplayer_set_video_stream_info* stream;
+ * stream->width = 640;
+ * stream->height = 352;
+ * stream->mime_type = ESPLUSPLAYER_VIDEO_MIME_TYPE_H264;
+ * stream->framerate_num = 30;
+ * stream->framerate_den = 1;
+ * esplusplayer_set_video_stream_info(esplayer, stream);
+ * esplusplayer_activate(esplayer, ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * esplusplayer_destroy(esplayer);
+ * @endcode
+ * @pre The player state must be at least #ESPLUSPLAYER_STATE_READY
+ * @post The player state is same as before calling
+ * esplusplayer_activate(). Rebuild pipeline to render the stream.
+ * @exception None
+ * @see esplusplayer_prepare_async()
+ */
+int esplusplayer_activate(esplusplayer_handle handle,
+ esplusplayer_stream_type type);
+
+/**
+ * @brief Prepare the player for playback, asynchronously.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * static void OnPrepareDone(bool ret, void* userdata) {
+ * //Something you want to do when prepare done, but, we strongly
+ * //recommend DO NOT CALL PLAYER APIs in this callbck
+ * printf("OnPrepareDone\n");
+ * }
+ * esplusplayer_handle esplayer = esplusplayer_create();
+ * esplusplayer_set_prepare_async_done_cb(esplayer, OnPrepareDone,nullptr);
+ * esplusplayer_open(esplayer);
+ * esplusplayer_prepare_async(esplayer);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * esplusplayer_destroy(esplayer);
+ * @endcode
+ * @pre The player state must be #ESPLUSPLAYER_STATE_IDLE. \n
+ * Call at least one of esplusplayer_set_video_stream_info() or
+ * esplusplayer_set_audio_stream_info(). \n
+ * @post It invokes esplusplayer_prepare_async_done_cb() when prepare is
+ * finished. \n
+ * Prepare result can be succeeded or not at this moment. \n
+ * If the result is succeeded, the player state will be
+ * #ESPLUSPLAYER_STATE_READY and one frame will be displayed
+ * unless esplusplayer_set_display_visible() is set to @c false.
+ * @exception None
+ * @remark esplusplayer_prepare_async_done_cb() can be invoked only when as
+ * many es packets as at least one decoded frame is submitted. \n
+ * The player can receive es packets after
+ * esplusplayer_ready_to_seek_cb() is called.
+ * @see esplusplayer_open() \n
+ * esplusplayer_stop() \n
+ * esplusplayer_submit_packet() \n
+ * esplusplayer_ready_to_prepare_cb() \n
+ * esplusplayer_close()
+ */
+int esplusplayer_prepare_async(esplusplayer_handle handle);
+
+/**
+ * @brief Start playback.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * esplusplayer_start(esplayer);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player state should be #ESPLUSPLAYER_STATE_READY.
+ * @post The player state will be #ESPLUSPLAYER_STATE_PLAYING.
+ * @exception None
+ * @see esplusplayer_open() \n
+ * esplusplayer_prepare_async() \n
+ * esplusplayer_stop() \n
+ * esplusplayer_close()
+ */
+int esplusplayer_start(esplusplayer_handle handle);
+
+/**
+ * @brief Stop playing media content.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post The player state will be #ESPLUSPLAYER_STATE_IDLE.
+ * @exception None
+ * @remark esplusplayer_close() must be called once after player is stopped
+ * @see esplusplayer_open() \n
+ * esplusplayer_prepare_async() \n
+ * esplusplayer_start() \n
+ * esplusplayer_pause() \n
+ * esplusplayer_resume() \n
+ * esplusplayer_close()
+ */
+int esplusplayer_stop(esplusplayer_handle handle);
+
+/**
+ * @brief Pause playing media content.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_pause(esplayer);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_READY or
+ * #ESPLUSPLAYER_STATE_PAUSED or #ESPLUSPLAYER_STATE_PLAYING.
+ * @post The player state will be #ESPLUSPLAYER_STATE_PAUSE.
+ * @exception None
+ * @see esplusplayer_start() \n
+ * esplusplayer_resume() \n
+ * esplusplayer_prepare_async()
+ */
+int esplusplayer_pause(esplusplayer_handle handle);
+
+/**
+ * @brief Resume playing media content.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_pause(esplayer);
+ * // ... your codes ...
+ * esplusplayer_resume(esplayer);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_PAUSED or
+ * #ESPLUSPLAYER_STATE_PLAYING.
+ * @post The player state will be #ESPLUSPLAYER_STATE_PLAYING.
+ * @exception None
+ * @see esplusplayer_start() \n
+ * esplusplayer_pause() \n
+ * esplusplayer_prepare_async()
+ */
+int esplusplayer_resume(esplusplayer_handle handle);
+
+/**
+ * @brief Set playback rate.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] playback_rate : the playback rate from 0.0 to 2.0.
+ * @param [in] audio_mute : the audio is mute on/off, true: mute on, false:
+ * mute off.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_set_playback_rate(esplayer,2,true);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_READY or
+ * #ESPLUSPLAYER_STATE_PAUSED or #ESPLUSPLAYER_STATE_PLAYING. \n
+ * User has to push the data as fast as playback rate.
+ * @post None
+ * @exception None
+ * @see esplusplayer_prepare_async()
+ */
+int esplusplayer_set_playback_rate(esplusplayer_handle handle,
+ const double playback_rate,
+ const bool audio_mute);
+
+/**
+ * @brief Seek for playback, asynchronously.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] time : seek time default in milliseconds, can be set by @esplusplayer_set_timeunit_type
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * const uint64_t ms_to_seek = 0;
+ * esplusplayer_seek(esplayer,ms_to_seek);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_READY or
+ * #ESPLUSPLAYER_STATE_PAUSED or #ESPLUSPLAYER_STATE_PLAYING.
+ * In ESPLUSPLAYER_STATE_IDLE, this api can be called exceptionally
+ * between esplusplayer_open() and esplusplayer_prepare_async().
+ * the start time of plyabak can be set explicitly when starting
+ * first playback. In this case, esplusplayer_set_seek_done_cb is not
+ * called.
+ * @post None
+ * @exception None
+ * @remark esplusplayer_set_seek_done_cb() will be invoked if seek operation
+ * is finished. \n
+ * Seek result can be succeeded or not at this moment. \n
+ * esplusplayer_set_seek_done_cb() can be invoked only when as many
+ * es packets as at least one decoded frame is submitted. \n
+ * The player can receive es packets from seek time after
+ * esplusplayer_ready_to_seek_cb() is invoked.
+ * @see esplusplayer_ready_to_seek_cb() \n
+ * esplusplayer_prepare_async()
+ */
+int esplusplayer_seek(esplusplayer_handle handle, uint64_t time);
+
+/**
+ * @brief Set App id to esplayer to control resource confliction.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] app_info : application id, version, type.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_app_info appinfo;
+ * appinfo.id = "youtube";
+ * appinfo.version = "3.0";
+ * appinfo.type = "MSE";
+ * esplusplayer_handle esplayer = esplusplayer_create();
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_app_info(esplayer,&appinfo);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_app_info(esplusplayer_handle handle,
+ const esplusplayer_app_info* app_info);
+
+/**
+ * @brief Set the video display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : display type.
+ * @param [in] window : the handle to display window.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_display(esplayer,ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,window);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @remark Esplusplayer is not supporting changing display. \n
+ * This API have to be called before calling
+ * esplusplayer_prepare_async() to reflect the display type.
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_display_mode() \n
+ * esplusplayer_set_display_roi() \n
+ * esplusplayer_set_display_visible()
+ */
+int esplusplayer_set_display(esplusplayer_handle handle,
+ esplusplayer_display_type type, void* window);
+/**
+ * @brief Set the video display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : display type.
+ * @param [in] subsurface : the ecore wayland subsurface handle.
+ * @param [in] x : the x coordinate of subsurface.
+ * @param [in] y : the y coordinate of subsurface.
+ * @param [in] width : the width of subsurface.
+ * @param [in] height : the height of subsurface.
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @pre The player state must be #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @version 3.1
+ * @see esplusplayer_set_display_mode() \n
+ * esplusplayer_set_display_roi() \n
+ * esplusplayer_set_display_visible()
+ */
+int esplusplayer_set_display_ecore_subsurface(esplusplayer_handle handle,
+ esplusplayer_display_type type,
+ void* subsurface, int x, int y,
+ int width, int height);
+/**
+ * @brief Set the video display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : display type.
+ * @param [in] window : the ecore wayland2 window handle.
+ * @param [in] x : the x coordinate of window.
+ * @param [in] y : the ycoordinate of window.
+ * @param [in] width : the width of window.
+ * @param [in] height : the height of window.
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @pre The player state must be #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @version 4.0
+ * @see esplusplayer_set_display_mode() \n
+ * esplusplayer_set_display_roi() \n
+ * esplusplayer_set_display_visible()
+ */
+int esplusplayer_set_ecore_display(esplusplayer_handle handle,
+ esplusplayer_display_type type, void* window,
+ int x, int y, int width, int height);
+/**
+ * @brief Set the video display mode.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] mode : display mode.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_display_mode(esplayer,ESPLUSPLAYER_DISPLAY_MODE_DST_ROI);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @remark If no display is set, no operation is performed.
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_display_mode() \n
+ * esplusplayer_set_display_roi() \n
+ * esplusplayer_set_display_visible()
+ */
+int esplusplayer_set_display_mode(esplusplayer_handle handle,
+ esplusplayer_display_mode mode);
+
+/**
+ * @brief Set the ROI(Region Of Interest) area of display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] x : var startPointX in src video area.
+ * @param [in] y : var startPointY in src video area.
+ * @param [in] width : width of display in src video area.
+ * @param [in] height : height of display in src video area.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_display(esplayer,ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,window);
+ * esplusplayer_set_display_mode(esplayer,ESPLUSPLAYER_DISPLAY_MODE_DST_ROI);
+ * esplusplayer_set_display_roi(esplayer,0,0,600,500);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE. \n
+ * Before set display ROI, #ESPLUSPLAYER_DISPLAY_MODE_DST_ROI
+ * must be set with esplusplayer_set_display_mode().
+ * @post None
+ * @exception None
+ * @remark The minimum value of width and height are 1.
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_display() \n
+ * esplusplayer_set_display_mode() \n
+ * esplusplayer_set_display_visible()
+ */
+int esplusplayer_set_display_roi(esplusplayer_handle handle, int x, int y,
+ int width, int height);
+
+
+
+/**
+ * @brief Set the video stretch mode on 21:9 TV.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] mode : stretch mode.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_stretch_mode(esplayer,1);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @version 4.8
+ * @remark If no display is set, no operation is performed.
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_video_roi()
+ */
+int esplusplayer_set_stretch_mode(esplusplayer_handle handle,
+ int mode);
+
+
+/**
+ * @brief Set the Crop Area(Region Of Src ratio) area of display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] scale_x: x label ratio in src video area.
+ * @param [in] scale_y: y label ratio in src video area.
+ * @param [in] scale_w: width ratio in src video area.
+ * @param [in] scale_h: height ratio in src video area.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_display(esplayer,ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,window);
+ * esplusplayer_set_video_roi(esplayer,0,0,0.5,0.5);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE. \n
+ * @post None
+ * @exception None
+ * @remark The minimum value of input are 0,maximun value is 1.
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_display() \n
+ * esplusplayer_set_display_visible()
+ */
+int esplusplayer_set_video_roi(esplusplayer_handle handle, double scale_x,
+ double scale_y, double scale_w, double scale_h);
+
+/**
+ * @brief Resize the render rectangle(the max region that video can be
+ * displayed).
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] x: x coordinate of render rectangle.
+ * @param [in] y: y coordinate of render rectangle.
+ * @param [in] width: width of render rectangle.
+ * @param [in] height: height of render rectangle.
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @pre Should be called after esplusplayer_set_display() \n
+ * esplusplayer_set_surface_display() \n
+ * esplusplayer_set_ecore_display() \n
+ * esplusplayer_set_display_ecore_subsurface
+ * @post None
+ * @exception None
+ * @version 3.2
+ * @remark The minimum value of width and height are 1.
+ * @see esplusplayer_set_display() \n
+ * esplusplayer_set_surface_display() \n
+ * esplusplayer_set_ecore_display() \n
+ * esplusplayer_set_display_ecore_subsurface
+ */
+int esplusplayer_resize_render_rect(esplusplayer_handle handle, int x, int y,
+ int width, int height);
+
+/**
+ * @brief Set the visibility of the video display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] visible : the visibility of the display.
+ * (@c true = visible, @c false = non-visible)
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_display(esplayer,ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,window);
+ * esplusplayer_set_display_visible(esplayer,false);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_display()
+ */
+int esplusplayer_set_display_visible(esplusplayer_handle handle, bool visible);
+
+/**
+ * @brief Set the rotate angle of the video display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] rotation : the rotate angle of the display.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_display(esplayer,ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,window);
+ * esplusplayer_set_display_rotation(esplayer_,ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_90);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post this API worked only when video sink created.
+ * @exception None
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_display()
+ */
+int esplusplayer_set_display_rotation(
+ esplusplayer_handle handle, esplusplayer_display_rotation_type rotation);
+
+/**
+ * @brief Get the rotate angle of the video display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [out] rotation : the rotate angle of the display which want to
+ * get.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_display_rotation(esplayer,ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_90);
+ * esplusplayer_display_rotation_type rotation_get = ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_NONE;
+ * // ... your codes ...
+ * esplusplayer_get_display_rotation(esplayer,&rotation_get);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post this API worked only when video sink created.
+ * @exception None
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_display_rotation()
+ */
+int esplusplayer_get_display_rotation(
+ esplusplayer_handle handle, esplusplayer_display_rotation_type* rotation);
+
+/**
+ * @deprecated Deprecated since API V2.0. Use
+ * esplusplayer_set_submit_data_type() instead.
+ * @brief Set whether to send decrypted es packets in the trust zone.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] using_tz : whether to use trust zone memory.
+ * (@c true = if decrypted packets are sent in trust zone, @c false =
+ * otherwise @c)
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_tz_use(esplayer, true);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @remark This API have to be called before calling
+ * esplusplayer_prepare_async(). \n If using_tz is set to true, use
+ * esplusplayer_submit_trust_zone_packet() to send decrypted packets.
+ * @see esplusplayer_open() \n
+ * esplusplayer_submit_trust_zone_packet()
+ */
+int esplusplayer_set_tz_use(esplusplayer_handle handle, bool using_tz);
+
+/**
+ * @brief Set whether to send decrypted es packets in the trust zone or
+ * encrypted es packets.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : whether to use trust zone memory or encrypted data
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_submit_data_type(esplayer,ESPLUSPLAYER_SUBMIT_DATA_TYPE_CLEAN_DATA);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @remark This API have to be called before calling
+ * esplusplayer_prepare_async(). \n
+ * If type is ESPLUSPLAYER_SUBMIT_DATA_TYPE_CLEAN_DATA use
+ * esplusplayer_submit_packet() to send clean packets. \n
+ * If type is ESPLUSPLAYER_SUBMIT_DATA_TYPE_TRUSTZONE_DATA, use
+ * esplusplayer_submit_trust_zone_packet() to send decrypted packets
+ * in trust zone \n
+ * If type is ESPLUSPLAYER_SUBMIT_DATA_TYPE_ENCRYPTED_DATA, use
+ * esplusplayer_submit_encrypted_packet() to send encrypted packets.
+ * @see esplusplayer_open() \n
+ * esplusplayer_submit_trust_zone_packet() \n
+ * esplusplayer_submit_encrypted_packet() \n
+ * esplusplayer_submit_data_type
+ */
+int esplusplayer_set_submit_data_type(esplusplayer_handle handle,
+ esplusplayer_submit_data_type type);
+
+/**
+ * @brief Set on mute of the audio sound.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] mute : on mute of the sound.
+ * (@c true = mute, @c false = non-mute)
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success, otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_audio_mute(esplayer, true);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_audio_mute(esplusplayer_handle handle, bool mute);
+
+/**
+ * @brief Get current state of player.
+ * @param [in] handle : esplusplayer handle.
+ * @return current #esplusplayer_state of player.
+ * @code
+ * esplusplayer_handle esplayer = esplusplayer_create();
+ * // ... your codes ...
+ * esplusplayer_state ret = esplusplayer_get_state(esplayer);
+ * // ... your codes ...
+ * esplusplayer_destroy(esplayer);
+ * @endcode
+ * @pre None
+ * @post None
+ * @exception None
+ */
+esplusplayer_state esplusplayer_get_state(esplusplayer_handle handle);
+
+/**
+ * @brief Submit es packet to decode audio or video.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] packet : es packet pointer.
+ * @return @c ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS : succeed to submit es
+ * packet,
+ * otherwise @c : fail to submit es packet.
+ * @code
+ * static void OnPrepareDone(bool ret, void* userdata) {
+ * // ... your codes ...
+ * printf ("OnPrepareDone\n");
+ * }
+ * static void OnReadyToPrepare(const esplusplayer_stream_type type,void* userdata) {
+ * if (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {
+ * //Something you want to do when feed es video stream is allowed
+ * } else {
+ * //Something you want to do when feed es audio stream is allowed
+ * }
+ * //Something you want to do when OnReadyToPrepare
+ * printf ("OnReadyToPrepare\n");
+ * }
+ * static void OnBufferByteStatus(const esplusplayer_stream_type type,
+ * const esplusplayer_buffer_status status,
+ * uint64_t byte_size, void* userdata) {
+ * if (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {
+ * if (status == ESPLUSPLAYER_BUFFER_STATUS_UNDERRUN) {
+ * //Something you want to do when es video buffer is enough
+ * } else {
+ * //Something you want to do when es video buffer is not enough
+ * }
+ * } else {
+ * if (status == ESPLUSPLAYER_BUFFER_STATUS_UNDERRUN) {
+ * //Something you want to do when es audio buffer is enough
+ * } else {
+ * //Something you want to do when es audio buffer is not enough
+ * }
+ * }
+ * //Something you want to do when OnBufferByteStatus
+ * printf ("OnBufferByteStatus\n");
+ * }
+ * static void OnBufferTimeStatus(const esplusplayer_stream_type type,
+ * const esplusplayer_buffer_status status,
+ * uint64_t time_size,void* userdata) {
+ * if (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {
+ * if (status == ESPLUSPLAYER_BUFFER_STATUS_UNDERRUN) {
+ * //Something you want to do when es video buffer is enough
+ * } else {
+ * //Something you want to do when es video buffer is not enough
+ * }
+ * } else {
+ * if (status == ESPLUSPLAYER_BUFFER_STATUS_UNDERRUN) {
+ * //Something you want to do when es audio buffer is enough
+ * } else {
+ * //Something you want to do when es audio buffer is not enough
+ * }
+ * }
+ * //Something you want to do when OnBufferTimeStatus
+ * printf ("OnBufferTimeStatus\n");
+ * }
+ * void FeedEsPacket(esplusplayer_handle player,esplusplayer_es_packet pkt) {
+ * // ... your codes ...
+ * if(feed is allowed && buffer is enough) {
+ * esplusplayer_submit_packet(player, &pkt);
+ * }
+ * // ... your codes ...
+ * )
+ * esplusplayer_handle esplayer = esplusplayer_create();
+ * esplusplayer_set_prepare_async_done_cb(esplayer,OnPrepareDone,&esplayer);
+ * esplusplayer_set_ready_to_prepare_cb(esplayer,OnReadyToPrepare,&esplayer);
+ * esplusplayer_set_buffer_byte_status_cb(esplayer,OnBufferByteStatus,&esplayer);
+ * esplusplayer_set_buffer_time_status_cb(esplayer,OnBufferTimeStatus,&esplayer);
+ * esplusplayer_open(esplayer);
+ * esplusplayer_prepare_async(esplayer);
+ * // ... your codes ...
+ * //FeedEsPacket()(call a new thread to do this)
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * esplusplayer_destroy(esplayer);
+ * @endcode
+ * @pre User can submit es packets after
+ * esplusplayer_ready_to_prepare_cb() or
+ * esplusplayer_ready_to_seek_cb() is called.
+ * @post None
+ * @exception None
+ * @remark Amount of packets for at least one decoded frame must be submitted
+ * after calling esplusplayer_prepare_async() or esplusplayer_seek()
+ * for invoking esplusplayer_prepare_async_done_cb() or
+ * esplusplayer_seek_done_cb() \n
+ * This api must be called from a different thread than other apis.
+ * @see esplusplayer_set_submit_data_type() \n
+ * esplusplayer_es_packet \n
+ * esplusplayer_buffer_status_cb() \n
+ * esplusplayer_ready_to_prepare_cb() \n
+ * esplusplayer_ready_to_seek_cb()
+ */
+esplusplayer_submit_status esplusplayer_submit_packet(
+ esplusplayer_handle handle, esplusplayer_es_packet* packet);
+
+/**
+ * @brief Submit es packet to decode audio or video.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] packet : es packet pointer.
+ * @param [in] tz_handle : es decrypted tz handle.
+ * @return @c ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS : succeed to submit es
+ * packet,
+ * otherwise @c : fail to submit es packet.
+ * @code
+ * refer to the sample code of esplusplayer_submit_packet();
+ * @endcode
+ * @pre User can submit es packets after
+ * esplusplayer_ready_to_prepare_cb() or
+ * esplusplayer_ready_to_seek_cb() is called.
+ * @post None
+ * @exception None
+ * @remark Amount of packets for at least one decoded frame must be submitted
+ * after calling esplusplayer_prepare_async() or esplusplayer_seek()
+ * for invoking esplusplayer_prepare_async_done_cb() or
+ * esplusplayer_seek_done_cb(). \n
+ * To use this api, Must set
+ * ESPLUSPLAYER_SUBMIT_DATA_TYPE_TRUSTZONE_DATA using
+ * esplusplayer_set_submit_data_type() \n This api must be called from a
+ * different thread than other apis.
+ * @see esplusplayer_es_packet \n
+ * esplusplayer_buffer_status_cb() \n
+ * esplusplayer_ready_to_prepare_cb() \n
+ * esplusplayer_ready_to_seek_cb()
+ */
+esplusplayer_submit_status esplusplayer_submit_trust_zone_packet(
+ esplusplayer_handle handle, esplusplayer_es_packet* packet,
+ uint32_t tz_handle);
+
+/**
+ * @brief Submit encrypted es packet to decode and decrypt audio or video.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] packet : es packet pointer.
+ * @param [in] drm_info : information to decrypt es packet.
+ * esplusplayer doesn't take ownership. user should
+ * free it. if you deliver it as (null), this api
+ * works as esplusplayer_submit_packet().
+ * @return @c ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS : succeed to submit es
+ * packet,
+ * otherwise @c : fail to submit es packet.
+ * @code
+ * refer to the sample code of esplusplayer_submit_packet();
+ * @endcode
+ * @pre User can submit es packets after
+ * esplusplayer_ready_to_prepare_cb() or
+ * esplusplayer_ready_to_seek_cb() is called.
+ * @post None
+ * @exception None
+ * @remark Amount of packets for at least one decoded frame must be submitted
+ * after calling esplusplayer_prepare_async() or esplusplayer_seek()
+ * for invoking esplusplayer_prepare_async_done_cb() or
+ * esplusplayer_seek_done_cb(). \n
+ * To use this api, Must set
+ * ESPLUSPLAYER_SUBMIT_DATA_TYPE_ENCRYPTED_DATA using
+ * esplusplayer_set_submit_data_type() \n This api must be called from a
+ * different thread than other apis.
+ * @see esplusplayer_es_packet \n
+ * esplusplayer_drm_info \n
+ * esplusplayer_buffer_status_cb() \n
+ * esplusplayer_ready_to_prepare_cb() \n
+ * esplusplayer_ready_to_seek_cb() \n
+ * esplusplayer_submit_packet()
+ */
+esplusplayer_submit_status esplusplayer_submit_encrypted_packet(
+ esplusplayer_handle handle, esplusplayer_es_packet* packet,
+ esplusplayer_drm_info* drm_info);
+
+/**
+ * @brief Generate EOS(End Of Stream) packet explicitly and submit it to the
+ * player.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : stream type which reaches eos.
+ * @return @c ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS : succeed to submit EOS
+ * packet,
+ * otherwise @c : fail to submit EOS packet.
+ * @code
+ * esplusplayer_handle esplayer = esplusplayer_create();
+ * // ... your codes ...
+ * esplusplayer_submit_eos_packet(esplayer,ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ * // ... your codes ...
+ * @endcode
+ * @pre None
+ * @post None
+ * @exception None
+ */
+esplusplayer_submit_status esplusplayer_submit_eos_packet(
+ esplusplayer_handle handle, esplusplayer_stream_type type);
+
+/**
+ * @brief Set audio stream to have contents information.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] stream : audio stream pointer.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_audio_stream_info audio_stream;
+ * audio_stream.codec_data = nullptr;
+ * audio_stream.codec_data_length = 0;
+ * esplusplayer_set_audio_stream_info(esplayer, &audio_stream);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE except
+ * audio stream is deactivated.
+ * @post None
+ * @exception None
+ * @remark This API have to be called before calling the
+ * esplusplayer_prepare_async().
+ * @see esplusplayer_open() \n
+ * esplusplayer_audio_stream_info
+ */
+int esplusplayer_set_audio_stream_info(esplusplayer_handle handle,
+ esplusplayer_audio_stream_info* stream);
+
+/**
+ * @brief Set video stream to have contents information.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] stream : video stream pointer.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE except
+ * video stream is deactivated.
+ * @post None
+ * @exception None
+ * @remark This API have to be called before calling the
+ * esplusplayer_prepare_async().
+ * @see esplusplayer_audio_stream_info
+ * esplusplayer_activate
+ */
+int esplusplayer_set_video_stream_info(esplusplayer_handle handle,
+ esplusplayer_video_stream_info* stream);
+
+/**
+ * @brief Get the current playing time of the associated media.
+ * @param [in] handle : esplusplayer handle.
+ * @param [out] cur_time : current playing time default in milliseconds, can be set by @esplusplayer_set_timeunit_type
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * esplusplayer_start(esplayer);
+ * // ... your codes ...
+ * uint64_t cur_time = 0;
+ * esplusplayer_get_playing_time(esplayer, &cur_time);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player must be one of #ESPLUSPLAYER_STATE_PAUSE or
+ * #ESPLUSPLAYER_STATE_PLAYING.
+ * @post None
+ * @exception None
+ * @see esplusplayer_prepare_async()
+ */
+int esplusplayer_get_playing_time(esplusplayer_handle handle,
+ uint64_t* cur_time);
+/**
+ * @brief Get dropped frame counts in videosink.
+ * @param [in] handle : esplusplayer handle.
+ * @param [out] padaptive_info : dropped frame counts.
+ * @param [in] adaptive_type : type of adaptive info which APP want to get.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * uint64_t count = 0;
+ * esplusplayer_get_adaptive_info(esplayer,
+ * static_cast<void*>(&count),ESPLUSPLAYER_ADAPT_INFO_TYPE_DROPPED_FRAMES);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player must be one of #ESPLUSPLAYER_STATE_READY,
+ * #ESPLUSPLAYER_STATE_PAUSE or #ESPLUSPLAYER_STATE_PLAYING.
+ * @post None
+ * @exception None
+ * @see esplusplayer_prepare_async()
+ */
+int esplusplayer_get_adaptive_info(
+ esplusplayer_handle handle, void* padaptive_info,
+ esplusplayer_adaptive_info_type adaptive_type);
+
+/**
+ * @brief Set volume to player
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] volume : volume level(0 ~ 100).
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * int vol = 80;
+ * esplusplayer_set_volume(esplayer, vol)
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be not #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_volume(esplusplayer_handle handle, const int volume);
+
+/**
+ * @brief Get volume from player
+ * @param [in] handle : esplusplayer handle.
+ * @param [out] volume : volume ptr.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * // ... your codes ...
+ * int vol = 0;
+ * esplusplayer_get_volume(esplayer, &vol)
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be not #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @see esplusplayer_open()
+ */
+int esplusplayer_get_volume(esplusplayer_handle handle, int* volume);
+
+/**
+ * @brief Set decoded video frame buffer type.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : one of the video decoded buffer type to set .
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_video_frame_buffer_type(esplayer,
+ * ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_NONE);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE when type
+ is not equal to be
+ ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE
+ The player state must be not #ESPLUSPLAYER_STATE_NONE when
+ type is equal to be
+ ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE
+ * @post None
+ * @exception None
+ * @remark reference can't support sw codec type.
+ * esplusplayer_set_media_packet_video_decoded_cb()
+ * esplusplayer_set_video_codec_type()
+ * when type is SCALE, the target scale resolution can be set by
+ * esplusplayer_set_video_frame_buffer_scale_resolution()
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_video_frame_buffer_type(
+ esplusplayer_handle handle,
+ esplusplayer_decoded_video_frame_buffer_type type);
+
+/**
+ * @brief Set the request frame rate of decoded video
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] request_framerate : the request frame rate of returned decoded video frame
+ * The value of track_framerate(A) and request_framerate(B) 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 @c one of esplusplayer_error_type values will be returned.
+ * @pre The player state must be not #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @version 3.4
+ * @remark only works when decoded video frame buffer type is scale
+ * esplusplayer_set_video_frame_buffer_type()
+ * esplusplayer_set_media_packet_video_decoded_cb()
+ */
+int esplusplayer_set_decoded_video_frame_rate(
+ esplusplayer_handle handle, esplusplayer_rational request_framerate);
+
+/**
+ * @brief Set the target scale resolution when decoded video frame buffer
+ * type is scale
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] target_width : scale target width of video frame buffer.
+ * @param [in] target_width : scale target height of video frame buffer.
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @pre The player state can be set in all states except #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @version 3.1
+ * @remark esplusplayer_set_video_frame_buffer_type()
+ * esplusplayer_set_media_packet_video_decoded_cb()
+ * If user don't call this api to set target_width and target_height,
+ * the default
+ * target scale resolution is 960x540
+ */
+int esplusplayer_set_video_frame_buffer_scale_resolution(
+ esplusplayer_handle handle, uint32_t target_width, uint32_t target_height);
+
+/**
+ * @brief Flush buffers for a player.
+ * @param [in] handle : esplusplayer handle ptr.
+ * @param [in] type : choose which stream data need to be
+ * flush,audio/video,if need flush all pipeline can call this API
+ * twice.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_flush(esplayer, ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player state should greater than #ESPLUSPLAYER_STATE_IDLE
+ * @post None
+ * @exception None
+ * @see esplusplayer_prepare_async()
+ */
+int esplusplayer_flush(esplusplayer_handle handle,
+ esplusplayer_stream_type type);
+
+/**
+ * @brief Convert the esplusplayer error type to a string.
+ * @param [in] type : esplusplayer error type
+ * @return @c not nullptr the converted error string otherwise @c failed to
+ * convert the error.
+ * @code
+ * // ... your codes ...
+ * const char* error;
+ * error = esplusplayer_get_error_string(ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FILE);
+ * // ... your codes ...
+ * @endcode
+ * @pre None
+ * @post None
+ * @exception None
+ */
+const char* esplusplayer_get_error_string(esplusplayer_error_type type);
+
+/**
+ * @brief Sets a callback function to be invoked when an error occurs.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] error_cb : the error callback function to register.
+ * @param [in] userdata : userdata of esplusplayer_error_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * static void OnError(const esplusplayer_error_type err_code, void*
+ * userdata) {
+ * //Something you want to do when error occur
+ * printf ("OnError\n");
+ * }
+ * esplusplayer_handle esplayer = esplusplayer_create();
+ * esplusplayer_set_error_cb(esplayer, OnError, nullptr);
+ * // ... your codes ...
+ * esplusplayer_destroy(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_error_cb() will be invoked.
+ * @exception None
+ * @remark esplusplayer_error_cb()
+ * if error_cb is set to null, esplusplayer_error_cb() will not be
+ * invoked anymore.
+ */
+int esplusplayer_set_error_cb(esplusplayer_handle handle,
+ esplusplayer_error_cb error_cb, void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when buffer underrun or
+ * overflow is occurred.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] buffer_status_cb : the buffer status callback function to
+ * register.
+ * @param [in] userdata : userdata of esplusplayer_buffer_status_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_buffer_status_cb() will be invoked.
+ * @exception None
+ * @remark esplusplayer_buffer_status_cb()
+ * if buffer_status_cb is set to null,
+ * esplusplayer_buffer_status_cb() will not be invoked anymore.
+ */
+int esplusplayer_set_buffer_status_cb(
+ esplusplayer_handle handle, esplusplayer_buffer_status_cb buffer_status_cb,
+ void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when buffer underrun or
+ * overflow is occurred and buffer size in byte will be passed.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] buffer_status_cb : the buffer byte status callback function
+ * to register.
+ * @param [in] userdata : userdata of esplusplayer_buffer_byte_status_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_submit_packet();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_buffer_byte_status_cb() will be invoked.
+ * @exception None
+ * @remark esplusplayer_buffer_byte_status_cb()
+ */
+int esplusplayer_set_buffer_byte_status_cb(
+ esplusplayer_handle handle,
+ esplusplayer_buffer_byte_status_cb buffer_status_cb, void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when buffer underrun or
+ * overflow is occurred and buffer size in time will be passed.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] buffer_status_cb : the buffer time status callback function
+ * to register.
+ * @param [in] userdata : userdata of esplusplayer_buffer_time_status_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ refer to the sample code of esplusplayer_submit_packet();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_buffer_time_status_cb() will be invoked.
+ * @exception None
+ * @remark esplusplayer_buffer_time_status_cb(),
+ * esplusplayer_buffer_time_status_cb() will be invoked only
+ * when the duration value of espacket is set.
+ * if buffer_status_cb is set to null,
+ * esplusplayer_buffer_time_status_cb() will not be invoked anymore.
+ */
+int esplusplayer_set_buffer_time_status_cb(
+ esplusplayer_handle handle,
+ esplusplayer_buffer_time_status_cb buffer_status_cb, void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when video latency status
+ * is changed.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] video_latency_status_cb : the video latency status callback function to register.
+ * @param [in] userdata : userdata of
+ * esplusplayer_set_video_latency_status_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_video_latency_status_cb() will be invoked.
+ * @exception None
+ * @version 2.7
+ * @remark esplusplayer_video_latency_status_cb() will be invoked only
+ * when mid / high latency threshold is set.
+ */
+int esplusplayer_set_video_latency_status_cb(
+ esplusplayer_handle handle,
+ esplusplayer_video_latency_status_cb video_latency_status_cb,
+ void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when audio latency status
+ * is changed.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] audio_latency_status_cb : the audio latency status callback function to register.
+ * @param [in] userdata : userdata of
+ * esplusplayer_set_audio_latency_status_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_audio_latency_status_cb() will be invoked.
+ * @exception None
+ * @version 2.7
+ * @remark esplusplayer_audio_latency_status_cb() will be invoked only
+ * when mid / high latency threshold is set.
+ */
+int esplusplayer_set_audio_latency_status_cb(
+ esplusplayer_handle handle,
+ esplusplayer_audio_latency_status_cb audio_latency_status_cb,
+ void* userdata);
+
+/**
+ * @brief Set buffer size with different option
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] option : the option of buffer size.
+ * @param [in] size : size of selected buffer option.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_buffer_size(esplayer,ESPLUSPLAYER_BUFFER_AUDIO_MAX_BYTE_SIZE,10240)
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @remark esplusplayer_buffer_option
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_buffer_size(esplusplayer_handle handle,
+ esplusplayer_buffer_option option,
+ uint64_t size);
+/**
+ * @brief Set a callback function to be invoked when resource confliction is
+ * occurred.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] resource_conflicted_cb : the resource conflicted callback
+ * function to register.
+ * @param [in] userdata : userdata of resource_conflicted_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_resource_conflicted_cb() will be invoked.
+ * @exception None
+ * @remark esplusplayer_resource_conflicted_cb()
+ * if resource_conflicted_cb is set to null,
+ * esplusplayer_resource_conflicted_cb() will not be invoked
+ * anymore.
+ */
+int esplusplayer_set_resource_conflicted_cb(
+ esplusplayer_handle handle,
+ esplusplayer_resource_conflicted_cb resource_conflicted_cb, void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when player has reached the
+ * end of stream.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] eos_cb : the eos callback function to register.
+ * @param [in] userdata : userdata of esplusplayer_eos_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_eos_cb() will be invoked.
+ * @exception None
+ * @remark esplusplayer_eos_cb()
+ * if eos_cb is set to null, esplusplayer_eos_cb() will not be
+ * invoked anymore.
+ */
+int esplusplayer_set_eos_cb(esplusplayer_handle handle,
+ esplusplayer_eos_cb eos_cb, void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when player is prepared to
+ * receive es packets after calling esplusplayer_prepare_async().
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] ready_to_prepare_cb : the ready to prepare callback function
+ * to register.
+ * @param [in] userdata : userdata of ready_to_prepare_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_submit_packet();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @remark esplusplayer_prepare_async()
+ * if ready_to_prepare_cb is set to null,
+ * esplusplayer_ready_to_prepare_cb() will not be invoked anymore.
+ */
+int esplusplayer_set_ready_to_prepare_cb(
+ esplusplayer_handle handle,
+ esplusplayer_ready_to_prepare_cb ready_to_prepare_cb, void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when player is prepared to
+ * be started.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] prepare_async_done_cb : the repare async done callback
+ * function to register.
+ * @param [in] userdata : userdata of prepare_async_done_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of plusplayer_prepare_async();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_prepare_async_done_cb() will be invoked.
+ * @exception It is prohibited to call any player APIs at
+ * esplusplayer_prepare_async_done_cb callback.
+ * @remark esplusplayer_prepare_async_done_cb()
+ * if prepare_async_done_cb is set to null,
+ * esplusplayer_prepare_async_done_cb() will not be
+ * invoked anymore.
+ * @see plusplayer_prepare_async
+ */
+int esplusplayer_set_prepare_async_done_cb(
+ esplusplayer_handle handle,
+ esplusplayer_prepare_async_done_cb prepare_async_done_cb, void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when player is prepared to
+ * be started.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] seek_done_cb : the seek done callback function to register.
+ * @param [in] userdata : userdata of esplusplayer_seek_done_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_seek_done_cb() will be invoked.
+ * if seek_done_cb is set to null, esplusplayer_seek_done_cb() will
+ * not be invoked anymore.
+ * @exception None
+ */
+int esplusplayer_set_seek_done_cb(esplusplayer_handle handle,
+ esplusplayer_seek_done_cb seek_done_cb,
+ void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when player is prepared to
+ * receive es packets after flushing all submitted es packets for
+ * seek.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] ready_to_seek_cb : the ready to seek callback function to
+ * register.
+ * @param [in] userdata : userdata of esplusplayer_ready_to_seek_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @remark esplusplayer_seek()
+ * if ready_to_seek_cb is set to null, esplusplayer_ready_to_seek_cb()
+ * will not be invoked anymore.
+ */
+int esplusplayer_set_ready_to_seek_cb(
+ esplusplayer_handle handle, esplusplayer_ready_to_seek_cb ready_to_seek_cb,
+ void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when player decoded video
+ * frame. A video frame can be retrieved using a registered callback.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] media_packet_video_decoded_cb : the media packet video
+ * decoded callback function to register.
+ * @param [in] userdata : userdata of
+ * esplusplayer_set_media_packet_video_decoded_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @remark esplusplayer_set_video_frame_buffer_type()
+ * if media_packet_video_decoded_cb is set to null,
+ * esplusplayer_error_cb() will not be invoked anymore.
+ * media packets have to be released by calling
+ * esplusplayer_decoded_buffer_destroy().
+ * @see esplusplayer_set_video_frame_buffer_scale_resolution
+ */
+int esplusplayer_set_media_packet_video_decoded_cb(
+ esplusplayer_handle handle,
+ esplusplayer_media_packet_video_decoded_cb media_packet_video_decoded_cb,
+ void* userdata);
+
+/**
+ * @brief Set closed caption callback function.
+ * @description In this function set closed caption callback to handle the
+ * closed caption. If there is closed caption to display,
+ * esplusplayer_closed_caption_cb will be called to notify there
+ * is closed caption to display.
+ * @param [in] handle : esplusplayer handle ptr.
+ * @param [in] closed_caption_cb : the closed caption callback function to
+ * register.
+ * @param [in] userdata : userdata of esplusplayer_closed_caption_cb
+ * callback function.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post When there is closed caption data, call
+ * esplusplayer_closed_caption_cb to nofity that there is closed
+ * caption needed to be displayed.
+ * @exception None
+ * @remark esplusplayer_closed_caption_cb \n
+ * [in] data : closed caption data \n
+ * [in] size : length of closed caption data \n
+ * [in] userdata : userdata of esplusplayer_closed_caption_cb
+ * callback function.
+ * if closed_caption_cb is set to null, esplusplayer_closed_caption_cb()
+ * will not be invoked anymore.
+ */
+int esplusplayer_set_closed_caption_cb(
+ esplusplayer_handle handle,
+ esplusplayer_closed_caption_cb closed_caption_cb, void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when player is flush
+ * successed.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] flush_done_cb : the flush done callback function to register.
+ * @param [in] userdata : userdata of esplusplayer_flush_done_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre This api should be called before esplusplayer_flush() is called
+ * @post esplusplayer_flush_done_cb() will be invoked.
+ * @exception None
+ * @remark called before esplusplayer_flush().
+ * if flush_done_cb is set to null, esplusplayer_error_cb() will
+ * not be invoked anymore.
+ */
+int esplusplayer_set_flush_done_cb(esplusplayer_handle handle,
+ esplusplayer_flush_done_cb flush_done_cb,
+ void* userdata);
+/**
+ * @brief Set a callback function to be invoked when a specific event
+ * occurs.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] event_cb : the callback function to register.
+ * @param [in] userdata : userdata of esplusplayer_event_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_event_cb() will be invoked.
+ * @exception None
+ * @remark esplusplayer_set_event_cb()
+ * if event_cb is set to null, esplusplayer_event_cb() will not be
+ * invoked anymore.
+ */
+int esplusplayer_set_event_cb(esplusplayer_handle handle,
+ esplusplayer_event_cb event_cb, void* userdata);
+/**
+ * @brief Provided api for destroying decoded buffer.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] packet : the decoded buffer.
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @pre The player state can be greater than #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_decoded_buffer_destroy will be invoked for video
+ * texturing
+ * @exception None
+ * @remark esplusplayer_decoded_buffer_destroy().
+ */
+int esplusplayer_decoded_buffer_destroy(
+ esplusplayer_handle handle, esplusplayer_decoded_video_packet* packet);
+
+/**
+ * @brief Provided api for setting low latency mode, multiple modes can be
+ * set to duplicate.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] mode : one of the low latency mode to set.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_low_latency_mode(esplayer,ESPLUSPLAYER_LOW_LATENCY_MODE_NONE);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @remark esplusplayer_set_low_latency_mode().
+ * if set ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC:
+ * 1, esplusplayer_buffer_status_cb/
+ * esplusplayer_buffer_byte_status_cb/
+ * esplusplayer_buffer_time_status_cb/
+ * esplusplayer_ready_to_prepare_cb/
+ * esplusplayer_ready_to_seek_cb/esplusplayer_seek_done_cb
+ * callbacks are not invoked
+ * 2, If es packets are sent after esplusplayer_start() is called,
+ * it will be played immediately.
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_low_latency_mode(esplusplayer_handle handle,
+ esplusplayer_low_latency_mode mode);
+
+/**
+ * @brief Provided api for enabling video frame peek mode
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_video_frame_peek_mode(esplayer);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @see esplusplayer_open() \n
+ * esplusplayer_render_video_frame().
+ */
+int esplusplayer_set_video_frame_peek_mode(esplusplayer_handle handle);
+
+/**
+ * @brief Provided api for rendering a video frame which is holded by video
+ * frame peek mode.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_render_video_frame(esplayer);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre In order to use this api,
+ * The player state must be one of #ESPLUSPLAYER_STATE_READY or
+ * #ESPLUSPLAYER_STATE_PAUSED after esplusplayer_seek_done_cb or
+ * esplusplayer_prepare_async_done_cb is called \n
+ * @post None
+ * @exception None
+ * @see esplusplayer_set_video_frame_peek_mode() \n
+ * esplusplayer_prepare_async()
+ */
+int esplusplayer_render_video_frame(esplusplayer_handle handle);
+
+/**
+ * @brief Provided api for setting unlimited max buffer mode, the player
+ * does not limit es packet transmission although in buffer overrun
+ * status
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_unlimited_max_buffer_mode(esplayer);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @remark esplusplayer_set_unlimited_max_buffer_mode().
+ * esplusplayer_buffer_status_cb() will be invoked in
+ * overrun/underrun buffer status. but
+ * esplusplayer_submit_packet()/esplusplayer_submit_trust_zone_packet()
+ * /esplusplayer_submit_encrypted_packet()
+ * does not return ESPLUSPLAYER_SUBMIT_STATUS_FULL
+ * @see esplusplayer_open() \n
+ * esplusplayer_submit_packet() \n
+ * esplusplayer_submit_trust_zone_packet() \n
+ * esplusplayer_submit_encrypted_packet()
+ */
+int esplusplayer_set_unlimited_max_buffer_mode(esplusplayer_handle handle);
+/**
+ * @brief Provided api to deliver fmm signal and getting fmm auto status.
+ * The player just delivers fmm signal to system. It doesn't gaurantee
+ * activating fmm mode. System refers to fmm signal when it decides to activate
+ * fmm mode or not.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE fmm auto mode on
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION fmm auto mode off
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_STATE Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_fmm_mode(esplayer);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_IDLE,
+ * #ESPLUSPLAYER_STATE_READY, #ESPLUSPLAYER_STATE_PLAYING
+ * or #ESPLUSPLAYER_STATE_PAUSED
+ * @post None
+ * @exception None
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_fmm_mode(esplusplayer_handle handle);
+/**
+ * @brief Provided api for setting audio codec type for playback.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : codec type(hardware/software).
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_audio_codec_type(esplayer,ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ When the audio stream is not set or deactivated, it can be set
+ in #ESPLUSPLAYER_STATE_READY, #ESPLUSPLAYER_STATE_PAUSED and
+ #ESPLUSPLAYER_STATE_PLAYING. The set codec type will be
+ applied when esplusplayer_activate() is called.
+ * @post None
+ * @exception None
+ * @see esplusplayer_open() \n
+ * esplusplayer_deactivate() \n
+ esplusplayer_activate() \n
+ esplusplayer_set_audio_stream_info()
+ */
+int esplusplayer_set_audio_codec_type(esplusplayer_handle handle,
+ esplusplayer_audio_codec_type type);
+/**
+ * @brief Provided api for setting video codec type for playback.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : codec type(hardware/software).
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_video_codec_type(esplayer,ESPLUSPLAYER_VIDEO_CODEC_TYPE_SW);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ When the video stream is not set or deactivated, it can be set
+ in #ESPLUSPLAYER_STATE_READY, #ESPLUSPLAYER_STATE_PAUSED and
+ #ESPLUSPLAYER_STATE_PLAYING. The set codec type will be
+ applied when esplusplayer_activate() is called.
+ * @post None
+ * @exception None
+ * @see esplusplayer_open() \n
+ * esplusplayer_deactivate() \n
+ esplusplayer_activate() \n
+ esplusplayer_set_video_stream_info()
+ */
+int esplusplayer_set_video_codec_type(esplusplayer_handle handle,
+ esplusplayer_video_codec_type type);
+/**
+ * @brief Provided api for setting alternative video resource(sub decoder
+ * and sub scaler)
+ * @param [in] handle : esplusplayer handle ptr.
+ * @param [in] rsc_type : set alternative video resource
+ * (@c 0 [defualt] = set all video resources(decoder/scaler) to main
+ * resources,
+ * @c 1 = set all video resources(decoder/scaler) to sub resources,
+ * @c 2 = set only decoder to sub resource,
+ * @c 3 = set only scaler to sub resource)
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_alternative_video_resource(esplayer,1);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #State except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @remark if app has set not default resource allocate policy via
+ * esplusplayer_set_resource_allocate_policy(), this api will return false.
+ * @version 3.0
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_alternative_video_resource(esplusplayer_handle handle,
+ unsigned int rsc_type);
+/**
+ * @brief Provided api for setting alternative audio resource(sub decoder
+ * and simple mix audio out)
+ * @param [in] handle : esplusplayer handle ptr.
+ * @param [in] rsc_type : set alternative audio resource type
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_audio_codec_type(esplayer, ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW)
+ * esplusplayer_set_alternative_audio_resource(esplayer, ESPLUSPLAYER_AUDIO_RESOURCE_SIMPLE_MIX_OUT);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ *
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_deactivate(esplayer, ESPLUSPLAYER_STREAM_TYPE_AUDIO)
+ * esplusplayer_set_audio_codec_type(esplayer, ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW)
+ * esplusplayer_set_alternative_audio_resource(esplayer, ESPLUSPLAYER_AUDIO_RESOURCE_SIMPLE_MIX_OUT);
+ * esplusplayer_activate(esplayer, ESPLUSPLAYER_STREAM_TYPE_AUDIO)
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #State except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @remark Simple out is no sound effect. Simple mix out sound is mixed with main sound and output
+ * through only main speaker. Simple mix out output format is fixed(eg. 48kh, 2ch).
+ * If you use simple mix out resource, audio decoder should be S/W type.
+ * @version 3.8
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_audio_codec_type()
+ */
+int esplusplayer_set_alternative_audio_resource(
+ esplusplayer_handle handle, esplusplayer_audio_resource_type rsc_type);
+/**
+ * @brief Provided api for switching audio stream between the different
+ * audio codec types on the fly
+ * @param [in] handle : esplusplayer handle ptr.
+ * @param [in] stream : audio stream pointer
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_audio_stream_info audio_stream;
+ * audio_stream.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_AC3;
+ * audio_stream.sample_rate = 48000;
+ * audio_stream.channels = 2;
+ * esplusplayer_switch_audio_stream_onthefly(esplayer,
+ * &audio_stream);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_READY,
+ * #ESPLUSPLAYER_STATE_PAUSED or #ESPLUSPLAYER_STATE_PLAYING
+ * @post None
+ * @exception None
+ * @remark Audio codec can be switched between only
+ * #ESPLUSPLAYER_AUDIO_MIME_TYPE_AAC,
+ * #ESPLUSPLAYER_AUDIO_MIME_TYPE_EAC3
+ * and #ESPLUSPLAYER_AUDIO_MIME_TYPE_AC3.
+ * if other codec is set, this api will return false.
+ * @version 3.0
+ * @see esplusplayer_prepare_async()
+ */
+int esplusplayer_switch_audio_stream_onthefly(
+ esplusplayer_handle handle, esplusplayer_audio_stream_info* stream);
+/**
+ * @brief Provided api for setting aifilter
+ * @param [in] handle : esplusplayer handle ptr.
+ * @param [in] aifilter : aifilter plugin.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * GstElement* aifilter_ =
+ * gst_element_factory_make("aifilter_autozoom","auto_zoom");
+ * g_object_set(G_OBJECT(aifilter_), "az_cb_out_fps", 30, NULL);
+ * g_object_set(G_OBJECT(aifilter_), "az_inference_fps", 2, NULL);
+ * g_object_set(G_OBJECT(aifilter_), "az_disp_width", 1920, NULL);
+ * g_object_set(G_OBJECT(aifilter_), "az_disp_height", 1080, NULL);
+ * g_object_set(G_OBJECT(aifilter_), "az_detection_type", 2, NULL);
+ * g_object_set(G_OBJECT(aifilter_), "az_scaler_type", 1, NULL);
+ * g_object_set(G_OBJECT(aifilter_), "az_target_num", 2, NULL);
+ * esplusplayer_set_aifilter(esplayer,aifilter_);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_aifilter(esplusplayer_handle handle, void* aifilter);
+
+/**
+ * @brief Provided api for setting render time offset
+ * @param [in] handle : esplusplayer handle ptr.
+ * @param [in] type : stream type
+ * @param [in] offset : offset (default in milliseconds, can be set by @esplusplayer_set_timeunit_type).
+ * G_MININT64 <= offset * 1000000 <= G_MAXINT64
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_low_latency_mode(esplayer,ESPLUSPLAYER_LOW_LATENCY_MODE_NONE);
+ * prepare esplayer
+ * // ... your codes ...
+ * int64_t set_offset = 10;
+ * esplusplayer_set_render_time_offset(esplayer,ESPLUSPLAYER_STREAM_TYPE_VIDEO,
+ * set_offset);
+ * int64_t get_offset = 0;
+ * esplusplayer_get_render_time_offset(esplayer_,ESPLUSPLAYER_STREAM_TYPE_VIDEO,
+ * &get_offset);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_READY,
+ * #ESPLUSPLAYER_STATE_PAUSED or #ESPLUSPLAYER_STATE_PLAYING.
+ * It have to be set to low latency mode. (all mode except
+ * # ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC)
+ * @remark esplusplayer_set_low_latency_mode().
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_render_time_offset(esplusplayer_handle handle,
+ esplusplayer_stream_type type,
+ int64_t offset);
+/**
+ * @brief Provided api for getting render time offset
+ * @param [in] handle : esplusplayer handle ptr.
+ * @param [in] type : stream type
+ * @param [in] offset : offset ptr (default in milliseconds, can be set by @esplusplayer_set_timeunit_type).
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_READY,
+ * #ESPLUSPLAYER_STATE_PAUSED or #ESPLUSPLAYER_STATE_PLAYING.
+ * It have to be set to low latency mode.
+ * @remark esplusplayer_set_low_latency_mode().
+ * @post None
+ * @exception None
+ * @version 3.0
+ * see esplusplayer_set_render_time_offset
+ */
+int esplusplayer_get_render_time_offset(esplusplayer_handle handle,
+ esplusplayer_stream_type type,
+ int64_t* offset);
+/**
+ * @brief Provided api for setting catch up speed level in low latency mode
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] level : speed level to catch up
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_low_latency_mode(esplayer,ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO);
+ * esplusplayer_set_catch_up_speed(esplayer,ESPLUSPLAYER_CATCH_UP_SPEED_MID);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_IDLE,
+ * #ESPLUSPLAYER_STATE_READY, #ESPLUSPLAYER_STATE_PLAYING
+ * or #ESPLUSPLAYER_STATE_PAUSED
+ * esplusplayer_set_low_latency_mode() should be called as below
+ * before this api is called.
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_AUDIO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY)
+ * @post None
+ * @exception None
+ * @version 2.7
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_catch_up_speed(esplusplayer_handle handle,
+ esplusplayer_catch_up_speed level);
+
+/**
+ * @brief Provided api for getting current video latency status
+ * @param [in] handle : esplusplayer handle.
+ * @param [out] status : current latency status
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_latency_status current_status =
+ * ESPLUSPLAYER_LATENCY_LOW;
+ * esplusplayer_get_video_latency_status(esplayer, ¤t_status);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_READY,
+ * #ESPLUSPLAYER_STATE_PLAYING or #ESPLUSPLAYER_STATE_PAUSED
+ * @post None
+ * @exception None
+ * @version 2.7
+ * @see esplusplayer_prepare_async()
+ */
+int esplusplayer_get_video_latency_status(esplusplayer_handle handle,
+ esplusplayer_latency_status* status);
+
+/**
+ * @brief Provided api for getting current audio latency status
+ * @param [in] handle : esplusplayer handle.
+ * @param [out] status : current latency status
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_latency_status current_status =
+ * ESPLUSPLAYER_LATENCY_LOW;
+ * esplusplayer_get_audio_latency_status(esplayer, ¤t_status);
+ * // ... your codes ...
+ * esplusplayer_stop(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_READY,
+ * #ESPLUSPLAYER_STATE_PLAYING or #ESPLUSPLAYER_STATE_PAUSED
+ * @post None
+ * @exception None
+ * @version 2.7
+ * @see esplusplayer_prepare_async()
+ */
+int esplusplayer_get_audio_latency_status(esplusplayer_handle handle,
+ esplusplayer_latency_status* status);
+
+/**
+ * @brief Provided api for setting video mid latency threshold for low
+ * latency
+ * playback
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] threshold: the threshold(number) of the video frames for mid
+ * latency.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_low_latency_mode(esplayer,ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO);
+ * esplusplayer_set_video_mid_latency_threshold(esplayer,2);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_IDLE,
+ * #ESPLUSPLAYER_STATE_READY, #ESPLUSPLAYER_STATE_PLAYING
+ * or #ESPLUSPLAYER_STATE_PAUSED
+ * esplusplayer_set_low_latency_mode() should be called as below
+ * before this api is called.
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_AUDIO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE)
+ * @post None
+ * @exception None
+ * @version 2.7
+ * @see esplusplayer_open()
+ */
+/// TODO:: set the min/max value of the threshold
+int esplusplayer_set_video_mid_latency_threshold(esplusplayer_handle handle,
+ const unsigned int threshold);
+
+/**
+ * @brief Provided api for setting audio mid latency threshold for low
+ * latency
+ * playback
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] threshold: the threshold(number) of the audio frames for mid
+ * latency.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_low_latency_mode(esplayer,ESPLUSPLAYER_LOW_LATENCY_MODE_AUDIO);
+ * esplusplayer_set_audio_mid_latency_threshold(esplayer,2);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_IDLE,
+ * #ESPLUSPLAYER_STATE_READY, #ESPLUSPLAYER_STATE_PLAYING
+ * or #ESPLUSPLAYER_STATE_PAUSED
+ * esplusplayer_set_low_latency_mode() should be called as below
+ * before this api is called.
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_AUDIO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE)
+ * @post None
+ * @exception None
+ * @version 2.7
+ * @see esplusplayer_open()
+ */
+/// TODO:: set the min/max value of the threshold
+int esplusplayer_set_audio_mid_latency_threshold(esplusplayer_handle handle,
+ const unsigned int threshold);
+
+/**
+ * @brief Provided api for setting video high latency threshold for low
+ * latency
+ * playback
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] threshold: the threshold(number) of the video frames for high
+ * latency.
+ * @param [in] video_high_latency_cb : high latency callback function to
+ * register
+ * @param [in] userdata : userdata of esplusplayer_high_latency_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * static void OnVideoHighLatency(void* userdata) {
+ * printf ("OnVideoHighLatency\n");
+ * }
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_low_latency_mode(esplayer,ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO);
+ * esplusplayer_set_video_high_latency_threshold(esplayer, 2,
+ * OnVideoHighLatency, nullptr);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_IDLE,
+ * #ESPLUSPLAYER_STATE_READY, #ESPLUSPLAYER_STATE_PLAYING
+ * or #ESPLUSPLAYER_STATE_PAUSED
+ * esplusplayer_set_low_latency_mode() should be called as below
+ * before this api is called.
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_AUDIO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE)
+ * @post None
+ * @exception None
+ * @version 2.7
+ * @see esplusplayer_open()
+ */
+/// TODO:: set the min/max value of the threshold
+int esplusplayer_set_video_high_latency_threshold(
+ esplusplayer_handle handle, const unsigned int threshold,
+ esplusplayer_video_high_latency_cb video_high_latency_cb, void* userdata);
+
+/**
+ * @brief Provided api for setting audio high latency threshold for low
+ * latency
+ * playback
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] threshold: the threshold(number) of the audio frames for high
+ * latency.
+ * @param [in] audio_high_latency_cb : high latency callback function to
+ * register
+ * @param [in] userdata : userdata of esplusplayer_high_latency_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * static void OnAudioHighLatency(void* userdata) {
+ * printf ("OnAudioHighLatency\n");
+ * }
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_low_latency_mode(esplayer,ESPLUSPLAYER_LOW_LATENCY_MODE_AUDIO);
+ * esplusplayer_set_audio_high_latency_threshold(esplayer, 2,
+ * OnAudioHighLatency, nullptr);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_IDLE,
+ * #ESPLUSPLAYER_STATE_READY, #ESPLUSPLAYER_STATE_PLAYING
+ * or #ESPLUSPLAYER_STATE_PAUSED
+ * esplusplayer_set_low_latency_mode() should be called as below
+ * before this api is called.
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_AUDIO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_VIDEO),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC),
+ * esplusplayer_set_low_latency_mode(handle,
+ * ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE)
+ * @post None
+ * @exception None
+ * @version 2.7
+ * @see esplusplayer_open()
+ */
+/// TODO:: set the min/max value of the threshold
+int esplusplayer_set_audio_high_latency_threshold(
+ esplusplayer_handle handle, const unsigned int threshold,
+ esplusplayer_audio_high_latency_cb audio_high_latency_cb, void* userdata);
+
+/**
+ * @brief Initialize easing info to esplayer.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] init_volume : initial easing volume (0 ~ 100).
+ * @param [in] elapsed_time : initial elapsed time (millisecond).
+ * @param [in] easing_info : target volume, duration, type.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * uint32_t volume = 50, elapsed_time = 10000;
+ * esplusplayer_target_audio_easing_info easing_info;
+ * easing_info.volume = 30;
+ * easing_info.duration = 100;
+ * easing_info.type = ESPLUSPLAYER_AUDIO_EASING_INCUBIC;
+ * esplusplayer_init_audio_easing_info(esplayer,volume,elapsed_time,&easing_info);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @see esplusplayer_open()
+ */
+int esplusplayer_init_audio_easing_info(
+ esplusplayer_handle handle, uint32_t init_volume, uint32_t elapsed_time,
+ const esplusplayer_target_audio_easing_info* easing_info);
+
+/**
+ * @brief Update easing info to esplayer to update target info.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] easing_info : target volume, duration, type.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * // ... your codes ...
+ * esplusplayer_target_audio_easing_info easing_info;
+ * easing_info.volume = 30;
+ * easing_info.duration = 100;
+ * easing_info.type = ESPLUSPLAYER_AUDIO_EASING_INCUBIC;
+ * esplusplayer_update_audio_easing_info(esplayer,&easing_info);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * This api should be called after
+ * esplusplayer_init_audio_easing_info() is called
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @see esplusplayer_open()
+ */
+int esplusplayer_update_audio_easing_info(
+ esplusplayer_handle handle,
+ const esplusplayer_target_audio_easing_info* easing_info);
+
+/**
+ * @brief Get easing info currently in easing operation from esplayer
+ * @param [in] handle : esplusplayer handle.
+ * @param [out] current_volume : current volume (0 ~ 100).
+ * @param [out] elapsed_time : elapsed time (millisecond).
+ * @param [out] easing_info : target volume, duration, type.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * // ... your codes ...
+ * uint32_t cur_volume = 50, elapsed_time = 0;
+ * esplusplayer_target_audio_easing_info easing_info;
+ * esplusplayer_get_audio_easing_info(esplayer,&cur_volume,&elapsed_time,&easing_info);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * This api should be called after
+ * esplusplayer_init_audio_easing_info() is called
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @see esplusplayer_open()
+
+ */
+int esplusplayer_get_audio_easing_info(
+ esplusplayer_handle handle, uint32_t* current_volume,
+ uint32_t* elapsed_time, esplusplayer_target_audio_easing_info* easing_info);
+
+/**
+ * @brief Start audio easing using a registered audio easing info
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * uint32_t volume = 50, elapsed_time = 10000;
+ * esplusplayer_target_audio_easing_info easing_info;
+ * easing_info.volume = 30;
+ * easing_info.duration = 100;
+ * easing_info.type = ESPLUSPLAYER_AUDIO_EASING_INCUBIC;
+ * esplusplayer_init_audio_easing_info(esplayer,volume,elapsed_time,&easing_info);
+ * esplusplayer_start_audio_easing(esplayer);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state should be at least #ESPLUSPLAYER_STATE_READY.
+ * This api should be called after
+ * esplusplayer_init_audio_easing_info() is called
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @see esplusplayer_open() \n
+ * esplusplayer_init_audio_easing_info() \n
+ * esplusplayer_update_audio_easing_info() \n
+ * esplusplayer_stop_audio_easing() \n
+ * esplusplayer_prepare_async()
+ */
+int esplusplayer_start_audio_easing(esplusplayer_handle handle);
+
+/**
+ * @brief Stop audio easing
+ * @param [in] handle : esplusplayer handle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * // ... your codes ...
+ * esplusplayer_stop_audio_easing(esplayer);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * This api should be called after
+ * esplusplayer_init_audio_easing_info() is called
+ * @post None
+ * @exception None
+ * @version 3.0
+ * @see esplusplayer_open() \n
+ * esplusplayer_start_audio_easing()
+ */
+int esplusplayer_stop_audio_easing(esplusplayer_handle handle);
+
+/**
+ * @brief Get virtual resource id
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : The resource type of virtual id.
+ * @param [out] virtual_id : Stored virtual resource id value.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * prepare esplayer done
+ * // ... your codes ...
+ * int virtual_id;
+ * esplusplayer_get_virtual_rsc_id(esplayer,ESPLUSPLAYER_RSC_TYPE_VIDEO_RENDERER,&virtual_id);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * esplusplayer_destroy(esplayer);
+ * @endcode
+ * @pre The player state should be #State::kReady, #State::kPlaying or
+ * #State::kPaused
+ * @post None
+ * @return @c True on success, otherwise @c False ("virtual_id" will be -1)
+ * @exception None
+ * @version 3.0
+ * @remark This function returns virtual resource id which player is
+ * allocated from resource manager. For example, virtual scaler id is
+ * required for an application to use capture API directly.
+ * @see esplusplayer_prepare_async()
+
+ */
+int esplusplayer_get_virtual_rsc_id(esplusplayer_handle handle,
+ const esplusplayer_rsc_type type,
+ int* virtual_id);
+
+/**
+ * @brief Set advanced picture quality type.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : The picture quality type.
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @exception None
+ * @version 3.1
+ */
+int esplusplayer_set_advanced_picture_quality_type(
+ esplusplayer_handle handle,
+ esplusplayer_advanced_picture_quality_type type);
+
+/**
+ * @brief Set resource allocate policy.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] policy : The resource allocate policy.
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @exception None
+ * @version 3.3
+ * @remark If app has set not default alternaltive resource via
+ * esplusplayer_set_alternative_video_resource(), alternative
+ * resource setting will be ignored.
+ */
+int esplusplayer_set_resource_allocate_policy(
+ esplusplayer_handle handle, esplusplayer_rsc_alloc_policy policy);
+
+/**
+ * @brief Requests decoded video frame packet to acquire it. it works only
+ * with #ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_MANUAL_COPY
+ * mode
+ * @param [in] handle : esplusplayer handle.
+ * @param [out] packet : the decoded buffer.
+ * @param [out] status : (nullable) the result of video frame requested
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_READY or
+ * #ESPLUSPLAYER_STATE_PAUSED or #ESPLUSPLAYER_STATE_PLAYING.
+ * @post None
+ * @return #ESPLUSPLAYER_ERROR_TYPE_NONE on success, otherwise one of
+ * esplusplayer_error_type values will be returned.
+ * @exception None
+ * @version 4.0
+ * @see esplusplayer_set_video_frame_buffer_type()
+ * @see esplusplayer_decoded_buffer_destroy()
+ * @see esplusplayer_decoded_video_frame_buffer_type
+ * @code
+ * ...
+ * esplusplayer_set_video_frame_buffer_type(handle,
+ * ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_MANUAL_COPY);
+ * ...
+ * esplusplayer_prepare_async(handle);
+ * ...
+ * // after prepared
+ * esplusplayer_decoded_video_packet packet;
+ * esplusplayer_get_decoded_video_frame_status_type state;
+ * int retval = esplusplayer_get_decoded_video_packet(handle, &packet, &state);
+ * if (state == ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_SUCCESS) {
+ * // successful case.
+ * // in this case, retval will be ESPLUSPLAYER_ERROR_TYPE_NONE
+ * // you have to call esplusplayer_decoded_buffer_destroy() after using the
+ * // packet
+ * ...
+ * esplusplayer_decoded_buffer_destroy(handle, &packet);
+ * } else if (state ==
+ * ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_NO_REMAINING_BUFFER) {
+ * // app has to call esplusplayer_decoded_buffer_destroy() with previous
+ * // video packet.
+ * // it means player couldn't draw the video frame into a buffer due to no
+ * buffer.
+ * // in this case ,retval will be ESPLUSPLAYER_ERROR_TYPE_NONE
+ * } else if (state ==
+ * ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_NO_FILLED_BUFFER) {
+ * // it means app should retry to get decoded video packet.
+ * // in most case, there were no buffers drawn in the timing you called this
+ * // api.
+ * // in this case, retval will be ESPLUSPLAYER_ERROR_TYPE_NONE
+ * } else if (state == ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_UNKNOWN) {
+ * // internal error happened
+ * // in this case, retval will be ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION
+ * }
+ * ...
+ * @endcode
+ */
+int esplusplayer_get_decoded_video_packet(
+ esplusplayer_handle handle, esplusplayer_decoded_video_packet* packet,
+ esplusplayer_get_decoded_video_frame_status_type* status);
+
+/**
+ * @brief Set preloading of the audio pipeline.
+ * @param [in] handle : esplusplayer handle.
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @remark If the other player is used by pre-loading mode, this player can
+ * be prepared before the other player is released.
+ * If app call esplusplayer_start before the original player is
+ * released, the sound of original player can be affected.
+ * @exception None
+ * @version 4.0
+ * @code
+ * ...
+ * esplusplayer_handle esplayer1 = esplusplayer_create();
+ * esplusplayer_open(esplayer1);
+ * esplusplayer_set_audio_preloading(esplayer1);
+ * esplusplayer_audio_stream_info audio_stream_1;
+ * if (audio_stream_1's mime type is PCM or G711_MULAW)
+ * audio_stream_1.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_G711_MULAW;
+ * else
+ * audio_stream_1.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_AAC;
+ * esplusplayer_set_audio_stream_info(esplayer1, &audio_stream_1);
+ * esplusplayer_prepare_async(esplayer1);
+ * esplusplayer_start(esplayer1);
+ * // ... while playing esplayer1 ...
+ * // the other player can be prepared if original player is preloading mode.
+ * esplusplayer_handle esplayer2 = esplusplayer_create();
+ * esplusplayer_open(esplayer2);
+ * esplusplayer_set_audio_preloading(esplayer2);
+ * esplusplayer_audio_stream_info audio_stream_2;
+ * if (audio_stream_1's mime type is PCM or G711_MULAW) {
+ * // audio_stream_2 can use the mime type except for PCM or G711_MULAW type.
+ * audio_stream_2.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_AAC;
+ * esplusplayer_set_audio_stream_info(esplayer2, &audio_stream_2);
+ * } else {
+ * if (audio_stream_2's mime type is PCM or G711_MULAW) {
+ * audio_stream_2.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_G711_MULAW;
+ * esplusplayer_set_audio_stream_info(esplayer2, &audio_stream_2);
+ * } else {
+ * // if all player don't use PCM or G711_MULAW type, one player need to set
+ * // sw codec type.
+ * audio_stream_2.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_AAC;
+ * esplusplayer_set_audio_stream_info(esplayer2, &audio_stream_2);
+ * esplusplayer_set_audio_codec_type(esplayer2,
+ * ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW);
+ * }
+ * }
+ * esplusplayer_prepare_async(esplayer2);
+ * // the player can be started after the original player is stopped or the
+ * // original player's audio stream is deactivated.
+ * if (the app want to keep the original player instance)
+ * esplusplayer_deactivate(esplayer1,ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+ * else
+ * esplusplayer_stop(esplayer1);
+ * esplusplayer_start(esplayer2);
+ * ...
+ * @endcode
+ */
+int esplusplayer_set_audio_preloading(esplusplayer_handle handle);
+
+/**
+ * @brief Set a callback function to be invoked when video frames are
+ * dropped by SoC.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] video_frame_dropped_cb : the callback function to register.
+ * @param [in] userdata : userdata of esplusplayer_set_video_frame_dropped_cb()
+ * @pre The player state must be one of #ESPLUSPLAYER_STATE_READY or
+ * #ESPLUSPLAYER_STATE_PAUSED or #ESPLUSPLAYER_STATE_PLAYING.
+ * @post esplusplayer_video_frame_dropped_cb() will be invoked.
+ * if error_cb is set to null, esplusplayer_error_cb() will not be
+ * @return #ESPLUSPLAYER_ERROR_TYPE_NONE on success, otherwise one of
+ * esplusplayer_error_type values will be returned.
+ * @exception None
+ * @version 2.7
+ * @see esplusplayer_video_frame_dropped_cb()
+ * @code
+ * void video_frame_dropped_cb(const uint64_t count, void* userdata){
+ * // do you job
+ * }
+ * esplusplayer_set_video_frame_dropped_cb(handle,
+ * video_frame_dropped_cb, userdata);
+ * ...
+ * esplusplayer_prepare_async(handle);
+ * ...
+ * @endcode
+ */
+int esplusplayer_set_video_frame_dropped_cb(
+ esplusplayer_handle handle,
+ esplusplayer_video_frame_dropped_cb video_frame_dropped_cb, void* userdata);
+
+/**
+ * @brief Set video scan type.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : The video scan type.
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @exception None
+ * @version 4.0
+ */
+int esplusplayer_set_video_scan_type(esplusplayer_handle handle,
+ esplusplayer_video_scan_type type);
+
+/**
+ * @brief Get decoding time of the stream
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : stream type.
+ * @param [out] time_in_milliseconds : current decoding time in milliseconds.
+ * @pre The player must be one of #ESPLUSPLAYER_STATE_PAUSE or
+ * #ESPLUSPLAYER_STATE_PLAYING
+ * @post None
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @exception None
+ * @version 2.7
+ */
+int esplusplayer_get_decoding_time(esplusplayer_handle handle,
+ esplusplayer_stream_type type,
+ int32_t* time_in_milliseconds);
+
+/**
+ * @brief Set the timeunit ms or us. All other espp time related API should
+ * follow this time unit type.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : time unit type.
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_IDLE
+ * @post None
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @exception None
+ * @version 5.0
+ */
+int esplusplayer_set_timeunit_type(esplusplayer_handle handle,
+ esplusplayer_time_unit_type type);
+
+/**
+ * @brief Set a callback function to be invoked when decoder receive one input buffer.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] decoder_buffer_time_cb : the decoder input buffer time callback function to register.
+ * @param [in] userdata : userdata of
+ * esplusplayer_set_decoder_input_buffer_time_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @version 5.0
+ */
+int esplusplayer_set_decoder_input_buffer_time_cb(
+ esplusplayer_handle handle,
+ esplusplayer_decoder_buffer_time_cb decoder_buffer_time_cb,
+ void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when get one output buffer from decoder.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] decoder_buffer_time_cb : the decoder output buffer time callback function to register.
+ * @param [in] userdata : userdata of
+ * esplusplayer_set_decoder_output_buffer_time_cb()
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * refer to the sample code of esplusplayer_set_error_cb();
+ * @endcode
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE
+ * or #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @version 5.0
+ */
+int esplusplayer_set_decoder_output_buffer_time_cb(
+ esplusplayer_handle handle,
+ esplusplayer_decoder_buffer_time_cb decoder_buffer_time_cb,
+ void* userdata);
+
+/**
+ * @brief Set App id to esplayer to control resource confliction.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] app_info : application id, version, type, runtitle.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_app_info appinfo;
+ * appinfo.id = "youtube";
+ * appinfo.version = "3.0";
+ * appinfo.type = "MSE";
+ * appinfo.runtitle = "YouTube";
+ * esplusplayer_handle esplayer = esplusplayer_create();
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_app_info_ex(esplayer,&appinfo);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state must be #ESPLUSPLAYER_STATE_IDLE.
+ * @post None
+ * @exception None
+ * @version 5.0
+ * @see esplusplayer_open()
+ */
+int esplusplayer_set_app_info_ex(esplusplayer_handle handle,
+ const esplusplayer_app_info_ex* app_info);
+
+/**
+ * @brief Set the rotate angle of the video stream.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] rotation : the rotate angle of the video.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_video_stream_rotation_info(esplayer_,ESPLUSPLAYER_VIDEO_ROTATION_90);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post this API worked only when video sink created.
+ * @exception None
+ * @version 5.2
+ * @see esplusplayer_open() \n
+ */
+int esplusplayer_set_video_stream_rotation_info(esplusplayer_handle handle,
+ const esplusplayer_video_stream_rotation_type rotation);
+
+/**
+ * @brief Get the rotate angle of the video stream.
+ * @param [in] handle : esplusplayer handle.
+ * @param [out] rotation : the rotate angle of the video stream which want to
+ * get.
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of esplusplayer_error_type
+ * values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation failed
+ * @code
+ * esplusplayer_display_rotation_type rotation_get = ESPLUSPLAYER_VIDEO_ROTATION_NONE;
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_video_stream_rotation_info(esplayer,ESPLUSPLAYER_VIDEO_ROTATION_90);
+ * // ... your codes ...
+ * esplusplayer_get_video_stream_rotation_info(esplayer,&rotation_get);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post this API worked only when video sink created.
+ * @exception None
+ * @version 5.2
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_video_stream_rotation_info()
+ */
+int esplusplayer_get_video_stream_rotation_info(esplusplayer_handle handle,
+ esplusplayer_video_stream_rotation_type* rotation);
+
+/**
+ * @brief Provided api for setting buffer level of simple mix out
+ * @param [in] handle : esplusplayer handle ptr.
+ * @param [in] level : set buffer level of simple mix out
+ * @return @c ESPLUSPLAYER_ERROR_TYPE_NONE on success,otherwise @c one of
+ * esplusplayer_error_type values will be returned.
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_NONE Successful
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER Invalid parameter
+ * @retval #ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION Internal operation
+ * failed
+ * @code
+ * esplusplayer_open(esplayer);
+ * esplusplayer_set_audio_codec_type(esplayer, ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW)
+ * esplusplayer_set_alternative_audio_resource(esplayer, ESPLUSPLAYER_AUDIO_RESOURCE_SIMPLE_MIX_OUT);
+ * esplusplayer_set_simple_mix_out_buffer_level(esplayer, ESPLUSPLAYER_SIMPLE_MIX_OUT_BUFFER_LOW);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ *
+ * prepare esplayer done
+ * // ... your codes ...
+ * esplusplayer_deactivate(esplayer, ESPLUSPLAYER_STREAM_TYPE_AUDIO)
+ * esplusplayer_set_audio_codec_type(esplayer, ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW)
+ * esplusplayer_set_alternative_audio_resource(esplayer, ESPLUSPLAYER_AUDIO_RESOURCE_SIMPLE_MIX_OUT);
+ * esplusplayer_set_simple_mix_out_buffer_level(esplayer, ESPLUSPLAYER_SIMPLE_MIX_OUT_BUFFER_LOW);
+ * esplusplayer_activate(esplayer, ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+ * // ... your codes ...
+ * esplusplayer_close(esplayer);
+ * @endcode
+ * @pre The player state can be all of #State except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @remark This API can set only before esplusplayer_prepare_async() or
+ * after esplusplayer_deactivate(ESPLUSPLAYER_STREAM_TYPE_AUDIO).
+ * @version 3.8
+ * @see esplusplayer_open() \n
+ * esplusplayer_set_audio_codec_type()
+ * esplusplayer_set_alternative_audio_resource()
+ */
+int esplusplayer_set_simple_mix_out_buffer_level(
+ esplusplayer_handle handle, esplusplayer_simple_mix_out_buffer_level level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ESPLUSPLAYER_CAPI_H__
--- /dev/null
+/**
+ * @file esplusplayer_internal.h
+ * @brief EsPlusPlayer internally used api c version
+ * @interfacetype module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.0
+ * @SDK_Support N
+ * @remark This is esplusplayer api header implemented as C style to
+ * avoid binary compatibility issues.
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ESPLUSPLAYER_INTERNAL_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ESPLUSPLAYER_INTERNAL_H__
+
+#include "esplusplayer_capi/buffer.h"
+#include "esplusplayer_capi/display.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+typedef void (*esplusplayer_decoder_underrun_cb)(void* userdata);
+typedef void* esplusplayer_handle;
+typedef void (*esplusplayer_first_video_decoding_done_cb)(void*);
+
+/**
+ * @brief Set the video display.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] type : display type.
+ * @param [in] surface_id : resource id of window.
+ * @param [in] x : the x coordinate of window.
+ * @param [in] y : the ycoordinate of window.
+ * @param [in] width : the width of window.
+ * @param [in] height : the height of window.
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @pre The player state can be all of #esplusplayer_state except
+ * #ESPLUSPLAYER_STATE_NONE.
+ * @post None
+ * @exception None
+ * @see esplusplayer_set_display_mode() \n
+ * esplusplayer_set_display_roi() \n
+ * esplusplayer_set_display_visible()
+ */
+int esplusplayer_set_surface_display(esplusplayer_handle handle,
+ esplusplayer_display_type type,
+ unsigned int surface_id, int x, int y,
+ int width, int height);
+
+int esplusplayer_set_first_video_decoding_done_cb(
+ esplusplayer_handle handle,
+ esplusplayer_first_video_decoding_done_cb first_video_decoding_done_cb,
+ void* userdata);
+
+/**
+ * @brief Set a callback function to be invoked when buffer underrun is
+ * occurred from a video decoder.
+ * @param [in] handle : esplusplayer handle.
+ * @param [in] callback : the callback function to register.
+ * @param [in] userdata : userdata of esplusplayer_decoder_underrun_cb()
+ * @return @c one of esplusplayer_error_type values will be returned.
+ * @pre The player state must be set to #ESPLUSPLAYER_STATE_NONE or
+ * #ESPLUSPLAYER_STATE_IDLE.
+ * @post esplusplayer_decoder_underrun_cb() will be invoked.
+ * @exception None
+ * @remark esplusplayer_decoder_underrun_cb().
+ * if video_decoder_underrun_cb is set to null,
+ * esplusplayer_decoder_underrun_cb() will not be invoked anymore.
+ */
+int esplusplayer_set_video_decoder_underrun_cb(
+ esplusplayer_handle handle,
+ esplusplayer_decoder_underrun_cb video_decoder_underrun_cb, void* userdata);
+
+/**
+ * @brief Get the size of struct esplusplayer_app_info
+ * @param None
+ * @return Total size of struct esplusplayer_app_info
+ * @pre None
+ * @post None
+ * @exception None
+ */
+int get_size_of_esplusplayer_app_info(void);
+
+/**
+ * @brief Get the size of struct esplusplayer_es_packet
+ * @param None
+ * @return Total size of struct esplusplayer_es_packet
+ * @pre None
+ * @post None
+ * @exception None
+ */
+int get_size_of_esplusplayer_es_packet(void);
+
+/**
+ * @brief Get the size of struct esplusplayer_es_tz_packet
+ * @param None
+ * @return Total size of struct esplusplayer_es_tz_packet
+ * @pre None
+ * @post None
+ * @exception None
+ */
+int get_size_of_esplusplayer_es_tz_packet(void);
+
+/**
+ * @brief Get the size of struct esplusplayer_audio_stream_info
+ * @param None
+ * @return Total size of struct esplusplayer_audio_stream_info
+ * @pre None
+ * @post None
+ * @exception None
+ */
+int get_size_of_esplusplayer_audio_stream_info(void);
+
+/**
+ * @brief Get the size of struct esplusplayer_video_stream_info
+ * @param None
+ * @return Total size of struct esplusplayer_video_stream_info
+ * @pre None
+ * @post None
+ * @exception None
+ */
+int get_size_of_esplusplayer_video_stream_info(void);
+
+/**
+ * @brief Get the size of struct esplusplayer_drm_info
+ * @param None
+ * @return Total size of struct esplusplayer_drm_info
+ * @pre None
+ * @post None
+ * @exception None
+ */
+int get_size_of_esplusplayer_drm_info(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_ESPLUSPLAYER_INTERNAL_H__
--- /dev/null
+/**
+ * @file
+ * @brief The event for playback.
+ * @interfacetype Platform
+ * @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) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_EVENT_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_EVENT_H__
+
+#include <cstdint>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Event message
+ */
+typedef struct {
+ /**
+ * @description event message data
+ * eg) ESPLUSPLAYER_EVENT_RESOLUTION_CHANGED : "1920x1080"
+ */
+ char* data;
+ /**
+ * @description the length of event message data
+ */
+ uint64_t len;
+} esplusplayer_event_msg;
+
+/**
+ * @brief Enumerations for event message types
+ */
+enum esplusplayer_event_type {
+ ESPLUSPLAYER_EVENT_NONE,
+ ESPLUSPLAYER_EVENT_RESOLUTION_CHANGED,
+ /**
+ * @description requested first render video frame to display module.
+ * Actual displaying timing on screen could be delay. It depends on H/W
+ * rendering system.
+ * Ths event will happen in case of ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC
+ * or ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_PREROLL mode.
+ */
+ ESPLUSPLAYER_EVENT_REQUESTED_FIRST_RENDER_FRAME,
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_EVENT_H__
--- /dev/null
+/**
+ * @file
+ * @brief Latency enum.
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.7
+ * @SDK_Support N
+ * @remark This is a group of C style state related enum.
+ * @see State enum convertion.
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_LATENCY_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_LATENCY_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ * @brief Enumerations for esplusplayer catch up speed
+ */
+enum esplusplayer_catch_up_speed {
+ ESPLUSPLAYER_CATCH_UP_SPEED_NONE, /**< do not use catch up mode */
+ ESPLUSPLAYER_CATCH_UP_SPEED_SLOW, /**< catch up speed is slow */
+ ESPLUSPLAYER_CATCH_UP_SPEED_MID, /**< catch up speed is normal */
+ ESPLUSPLAYER_CATCH_UP_SPEED_FAST /**< catch up speed is fast */
+};
+
+/**
+ * @brief Enumerations for esplusplayer latency status
+ */
+enum esplusplayer_latency_status {
+ ESPLUSPLAYER_LATENCY_LOW, /**< latency status is low */
+ ESPLUSPLAYER_LATENCY_MID, /**< latency status is middle */
+ ESPLUSPLAYER_LATENCY_HIGH /**< latency status is high */
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_LATENCY_H__
--- /dev/null
+/**
+ * @file
+ * @brief The matroska color info
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.0
+ * @SDK_Support N
+ * @see esplusplayer::EsPlusPlayer class
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_MATROSKA_COLOR_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_MATROSKA_COLOR_H__
+
+#include <cstdint>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Structure of matroska matering metadata
+ */
+typedef struct {
+ /**
+ * @description Red X chromaticity coordinate as defined by CIE 1931.
+ */
+ double primary_r_chromaticity_x;
+ /**
+ * @description Red Y chromaticity coordinate as defined by CIE 1931.
+ */
+ double primary_r_chromaticity_y;
+ /**
+ * @description Green X chromaticity coordinate as defined by CIE 1931.
+ */
+ double primary_g_chromaticity_x;
+ /**
+ * @description Green Y chromaticity coordinate as defined by CIE 1931.
+ */
+ double primary_g_chromaticity_y;
+ /**
+ * @description Blue X chromaticity coordinate as defined by CIE 1931.
+ */
+ double primary_b_chromaticity_x;
+ /**
+ * @description Blue Y chromaticity coordinate as defined by CIE 1931.
+ */
+ double primary_b_chromaticity_y;
+ /**
+ * @description White X chromaticity coordinate as defined by CIE 1931.
+ */
+ double white_point_chromaticity_x;
+ /**
+ * @description White Y chromaticity coordinate as defined by CIE 1931.
+ */
+ double white_point_chromaticity_y;
+ /**
+ * @description Maximum luminance. Represented in candelas per square meter
+ * (cd/m²).
+ */
+ double luminance_max;
+ /**
+ * @description Mininum luminance. Represented in candelas per square meter
+ * (cd/m²).
+ */
+ double luminance_min;
+} esplusplayer_matroska_mastering_metadata;
+
+/**
+ * @brief Structure of matroska color information
+ */
+typedef struct {
+ /**
+ * @description The Matrix Coefficients of the video used to derive luma and
+ * chroma values from red, green, and blue color primaries. For clarity, the
+ * value and meanings for MatrixCoefficients are adopted from Table 4 of
+ * ISO/IEC 23001-8:2013/DCOR1.
+ */
+ uint32_t matrix_coefficients;
+ /**
+ * @description Number of decoded bits per channel. A value of 0 indicates
+ * that the BitsPerChannel is unspecified.
+ */
+ uint32_t bits_per_channel;
+ /**
+ * @description The amount of pixels to remove in the Cr and Cb channels for
+ * every pixel not removed horizontally.
+ */
+ uint32_t chroma_subsampling_horizontal;
+ /**
+ * @description The amount of pixels to remove in the Cr and Cb channels for
+ * every pixel not removed vertically.
+ */
+ uint32_t chroma_subsampling_vertical;
+ /**
+ * @description The amount of pixels to remove in the Cb channel for every
+ * pixel not removed horizontally. This is additive with
+ * chroma_subsampling_horizontal.
+ */
+ uint32_t cb_subsampling_horizontal;
+ /**
+ * @description The amount of pixels to remove in the Cb channel for every
+ * pixel not removed vertically. This is additive with
+ * chroma_subsampling_vertical.
+ */
+ uint32_t cb_subsampling_vertical;
+ /**
+ * @description How chroma is subsampled horizontally.
+ */
+ uint32_t chroma_siting_horizontal;
+ /**
+ * @description How chroma is subsampled vertically.
+ */
+ uint32_t chroma_siting_vertical;
+ /**
+ * @description Clipping of the color ranges.
+ */
+ uint32_t range;
+ /**
+ * @description The transfer characteristics of the video. For clarity, the
+ * value and meanings for transfer_characteristics 1-15 are adopted from Table
+ * 3 of ISO/IEC 23001-8:2013/DCOR1. transfer_characteristics 16-18 are
+ * proposed values.
+ */
+ uint32_t transfer_characteristics;
+ /**
+ * @description The colour primaries of the video. For clarity, the value
+ * and meanings for primaries are adopted from Table 2 of ISO/IEC
+ * 23001-8:2013/DCOR1.
+ */
+ uint32_t primaries;
+ /**
+ * @description Maximum brightness of a single pixel (Maximum Content Light
+ * Level) in candelas per square meter (cd/m²).
+ */
+ uint32_t max_cll;
+ /**
+ * @description Maximum brightness of a single full frame (Maximum
+ * Frame-Average Light Level) in candelas per square meter (cd/m²).
+ */
+ uint32_t max_fall;
+ /**
+ * @description SMPTE 2086 mastering data.
+ */
+ esplusplayer_matroska_mastering_metadata metadata;
+ /**
+ * @description flag to check if this file is hdr10+ (cd/m²).
+ */
+ uint32_t isHDR10p;
+} esplusplayer_matroska_color;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_MATROSKA_COLOR_H__
--- /dev/null
+/**
+ * @file
+ * @brief State enum.
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.0
+ * @SDK_Support N
+ * @remark This is a group of C style state related enum.
+ * @see State enum convertion.
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_STATE_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_STATE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for es player state.
+ */
+enum esplusplayer_state {
+ ESPLUSPLAYER_STATE_NONE, /**<Player is created, but not opened*/
+ ESPLUSPLAYER_STATE_IDLE, /**<Player is opened, but not prepared or player is
+ stopped*/
+ ESPLUSPLAYER_STATE_READY, /**<Player is ready to play(start)*/
+ ESPLUSPLAYER_STATE_PLAYING, /**<Player is playing media*/
+ ESPLUSPLAYER_STATE_PAUSED, /**<Player is playing media*/
+ ESPLUSPLAYER_STATE_MAX
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_STATE_H__
--- /dev/null
+/**
+ * @file
+ * @brief Stream info related data structures and enums
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.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) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_TRACK_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_TRACK_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the stream type
+ */
+enum esplusplayer_stream_type {
+ ESPLUSPLAYER_STREAM_TYPE_AUDIO,
+ ESPLUSPLAYER_STREAM_TYPE_VIDEO,
+ ESPLUSPLAYER_STREAM_TYPE_MAX
+};
+
+/**
+ * @brief Enumerations for audio mime type
+ */
+enum esplusplayer_audio_mime_type {
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_UNKNOWN,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_AAC,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_MP2,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_MP3,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_AC3,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_EAC3,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_VORBIS,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_OPUS,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_S16LE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_S16BE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_U16LE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_U16BE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_S24LE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_S24BE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_U24LE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_U24BE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_S32LE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_S32BE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_U32LE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_U32BE,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_G711_MULAW,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_AC4,
+ ESPLUSPLAYER_AUDIO_MIME_TYPE_MPEGH
+};
+
+/**
+ * @brief Enumerations for video mime type
+ */
+enum esplusplayer_video_mime_type {
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_UNKNOWN,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_H263,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_H264,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_HEVC,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_MPEG1,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_MPEG2,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_MPEG4,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_VP8,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_VP9,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_WMV3,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_AV1,
+ ESPLUSPLAYER_VIDEO_MIME_TYPE_MJPEG
+};
+
+/**
+ * @brief Audio stream information structure
+ */
+typedef struct {
+ /**
+ * @description The audio codec data pointer.
+ */
+ char* codec_data;
+ /**
+ * @description The audio codec data length.
+ */
+ uint32_t codec_data_length;
+ /**
+ * @description The audio mime type.
+ */
+ esplusplayer_audio_mime_type mime_type;
+ /**
+ * @description The audio bitrate value.
+ */
+ uint32_t bitrate;
+ /**
+ * @description The audio channel number.
+ */
+ uint32_t channels;
+ /**
+ * @description The audio sample rate value.
+ */
+ uint32_t sample_rate;
+} esplusplayer_audio_stream_info;
+
+/**
+ * @brief Video stream information structure
+ */
+typedef struct {
+ /**
+ * @description The video codec data pointer.
+ */
+ char* codec_data;
+ /**
+ * @description The video codec data length.
+ */
+ uint32_t codec_data_length;
+ /**
+ * @description The video mime type.
+ */
+ esplusplayer_video_mime_type mime_type;
+ /**
+ * @description The width value of the video.
+ */
+ uint32_t width;
+ /**
+ * @description The height value of the video.
+ */
+ uint32_t height;
+ /**
+ * @description The max width value of the video.
+ */
+ uint32_t max_width;
+ /**
+ * @description The max height value of the video.
+ */
+ uint32_t max_height;
+ /**
+ * @description The numerator value of the video frame rate.
+ */
+ uint32_t framerate_num;
+ /**
+ * @description The denominator value of the video frame rate.
+ */
+ uint32_t framerate_den;
+} esplusplayer_video_stream_info;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_TRACK_H__
--- /dev/null
+/**
+ * @file
+ * @brief For submit data type enum.
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.0
+ * @SDK_Support N
+ * @remark This is a group of C style submitstatus related enum.
+ * @see SubmitDataType enum conversion.
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_SUBMITDATATYPE_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_SUBMITDATATYPE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the type of buffers submitted
+ */
+enum esplusplayer_submit_data_type {
+ ESPLUSPLAYER_SUBMIT_DATA_TYPE_CLEAN_DATA,
+ ESPLUSPLAYER_SUBMIT_DATA_TYPE_ENCRYPTED_DATA,
+ ESPLUSPLAYER_SUBMIT_DATA_TYPE_TRUSTZONE_DATA,
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_SUBMITDATATYPE_H__
--- /dev/null
+/**
+ * @file
+ * @brief For submitstatus enum.
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 2.0
+ * @SDK_Support N
+ * @remark This is a group of C style submitstatus related enum.
+ * @see Submitstatus enum convertion.
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_SUBMITSTATUS_H__
+#define __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_SUBMITSTATUS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the buffer status
+ */
+enum esplusplayer_submit_status {
+ ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED,
+ ESPLUSPLAYER_SUBMIT_STATUS_INVALID_PACKET,
+ ESPLUSPLAYER_SUBMIT_STATUS_OUT_OF_MEMORY,
+ ESPLUSPLAYER_SUBMIT_STATUS_FULL,
+ ESPLUSPLAYER_SUBMIT_STATUS_SUCCESS
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_ESPLUSPLAYER_CAPI_SUBMITSTATUS_H__
--- /dev/null
+/**
+ * @file decodedvideoinfo.h
+ * @brief Information for decoded video frames
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_MIXER_DECODED_VIDEO_INFO_H__
+#define __ESPLUSPLAYER_MIXER_DECODED_VIDEO_INFO_H__
+
+#include <tbm_type_common.h>
+
+#include <cstdint>
+
+namespace esplusplayer {
+
+struct DecodedRawPlaneInfo {
+ std::uint32_t phyaddr;
+ std::uint32_t viraddr;
+ std::uint32_t linesize;
+};
+
+struct DecodedRawInfo {
+ std::uint32_t width;
+ std::uint32_t height;
+ DecodedRawPlaneInfo y_info;
+ DecodedRawPlaneInfo uv_info;
+};
+
+struct DecodedVideoKeyTypeInfo {
+ std::uint32_t width;
+ std::uint32_t height;
+ tbm_key key;
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_DECODED_VIDEO_INFO_H__
--- /dev/null
+/**
+ * @file mixer.h
+ * @brief Mixes raw video frame and rendering mixed frame
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_MIXER_MIXER__H__
+#define __ESPLUSPLAYER_MIXER_MIXER__H__
+
+#include <boost/core/noncopyable.hpp>
+#include <memory>
+
+#include "mixer/mixer_eventlistener.h"
+#include "mixer/mixerticket.h"
+#include "esplusplayer/types/display.h"
+// LCOV_EXCL_START
+namespace esplusplayer {
+/**
+ * @brief Enumerations for Resource allocation policy
+ */
+enum class RscAllocMode {
+ kDefault, /**< Main -> Sub -> S/W */
+ kNdecoder, /**< Only N decoder */
+ kDisable /**< Mixer is NOT involved in resource allocation */
+};
+/**
+ * @brief Class Mixer
+ */
+/**
+ * @brief Provides methods to control mixer pipeline and modify
+ * mixed frame.
+ * @remark It internally manages MixerTicket objects which are
+ * combined with player objects.
+ * @see Mixerticket
+ */
+class Mixer : private boost::noncopyable {
+ public:
+ using Ptr = std::unique_ptr<Mixer>;
+ /**
+ * @brief Create a mixer object
+ * @remarks You must use this to get mixer object
+ * @pre None
+ * @post None
+ * @exception None
+ * @return mixer object (unique_ptr<Mixer>)
+ */
+ static Ptr Create();
+ /**
+ * @brief Struct for Resolution info of mixed frame.
+ * @brief Default info is 1920x1080, 30fps
+ */
+ struct ResolutionInfo {
+ int width = 1920; /**< Width of mixed frame */
+ int height = 1080; /**< Height of mixed frame */
+ int framerate_num = 30; /**< Framerate numerator of mixed frame */
+ int framerate_den = 1; /**< Framerate denominator of mixed frame */
+ };
+
+ public:
+ /**
+ * @brief Destructor of Mixer
+ * @pre None
+ * @post None
+ * @exception None
+ * @return None
+ * @see Mixer::Create()
+ */
+ virtual ~Mixer(){};
+ /**
+ * @brief Starts Mixer
+ * @pre None
+ * @post Black frame or mixed frame will be displayed on the screen
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ * @see Mixer::Stop()
+ */
+// LCOV_EXCL_START
+ virtual bool Start() { return false; }
+ /**
+ * @brief Stops Mixer
+ * @pre Mixer::Start() was called
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ * @see Mixer::Start()
+ */
+ virtual bool Stop() { return false; }
+ /**
+ * @brief Gets maximal number of players which can be connected to Mixer
+ * @pre None
+ * @post None
+ * @exception None
+ * @return Non-zero value on success, otherwise zero
+ */
+ virtual int GetMaximumAllowedNumberOfPlayer() { return 0; }
+ /**
+ * @brief Sets the video display
+ * @remarks We are not supporting changing display.
+ * @remarks This API have to be called before calling the
+ * Mixer::Start() to reflect the display type.
+ * @param [in] type : display type
+ * @param [in] obj : The handle to display window
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ * @see DisplayType
+ * Mixer::SetDisplayRoi()
+ */
+ virtual bool SetDisplay(const DisplayType type, void* obj) { return false; }
+ /**
+ * @brief Sets the video display
+ * @remarks We are not supporting changing display.
+ * @remarks This API have to be called before calling the
+ * Mixer::Start() to reflect the display type.
+ * @param [in] type : display type
+ * @param [in] serface_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
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ * @see DisplayType
+ * Mixer::SetDisplayRoi()
+ */
+ virtual bool SetDisplay(const DisplayType type, const uint32_t surface_id, const int x, const int y, const int w, const int h) { return false; }
+ /**
+ * @brief Set the video display mode
+ * @param [in] mode : display mode
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ * @see DisplayMode
+ * @see Mixer::SetDisplay()
+ * @see Mixer::SetDisplayRoi()
+ */
+ virtual bool SetDisplayMode(const DisplayMode& mode) { return false; }
+ /**
+ * @brief Sets the ROI(Region Of Interest) area of display
+ * @remarks The minimum value of width and height are 1.
+ * @param [in] geometry : Roi Geometry
+ * @pre Before set display ROI, #DisplayMode::kDstRoi must be set
+ * with Mixer::SetDisplayMode().
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ * @see DisplayMode \n
+ * Mixer::SetDisplay() \n
+ * Mixer::SetDisplayMode()
+ */
+ virtual bool SetDisplayRoi(const Geometry& geometry) { return false; }
+ /**
+ * @brief Set Resource allocation policy.
+ * @param [in] mode : Resource allocation policy
+ * @pre This api should be called before setting player's display type to
+ * mixer type.
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetRscAllocMode(const RscAllocMode& mode) { return false; }
+ /**
+ * @brief Disable audio focus setting.
+ * The mixer has no authority to set audio focus and player can
+ * control audio pipeline's activation/deactivation.
+ * @pre This api should be called before setting player's display type to
+ * mixer type.
+ * @post SetAudioFocus() will return false.
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool DisableAudioFocusSetting() { return false; }
+ /**
+ * @brief Set the alternative video scaler.
+ * The mixer change to use the sub video scaler instead of main video
+ * scaler(default).
+ * @pre This api should be called before setting player's display type to
+ * mixer type.
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetAlternativeVideoScaler() { return false; }
+ /**
+ * @brief Sets audio focus on the specific player object
+ * @param [in] player_instance : The handle to player instance
+ * @pre None
+ * @post The player which gets audio focus will activate its audio
+ * pipeline.
+ * By default, players deactivate audio until setting audio focus.
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetAudioFocus(const void* player_instance) { return false; }
+ /**
+ * @brief Applies geometry changes on mixed frame
+ * @pre SetDisplayRoi() was called for players which are connected to
+ * Mixer.
+ * @post All the geometry changes will be applied to mixed frame.
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool Commit() { return false; }
+ /**
+ * @brief Sets resolution of mixed frame
+ * @remarks By default, the resolution of mixed frame is 1920x1080.
+ * @param [in] info : The resolution info of mixed frame
+ * @pre Mixer has no connected players yet.
+ * @post The resolution of mixed frame will be changed.
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool SetResolution(const ResolutionInfo& info) { return false; }
+ /**
+ * @brief Register eventlistener to Mixer
+ * @param [in] listener : listener object
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ * @see MixerEventListener
+ */
+ virtual bool RegisterListener(MixerEventListener* listener) { return false; }
+ /**
+ * @brief Creates MixerTicket object for the specific player
+ * @param [in] player_instance : The handle to player instance
+ * @pre None
+ * @post None
+ * @exception None
+ * @return A valid @c MixerTicket object on success, otherwise @c nullptr
+ */
+ virtual MixerTicket* CreateTicket(const void* player_instance) {
+ return nullptr;
+ }
+// LCOV_EXCL_STOP
+
+ protected:
+ /**
+ * @brief Constructor of Mixer
+ * @pre None
+ * @post None
+ * @exception None
+ * @return None
+ * @see Mixer::Create()
+ */
+ Mixer() noexcept {};
+};
+
+} // namespace esplusplayer
+// LCOV_EXCL_STOP
+
+#endif // __ESPLUSPLAYER_MIXER_MIXER__H__
--- /dev/null
+/**
+ * @file mixer_eventlistener.h
+ * @brief Handles various events which comes from Mixer
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_SRC_MIXER_MIXER_EVENTLISTENER__H__
+#define __ESPLUSPLAYER_SRC_MIXER_MIXER_EVENTLISTENER__H__
+
+namespace esplusplayer {
+/**
+ * @brief Class MixerEventListener
+ */
+/**
+ * @brief Notifies event that an application needs to handle.
+ * @remark An application should implement concrete class.
+ * @see Mixer::RegisterListener
+ */
+class MixerEventListener {
+ public:
+ /**
+ * @brief Constuctor of MixerEventListener
+ * @pre None
+ * @post None
+ * @exception None
+ * @return None
+ */
+ MixerEventListener(){};
+ /**
+ * @brief Destructor of MixerEventListener
+ * @pre None
+ * @post None
+ * @exception None
+ * @return None
+ */
+ virtual ~MixerEventListener(){};
+ /**
+ * @brief It will be invoked when an error is occured in Mixer object
+ * @remark OnError means that Mixer can't continue its operation.
+ * An application may need to terminate Mixer usage.
+ * @pre MixerEventListener object is registered
+ * @post None
+ * @exception None
+ * @return None
+ */
+// LCOV_EXCL_START
+ virtual void OnError() {}
+ /**
+ * @brief It will be invoked when Mixer's own pipeline is notified
+ * resource confliction
+ * @remark OnResourceConflicted means that Mixer can't play anymore.
+ * An application must terminate Mixer usage.
+ * @pre MixerEventListener object is registered
+ * @post None
+ * @exception None
+ * @return None
+ */
+ virtual void OnResourceConflicted() {}
+// LCOV_EXCL_STOP
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_MIXER_MIXER_EVENTLISTENER__H__
--- /dev/null
+/**
+ * @file mixerticket.h
+ * @brief Connects player to Mixer and allows it to send raw video
+ * frame
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_SRC_MIXER_MIXERTICKET__H__
+#define __ESPLUSPLAYER_SRC_MIXER_MIXERTICKET__H__
+
+#include <memory>
+
+#include "mixer/decodedvideoinfo.h"
+#include "mixer/mixerticket_eventlistener.h"
+
+namespace esplusplayer {
+/**
+ * @brief Enumerations for Resource category
+ * @brief Each player informs mixer with this category so that
+ * Mixer can understand which player uses which resources.
+ */
+enum class ResourceCategory {
+ kVideoDecoder, /**< Video decoder category */
+ kAudioDecoder, /**< Audio decoder category */
+ kScaler /**< Scaler category */
+};
+/**
+ * @brief Enumerations for Resource type
+ * @brief Each player informs mixer with this type so that
+ * Mixer can understand which player uses main or sub type.
+ */
+enum class ResourceType {
+ kHwMain, /**< H/W Main resource type */
+ kHwSub, /**< H/W Sub resource type */
+ kSw, /**< S/W resource type. Only valid for decoder category */
+ kNdecoder, /**< N decoder resource type */
+ kMax /**< Size of this enumeration (not used) */
+};
+/**
+ * @brief Class MixerTicket
+ */
+/**
+ * @brief Provides methods for player to send their raw video
+ * frame to Mixer.
+ * @remark It also helps player to request H/W resources without
+ * causing confliction.
+ * @see Mixer
+ */
+class MixerTicket {
+ public:
+ /**
+ * @brief Constuctor of MixerTicket
+ * @pre None
+ * @post None
+ * @exception None
+ * @return None
+ */
+ MixerTicket(){};
+ /**
+ * @brief Destructor of MixerTicket
+ * @pre None
+ * @post None
+ * @exception None
+ * @return None
+ */
+// LCOV_EXCL_START
+ virtual ~MixerTicket(){};
+ /**
+ * @brief Gets currently available resource type for the player
+ * @param [in] category : The resource category that player wants to use
+ * @param [out] type : The resource type that player can allocate
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool GetAvailableResourceType(const ResourceCategory& category,
+ ResourceType* type) {
+ return false;
+ }
+ /**
+ * @brief Informs mixer about resource allocation status
+ * @param [in] category : The resource category that player is using
+ * @param [in] type : The resource type that player is using
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool Alloc(const ResourceCategory& category,
+ const ResourceType& type) {
+ return false;
+ }
+ /**
+ * @brief Renders decoded video frame into mixed frame in Mixer object
+ * @param [in] info : The decoded physical address by H/W deocder
+ * and the resolution of video frame.
+ * @pre MixerTicket::Prepare() must be performed without error
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool Render(const DecodedRawInfo& info) { return false; }
+ /**
+ * @brief Renders decoded video frame into mixed frame in Mixer object
+ * @param [in] info : The information for tbm_key exported and the
+ * resolution of video frame.
+ * @pre MixerTicket::Prepare() must be performed without error
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool Render(const DecodedVideoKeyTypeInfo& info) { return false; }
+ /**
+ * @brief Registers eventlistener to MixerTicket
+ * @param [in] listener : listener object
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ * @see MixerTicketEventListener
+ */
+ virtual bool RegisterListener(MixerTicketEventListener* listener) {
+ return false;
+ }
+ /**
+ * @brief Prepares MixerTicket objects to be ready for use
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool Prepare() { return false; }
+ /**
+ * @brief Get the status whether the mixer can handle player's audio focus.
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True : The mixer can handle player's audio focus,
+ * @c False : The mixer can't handle player's audio focus and
+ * the attached players will determin whether to activate
+ * audio track directly.
+ */
+ virtual bool IsAudioFocusHandler() { return false; }
+ /**
+ * @brief Get the status whether Mixer can handle player's resource
+ * allocation.
+ * @pre None
+ * @post None
+ * @exception None
+ * @return @c True : The mixer will allocate appropriate video decoder
+ * resources to the attached players,
+ * @c False : The attached player sets the video decoder resource to
+ * be allocated directly instead of the mixer.
+ */
+ virtual bool IsRscAllocHandler() { return false; }
+// LCOV_EXCL_STOP
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_MIXER_MIXERTICKET__H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file mixerticket_eventlistener.h
+ * @brief Handles various events which comes from MixerTicket
+ * @interfacetype Module
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 0.0.1
+ * @SDK_Support N
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_SRC_MIXER_MIXERTICKET_EVENTLISTENER__H__
+#define __ESPLUSPLAYER_SRC_MIXER_MIXERTICKET_EVENTLISTENER__H__
+
+#include "esplusplayer/types/display.h"
+
+namespace esplusplayer {
+/**
+ * @brief Class MixerTicketEventListener
+ */
+/**
+ * @brief Notifies event that player needs to handle.
+ * @remark Player should implement concrete class.
+ * @see MixerTicket::RegisterListener
+ */
+class MixerTicketEventListener {
+ public:
+ /**
+ * @brief Constuctor of MixerTicketEventListener
+ * @pre None
+ * @post None
+ * @exception None
+ * @return None
+ */
+ MixerTicketEventListener(){};
+ /**
+ * @brief Destructor of MixerTicketEventListener
+ * @pre None
+ * @post None
+ * @exception None
+ * @return None
+ */
+ virtual ~MixerTicketEventListener(){};
+ /**
+ * @brief It will be invoked when audio focus is being changed
+ * @param [in] active : @c True if the player gets focused, otherwise @c
+ * False.
+ * @pre MixerTicketEventListener object is registered
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+// LCOV_EXCL_START
+ virtual bool OnAudioFocusChanged(bool active) { return false; }
+ /**
+ * @brief It will be invoked when the mixed frame's geometry is being
+ * changed
+ * @param [in] cur_info : current display info that Mixer stores for this
+ * player
+ * @param [out] new_info : update display info that player needs to
+ * provide for Mixer
+ * @pre MixerTicketEventListener object is registered
+ * @post None
+ * @exception None
+ * @return @c True on success, otherwise @c False
+ */
+ virtual bool OnUpdateDisplayInfo(const DisplayInfo& cur_info,
+ DisplayInfo* new_info) {
+ return false;
+ }
+// LCOV_EXCL_STOP
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_MIXER_MIXERTICKET_EVENTLISTENER__H__
--- /dev/null
+/**
+ * @file
+ * @brief Display related enums
+ * @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 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) 2020 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 __ESPLUSPLAYER_MIXER_CAPI_DISPLAY_H__
+#define __ESPLUSPLAYER_MIXER_CAPI_DISPLAY_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumerations for the display mode
+ */
+enum mixer_display_mode {
+ MIXER_DISPLAY_MODE_LETTER_BOX,
+ MIXER_DISPLAY_MODE_ORIGIN_SIZE,
+ MIXER_DISPLAY_MODE_FULL_SCREEN,
+ MIXER_DISPLAY_MODE_CROPPED_FULL,
+ MIXER_DISPLAY_MODE_ORIGIN_OR_LETTER,
+ MIXER_DISPLAY_MODE_DST_ROI
+};
+
+/**
+ * @brief Enumerations for the display type
+ */
+enum mixer_display_type {
+ MIXER_DISPLAY_TYPE_NONE,
+ MIXER_DISPLAY_TYPE_OVERLAY,
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_MIXER_CAPI_DISPLAY_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file
+ * @brief Error related enums
+ * @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) 2020 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 __ESPLUSPLAYER_MIXER_CAPI_ERROR_H__
+#define __ESPLUSPLAYER_MIXER_CAPI_ERROR_H__
+
+#include "tizen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MIXER_ERROR_CLASS TIZEN_ERROR_PLAYER | 0x20
+
+/**
+ * @brief Enumerations for the error type
+ */
+enum mixer_error_type {
+ MIXER_ERROR_TYPE_NONE = TIZEN_ERROR_NONE, /**< Successful */
+ MIXER_ERROR_TYPE_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */
+ MIXER_ERROR_TYPE_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION, /**< Invalid operation */
+};
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __ESPLUSPLAYER_MIXER_CAPI_ERROR_H__
\ No newline at end of file
--- /dev/null
+/**
+ * @file mixer_capi.h
+ * @brief Mixer api c version
+ * @interfacetype Platform
+ * @privlevel None-privilege
+ * @privilege None
+ * @product TV, AV, B2B
+ * @version 3.0
+ * @SDK_Support N
+ * @remark This is mixer api header implemented as C style to
+ * avoid binary compatibility issues.
+ *
+ * Copyright (c) 2020 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 __ESPLUSPLAYER_MIXER_CAPI_MIXER_CAPI_H__
+#define __ESPLUSPLAYER_MIXER_CAPI_MIXER_CAPI_H__
+
+#include "mixer_capi/error.h"
+#include "mixer_capi/display.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+typedef void (*mixer_resource_conflicted_cb)(void*);
+
+typedef void* mixer_handle;
+typedef void* mixer_ticket_h;
+
+/**
+ * @brief Enumerations for the resource allocation mode
+ */
+enum mixer_rsc_alloc_mode {
+ MIXER_RSC_ALLOC_MODE_DEFAULT, /**< Main -> Sub -> S/W */
+ MIXER_RSC_ALLOC_MODE_NDECODER, /**< Only N decoder */
+ MIXER_RSC_ALLOC_MODE_DISABLE /**< Mixer is NOT involved in resource allocation */
+};
+
+/**
+ * @brief Create a mixer handle
+ * @param None
+ * @return return mixer handle pointer
+ * @code
+ // basic api call sequence
+ mixer_handle mixer = mixer_create();
+ mixer_set_display(mixer, MIXER_DISPLAY_TYPE_OVERLAY, window);
+
+ esplusplayer_handle player1 = esplusplayer_create();
+ esplusplayer_open(player1);
+
+ esplusplayer_set_display(player1, ESPLUSPLAYER_DISPLAY_TYPE_MIXER, mixer);
+ eplusplayer_set_display_roi(player1, x, y, w, h);
+
+ mixer_set_audio_focus(mixer, player1);
+
+ esplusplayer_set_video_stream_info(player1, video_info);
+ esplusplayer_set_audio_stream_info(player1, audio_info);
+ esplusplayer_prepare_async(player1);
+
+ mixer_start(mixer);
+ esplusplayer_start(player1);
+
+ mixer_stop(mixer);
+ esplusplayer_close(player1);
+ mixer_destroy(mixer);
+ esplusplayer_destroy(player1);
+ * @endcode
+ * @pre None
+ * @post None
+ * @exception None
+ */
+mixer_handle mixer_create();
+
+/**
+ * @brief Release mixer handle
+ * @param [in] handle : mixer handle
+ * @return @c one of mixer_error_type values will be returned
+ * @pre None
+ * @post mixer handle will be removed
+ * @exception None
+ * @see mixer_create()
+ */
+int mixer_destroy(mixer_handle handle);
+
+/**
+ * @brief Starts Mixer.
+ * @param [in] handle : mixer handle
+ * @return @c one of mixer_error_type values will be returned
+ * @pre None
+ * @post Black frame or mixed frame will be displayed on the screen
+ * @exception None
+ * @see mixer_stop()
+ */
+int mixer_start(mixer_handle handle);
+
+/**
+ * @brief Stops Mixer
+ * @param [in] handle : Mixer handle
+ * @return @c one of mixer_error_type values will be returned
+ * @pre mixer_start() should be called
+ * @post None
+ * @exception None
+ * @see mixer_start()
+ */
+int mixer_stop(mixer_handle handle);
+
+/**
+ * @brief Gets maximal number of players which can be connected to Mixer
+ * @param [in] handle : Mixer handle
+ * @return Non-zero value on success, otherwise zero
+ * @pre None
+ * @post None
+ * @exception None
+ */
+int mixer_get_max_allowed_number_of_player(mixer_handle handle);
+
+/**
+ * @brief Sets the video display
+ * @param [in] handle : Mixer handle
+ * @param [in] type : display type
+ * @param [in] window : The handle to display window
+ * @return @c one of mixer_error_type values will be returned
+ * @pre This API have to be called before calling the
+ * mixer_start() to reflect the display type
+ * @post None
+ * @exception None
+ * @remarks We are not supporting changing display
+ * @see mixer_display_type \n
+ * mixer_set_display_roi()
+ */
+int mixer_set_display(mixer_handle handle, mixer_display_type type,
+ void* window);
+
+/**
+ * @brief Set the video display mode
+ * @param [in] handle : Mixer handle
+ * @param [in] mode : display mode
+ * @return @c one of mixer_error_type values will be returned
+ * @pre None
+ * @post None
+ * @exception None
+ * @see mixer_display_mode
+ * @see mixer_set_display() \n
+ * mixer_set_display_roi()
+ */
+int mixer_set_display_mode(mixer_handle handle, mixer_display_mode mode);
+
+/**
+ * @brief Sets the ROI(Region Of Interest) area of display
+ * @param [in] handle : Mixer handle
+ * @param [in] geometry : Roi Geometry
+ * @return @c one of mixer_error_type values will be returned
+ * @code
+ // The ROI of mixer means display area in tv screen.
+ // The ROI of each player means display area in mixer's ROI.
+
+ mixer_set_display_mode(mixer, MIXER_DISPLAY_MODE_DST_ROI);
+ mixer_set_display_roi(mixer, 0, 0, 1920, 1080);
+
+ esplusplayer_set_display_roi(player1, 20, 20, 700, 400);
+ esplusplayer_set_display_roi(player2, 20, 20, 700, 400);
+
+ mixer_commit(mixer);
+ * @endcode
+ * @pre Before set display ROI, #MIXER_DISPLAY_MODE_DST_ROI must be set
+ * with mixer_set_display_mode()
+ * @post None
+ * @exception None
+ * @remarks The minimum value of width and height are 1.
+ * @see mixer_display_mode \n
+ * mixer_set_display() \n
+ * mixer_set_display_mode()
+ */
+int mixer_set_display_roi(mixer_handle handle, const int x, const int y,
+ const int width, const int height);
+
+ /**
+ * @brief Applies geometry changes on mixed frame
+ * @param [in] handle : Mixer handle
+ * @return @c one of mixer_error_type values will be returned
+ * @pre set display roi api was called for players which are connected to
+ * Mixer.
+ * @post All the geometry changes will be applied to mixed frame.
+ * @exception None
+ * @see mixer_set_display_roi()
+ */
+ int mixer_commit(mixer_handle handle);
+
+ /**
+ * @brief Set Resource allocation policy
+ * @param [in] handle : Mixer handle
+ * @param [in] mode : Resource allocation policy
+ * @return @c one of mixer_error_type values will be returned
+ * @pre This api should be called before setting player's display type to
+ * mixer type.
+ * @post None
+ * @exception None
+ */
+ int mixer_set_rsc_alloc_mode(mixer_handle handle,
+ const mixer_rsc_alloc_mode mode);
+
+/**
+ * @brief Disable audio focus setting.
+ * The mixer has no authority to set audio focus and player can
+ * control audio pipeline's activation/deactivation.
+ * @param [in] handle : Mixer handle
+ * @return @c one of mixer_error_type values will be returned
+ * @pre This api should be called before setting player's display type to
+ * mixer type.
+ * @post mixer_set_audio_focus() will return error.
+ * @exception None
+ */
+int mixer_disable_audio_focus_setting(mixer_handle handle);
+
+/**
+ * @brief Set the alternative video scaler.
+ * The mixer change to use the sub video scaler instead of main video
+ * scaler(default).
+ * @param [in] handle : Mixer handle
+ * @return @c one of mixer_error_type values will be returned
+ * @pre This api should be called before mixer_start().
+ * @post None
+ * @exception None
+ */
+int mixer_set_alternative_video_scaler(mixer_handle handle);
+
+/**
+ * @brief Sets audio focus on the specific player object
+ * @param [in] handle : Mixer handle
+ * @param [in] player_instance : The handle to player instance
+ * @return @c one of mixer_error_type values will be returned
+ * @pre None
+ * @post The player which gets audio focus will activate its audio
+ * pipeline. By default, players deactivate audio until setting
+ * audio focus.
+ * @exception None
+ */
+int mixer_set_audio_focus(mixer_handle handle, const void* player_instance);
+
+/**
+ * @brief Sets resolution of mixed frame
+ * @param [in] handle : Mixer handle
+ * @param [in] info : The resolution info of mixed frame
+ * @return @c one of mixer_error_type values will be returned
+ * @pre Mixer has no connected players yet.
+ * @post The resolution of mixed frame will be changed.
+ * @exception None
+ * @remarks By default, the resolution of mixed frame is 1920x1080.
+ */
+int mixer_set_resolution(mixer_handle handle, const int width, const int height,
+ const int framerate_num, const int framerate_den);
+
+/**
+ * @brief Creates MixerTicket object for the specific player
+ * @param [in] handle : Mixer handle
+ * @param [in] player_instance : The handle to player instance
+ * @return A valid @c MixerTicket object on success, otherwise @c nullptr
+ * @pre None
+ * @post None
+ * @exception None
+ */
+mixer_ticket_h mixer_create_ticket(mixer_handle handle,
+ const void* player_instance);
+
+// callback function
+
+/**
+ * @brief It will be invoked when Mixer's own pipeline is notified
+ * resource confliction
+ * @param [in] handle : Mixer handle
+ * @param [in] callback : the callback function to register
+ * @param [in] userdata : userdata of resource_conflicted_cb()
+ * @return @c one of mixer_error_type values will be returned
+ * @pre None
+ * @post resource_conflicted_cb will be invoked when resources are
+ * conflicted
+ * @exception None
+ * @remark resource_conflicted_cb means that Mixer can't play anymore.
+ * An application must terminate Mixer usage.
+ */
+int mixer_set_resource_conflicted_cb(
+ mixer_handle handle, mixer_resource_conflicted_cb resource_conflicted_cb,
+ void* userdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __ESPLUSPLAYER_MIXER_CAPI_MIXER_CAPI_H__
+
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+</manifest>
\ No newline at end of file
--- /dev/null
+# %bcond_with : disable ESPLUSPLAYER_UT by default, %bcond_without : enable ESPLUSPLAYER_UT
+%if ("%{_vd_cfg_product_type}" != "AUDIO")
+%bcond_without ESPLUSPLAYER_UT
+%else
+%bcond_with ESPLUSPLAYER_UT
+%endif
+#echo "Product Type: %{_vd_cfg_product_type}"
+Name: esplusplayer
+Summary: new multimedia streaming player
+Version: 0.0.1
+Release: 0
+Group: Multimedia/Libraries
+License: Apache-2.0
+Source0: %{name}-%{version}.tar.gz
+Source1001: esplusplayer.manifest
+BuildRequires: cmake
+BuildRequires: curl
+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(libtzplatform-config)
+BuildRequires: pkgconfig(jsoncpp)
+BuildRequires: pkgconfig(gstreamer-ffsubtitle-1.0)
+BuildRequires: pkgconfig(icu-i18n)
+BuildRequires: pkgconfig(drmdecrypt)
+BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(logger)
+BuildRequires: pkgconfig(gio-2.0)
+BuildRequires: pkgconfig(libtbm)
+BuildRequires: pkgconfig(lwipc)
+BuildRequires: pkgconfig(capi-screensaver)
+BuildRequires: pkgconfig(context-aware-api)
+BuildRequires: pkgconfig(capi-trackrenderer-tv)
+BuildRequires: pkgconfig(libtbm)
+BuildRequires: pkgconfig(smart-deadlock)
+
+%if ("%{_vd_cfg_product_type}" != "AUDIO")
+BuildRequires: pkgconfig(graphics-control)
+BuildRequires: pkgconfig(libavoc)
+%else
+BuildRequires: pkgconfig(libavoc-av)
+%endif
+
+%if ("%{_vd_cfg_licensing}" == "n")
+# for ut
+BuildRequires: pkgconfig(capi-media-player)
+BuildRequires: gtest-devel
+BuildRequires: pkgconfig(appcore-efl)
+BuildRequires: pkgconfig(libresourced)
+%endif
+
+%define _packagedir /usr
+%define _bindir %{_packagedir}/bin
+%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")
+%if ("%{_vd_cfg_product_type}" != "AUDIO")
+%{?!TOMATO: %define TOMATO n}
+
+%define _tomatoname esplusplayer
+%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: gtest-devel
+BuildRequires: pkgconfig(video-capture)
+BuildRequires: pkgconfig(audio-control)
+BuildRequires: libjpeg-turbo-devel
+BuildRequires: elementary-devel
+BuildRequires: pkgconfig(capi-appfw-application)
+BuildRequires: pkgconfig(video-sink)
+BuildRequires: pkgconfig(capi-system-info)
+Requires: %{name} = %{version}-%{release}
+
+%description ut-component-tomato
+This package is for test
+
+%files ut-component-tomato
+%defattr(-,root,root,-)
+%{_bindir}/esplusplayer_ut
+%{_tomatodir}/*
+
+%endif
+%endif
+
+%build
+export CFLAGS+=" -Wno-deprecated-declarations"
+export CXXFLAGS+=" -Wno-deprecated-declarations"
+
+%if ("%{_vd_cfg_product_type}" == "AUDIO")
+export CFLAGS+=" -DIS_AUDIO_PRODUCT"
+export CXXFLAGS+=" -DIS_AUDIO_PRODUCT"
+%define PRODUCT_TYPE_AUDIO yes
+%else
+%define PRODUCT_TYPE_AUDIO no
+%endif
+
+%if ("%{_vd_cfg_product_type}" == "AV")
+export CFLAGS+=" -DIS_AV_PRODUCT"
+export CXXFLAGS+=" -DIS_AV_PRODUCT"
+%endif
+
+%if ("%{_vd_cfg_product_type}" == "AUDIO")
+export CFLAGS+=" -DSOUNDBAR_PRODUCT"
+export CXXFLAGS+=" -DSOUNDBAR_PRODUCT"
+%define _support_soundbar -DSUPPORT_SOUNDBAR=ON
+%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"
+#export CFLAGS+=" -DIS_TOMATO"
+#export CXXFLAGS+=" -DIS_TOMATO"
+%endif
+
+export CXXFLAGS+=" -Wno-pessimizing-move"
+
+%if ("%{_vd_cfg_licensing}" == "n")
+%if %{with ESPLUSPLAYER_UT}
+%cmake . -DESPLUSPLAYER_BUILD_UT=ON -DPRODUCT_TYPE_AUDIO=%PRODUCT_TYPE_AUDIO
+%else
+%cmake . -DPRODUCT_TYPE_AUDIO=%PRODUCT_TYPE_AUDIO
+%endif
+%else
+%cmake . -DPRODUCT_TYPE_AUDIO=%PRODUCT_TYPE_AUDIO
+%endif
+
+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
+mkdir -p %{buildroot}%TZ_SYS_RO_ETC/multimedia
+#mkdir -p %{buildroot}%TZ_SYS_RW_APP/multimedia
+#mkdir -p %{buildroot}/opt
+#mkdir -p %{buildroot}/opt/usr
+#mkdir -p %{buildroot}/opt/usr/home
+#mkdir -p %{buildroot}/opt/usr/home/owner
+#mkdir -p %{buildroot}/opt/usr/home/owner/models
+cp -rf config/esplusplayer.ini %{buildroot}%TZ_SYS_RO_ETC/multimedia/esplusplayer.ini
+
+%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 esplusplayer.manifest
+%license LICENSE.APLv2
+%{_libdir}/libespplayer-core.so
+%{_libdir}/libesplusplayer.so
+%if ("%{_vd_cfg_product_type}" != "AUDIO")
+%{_libdir}/libmixer.so
+%endif
+
+%TZ_SYS_RO_ETC/multimedia/esplusplayer.ini
+#%attr(0775, owner,users) %TZ_SYS_RW_APP/multimedia
+
+#remove esplusplayer_ut in esplusplayer package by flash memory issue
+#%if ("%{_vd_cfg_licensing}" == "n")
+#%if %{with ESPLUSPLAYER_UT}
+#%{_bindir}/esplusplayer_ut
+#%defattr(-,root,root,-)
+#%{_tomatodir}/*
+#%endif
+#%endif
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/esplusplayer/*.h
+%{_includedir}/esplusplayer/types/*.h
+%{_includedir}/esplusplayer_capi/*.h
+%{_includedir}/mixer/*.h
+%{_includedir}/mixer_capi/*.h
+%{_pkgconfigdir}/esplusplayer.pc
+
+%files config
+%defattr(-,root,root,-)
+%manifest esplusplayer.manifest
+%license LICENSE.APLv2
+%TZ_SYS_RO_ETC/multimedia/esplusplayer.ini
+%attr(0775, owner,users) %TZ_SYS_RO_ETC/multimedia
+
+%if 0%{?vd_gcov:1}
+%files gcov
+%{_datadir}/gcov/*
+%endif
--- /dev/null
+ADD_SUBDIRECTORY(plusplayer-core)
+ADD_SUBDIRECTORY(esplusplayer)
+IF(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+ADD_SUBDIRECTORY(mixer)
+ENDIF(${PRODUCT_TYPE_AUDIO} STREQUAL)
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2009 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Does google-lint on c++ files.
+
+The goal of this script is to identify places in the code that *may*
+be in non-compliance with google style. It does not attempt to fix
+up these problems -- the point is to educate. It does also not
+attempt to find all problems, or to ensure that everything it does
+find is legitimately a problem.
+
+In particular, we can get very confused by /* and // inside strings!
+We do a small hack, which is to ignore //'s with "'s after them on the
+same line, but it is far from perfect (in either direction).
+"""
+
+import codecs
+import copy
+import getopt
+import math # for log
+import os
+import re
+import sre_compile
+import string
+import sys
+import unicodedata
+
+
+_USAGE = """
+Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
+ [--counting=total|toplevel|detailed] [--root=subdir]
+ [--linelength=digits] [--headers=x,y,...]
+ <file> [file] ...
+
+ The style guidelines this tries to follow are those in
+ https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
+
+ Every problem is given a confidence score from 1-5, with 5 meaning we are
+ certain of the problem, and 1 meaning it could be a legitimate construct.
+ This will miss some errors, and is not a substitute for a code review.
+
+ To suppress false-positive errors of a certain category, add a
+ 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*)
+ suppresses errors of all categories on that line.
+
+ The files passed in will be linted; at least one file must be provided.
+ Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the
+ extensions with the --extensions flag.
+
+ Flags:
+
+ output=vs7
+ By default, the output is formatted to ease emacs parsing. Visual Studio
+ compatible output (vs7) may also be used. Other formats are unsupported.
+
+ verbose=#
+ Specify a number 0-5 to restrict errors to certain verbosity levels.
+
+ filter=-x,+y,...
+ Specify a comma-separated list of category-filters to apply: only
+ error messages whose category names pass the filters will be printed.
+ (Category names are printed with the message and look like
+ "[whitespace/indent]".) Filters are evaluated left to right.
+ "-FOO" and "FOO" means "do not print categories that start with FOO".
+ "+FOO" means "do print categories that start with FOO".
+
+ Examples: --filter=-whitespace,+whitespace/braces
+ --filter=whitespace,runtime/printf,+runtime/printf_format
+ --filter=-,+build/include_what_you_use
+
+ To see a list of all the categories used in cpplint, pass no arg:
+ --filter=
+
+ counting=total|toplevel|detailed
+ The total number of errors found is always printed. If
+ 'toplevel' is provided, then the count of errors in each of
+ the top-level categories like 'build' and 'whitespace' will
+ also be printed. If 'detailed' is provided, then a count
+ is provided for each category like 'build/class'.
+
+ root=subdir
+ The root directory used for deriving header guard CPP variable.
+ By default, the header guard CPP variable is calculated as the relative
+ path to the directory that contains .git, .hg, or .svn. When this flag
+ is specified, the relative path is calculated from the specified
+ directory. If the specified directory does not exist, this flag is
+ ignored.
+
+ Examples:
+ Assuming that src/.git exists, the header guard CPP variables for
+ src/chrome/browser/ui/browser.h are:
+
+ No flag => CHROME_BROWSER_UI_BROWSER_H_
+ --root=chrome => BROWSER_UI_BROWSER_H_
+ --root=chrome/browser => UI_BROWSER_H_
+
+ linelength=digits
+ This is the allowed line length for the project. The default value is
+ 80 characters.
+
+ Examples:
+ --linelength=120
+
+ extensions=extension,extension,...
+ The allowed file extensions that cpplint will check
+
+ Examples:
+ --extensions=hpp,cpp
+
+ headers=x,y,...
+ The header extensions that cpplint will treat as .h in checks. Values are
+ automatically added to --extensions list.
+
+ Examples:
+ --headers=hpp,hxx
+ --headers=hpp
+
+ cpplint.py supports per-directory configurations specified in CPPLINT.cfg
+ files. CPPLINT.cfg file can contain a number of key=value pairs.
+ Currently the following options are supported:
+
+ set noparent
+ filter=+filter1,-filter2,...
+ exclude_files=regex
+ linelength=80
+ root=subdir
+ headers=x,y,...
+
+ "set noparent" option prevents cpplint from traversing directory tree
+ upwards looking for more .cfg files in parent directories. This option
+ is usually placed in the top-level project directory.
+
+ The "filter" option is similar in function to --filter flag. It specifies
+ message filters in addition to the |_DEFAULT_FILTERS| and those specified
+ through --filter command-line flag.
+
+ "exclude_files" allows to specify a regular expression to be matched against
+ a file name. If the expression matches, the file is skipped and not run
+ through liner.
+
+ "linelength" allows to specify the allowed line length for the project.
+
+ The "root" option is similar in function to the --root flag (see example
+ above).
+
+ The "headers" option is similar in function to the --headers flag
+ (see example above).
+
+ CPPLINT.cfg has an effect on files in the same directory and all
+ sub-directories, unless overridden by a nested configuration file.
+
+ Example file:
+ filter=-build/include_order,+build/include_alpha
+ exclude_files=.*\.cc
+
+ The above example disables build/include_order warning and enables
+ build/include_alpha as well as excludes all .cc from being
+ processed by linter, in the current directory (where the .cfg
+ file is located) and all sub-directories.
+"""
+
+# We categorize each error message we print. Here are the categories.
+# We want an explicit list so we can list them all in cpplint --filter=.
+# If you add a new error message with a new category, add it to the list
+# here! cpplint_unittest.py should tell you if you forget to do this.
+_ERROR_CATEGORIES = [
+ 'build/class',
+ 'build/c++11',
+ 'build/c++14',
+ 'build/c++tr1',
+ 'build/deprecated',
+ 'build/endif_comment',
+ 'build/explicit_make_pair',
+ 'build/forward_decl',
+ 'build/header_guard',
+ 'build/include',
+ 'build/include_alpha',
+ 'build/include_order',
+ 'build/include_what_you_use',
+ 'build/namespaces',
+ 'build/printf_format',
+ 'build/storage_class',
+ 'legal/copyright',
+ 'readability/alt_tokens',
+ 'readability/braces',
+ 'readability/casting',
+ 'readability/check',
+ 'readability/constructors',
+ 'readability/fn_size',
+ 'readability/inheritance',
+ 'readability/multiline_comment',
+ 'readability/multiline_string',
+ 'readability/namespace',
+ 'readability/nolint',
+ 'readability/nul',
+ 'readability/strings',
+ 'readability/todo',
+ 'readability/utf8',
+ 'runtime/arrays',
+ 'runtime/casting',
+ 'runtime/explicit',
+ 'runtime/int',
+ 'runtime/init',
+ 'runtime/invalid_increment',
+ 'runtime/member_string_references',
+ 'runtime/memset',
+ 'runtime/indentation_namespace',
+ 'runtime/operator',
+ 'runtime/printf',
+ 'runtime/printf_format',
+ 'runtime/references',
+ 'runtime/string',
+ 'runtime/threadsafe_fn',
+ 'runtime/vlog',
+ 'whitespace/blank_line',
+ 'whitespace/braces',
+ 'whitespace/comma',
+ 'whitespace/comments',
+ 'whitespace/empty_conditional_body',
+ 'whitespace/empty_if_body',
+ 'whitespace/empty_loop_body',
+ 'whitespace/end_of_line',
+ 'whitespace/ending_newline',
+ 'whitespace/forcolon',
+ 'whitespace/indent',
+ 'whitespace/line_length',
+ 'whitespace/newline',
+ 'whitespace/operators',
+ 'whitespace/parens',
+ 'whitespace/semicolon',
+ 'whitespace/tab',
+ 'whitespace/todo',
+ ]
+
+# These error categories are no longer enforced by cpplint, but for backwards-
+# compatibility they may still appear in NOLINT comments.
+_LEGACY_ERROR_CATEGORIES = [
+ 'readability/streams',
+ 'readability/function',
+ ]
+
+# The default state of the category filter. This is overridden by the --filter=
+# flag. By default all errors are on, so only add here categories that should be
+# off by default (i.e., categories that must be enabled by the --filter= flags).
+# All entries here should start with a '-' or '+', as in the --filter= flag.
+_DEFAULT_FILTERS = ['-build/include_alpha']
+
+# The default list of categories suppressed for C (not C++) files.
+_DEFAULT_C_SUPPRESSED_CATEGORIES = [
+ 'readability/casting',
+ ]
+
+# The default list of categories suppressed for Linux Kernel files.
+_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [
+ 'whitespace/tab',
+ ]
+
+# We used to check for high-bit characters, but after much discussion we
+# decided those were OK, as long as they were in UTF-8 and didn't represent
+# hard-coded international strings, which belong in a separate i18n file.
+
+# C++ headers
+_CPP_HEADERS = frozenset([
+ # Legacy
+ 'algobase.h',
+ 'algo.h',
+ 'alloc.h',
+ 'builtinbuf.h',
+ 'bvector.h',
+ 'complex.h',
+ 'defalloc.h',
+ 'deque.h',
+ 'editbuf.h',
+ 'fstream.h',
+ 'function.h',
+ 'hash_map',
+ 'hash_map.h',
+ 'hash_set',
+ 'hash_set.h',
+ 'hashtable.h',
+ 'heap.h',
+ 'indstream.h',
+ 'iomanip.h',
+ 'iostream.h',
+ 'istream.h',
+ 'iterator.h',
+ 'list.h',
+ 'map.h',
+ 'multimap.h',
+ 'multiset.h',
+ 'ostream.h',
+ 'pair.h',
+ 'parsestream.h',
+ 'pfstream.h',
+ 'procbuf.h',
+ 'pthread_alloc',
+ 'pthread_alloc.h',
+ 'rope',
+ 'rope.h',
+ 'ropeimpl.h',
+ 'set.h',
+ 'slist',
+ 'slist.h',
+ 'stack.h',
+ 'stdiostream.h',
+ 'stl_alloc.h',
+ 'stl_relops.h',
+ 'streambuf.h',
+ 'stream.h',
+ 'strfile.h',
+ 'strstream.h',
+ 'tempbuf.h',
+ 'tree.h',
+ 'type_traits.h',
+ 'vector.h',
+ # 17.6.1.2 C++ library headers
+ 'algorithm',
+ 'array',
+ 'atomic',
+ 'bitset',
+ 'chrono',
+ 'codecvt',
+ 'complex',
+ 'condition_variable',
+ 'deque',
+ 'exception',
+ 'forward_list',
+ 'fstream',
+ 'functional',
+ 'future',
+ 'initializer_list',
+ 'iomanip',
+ 'ios',
+ 'iosfwd',
+ 'iostream',
+ 'istream',
+ 'iterator',
+ 'limits',
+ 'list',
+ 'locale',
+ 'map',
+ 'memory',
+ 'mutex',
+ 'new',
+ 'numeric',
+ 'ostream',
+ 'queue',
+ 'random',
+ 'ratio',
+ 'regex',
+ 'scoped_allocator',
+ 'set',
+ 'sstream',
+ 'stack',
+ 'stdexcept',
+ 'streambuf',
+ 'string',
+ 'strstream',
+ 'system_error',
+ 'thread',
+ 'tuple',
+ 'typeindex',
+ 'typeinfo',
+ 'type_traits',
+ 'unordered_map',
+ 'unordered_set',
+ 'utility',
+ 'valarray',
+ 'vector',
+ # 17.6.1.2 C++ headers for C library facilities
+ 'cassert',
+ 'ccomplex',
+ 'cctype',
+ 'cerrno',
+ 'cfenv',
+ 'cfloat',
+ 'cinttypes',
+ 'ciso646',
+ 'climits',
+ 'clocale',
+ 'cmath',
+ 'csetjmp',
+ 'csignal',
+ 'cstdalign',
+ 'cstdarg',
+ 'cstdbool',
+ 'cstddef',
+ 'cstdint',
+ 'cstdio',
+ 'cstdlib',
+ 'cstring',
+ 'ctgmath',
+ 'ctime',
+ 'cuchar',
+ 'cwchar',
+ 'cwctype',
+ ])
+
+# Type names
+_TYPES = re.compile(
+ r'^(?:'
+ # [dcl.type.simple]
+ r'(char(16_t|32_t)?)|wchar_t|'
+ r'bool|short|int|long|signed|unsigned|float|double|'
+ # [support.types]
+ r'(ptrdiff_t|size_t|max_align_t|nullptr_t)|'
+ # [cstdint.syn]
+ r'(u?int(_fast|_least)?(8|16|32|64)_t)|'
+ r'(u?int(max|ptr)_t)|'
+ r')$')
+
+
+# These headers are excluded from [build/include] and [build/include_order]
+# checks:
+# - Anything not following google file name conventions (containing an
+# uppercase character, such as Python.h or nsStringAPI.h, for example).
+# - Lua headers.
+_THIRD_PARTY_HEADERS_PATTERN = re.compile(
+ r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$')
+
+# Pattern for matching FileInfo.BaseName() against test file name
+_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$'
+
+# Pattern that matches only complete whitespace, possibly across multiple lines.
+_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL)
+
+# Assertion macros. These are defined in base/logging.h and
+# testing/base/public/gunit.h.
+_CHECK_MACROS = [
+ 'DCHECK', 'CHECK',
+ 'EXPECT_TRUE', 'ASSERT_TRUE',
+ 'EXPECT_FALSE', 'ASSERT_FALSE',
+ ]
+
+# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE
+_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS])
+
+for op, replacement in [('==', 'EQ'), ('!=', 'NE'),
+ ('>=', 'GE'), ('>', 'GT'),
+ ('<=', 'LE'), ('<', 'LT')]:
+ _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement
+ _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement
+ _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement
+ _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement
+
+for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'),
+ ('>=', 'LT'), ('>', 'LE'),
+ ('<=', 'GT'), ('<', 'GE')]:
+ _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement
+ _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement
+
+# Alternative tokens and their replacements. For full list, see section 2.5
+# Alternative tokens [lex.digraph] in the C++ standard.
+#
+# Digraphs (such as '%:') are not included here since it's a mess to
+# match those on a word boundary.
+_ALT_TOKEN_REPLACEMENT = {
+ 'and': '&&',
+ 'bitor': '|',
+ 'or': '||',
+ 'xor': '^',
+ 'compl': '~',
+ 'bitand': '&',
+ 'and_eq': '&=',
+ 'or_eq': '|=',
+ 'xor_eq': '^=',
+ 'not': '!',
+ 'not_eq': '!='
+ }
+
+# Compile regular expression that matches all the above keywords. The "[ =()]"
+# bit is meant to avoid matching these keywords outside of boolean expressions.
+#
+# False positives include C-style multi-line comments and multi-line strings
+# but those have always been troublesome for cpplint.
+_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile(
+ r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)')
+
+
+# These constants define types of headers for use with
+# _IncludeState.CheckNextIncludeOrder().
+_C_SYS_HEADER = 1
+_CPP_SYS_HEADER = 2
+_LIKELY_MY_HEADER = 3
+_POSSIBLE_MY_HEADER = 4
+_OTHER_HEADER = 5
+
+# These constants define the current inline assembly state
+_NO_ASM = 0 # Outside of inline assembly block
+_INSIDE_ASM = 1 # Inside inline assembly block
+_END_ASM = 2 # Last line of inline assembly block
+_BLOCK_ASM = 3 # The whole block is an inline assembly block
+
+# Match start of assembly blocks
+_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)'
+ r'(?:\s+(volatile|__volatile__))?'
+ r'\s*[{(]')
+
+# Match strings that indicate we're working on a C (not C++) file.
+_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|'
+ r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))')
+
+# Match string that indicates we're working on a Linux Kernel file.
+_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)')
+
+_regexp_compile_cache = {}
+
+# {str, set(int)}: a map from error categories to sets of linenumbers
+# on which those errors are expected and should be suppressed.
+_error_suppressions = {}
+
+# The root directory used for deriving header guard CPP variable.
+# This is set by --root flag.
+_root = None
+
+# The allowed line length of files.
+# This is set by --linelength flag.
+_line_length = 80
+
+# The allowed extensions for file names
+# This is set by --extensions flag.
+_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh'])
+
+# Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc.
+# This is set by --headers flag.
+_hpp_headers = set(['h'])
+
+# {str, bool}: a map from error categories to booleans which indicate if the
+# category should be suppressed for every line.
+_global_error_suppressions = {}
+
+def ProcessHppHeadersOption(val):
+ global _hpp_headers
+ try:
+ _hpp_headers = set(val.split(','))
+ # Automatically append to extensions list so it does not have to be set 2 times
+ _valid_extensions.update(_hpp_headers)
+ except ValueError:
+ PrintUsage('Header extensions must be comma seperated list.')
+
+def IsHeaderExtension(file_extension):
+ return file_extension in _hpp_headers
+
+def ParseNolintSuppressions(filename, raw_line, linenum, error):
+ """Updates the global list of line error-suppressions.
+
+ Parses any NOLINT comments on the current line, updating the global
+ error_suppressions store. Reports an error if the NOLINT comment
+ was malformed.
+
+ Args:
+ filename: str, the name of the input file.
+ raw_line: str, the line of input text, with comments.
+ linenum: int, the number of the current line.
+ error: function, an error handler.
+ """
+ matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line)
+ if matched:
+ if matched.group(1):
+ suppressed_line = linenum + 1
+ else:
+ suppressed_line = linenum
+ category = matched.group(2)
+ if category in (None, '(*)'): # => "suppress all"
+ _error_suppressions.setdefault(None, set()).add(suppressed_line)
+ else:
+ if category.startswith('(') and category.endswith(')'):
+ category = category[1:-1]
+ if category in _ERROR_CATEGORIES:
+ _error_suppressions.setdefault(category, set()).add(suppressed_line)
+ elif category not in _LEGACY_ERROR_CATEGORIES:
+ error(filename, linenum, 'readability/nolint', 5,
+ 'Unknown NOLINT error category: %s' % category)
+
+
+def ProcessGlobalSuppresions(lines):
+ """Updates the list of global error suppressions.
+
+ Parses any lint directives in the file that have global effect.
+
+ Args:
+ lines: An array of strings, each representing a line of the file, with the
+ last element being empty if the file is terminated with a newline.
+ """
+ for line in lines:
+ if _SEARCH_C_FILE.search(line):
+ for category in _DEFAULT_C_SUPPRESSED_CATEGORIES:
+ _global_error_suppressions[category] = True
+ if _SEARCH_KERNEL_FILE.search(line):
+ for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES:
+ _global_error_suppressions[category] = True
+
+
+def ResetNolintSuppressions():
+ """Resets the set of NOLINT suppressions to empty."""
+ _error_suppressions.clear()
+ _global_error_suppressions.clear()
+
+
+def IsErrorSuppressedByNolint(category, linenum):
+ """Returns true if the specified error category is suppressed on this line.
+
+ Consults the global error_suppressions map populated by
+ ParseNolintSuppressions/ProcessGlobalSuppresions/ResetNolintSuppressions.
+
+ Args:
+ category: str, the category of the error.
+ linenum: int, the current line number.
+ Returns:
+ bool, True iff the error should be suppressed due to a NOLINT comment or
+ global suppression.
+ """
+ return (_global_error_suppressions.get(category, False) or
+ linenum in _error_suppressions.get(category, set()) or
+ linenum in _error_suppressions.get(None, set()))
+
+
+def Match(pattern, s):
+ """Matches the string with the pattern, caching the compiled regexp."""
+ # The regexp compilation caching is inlined in both Match and Search for
+ # performance reasons; factoring it out into a separate function turns out
+ # to be noticeably expensive.
+ if pattern not in _regexp_compile_cache:
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
+ return _regexp_compile_cache[pattern].match(s)
+
+
+def ReplaceAll(pattern, rep, s):
+ """Replaces instances of pattern in a string with a replacement.
+
+ The compiled regex is kept in a cache shared by Match and Search.
+
+ Args:
+ pattern: regex pattern
+ rep: replacement text
+ s: search string
+
+ Returns:
+ string with replacements made (or original string if no replacements)
+ """
+ if pattern not in _regexp_compile_cache:
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
+ return _regexp_compile_cache[pattern].sub(rep, s)
+
+
+def Search(pattern, s):
+ """Searches the string for the pattern, caching the compiled regexp."""
+ if pattern not in _regexp_compile_cache:
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
+ return _regexp_compile_cache[pattern].search(s)
+
+
+def _IsSourceExtension(s):
+ """File extension (excluding dot) matches a source file extension."""
+ return s in ('c', 'cc', 'cpp', 'cxx')
+
+
+class _IncludeState(object):
+ """Tracks line numbers for includes, and the order in which includes appear.
+
+ include_list contains list of lists of (header, line number) pairs.
+ It's a lists of lists rather than just one flat list to make it
+ easier to update across preprocessor boundaries.
+
+ Call CheckNextIncludeOrder() once for each header in the file, passing
+ in the type constants defined above. Calls in an illegal order will
+ raise an _IncludeError with an appropriate error message.
+
+ """
+ # self._section will move monotonically through this set. If it ever
+ # needs to move backwards, CheckNextIncludeOrder will raise an error.
+ _INITIAL_SECTION = 0
+ _MY_H_SECTION = 1
+ _C_SECTION = 2
+ _CPP_SECTION = 3
+ _OTHER_H_SECTION = 4
+
+ _TYPE_NAMES = {
+ _C_SYS_HEADER: 'C system header',
+ _CPP_SYS_HEADER: 'C++ system header',
+ _LIKELY_MY_HEADER: 'header this file implements',
+ _POSSIBLE_MY_HEADER: 'header this file may implement',
+ _OTHER_HEADER: 'other header',
+ }
+ _SECTION_NAMES = {
+ _INITIAL_SECTION: "... nothing. (This can't be an error.)",
+ _MY_H_SECTION: 'a header this file implements',
+ _C_SECTION: 'C system header',
+ _CPP_SECTION: 'C++ system header',
+ _OTHER_H_SECTION: 'other header',
+ }
+
+ def __init__(self):
+ self.include_list = [[]]
+ self.ResetSection('')
+
+ def FindHeader(self, header):
+ """Check if a header has already been included.
+
+ Args:
+ header: header to check.
+ Returns:
+ Line number of previous occurrence, or -1 if the header has not
+ been seen before.
+ """
+ for section_list in self.include_list:
+ for f in section_list:
+ if f[0] == header:
+ return f[1]
+ return -1
+
+ def ResetSection(self, directive):
+ """Reset section checking for preprocessor directive.
+
+ Args:
+ directive: preprocessor directive (e.g. "if", "else").
+ """
+ # The name of the current section.
+ self._section = self._INITIAL_SECTION
+ # The path of last found header.
+ self._last_header = ''
+
+ # Update list of includes. Note that we never pop from the
+ # include list.
+ if directive in ('if', 'ifdef', 'ifndef'):
+ self.include_list.append([])
+ elif directive in ('else', 'elif'):
+ self.include_list[-1] = []
+
+ def SetLastHeader(self, header_path):
+ self._last_header = header_path
+
+ def CanonicalizeAlphabeticalOrder(self, header_path):
+ """Returns a path canonicalized for alphabetical comparison.
+
+ - replaces "-" with "_" so they both cmp the same.
+ - removes '-inl' since we don't require them to be after the main header.
+ - lowercase everything, just in case.
+
+ Args:
+ header_path: Path to be canonicalized.
+
+ Returns:
+ Canonicalized path.
+ """
+ return header_path.replace('-inl.h', '.h').replace('-', '_').lower()
+
+ def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):
+ """Check if a header is in alphabetical order with the previous header.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ header_path: Canonicalized header to be checked.
+
+ Returns:
+ Returns true if the header is in alphabetical order.
+ """
+ # If previous section is different from current section, _last_header will
+ # be reset to empty string, so it's always less than current header.
+ #
+ # If previous line was a blank line, assume that the headers are
+ # intentionally sorted the way they are.
+ if (self._last_header > header_path and
+ Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])):
+ return False
+ return True
+
+ def CheckNextIncludeOrder(self, header_type):
+ """Returns a non-empty error message if the next header is out of order.
+
+ This function also updates the internal state to be ready to check
+ the next include.
+
+ Args:
+ header_type: One of the _XXX_HEADER constants defined above.
+
+ Returns:
+ The empty string if the header is in the right order, or an
+ error message describing what's wrong.
+
+ """
+ error_message = ('Found %s after %s' %
+ (self._TYPE_NAMES[header_type],
+ self._SECTION_NAMES[self._section]))
+
+ last_section = self._section
+
+ if header_type == _C_SYS_HEADER:
+ if self._section <= self._C_SECTION:
+ self._section = self._C_SECTION
+ else:
+ self._last_header = ''
+ return error_message
+ elif header_type == _CPP_SYS_HEADER:
+ if self._section <= self._CPP_SECTION:
+ self._section = self._CPP_SECTION
+ else:
+ self._last_header = ''
+ return error_message
+ elif header_type == _LIKELY_MY_HEADER:
+ if self._section <= self._MY_H_SECTION:
+ self._section = self._MY_H_SECTION
+ else:
+ self._section = self._OTHER_H_SECTION
+ elif header_type == _POSSIBLE_MY_HEADER:
+ if self._section <= self._MY_H_SECTION:
+ self._section = self._MY_H_SECTION
+ else:
+ # This will always be the fallback because we're not sure
+ # enough that the header is associated with this file.
+ self._section = self._OTHER_H_SECTION
+ else:
+ assert header_type == _OTHER_HEADER
+ self._section = self._OTHER_H_SECTION
+
+ if last_section != self._section:
+ self._last_header = ''
+
+ return ''
+
+
+class _CppLintState(object):
+ """Maintains module-wide state.."""
+
+ def __init__(self):
+ self.verbose_level = 1 # global setting.
+ self.error_count = 0 # global count of reported errors
+ # filters to apply when emitting error messages
+ self.filters = _DEFAULT_FILTERS[:]
+ # backup of filter list. Used to restore the state after each file.
+ self._filters_backup = self.filters[:]
+ self.counting = 'total' # In what way are we counting errors?
+ self.errors_by_category = {} # string to int dict storing error counts
+
+ # output format:
+ # "emacs" - format that emacs can parse (default)
+ # "vs7" - format that Microsoft Visual Studio 7 can parse
+ self.output_format = 'emacs'
+
+ def SetOutputFormat(self, output_format):
+ """Sets the output format for errors."""
+ self.output_format = output_format
+
+ def SetVerboseLevel(self, level):
+ """Sets the module's verbosity, and returns the previous setting."""
+ last_verbose_level = self.verbose_level
+ self.verbose_level = level
+ return last_verbose_level
+
+ def SetCountingStyle(self, counting_style):
+ """Sets the module's counting options."""
+ self.counting = counting_style
+
+ def SetFilters(self, filters):
+ """Sets the error-message filters.
+
+ These filters are applied when deciding whether to emit a given
+ error message.
+
+ Args:
+ filters: A string of comma-separated filters (eg "+whitespace/indent").
+ Each filter should start with + or -; else we die.
+
+ Raises:
+ ValueError: The comma-separated filters did not all start with '+' or '-'.
+ E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"
+ """
+ # Default filters always have less priority than the flag ones.
+ self.filters = _DEFAULT_FILTERS[:]
+ self.AddFilters(filters)
+
+ def AddFilters(self, filters):
+ """ Adds more filters to the existing list of error-message filters. """
+ for filt in filters.split(','):
+ clean_filt = filt.strip()
+ if clean_filt:
+ self.filters.append(clean_filt)
+ for filt in self.filters:
+ if not (filt.startswith('+') or filt.startswith('-')):
+ raise ValueError('Every filter in --filters must start with + or -'
+ ' (%s does not)' % filt)
+
+ def BackupFilters(self):
+ """ Saves the current filter list to backup storage."""
+ self._filters_backup = self.filters[:]
+
+ def RestoreFilters(self):
+ """ Restores filters previously backed up."""
+ self.filters = self._filters_backup[:]
+
+ def ResetErrorCounts(self):
+ """Sets the module's error statistic back to zero."""
+ self.error_count = 0
+ self.errors_by_category = {}
+
+ def IncrementErrorCount(self, category):
+ """Bumps the module's error statistic."""
+ self.error_count += 1
+ if self.counting in ('toplevel', 'detailed'):
+ if self.counting != 'detailed':
+ category = category.split('/')[0]
+ if category not in self.errors_by_category:
+ self.errors_by_category[category] = 0
+ self.errors_by_category[category] += 1
+
+ def PrintErrorCounts(self):
+ """Print a summary of errors by category, and the total."""
+ for category, count in self.errors_by_category.iteritems():
+ sys.stderr.write('Category \'%s\' errors found: %d\n' %
+ (category, count))
+ sys.stdout.write('Total errors found: %d\n' % self.error_count)
+
+_cpplint_state = _CppLintState()
+
+
+def _OutputFormat():
+ """Gets the module's output format."""
+ return _cpplint_state.output_format
+
+
+def _SetOutputFormat(output_format):
+ """Sets the module's output format."""
+ _cpplint_state.SetOutputFormat(output_format)
+
+
+def _VerboseLevel():
+ """Returns the module's verbosity setting."""
+ return _cpplint_state.verbose_level
+
+
+def _SetVerboseLevel(level):
+ """Sets the module's verbosity, and returns the previous setting."""
+ return _cpplint_state.SetVerboseLevel(level)
+
+
+def _SetCountingStyle(level):
+ """Sets the module's counting options."""
+ _cpplint_state.SetCountingStyle(level)
+
+
+def _Filters():
+ """Returns the module's list of output filters, as a list."""
+ return _cpplint_state.filters
+
+
+def _SetFilters(filters):
+ """Sets the module's error-message filters.
+
+ These filters are applied when deciding whether to emit a given
+ error message.
+
+ Args:
+ filters: A string of comma-separated filters (eg "whitespace/indent").
+ Each filter should start with + or -; else we die.
+ """
+ _cpplint_state.SetFilters(filters)
+
+def _AddFilters(filters):
+ """Adds more filter overrides.
+
+ Unlike _SetFilters, this function does not reset the current list of filters
+ available.
+
+ Args:
+ filters: A string of comma-separated filters (eg "whitespace/indent").
+ Each filter should start with + or -; else we die.
+ """
+ _cpplint_state.AddFilters(filters)
+
+def _BackupFilters():
+ """ Saves the current filter list to backup storage."""
+ _cpplint_state.BackupFilters()
+
+def _RestoreFilters():
+ """ Restores filters previously backed up."""
+ _cpplint_state.RestoreFilters()
+
+class _FunctionState(object):
+ """Tracks current function name and the number of lines in its body."""
+
+ _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.
+ _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER.
+
+ def __init__(self):
+ self.in_a_function = False
+ self.lines_in_function = 0
+ self.current_function = ''
+
+ def Begin(self, function_name):
+ """Start analyzing function body.
+
+ Args:
+ function_name: The name of the function being tracked.
+ """
+ self.in_a_function = True
+ self.lines_in_function = 0
+ self.current_function = function_name
+
+ def Count(self):
+ """Count line in current function body."""
+ if self.in_a_function:
+ self.lines_in_function += 1
+
+ def Check(self, error, filename, linenum):
+ """Report if too many lines in function body.
+
+ Args:
+ error: The function to call with any errors found.
+ filename: The name of the current file.
+ linenum: The number of the line to check.
+ """
+ if not self.in_a_function:
+ return
+
+ if Match(r'T(EST|est)', self.current_function):
+ base_trigger = self._TEST_TRIGGER
+ else:
+ base_trigger = self._NORMAL_TRIGGER
+ trigger = base_trigger * 2**_VerboseLevel()
+
+ if self.lines_in_function > trigger:
+ error_level = int(math.log(self.lines_in_function / base_trigger, 2))
+ # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
+ if error_level > 5:
+ error_level = 5
+ error(filename, linenum, 'readability/fn_size', error_level,
+ 'Small and focused functions are preferred:'
+ ' %s has %d non-comment lines'
+ ' (error triggered by exceeding %d lines).' % (
+ self.current_function, self.lines_in_function, trigger))
+
+ def End(self):
+ """Stop analyzing function body."""
+ self.in_a_function = False
+
+
+class _IncludeError(Exception):
+ """Indicates a problem with the include order in a file."""
+ pass
+
+
+class FileInfo(object):
+ """Provides utility functions for filenames.
+
+ FileInfo provides easy access to the components of a file's path
+ relative to the project root.
+ """
+
+ def __init__(self, filename):
+ self._filename = filename
+
+ def FullName(self):
+ """Make Windows paths like Unix."""
+ return os.path.abspath(self._filename).replace('\\', '/')
+
+ def RepositoryName(self):
+ """FullName after removing the local path to the repository.
+
+ If we have a real absolute path name here we can try to do something smart:
+ detecting the root of the checkout and truncating /path/to/checkout from
+ the name so that we get header guards that don't include things like
+ "C:\Documents and Settings\..." or "/home/username/..." in them and thus
+ people on different computers who have checked the source out to different
+ locations won't see bogus errors.
+ """
+ fullname = self.FullName()
+
+ if os.path.exists(fullname):
+ project_dir = os.path.dirname(fullname)
+
+ if os.path.exists(os.path.join(project_dir, ".svn")):
+ # If there's a .svn file in the current directory, we recursively look
+ # up the directory tree for the top of the SVN checkout
+ root_dir = project_dir
+ one_up_dir = os.path.dirname(root_dir)
+ while os.path.exists(os.path.join(one_up_dir, ".svn")):
+ root_dir = os.path.dirname(root_dir)
+ one_up_dir = os.path.dirname(one_up_dir)
+
+ prefix = os.path.commonprefix([root_dir, project_dir])
+ return fullname[len(prefix) + 1:]
+
+ # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by
+ # searching up from the current path.
+ root_dir = current_dir = os.path.dirname(fullname)
+ while current_dir != os.path.dirname(current_dir):
+ if (os.path.exists(os.path.join(current_dir, ".git")) or
+ os.path.exists(os.path.join(current_dir, ".hg")) or
+ os.path.exists(os.path.join(current_dir, ".svn"))):
+ root_dir = current_dir
+ current_dir = os.path.dirname(current_dir)
+
+ if (os.path.exists(os.path.join(root_dir, ".git")) or
+ os.path.exists(os.path.join(root_dir, ".hg")) or
+ os.path.exists(os.path.join(root_dir, ".svn"))):
+ prefix = os.path.commonprefix([root_dir, project_dir])
+ return fullname[len(prefix) + 1:]
+
+ # Don't know what to do; header guard warnings may be wrong...
+ return fullname
+
+ def Split(self):
+ """Splits the file into the directory, basename, and extension.
+
+ For 'chrome/browser/browser.cc', Split() would
+ return ('chrome/browser', 'browser', '.cc')
+
+ Returns:
+ A tuple of (directory, basename, extension).
+ """
+
+ googlename = self.RepositoryName()
+ project, rest = os.path.split(googlename)
+ return (project,) + os.path.splitext(rest)
+
+ def BaseName(self):
+ """File base name - text after the final slash, before the final period."""
+ return self.Split()[1]
+
+ def Extension(self):
+ """File extension - text following the final period."""
+ return self.Split()[2]
+
+ def NoExtension(self):
+ """File has no source file extension."""
+ return '/'.join(self.Split()[0:2])
+
+ def IsSource(self):
+ """File has a source file extension."""
+ return _IsSourceExtension(self.Extension()[1:])
+
+
+def _ShouldPrintError(category, confidence, linenum):
+ """If confidence >= verbose, category passes filter and is not suppressed."""
+
+ # There are three ways we might decide not to print an error message:
+ # a "NOLINT(category)" comment appears in the source,
+ # the verbosity level isn't high enough, or the filters filter it out.
+ if IsErrorSuppressedByNolint(category, linenum):
+ return False
+
+ if confidence < _cpplint_state.verbose_level:
+ return False
+
+ is_filtered = False
+ for one_filter in _Filters():
+ if one_filter.startswith('-'):
+ if category.startswith(one_filter[1:]):
+ is_filtered = True
+ elif one_filter.startswith('+'):
+ if category.startswith(one_filter[1:]):
+ is_filtered = False
+ else:
+ assert False # should have been checked for in SetFilter.
+ if is_filtered:
+ return False
+
+ return True
+
+
+def Error(filename, linenum, category, confidence, message):
+ """Logs the fact we've found a lint error.
+
+ We log where the error was found, and also our confidence in the error,
+ that is, how certain we are this is a legitimate style regression, and
+ not a misidentification or a use that's sometimes justified.
+
+ False positives can be suppressed by the use of
+ "cpplint(category)" comments on the offending line. These are
+ parsed into _error_suppressions.
+
+ Args:
+ filename: The name of the file containing the error.
+ linenum: The number of the line containing the error.
+ category: A string used to describe the "category" this bug
+ falls under: "whitespace", say, or "runtime". Categories
+ may have a hierarchy separated by slashes: "whitespace/indent".
+ confidence: A number from 1-5 representing a confidence score for
+ the error, with 5 meaning that we are certain of the problem,
+ and 1 meaning that it could be a legitimate construct.
+ message: The error message.
+ """
+ if _ShouldPrintError(category, confidence, linenum):
+ _cpplint_state.IncrementErrorCount(category)
+ if _cpplint_state.output_format == 'vs7':
+ sys.stderr.write('%s(%s): error cpplint: [%s] %s [%d]\n' % (
+ filename, linenum, category, message, confidence))
+ elif _cpplint_state.output_format == 'eclipse':
+ sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % (
+ filename, linenum, message, category, confidence))
+ else:
+ sys.stderr.write('%s:%s: %s [%s] [%d]\n' % (
+ filename, linenum, message, category, confidence))
+
+
+# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard.
+_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(
+ r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
+# Match a single C style comment on the same line.
+_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/'
+# Matches multi-line C style comments.
+# This RE is a little bit more complicated than one might expect, because we
+# have to take care of space removals tools so we can handle comments inside
+# statements better.
+# The current rule is: We only clear spaces from both sides when we're at the
+# end of the line. Otherwise, we try to remove spaces from the right side,
+# if this doesn't work we try on left side but only if there's a non-character
+# on the right.
+_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile(
+ r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' +
+ _RE_PATTERN_C_COMMENTS + r'\s+|' +
+ r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' +
+ _RE_PATTERN_C_COMMENTS + r')')
+
+
+def IsCppString(line):
+ """Does line terminate so, that the next symbol is in string constant.
+
+ This function does not consider single-line nor multi-line comments.
+
+ Args:
+ line: is a partial line of code starting from the 0..n.
+
+ Returns:
+ True, if next character appended to 'line' is inside a
+ string constant.
+ """
+
+ line = line.replace(r'\\', 'XX') # after this, \\" does not match to \"
+ return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1
+
+
+def CleanseRawStrings(raw_lines):
+ """Removes C++11 raw strings from lines.
+
+ Before:
+ static const char kData[] = R"(
+ multi-line string
+ )";
+
+ After:
+ static const char kData[] = ""
+ (replaced by blank line)
+ "";
+
+ Args:
+ raw_lines: list of raw lines.
+
+ Returns:
+ list of lines with C++11 raw strings replaced by empty strings.
+ """
+
+ delimiter = None
+ lines_without_raw_strings = []
+ for line in raw_lines:
+ if delimiter:
+ # Inside a raw string, look for the end
+ end = line.find(delimiter)
+ if end >= 0:
+ # Found the end of the string, match leading space for this
+ # line and resume copying the original lines, and also insert
+ # a "" on the last line.
+ leading_space = Match(r'^(\s*)\S', line)
+ line = leading_space.group(1) + '""' + line[end + len(delimiter):]
+ delimiter = None
+ else:
+ # Haven't found the end yet, append a blank line.
+ line = '""'
+
+ # Look for beginning of a raw string, and replace them with
+ # empty strings. This is done in a loop to handle multiple raw
+ # strings on the same line.
+ while delimiter is None:
+ # Look for beginning of a raw string.
+ # See 2.14.15 [lex.string] for syntax.
+ #
+ # Once we have matched a raw string, we check the prefix of the
+ # line to make sure that the line is not part of a single line
+ # comment. It's done this way because we remove raw strings
+ # before removing comments as opposed to removing comments
+ # before removing raw strings. This is because there are some
+ # cpplint checks that requires the comments to be preserved, but
+ # we don't want to check comments that are inside raw strings.
+ matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line)
+ if (matched and
+ not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//',
+ matched.group(1))):
+ delimiter = ')' + matched.group(2) + '"'
+
+ end = matched.group(3).find(delimiter)
+ if end >= 0:
+ # Raw string ended on same line
+ line = (matched.group(1) + '""' +
+ matched.group(3)[end + len(delimiter):])
+ delimiter = None
+ else:
+ # Start of a multi-line raw string
+ line = matched.group(1) + '""'
+ else:
+ break
+
+ lines_without_raw_strings.append(line)
+
+ # TODO(unknown): if delimiter is not None here, we might want to
+ # emit a warning for unterminated string.
+ return lines_without_raw_strings
+
+
+def FindNextMultiLineCommentStart(lines, lineix):
+ """Find the beginning marker for a multiline comment."""
+ while lineix < len(lines):
+ if lines[lineix].strip().startswith('/*'):
+ # Only return this marker if the comment goes beyond this line
+ if lines[lineix].strip().find('*/', 2) < 0:
+ return lineix
+ lineix += 1
+ return len(lines)
+
+
+def FindNextMultiLineCommentEnd(lines, lineix):
+ """We are inside a comment, find the end marker."""
+ while lineix < len(lines):
+ if lines[lineix].strip().endswith('*/'):
+ return lineix
+ lineix += 1
+ return len(lines)
+
+
+def RemoveMultiLineCommentsFromRange(lines, begin, end):
+ """Clears a range of lines for multi-line comments."""
+ # Having // dummy comments makes the lines non-empty, so we will not get
+ # unnecessary blank line warnings later in the code.
+ for i in range(begin, end):
+ lines[i] = '/**/'
+
+
+def RemoveMultiLineComments(filename, lines, error):
+ """Removes multiline (c-style) comments from lines."""
+ lineix = 0
+ while lineix < len(lines):
+ lineix_begin = FindNextMultiLineCommentStart(lines, lineix)
+ if lineix_begin >= len(lines):
+ return
+ lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin)
+ if lineix_end >= len(lines):
+ error(filename, lineix_begin + 1, 'readability/multiline_comment', 5,
+ 'Could not find end of multi-line comment')
+ return
+ RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)
+ lineix = lineix_end + 1
+
+
+def CleanseComments(line):
+ """Removes //-comments and single-line C-style /* */ comments.
+
+ Args:
+ line: A line of C++ source.
+
+ Returns:
+ The line with single-line comments removed.
+ """
+ commentpos = line.find('//')
+ if commentpos != -1 and not IsCppString(line[:commentpos]):
+ line = line[:commentpos].rstrip()
+ # get rid of /* ... */
+ return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line)
+
+
+class CleansedLines(object):
+ """Holds 4 copies of all lines with different preprocessing applied to them.
+
+ 1) elided member contains lines without strings and comments.
+ 2) lines member contains lines without comments.
+ 3) raw_lines member contains all the lines without processing.
+ 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw
+ strings removed.
+ All these members are of <type 'list'>, and of the same length.
+ """
+
+ def __init__(self, lines):
+ self.elided = []
+ self.lines = []
+ self.raw_lines = lines
+ self.num_lines = len(lines)
+ self.lines_without_raw_strings = CleanseRawStrings(lines)
+ for linenum in range(len(self.lines_without_raw_strings)):
+ self.lines.append(CleanseComments(
+ self.lines_without_raw_strings[linenum]))
+ elided = self._CollapseStrings(self.lines_without_raw_strings[linenum])
+ self.elided.append(CleanseComments(elided))
+
+ def NumLines(self):
+ """Returns the number of lines represented."""
+ return self.num_lines
+
+ @staticmethod
+ def _CollapseStrings(elided):
+ """Collapses strings and chars on a line to simple "" or '' blocks.
+
+ We nix strings first so we're not fooled by text like '"http://"'
+
+ Args:
+ elided: The line being processed.
+
+ Returns:
+ The line with collapsed strings.
+ """
+ if _RE_PATTERN_INCLUDE.match(elided):
+ return elided
+
+ # Remove escaped characters first to make quote/single quote collapsing
+ # basic. Things that look like escaped characters shouldn't occur
+ # outside of strings and chars.
+ elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided)
+
+ # Replace quoted strings and digit separators. Both single quotes
+ # and double quotes are processed in the same loop, otherwise
+ # nested quotes wouldn't work.
+ collapsed = ''
+ while True:
+ # Find the first quote character
+ match = Match(r'^([^\'"]*)([\'"])(.*)$', elided)
+ if not match:
+ collapsed += elided
+ break
+ head, quote, tail = match.groups()
+
+ if quote == '"':
+ # Collapse double quoted strings
+ second_quote = tail.find('"')
+ if second_quote >= 0:
+ collapsed += head + '""'
+ elided = tail[second_quote + 1:]
+ else:
+ # Unmatched double quote, don't bother processing the rest
+ # of the line since this is probably a multiline string.
+ collapsed += elided
+ break
+ else:
+ # Found single quote, check nearby text to eliminate digit separators.
+ #
+ # There is no special handling for floating point here, because
+ # the integer/fractional/exponent parts would all be parsed
+ # correctly as long as there are digits on both sides of the
+ # separator. So we are fine as long as we don't see something
+ # like "0.'3" (gcc 4.9.0 will not allow this literal).
+ if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head):
+ match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail)
+ collapsed += head + match_literal.group(1).replace("'", '')
+ elided = match_literal.group(2)
+ else:
+ second_quote = tail.find('\'')
+ if second_quote >= 0:
+ collapsed += head + "''"
+ elided = tail[second_quote + 1:]
+ else:
+ # Unmatched single quote
+ collapsed += elided
+ break
+
+ return collapsed
+
+
+def FindEndOfExpressionInLine(line, startpos, stack):
+ """Find the position just after the end of current parenthesized expression.
+
+ Args:
+ line: a CleansedLines line.
+ startpos: start searching at this position.
+ stack: nesting stack at startpos.
+
+ Returns:
+ On finding matching end: (index just after matching end, None)
+ On finding an unclosed expression: (-1, None)
+ Otherwise: (-1, new stack at end of this line)
+ """
+ for i in xrange(startpos, len(line)):
+ char = line[i]
+ if char in '([{':
+ # Found start of parenthesized expression, push to expression stack
+ stack.append(char)
+ elif char == '<':
+ # Found potential start of template argument list
+ if i > 0 and line[i - 1] == '<':
+ # Left shift operator
+ if stack and stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+ elif i > 0 and Search(r'\boperator\s*$', line[0:i]):
+ # operator<, don't add to stack
+ continue
+ else:
+ # Tentative start of template argument list
+ stack.append('<')
+ elif char in ')]}':
+ # Found end of parenthesized expression.
+ #
+ # If we are currently expecting a matching '>', the pending '<'
+ # must have been an operator. Remove them from expression stack.
+ while stack and stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+ if ((stack[-1] == '(' and char == ')') or
+ (stack[-1] == '[' and char == ']') or
+ (stack[-1] == '{' and char == '}')):
+ stack.pop()
+ if not stack:
+ return (i + 1, None)
+ else:
+ # Mismatched parentheses
+ return (-1, None)
+ elif char == '>':
+ # Found potential end of template argument list.
+
+ # Ignore "->" and operator functions
+ if (i > 0 and
+ (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))):
+ continue
+
+ # Pop the stack if there is a matching '<'. Otherwise, ignore
+ # this '>' since it must be an operator.
+ if stack:
+ if stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (i + 1, None)
+ elif char == ';':
+ # Found something that look like end of statements. If we are currently
+ # expecting a '>', the matching '<' must have been an operator, since
+ # template argument list should not contain statements.
+ while stack and stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+
+ # Did not find end of expression or unbalanced parentheses on this line
+ return (-1, stack)
+
+
+def CloseExpression(clean_lines, linenum, pos):
+ """If input points to ( or { or [ or <, finds the position that closes it.
+
+ If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the
+ linenum/pos that correspond to the closing of the expression.
+
+ TODO(unknown): cpplint spends a fair bit of time matching parentheses.
+ Ideally we would want to index all opening and closing parentheses once
+ and have CloseExpression be just a simple lookup, but due to preprocessor
+ tricks, this is not so easy.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ pos: A position on the line.
+
+ Returns:
+ A tuple (line, linenum, pos) pointer *past* the closing brace, or
+ (line, len(lines), -1) if we never find a close. Note we ignore
+ strings and comments when matching; and the line we return is the
+ 'cleansed' line at linenum.
+ """
+
+ line = clean_lines.elided[linenum]
+ if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]):
+ return (line, clean_lines.NumLines(), -1)
+
+ # Check first line
+ (end_pos, stack) = FindEndOfExpressionInLine(line, pos, [])
+ if end_pos > -1:
+ return (line, linenum, end_pos)
+
+ # Continue scanning forward
+ while stack and linenum < clean_lines.NumLines() - 1:
+ linenum += 1
+ line = clean_lines.elided[linenum]
+ (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack)
+ if end_pos > -1:
+ return (line, linenum, end_pos)
+
+ # Did not find end of expression before end of file, give up
+ return (line, clean_lines.NumLines(), -1)
+
+
+def FindStartOfExpressionInLine(line, endpos, stack):
+ """Find position at the matching start of current expression.
+
+ This is almost the reverse of FindEndOfExpressionInLine, but note
+ that the input position and returned position differs by 1.
+
+ Args:
+ line: a CleansedLines line.
+ endpos: start searching at this position.
+ stack: nesting stack at endpos.
+
+ Returns:
+ On finding matching start: (index at matching start, None)
+ On finding an unclosed expression: (-1, None)
+ Otherwise: (-1, new stack at beginning of this line)
+ """
+ i = endpos
+ while i >= 0:
+ char = line[i]
+ if char in ')]}':
+ # Found end of expression, push to expression stack
+ stack.append(char)
+ elif char == '>':
+ # Found potential end of template argument list.
+ #
+ # Ignore it if it's a "->" or ">=" or "operator>"
+ if (i > 0 and
+ (line[i - 1] == '-' or
+ Match(r'\s>=\s', line[i - 1:]) or
+ Search(r'\boperator\s*$', line[0:i]))):
+ i -= 1
+ else:
+ stack.append('>')
+ elif char == '<':
+ # Found potential start of template argument list
+ if i > 0 and line[i - 1] == '<':
+ # Left shift operator
+ i -= 1
+ else:
+ # If there is a matching '>', we can pop the expression stack.
+ # Otherwise, ignore this '<' since it must be an operator.
+ if stack and stack[-1] == '>':
+ stack.pop()
+ if not stack:
+ return (i, None)
+ elif char in '([{':
+ # Found start of expression.
+ #
+ # If there are any unmatched '>' on the stack, they must be
+ # operators. Remove those.
+ while stack and stack[-1] == '>':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+ if ((char == '(' and stack[-1] == ')') or
+ (char == '[' and stack[-1] == ']') or
+ (char == '{' and stack[-1] == '}')):
+ stack.pop()
+ if not stack:
+ return (i, None)
+ else:
+ # Mismatched parentheses
+ return (-1, None)
+ elif char == ';':
+ # Found something that look like end of statements. If we are currently
+ # expecting a '<', the matching '>' must have been an operator, since
+ # template argument list should not contain statements.
+ while stack and stack[-1] == '>':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+
+ i -= 1
+
+ return (-1, stack)
+
+
+def ReverseCloseExpression(clean_lines, linenum, pos):
+ """If input points to ) or } or ] or >, finds the position that opens it.
+
+ If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the
+ linenum/pos that correspond to the opening of the expression.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ pos: A position on the line.
+
+ Returns:
+ A tuple (line, linenum, pos) pointer *at* the opening brace, or
+ (line, 0, -1) if we never find the matching opening brace. Note
+ we ignore strings and comments when matching; and the line we
+ return is the 'cleansed' line at linenum.
+ """
+ line = clean_lines.elided[linenum]
+ if line[pos] not in ')}]>':
+ return (line, 0, -1)
+
+ # Check last line
+ (start_pos, stack) = FindStartOfExpressionInLine(line, pos, [])
+ if start_pos > -1:
+ return (line, linenum, start_pos)
+
+ # Continue scanning backward
+ while stack and linenum > 0:
+ linenum -= 1
+ line = clean_lines.elided[linenum]
+ (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack)
+ if start_pos > -1:
+ return (line, linenum, start_pos)
+
+ # Did not find start of expression before beginning of file, give up
+ return (line, 0, -1)
+
+
+def CheckForCopyright(filename, lines, error):
+ """Logs an error if no Copyright message appears at the top of the file."""
+
+ # We'll say it should occur by line 10. Don't forget there's a
+ # dummy line at the front.
+ for line in xrange(1, min(len(lines), 11)):
+ if re.search(r'Copyright', lines[line], re.I): break
+ else: # means no copyright line was found
+ error(filename, 0, 'legal/copyright', 5,
+ 'No copyright message found. '
+ 'You should have a line: "Copyright [year] <Copyright Owner>"')
+
+
+def GetIndentLevel(line):
+ """Return the number of leading spaces in line.
+
+ Args:
+ line: A string to check.
+
+ Returns:
+ An integer count of leading spaces, possibly zero.
+ """
+ indent = Match(r'^( *)\S', line)
+ if indent:
+ return len(indent.group(1))
+ else:
+ return 0
+
+
+def GetHeaderGuardCPPVariable(filename):
+ """Returns the CPP variable that should be used as a header guard.
+
+ Args:
+ filename: The name of a C++ header file.
+
+ Returns:
+ The CPP variable that should be used as a header guard in the
+ named file.
+
+ """
+
+ # Restores original filename in case that cpplint is invoked from Emacs's
+ # flymake.
+ filename = re.sub(r'_flymake\.h$', '.h', filename)
+ filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename)
+ # Replace 'c++' with 'cpp'.
+ filename = filename.replace('C++', 'cpp').replace('c++', 'cpp')
+
+ fileinfo = FileInfo(filename)
+ file_path_from_root = fileinfo.RepositoryName()
+ if _root:
+ suffix = os.sep
+ # On Windows using directory separator will leave us with
+ # "bogus escape error" unless we properly escape regex.
+ if suffix == '\\':
+ suffix += '\\'
+ file_path_from_root = re.sub('^' + _root + suffix, '', file_path_from_root)
+ return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_'
+
+
+def CheckForHeaderGuard(filename, clean_lines, error):
+ """Checks that the file contains a header guard.
+
+ Logs an error if no #ifndef header guard is present. For other
+ headers, checks that the full pathname is used.
+
+ Args:
+ filename: The name of the C++ header file.
+ clean_lines: A CleansedLines instance containing the file.
+ error: The function to call with any errors found.
+ """
+
+ # Don't check for header guards if there are error suppression
+ # comments somewhere in this file.
+ #
+ # Because this is silencing a warning for a nonexistent line, we
+ # only support the very specific NOLINT(build/header_guard) syntax,
+ # and not the general NOLINT or NOLINT(*) syntax.
+ raw_lines = clean_lines.lines_without_raw_strings
+ for i in raw_lines:
+ if Search(r'//\s*NOLINT\(build/header_guard\)', i):
+ return
+
+ cppvar = GetHeaderGuardCPPVariable(filename)
+
+ ifndef = ''
+ ifndef_linenum = 0
+ define = ''
+ endif = ''
+ endif_linenum = 0
+ for linenum, line in enumerate(raw_lines):
+ linesplit = line.split()
+ if len(linesplit) >= 2:
+ # find the first occurrence of #ifndef and #define, save arg
+ if not ifndef and linesplit[0] == '#ifndef':
+ # set ifndef to the header guard presented on the #ifndef line.
+ ifndef = linesplit[1]
+ ifndef_linenum = linenum
+ if not define and linesplit[0] == '#define':
+ define = linesplit[1]
+ # find the last occurrence of #endif, save entire line
+ if line.startswith('#endif'):
+ endif = line
+ endif_linenum = linenum
+
+ if not ifndef or not define or ifndef != define:
+ error(filename, 0, 'build/header_guard', 5,
+ 'No #ifndef header guard found, suggested CPP variable is: %s' %
+ cppvar)
+ return
+
+ # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__
+ # for backward compatibility.
+ if ifndef != cppvar:
+ error_level = 0
+ if ifndef != cppvar + '_':
+ error_level = 5
+
+ ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum,
+ error)
+ error(filename, ifndef_linenum, 'build/header_guard', error_level,
+ '#ifndef header guard has wrong style, please use: %s' % cppvar)
+
+ # Check for "//" comments on endif line.
+ ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum,
+ error)
+ match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif)
+ if match:
+ if match.group(1) == '_':
+ # Issue low severity warning for deprecated double trailing underscore
+ error(filename, endif_linenum, 'build/header_guard', 0,
+ '#endif line should be "#endif // %s"' % cppvar)
+ return
+
+ # Didn't find the corresponding "//" comment. If this file does not
+ # contain any "//" comments at all, it could be that the compiler
+ # only wants "/**/" comments, look for those instead.
+ no_single_line_comments = True
+ for i in xrange(1, len(raw_lines) - 1):
+ line = raw_lines[i]
+ if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line):
+ no_single_line_comments = False
+ break
+
+ if no_single_line_comments:
+ match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif)
+ if match:
+ if match.group(1) == '_':
+ # Low severity warning for double trailing underscore
+ error(filename, endif_linenum, 'build/header_guard', 0,
+ '#endif line should be "#endif /* %s */"' % cppvar)
+ return
+
+ # Didn't find anything
+ error(filename, endif_linenum, 'build/header_guard', 5,
+ '#endif line should be "#endif // %s"' % cppvar)
+
+
+def CheckHeaderFileIncluded(filename, include_state, error):
+ """Logs an error if a .cc file does not include its header."""
+
+ # Do not check test files
+ fileinfo = FileInfo(filename)
+ if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()):
+ return
+
+ headerfile = filename[0:len(filename) - len(fileinfo.Extension())] + '.h'
+ if not os.path.exists(headerfile):
+ return
+ headername = FileInfo(headerfile).RepositoryName()
+ first_include = 0
+ for section_list in include_state.include_list:
+ for f in section_list:
+ if headername in f[0] or f[0] in headername:
+ return
+ if not first_include:
+ first_include = f[1]
+
+ error(filename, first_include, 'build/include', 5,
+ '%s should include its header file %s' % (fileinfo.RepositoryName(),
+ headername))
+
+
+def CheckForBadCharacters(filename, lines, error):
+ """Logs an error for each line containing bad characters.
+
+ Two kinds of bad characters:
+
+ 1. Unicode replacement characters: These indicate that either the file
+ contained invalid UTF-8 (likely) or Unicode replacement characters (which
+ it shouldn't). Note that it's possible for this to throw off line
+ numbering if the invalid UTF-8 occurred adjacent to a newline.
+
+ 2. NUL bytes. These are problematic for some tools.
+
+ Args:
+ filename: The name of the current file.
+ lines: An array of strings, each representing a line of the file.
+ error: The function to call with any errors found.
+ """
+ for linenum, line in enumerate(lines):
+ if u'\ufffd' in line:
+ error(filename, linenum, 'readability/utf8', 5,
+ 'Line contains invalid UTF-8 (or Unicode replacement character).')
+ if '\0' in line:
+ error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.')
+
+
+def CheckForNewlineAtEOF(filename, lines, error):
+ """Logs an error if there is no newline char at the end of the file.
+
+ Args:
+ filename: The name of the current file.
+ lines: An array of strings, each representing a line of the file.
+ error: The function to call with any errors found.
+ """
+
+ # The array lines() was created by adding two newlines to the
+ # original file (go figure), then splitting on \n.
+ # To verify that the file ends in \n, we just have to make sure the
+ # last-but-two element of lines() exists and is empty.
+ if len(lines) < 3 or lines[-2]:
+ error(filename, len(lines) - 2, 'whitespace/ending_newline', 5,
+ 'Could not find a newline character at the end of the file.')
+
+
+def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
+ """Logs an error if we see /* ... */ or "..." that extend past one line.
+
+ /* ... */ comments are legit inside macros, for one line.
+ Otherwise, we prefer // comments, so it's ok to warn about the
+ other. Likewise, it's ok for strings to extend across multiple
+ lines, as long as a line continuation character (backslash)
+ terminates each line. Although not currently prohibited by the C++
+ style guide, it's ugly and unnecessary. We don't do well with either
+ in this lint program, so we warn about both.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Remove all \\ (escaped backslashes) from the line. They are OK, and the
+ # second (escaped) slash may trigger later \" detection erroneously.
+ line = line.replace('\\\\', '')
+
+ if line.count('/*') > line.count('*/'):
+ error(filename, linenum, 'readability/multiline_comment', 5,
+ 'Complex multi-line /*...*/-style comment found. '
+ 'Lint may give bogus warnings. '
+ 'Consider replacing these with //-style comments, '
+ 'with #if 0...#endif, '
+ 'or with more clearly structured multi-line comments.')
+
+ if (line.count('"') - line.count('\\"')) % 2:
+ error(filename, linenum, 'readability/multiline_string', 5,
+ 'Multi-line string ("...") found. This lint script doesn\'t '
+ 'do well with such strings, and may give bogus warnings. '
+ 'Use C++11 raw strings or concatenation instead.')
+
+
+# (non-threadsafe name, thread-safe alternative, validation pattern)
+#
+# The validation pattern is used to eliminate false positives such as:
+# _rand(); // false positive due to substring match.
+# ->rand(); // some member function rand().
+# ACMRandom rand(seed); // some variable named rand.
+# ISAACRandom rand(); // another variable named rand.
+#
+# Basically we require the return value of these functions to be used
+# in some expression context on the same line by matching on some
+# operator before the function name. This eliminates constructors and
+# member function calls.
+_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)'
+_THREADING_LIST = (
+ ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'),
+ ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'),
+ ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'),
+ ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'),
+ ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'),
+ ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'),
+ ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'),
+ ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'),
+ ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'),
+ ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'),
+ ('strtok(', 'strtok_r(',
+ _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'),
+ ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'),
+ )
+
+
+def CheckPosixThreading(filename, clean_lines, linenum, error):
+ """Checks for calls to thread-unsafe functions.
+
+ Much code has been originally written without consideration of
+ multi-threading. Also, engineers are relying on their old experience;
+ they have learned posix before threading extensions were added. These
+ tests guide the engineers to use thread-safe functions (when using
+ posix directly).
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST:
+ # Additional pattern matching check to confirm that this is the
+ # function we are looking for
+ if Search(pattern, line):
+ error(filename, linenum, 'runtime/threadsafe_fn', 2,
+ 'Consider using ' + multithread_safe_func +
+ '...) instead of ' + single_thread_func +
+ '...) for improved thread safety.')
+
+
+def CheckVlogArguments(filename, clean_lines, linenum, error):
+ """Checks that VLOG() is only used for defining a logging level.
+
+ For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and
+ VLOG(FATAL) are not.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line):
+ error(filename, linenum, 'runtime/vlog', 5,
+ 'VLOG() should be used with numeric verbosity level. '
+ 'Use LOG() if you want symbolic severity levels.')
+
+# Matches invalid increment: *count++, which moves pointer instead of
+# incrementing a value.
+_RE_PATTERN_INVALID_INCREMENT = re.compile(
+ r'^\s*\*\w+(\+\+|--);')
+
+
+def CheckInvalidIncrement(filename, clean_lines, linenum, error):
+ """Checks for invalid increment *count++.
+
+ For example following function:
+ void increment_counter(int* count) {
+ *count++;
+ }
+ is invalid, because it effectively does count++, moving pointer, and should
+ be replaced with ++*count, (*count)++ or *count += 1.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ if _RE_PATTERN_INVALID_INCREMENT.match(line):
+ error(filename, linenum, 'runtime/invalid_increment', 5,
+ 'Changing pointer instead of value (or unused value of operator*).')
+
+
+def IsMacroDefinition(clean_lines, linenum):
+ if Search(r'^#define', clean_lines[linenum]):
+ return True
+
+ if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]):
+ return True
+
+ return False
+
+
+def IsForwardClassDeclaration(clean_lines, linenum):
+ return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum])
+
+
+class _BlockInfo(object):
+ """Stores information about a generic block of code."""
+
+ def __init__(self, linenum, seen_open_brace):
+ self.starting_linenum = linenum
+ self.seen_open_brace = seen_open_brace
+ self.open_parentheses = 0
+ self.inline_asm = _NO_ASM
+ self.check_namespace_indentation = False
+
+ def CheckBegin(self, filename, clean_lines, linenum, error):
+ """Run checks that applies to text up to the opening brace.
+
+ This is mostly for checking the text after the class identifier
+ and the "{", usually where the base class is specified. For other
+ blocks, there isn't much to check, so we always pass.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ pass
+
+ def CheckEnd(self, filename, clean_lines, linenum, error):
+ """Run checks that applies to text after the closing brace.
+
+ This is mostly used for checking end of namespace comments.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ pass
+
+ def IsBlockInfo(self):
+ """Returns true if this block is a _BlockInfo.
+
+ This is convenient for verifying that an object is an instance of
+ a _BlockInfo, but not an instance of any of the derived classes.
+
+ Returns:
+ True for this class, False for derived classes.
+ """
+ return self.__class__ == _BlockInfo
+
+
+class _ExternCInfo(_BlockInfo):
+ """Stores information about an 'extern "C"' block."""
+
+ def __init__(self, linenum):
+ _BlockInfo.__init__(self, linenum, True)
+
+
+class _ClassInfo(_BlockInfo):
+ """Stores information about a class."""
+
+ def __init__(self, name, class_or_struct, clean_lines, linenum):
+ _BlockInfo.__init__(self, linenum, False)
+ self.name = name
+ self.is_derived = False
+ self.check_namespace_indentation = True
+ if class_or_struct == 'struct':
+ self.access = 'public'
+ self.is_struct = True
+ else:
+ self.access = 'private'
+ self.is_struct = False
+
+ # Remember initial indentation level for this class. Using raw_lines here
+ # instead of elided to account for leading comments.
+ self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum])
+
+ # Try to find the end of the class. This will be confused by things like:
+ # class A {
+ # } *x = { ...
+ #
+ # But it's still good enough for CheckSectionSpacing.
+ self.last_line = 0
+ depth = 0
+ for i in range(linenum, clean_lines.NumLines()):
+ line = clean_lines.elided[i]
+ depth += line.count('{') - line.count('}')
+ if not depth:
+ self.last_line = i
+ break
+
+ def CheckBegin(self, filename, clean_lines, linenum, error):
+ # Look for a bare ':'
+ if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]):
+ self.is_derived = True
+
+ def CheckEnd(self, filename, clean_lines, linenum, error):
+ # If there is a DISALLOW macro, it should appear near the end of
+ # the class.
+ seen_last_thing_in_class = False
+ for i in xrange(linenum - 1, self.starting_linenum, -1):
+ match = Search(
+ r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' +
+ self.name + r'\)',
+ clean_lines.elided[i])
+ if match:
+ if seen_last_thing_in_class:
+ error(filename, i, 'readability/constructors', 3,
+ match.group(1) + ' should be the last thing in the class')
+ break
+
+ if not Match(r'^\s*$', clean_lines.elided[i]):
+ seen_last_thing_in_class = True
+
+ # Check that closing brace is aligned with beginning of the class.
+ # Only do this if the closing brace is indented by only whitespaces.
+ # This means we will not check single-line class definitions.
+ indent = Match(r'^( *)\}', clean_lines.elided[linenum])
+ if indent and len(indent.group(1)) != self.class_indent:
+ if self.is_struct:
+ parent = 'struct ' + self.name
+ else:
+ parent = 'class ' + self.name
+ error(filename, linenum, 'whitespace/indent', 3,
+ 'Closing brace should be aligned with beginning of %s' % parent)
+
+
+class _NamespaceInfo(_BlockInfo):
+ """Stores information about a namespace."""
+
+ def __init__(self, name, linenum):
+ _BlockInfo.__init__(self, linenum, False)
+ self.name = name or ''
+ self.check_namespace_indentation = True
+
+ def CheckEnd(self, filename, clean_lines, linenum, error):
+ """Check end of namespace comments."""
+ line = clean_lines.raw_lines[linenum]
+
+ # Check how many lines is enclosed in this namespace. Don't issue
+ # warning for missing namespace comments if there aren't enough
+ # lines. However, do apply checks if there is already an end of
+ # namespace comment and it's incorrect.
+ #
+ # TODO(unknown): We always want to check end of namespace comments
+ # if a namespace is large, but sometimes we also want to apply the
+ # check if a short namespace contained nontrivial things (something
+ # other than forward declarations). There is currently no logic on
+ # deciding what these nontrivial things are, so this check is
+ # triggered by namespace size only, which works most of the time.
+ if (linenum - self.starting_linenum < 10
+ and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)):
+ return
+
+ # Look for matching comment at end of namespace.
+ #
+ # Note that we accept C style "/* */" comments for terminating
+ # namespaces, so that code that terminate namespaces inside
+ # preprocessor macros can be cpplint clean.
+ #
+ # We also accept stuff like "// end of namespace <name>." with the
+ # period at the end.
+ #
+ # Besides these, we don't accept anything else, otherwise we might
+ # get false negatives when existing comment is a substring of the
+ # expected namespace.
+ if self.name:
+ # Named namespace
+ if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' +
+ re.escape(self.name) + r'[\*/\.\\\s]*$'),
+ line):
+ error(filename, linenum, 'readability/namespace', 5,
+ 'Namespace should be terminated with "// namespace %s"' %
+ self.name)
+ else:
+ # Anonymous namespace
+ if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line):
+ # If "// namespace anonymous" or "// anonymous namespace (more text)",
+ # mention "// anonymous namespace" as an acceptable form
+ if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line):
+ error(filename, linenum, 'readability/namespace', 5,
+ 'Anonymous namespace should be terminated with "// namespace"'
+ ' or "// anonymous namespace"')
+ else:
+ error(filename, linenum, 'readability/namespace', 5,
+ 'Anonymous namespace should be terminated with "// namespace"')
+
+
+class _PreprocessorInfo(object):
+ """Stores checkpoints of nesting stacks when #if/#else is seen."""
+
+ def __init__(self, stack_before_if):
+ # The entire nesting stack before #if
+ self.stack_before_if = stack_before_if
+
+ # The entire nesting stack up to #else
+ self.stack_before_else = []
+
+ # Whether we have already seen #else or #elif
+ self.seen_else = False
+
+
+class NestingState(object):
+ """Holds states related to parsing braces."""
+
+ def __init__(self):
+ # Stack for tracking all braces. An object is pushed whenever we
+ # see a "{", and popped when we see a "}". Only 3 types of
+ # objects are possible:
+ # - _ClassInfo: a class or struct.
+ # - _NamespaceInfo: a namespace.
+ # - _BlockInfo: some other type of block.
+ self.stack = []
+
+ # Top of the previous stack before each Update().
+ #
+ # Because the nesting_stack is updated at the end of each line, we
+ # had to do some convoluted checks to find out what is the current
+ # scope at the beginning of the line. This check is simplified by
+ # saving the previous top of nesting stack.
+ #
+ # We could save the full stack, but we only need the top. Copying
+ # the full nesting stack would slow down cpplint by ~10%.
+ self.previous_stack_top = []
+
+ # Stack of _PreprocessorInfo objects.
+ self.pp_stack = []
+
+ def SeenOpenBrace(self):
+ """Check if we have seen the opening brace for the innermost block.
+
+ Returns:
+ True if we have seen the opening brace, False if the innermost
+ block is still expecting an opening brace.
+ """
+ return (not self.stack) or self.stack[-1].seen_open_brace
+
+ def InNamespaceBody(self):
+ """Check if we are currently one level inside a namespace body.
+
+ Returns:
+ True if top of the stack is a namespace block, False otherwise.
+ """
+ return self.stack and isinstance(self.stack[-1], _NamespaceInfo)
+
+ def InExternC(self):
+ """Check if we are currently one level inside an 'extern "C"' block.
+
+ Returns:
+ True if top of the stack is an extern block, False otherwise.
+ """
+ return self.stack and isinstance(self.stack[-1], _ExternCInfo)
+
+ def InClassDeclaration(self):
+ """Check if we are currently one level inside a class or struct declaration.
+
+ Returns:
+ True if top of the stack is a class/struct, False otherwise.
+ """
+ return self.stack and isinstance(self.stack[-1], _ClassInfo)
+
+ def InAsmBlock(self):
+ """Check if we are currently one level inside an inline ASM block.
+
+ Returns:
+ True if the top of the stack is a block containing inline ASM.
+ """
+ return self.stack and self.stack[-1].inline_asm != _NO_ASM
+
+ def InTemplateArgumentList(self, clean_lines, linenum, pos):
+ """Check if current position is inside template argument list.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ pos: position just after the suspected template argument.
+ Returns:
+ True if (linenum, pos) is inside template arguments.
+ """
+ while linenum < clean_lines.NumLines():
+ # Find the earliest character that might indicate a template argument
+ line = clean_lines.elided[linenum]
+ match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:])
+ if not match:
+ linenum += 1
+ pos = 0
+ continue
+ token = match.group(1)
+ pos += len(match.group(0))
+
+ # These things do not look like template argument list:
+ # class Suspect {
+ # class Suspect x; }
+ if token in ('{', '}', ';'): return False
+
+ # These things look like template argument list:
+ # template <class Suspect>
+ # template <class Suspect = default_value>
+ # template <class Suspect[]>
+ # template <class Suspect...>
+ if token in ('>', '=', '[', ']', '.'): return True
+
+ # Check if token is an unmatched '<'.
+ # If not, move on to the next character.
+ if token != '<':
+ pos += 1
+ if pos >= len(line):
+ linenum += 1
+ pos = 0
+ continue
+
+ # We can't be sure if we just find a single '<', and need to
+ # find the matching '>'.
+ (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1)
+ if end_pos < 0:
+ # Not sure if template argument list or syntax error in file
+ return False
+ linenum = end_line
+ pos = end_pos
+ return False
+
+ def UpdatePreprocessor(self, line):
+ """Update preprocessor stack.
+
+ We need to handle preprocessors due to classes like this:
+ #ifdef SWIG
+ struct ResultDetailsPageElementExtensionPoint {
+ #else
+ struct ResultDetailsPageElementExtensionPoint : public Extension {
+ #endif
+
+ We make the following assumptions (good enough for most files):
+ - Preprocessor condition evaluates to true from #if up to first
+ #else/#elif/#endif.
+
+ - Preprocessor condition evaluates to false from #else/#elif up
+ to #endif. We still perform lint checks on these lines, but
+ these do not affect nesting stack.
+
+ Args:
+ line: current line to check.
+ """
+ if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line):
+ # Beginning of #if block, save the nesting stack here. The saved
+ # stack will allow us to restore the parsing state in the #else case.
+ self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack)))
+ elif Match(r'^\s*#\s*(else|elif)\b', line):
+ # Beginning of #else block
+ if self.pp_stack:
+ if not self.pp_stack[-1].seen_else:
+ # This is the first #else or #elif block. Remember the
+ # whole nesting stack up to this point. This is what we
+ # keep after the #endif.
+ self.pp_stack[-1].seen_else = True
+ self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack)
+
+ # Restore the stack to how it was before the #if
+ self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if)
+ else:
+ # TODO(unknown): unexpected #else, issue warning?
+ pass
+ elif Match(r'^\s*#\s*endif\b', line):
+ # End of #if or #else blocks.
+ if self.pp_stack:
+ # If we saw an #else, we will need to restore the nesting
+ # stack to its former state before the #else, otherwise we
+ # will just continue from where we left off.
+ if self.pp_stack[-1].seen_else:
+ # Here we can just use a shallow copy since we are the last
+ # reference to it.
+ self.stack = self.pp_stack[-1].stack_before_else
+ # Drop the corresponding #if
+ self.pp_stack.pop()
+ else:
+ # TODO(unknown): unexpected #endif, issue warning?
+ pass
+
+ # TODO(unknown): Update() is too long, but we will refactor later.
+ def Update(self, filename, clean_lines, linenum, error):
+ """Update nesting state with current line.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Remember top of the previous nesting stack.
+ #
+ # The stack is always pushed/popped and not modified in place, so
+ # we can just do a shallow copy instead of copy.deepcopy. Using
+ # deepcopy would slow down cpplint by ~28%.
+ if self.stack:
+ self.previous_stack_top = self.stack[-1]
+ else:
+ self.previous_stack_top = None
+
+ # Update pp_stack
+ self.UpdatePreprocessor(line)
+
+ # Count parentheses. This is to avoid adding struct arguments to
+ # the nesting stack.
+ if self.stack:
+ inner_block = self.stack[-1]
+ depth_change = line.count('(') - line.count(')')
+ inner_block.open_parentheses += depth_change
+
+ # Also check if we are starting or ending an inline assembly block.
+ if inner_block.inline_asm in (_NO_ASM, _END_ASM):
+ if (depth_change != 0 and
+ inner_block.open_parentheses == 1 and
+ _MATCH_ASM.match(line)):
+ # Enter assembly block
+ inner_block.inline_asm = _INSIDE_ASM
+ else:
+ # Not entering assembly block. If previous line was _END_ASM,
+ # we will now shift to _NO_ASM state.
+ inner_block.inline_asm = _NO_ASM
+ elif (inner_block.inline_asm == _INSIDE_ASM and
+ inner_block.open_parentheses == 0):
+ # Exit assembly block
+ inner_block.inline_asm = _END_ASM
+
+ # Consume namespace declaration at the beginning of the line. Do
+ # this in a loop so that we catch same line declarations like this:
+ # namespace proto2 { namespace bridge { class MessageSet; } }
+ while True:
+ # Match start of namespace. The "\b\s*" below catches namespace
+ # declarations even if it weren't followed by a whitespace, this
+ # is so that we don't confuse our namespace checker. The
+ # missing spaces will be flagged by CheckSpacing.
+ namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line)
+ if not namespace_decl_match:
+ break
+
+ new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum)
+ self.stack.append(new_namespace)
+
+ line = namespace_decl_match.group(2)
+ if line.find('{') != -1:
+ new_namespace.seen_open_brace = True
+ line = line[line.find('{') + 1:]
+
+ # Look for a class declaration in whatever is left of the line
+ # after parsing namespaces. The regexp accounts for decorated classes
+ # such as in:
+ # class LOCKABLE API Object {
+ # };
+ class_decl_match = Match(
+ r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?'
+ r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))'
+ r'(.*)$', line)
+ if (class_decl_match and
+ (not self.stack or self.stack[-1].open_parentheses == 0)):
+ # We do not want to accept classes that are actually template arguments:
+ # template <class Ignore1,
+ # class Ignore2 = Default<Args>,
+ # template <Args> class Ignore3>
+ # void Function() {};
+ #
+ # To avoid template argument cases, we scan forward and look for
+ # an unmatched '>'. If we see one, assume we are inside a
+ # template argument list.
+ end_declaration = len(class_decl_match.group(1))
+ if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration):
+ self.stack.append(_ClassInfo(
+ class_decl_match.group(3), class_decl_match.group(2),
+ clean_lines, linenum))
+ line = class_decl_match.group(4)
+
+ # If we have not yet seen the opening brace for the innermost block,
+ # run checks here.
+ if not self.SeenOpenBrace():
+ self.stack[-1].CheckBegin(filename, clean_lines, linenum, error)
+
+ # Update access control if we are inside a class/struct
+ if self.stack and isinstance(self.stack[-1], _ClassInfo):
+ classinfo = self.stack[-1]
+ access_match = Match(
+ r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?'
+ r':(?:[^:]|$)',
+ line)
+ if access_match:
+ classinfo.access = access_match.group(2)
+
+ # Check that access keywords are indented +1 space. Skip this
+ # check if the keywords are not preceded by whitespaces.
+ indent = access_match.group(1)
+ if (len(indent) != classinfo.class_indent + 1 and
+ Match(r'^\s*$', indent)):
+ if classinfo.is_struct:
+ parent = 'struct ' + classinfo.name
+ else:
+ parent = 'class ' + classinfo.name
+ slots = ''
+ if access_match.group(3):
+ slots = access_match.group(3)
+ error(filename, linenum, 'whitespace/indent', 3,
+ '%s%s: should be indented +1 space inside %s' % (
+ access_match.group(2), slots, parent))
+
+ # Consume braces or semicolons from what's left of the line
+ while True:
+ # Match first brace, semicolon, or closed parenthesis.
+ matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line)
+ if not matched:
+ break
+
+ token = matched.group(1)
+ if token == '{':
+ # If namespace or class hasn't seen a opening brace yet, mark
+ # namespace/class head as complete. Push a new block onto the
+ # stack otherwise.
+ if not self.SeenOpenBrace():
+ self.stack[-1].seen_open_brace = True
+ elif Match(r'^extern\s*"[^"]*"\s*\{', line):
+ self.stack.append(_ExternCInfo(linenum))
+ else:
+ self.stack.append(_BlockInfo(linenum, True))
+ if _MATCH_ASM.match(line):
+ self.stack[-1].inline_asm = _BLOCK_ASM
+
+ elif token == ';' or token == ')':
+ # If we haven't seen an opening brace yet, but we already saw
+ # a semicolon, this is probably a forward declaration. Pop
+ # the stack for these.
+ #
+ # Similarly, if we haven't seen an opening brace yet, but we
+ # already saw a closing parenthesis, then these are probably
+ # function arguments with extra "class" or "struct" keywords.
+ # Also pop these stack for these.
+ if not self.SeenOpenBrace():
+ self.stack.pop()
+ else: # token == '}'
+ # Perform end of block checks and pop the stack.
+ if self.stack:
+ self.stack[-1].CheckEnd(filename, clean_lines, linenum, error)
+ self.stack.pop()
+ line = matched.group(2)
+
+ def InnermostClass(self):
+ """Get class info on the top of the stack.
+
+ Returns:
+ A _ClassInfo object if we are inside a class, or None otherwise.
+ """
+ for i in range(len(self.stack), 0, -1):
+ classinfo = self.stack[i - 1]
+ if isinstance(classinfo, _ClassInfo):
+ return classinfo
+ return None
+
+ def CheckCompletedBlocks(self, filename, error):
+ """Checks that all classes and namespaces have been completely parsed.
+
+ Call this when all lines in a file have been processed.
+ Args:
+ filename: The name of the current file.
+ error: The function to call with any errors found.
+ """
+ # Note: This test can result in false positives if #ifdef constructs
+ # get in the way of brace matching. See the testBuildClass test in
+ # cpplint_unittest.py for an example of this.
+ for obj in self.stack:
+ if isinstance(obj, _ClassInfo):
+ error(filename, obj.starting_linenum, 'build/class', 5,
+ 'Failed to find complete declaration of class %s' %
+ obj.name)
+ elif isinstance(obj, _NamespaceInfo):
+ error(filename, obj.starting_linenum, 'build/namespaces', 5,
+ 'Failed to find complete declaration of namespace %s' %
+ obj.name)
+
+
+def CheckForNonStandardConstructs(filename, clean_lines, linenum,
+ nesting_state, error):
+ r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.
+
+ Complain about several constructs which gcc-2 accepts, but which are
+ not standard C++. Warning about these in lint is one way to ease the
+ transition to new compilers.
+ - put storage class first (e.g. "static const" instead of "const static").
+ - "%lld" instead of %qd" in printf-type functions.
+ - "%1$d" is non-standard in printf-type functions.
+ - "\%" is an undefined character escape sequence.
+ - text after #endif is not allowed.
+ - invalid inner-style forward declaration.
+ - >? and <? operators, and their >?= and <?= cousins.
+
+ Additionally, check for constructor/destructor style violations and reference
+ members, as it is very convenient to do so while checking for
+ gcc-2 compliance.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: A callable to which errors are reported, which takes 4 arguments:
+ filename, line number, error level, and message
+ """
+
+ # Remove comments from the line, but leave in strings for now.
+ line = clean_lines.lines[linenum]
+
+ if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line):
+ error(filename, linenum, 'runtime/printf_format', 3,
+ '%q in format strings is deprecated. Use %ll instead.')
+
+ if Search(r'printf\s*\(.*".*%\d+\$', line):
+ error(filename, linenum, 'runtime/printf_format', 2,
+ '%N$ formats are unconventional. Try rewriting to avoid them.')
+
+ # Remove escaped backslashes before looking for undefined escapes.
+ line = line.replace('\\\\', '')
+
+ if Search(r'("|\').*\\(%|\[|\(|{)', line):
+ error(filename, linenum, 'build/printf_format', 3,
+ '%, [, (, and { are undefined character escapes. Unescape them.')
+
+ # For the rest, work with both comments and strings removed.
+ line = clean_lines.elided[linenum]
+
+ if Search(r'\b(const|volatile|void|char|short|int|long'
+ r'|float|double|signed|unsigned'
+ r'|schar|u?int8|u?int16|u?int32|u?int64)'
+ r'\s+(register|static|extern|typedef)\b',
+ line):
+ error(filename, linenum, 'build/storage_class', 5,
+ 'Storage-class specifier (static, extern, typedef, etc) should be '
+ 'at the beginning of the declaration.')
+
+ if Match(r'\s*#\s*endif\s*[^/\s]+', line):
+ error(filename, linenum, 'build/endif_comment', 5,
+ 'Uncommented text after #endif is non-standard. Use a comment.')
+
+ if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line):
+ error(filename, linenum, 'build/forward_decl', 5,
+ 'Inner-style forward declarations are invalid. Remove this line.')
+
+ if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?',
+ line):
+ error(filename, linenum, 'build/deprecated', 3,
+ '>? and <? (max and min) operators are non-standard and deprecated.')
+
+ if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line):
+ # TODO(unknown): Could it be expanded safely to arbitrary references,
+ # without triggering too many false positives? The first
+ # attempt triggered 5 warnings for mostly benign code in the regtest, hence
+ # the restriction.
+ # Here's the original regexp, for the reference:
+ # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?'
+ # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;'
+ error(filename, linenum, 'runtime/member_string_references', 2,
+ 'const string& members are dangerous. It is much better to use '
+ 'alternatives, such as pointers or simple constants.')
+
+ # Everything else in this function operates on class declarations.
+ # Return early if the top of the nesting stack is not a class, or if
+ # the class head is not completed yet.
+ classinfo = nesting_state.InnermostClass()
+ if not classinfo or not classinfo.seen_open_brace:
+ return
+
+ # The class may have been declared with namespace or classname qualifiers.
+ # The constructor and destructor will not have those qualifiers.
+ base_classname = classinfo.name.split('::')[-1]
+
+ # Look for single-argument constructors that aren't marked explicit.
+ # Technically a valid construct, but against style.
+ explicit_constructor_match = Match(
+ r'\s+(?:(?:inline|constexpr)\s+)*(explicit\s+)?'
+ r'(?:(?:inline|constexpr)\s+)*%s\s*'
+ r'\(((?:[^()]|\([^()]*\))*)\)'
+ % re.escape(base_classname),
+ line)
+
+ if explicit_constructor_match:
+ is_marked_explicit = explicit_constructor_match.group(1)
+
+ if not explicit_constructor_match.group(2):
+ constructor_args = []
+ else:
+ constructor_args = explicit_constructor_match.group(2).split(',')
+
+ # collapse arguments so that commas in template parameter lists and function
+ # argument parameter lists don't split arguments in two
+ i = 0
+ while i < len(constructor_args):
+ constructor_arg = constructor_args[i]
+ while (constructor_arg.count('<') > constructor_arg.count('>') or
+ constructor_arg.count('(') > constructor_arg.count(')')):
+ constructor_arg += ',' + constructor_args[i + 1]
+ del constructor_args[i + 1]
+ constructor_args[i] = constructor_arg
+ i += 1
+
+ defaulted_args = [arg for arg in constructor_args if '=' in arg]
+ noarg_constructor = (not constructor_args or # empty arg list
+ # 'void' arg specifier
+ (len(constructor_args) == 1 and
+ constructor_args[0].strip() == 'void'))
+ onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg
+ not noarg_constructor) or
+ # all but at most one arg defaulted
+ (len(constructor_args) >= 1 and
+ not noarg_constructor and
+ len(defaulted_args) >= len(constructor_args) - 1))
+ initializer_list_constructor = bool(
+ onearg_constructor and
+ Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0]))
+ copy_constructor = bool(
+ onearg_constructor and
+ Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&'
+ % re.escape(base_classname), constructor_args[0].strip()))
+
+ if (not is_marked_explicit and
+ onearg_constructor and
+ not initializer_list_constructor and
+ not copy_constructor):
+ if defaulted_args:
+ error(filename, linenum, 'runtime/explicit', 5,
+ 'Constructors callable with one argument '
+ 'should be marked explicit.')
+ else:
+ error(filename, linenum, 'runtime/explicit', 5,
+ 'Single-parameter constructors should be marked explicit.')
+ elif is_marked_explicit and not onearg_constructor:
+ if noarg_constructor:
+ error(filename, linenum, 'runtime/explicit', 5,
+ 'Zero-parameter constructors should not be marked explicit.')
+
+
+def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error):
+ """Checks for the correctness of various spacing around function calls.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Since function calls often occur inside if/for/while/switch
+ # expressions - which have their own, more liberal conventions - we
+ # first see if we should be looking inside such an expression for a
+ # function call, to which we can apply more strict standards.
+ fncall = line # if there's no control flow construct, look at whole line
+ for pattern in (r'\bif\s*\((.*)\)\s*{',
+ r'\bfor\s*\((.*)\)\s*{',
+ r'\bwhile\s*\((.*)\)\s*[{;]',
+ r'\bswitch\s*\((.*)\)\s*{'):
+ match = Search(pattern, line)
+ if match:
+ fncall = match.group(1) # look inside the parens for function calls
+ break
+
+ # Except in if/for/while/switch, there should never be space
+ # immediately inside parens (eg "f( 3, 4 )"). We make an exception
+ # for nested parens ( (a+b) + c ). Likewise, there should never be
+ # a space before a ( when it's a function argument. I assume it's a
+ # function argument when the char before the whitespace is legal in
+ # a function name (alnum + _) and we're not starting a macro. Also ignore
+ # pointers and references to arrays and functions coz they're too tricky:
+ # we use a very simple way to recognize these:
+ # " (something)(maybe-something)" or
+ # " (something)(maybe-something," or
+ # " (something)[something]"
+ # Note that we assume the contents of [] to be short enough that
+ # they'll never need to wrap.
+ if ( # Ignore control structures.
+ not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b',
+ fncall) and
+ # Ignore pointers/references to functions.
+ not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
+ # Ignore pointers/references to arrays.
+ not Search(r' \([^)]+\)\[[^\]]+\]', fncall)):
+ if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call
+ error(filename, linenum, 'whitespace/parens', 4,
+ 'Extra space after ( in function call')
+ elif Search(r'\(\s+(?!(\s*\\)|\()', fncall):
+ error(filename, linenum, 'whitespace/parens', 2,
+ 'Extra space after (')
+ if (Search(r'\w\s+\(', fncall) and
+ not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and
+ not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and
+ not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and
+ not Search(r'\bcase\s+\(', fncall)):
+ # TODO(unknown): Space after an operator function seem to be a common
+ # error, silence those for now by restricting them to highest verbosity.
+ if Search(r'\boperator_*\b', line):
+ error(filename, linenum, 'whitespace/parens', 0,
+ 'Extra space before ( in function call')
+ else:
+ error(filename, linenum, 'whitespace/parens', 4,
+ 'Extra space before ( in function call')
+ # If the ) is followed only by a newline or a { + newline, assume it's
+ # part of a control statement (if/while/etc), and don't complain
+ if Search(r'[^)]\s+\)\s*[^{\s]', fncall):
+ # If the closing parenthesis is preceded by only whitespaces,
+ # try to give a more descriptive error message.
+ if Search(r'^\s+\)', fncall):
+ error(filename, linenum, 'whitespace/parens', 2,
+ 'Closing ) should be moved to the previous line')
+ else:
+ error(filename, linenum, 'whitespace/parens', 2,
+ 'Extra space before )')
+
+
+def IsBlankLine(line):
+ """Returns true if the given line is blank.
+
+ We consider a line to be blank if the line is empty or consists of
+ only white spaces.
+
+ Args:
+ line: A line of a string.
+
+ Returns:
+ True, if the given line is blank.
+ """
+ return not line or line.isspace()
+
+
+def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line,
+ error):
+ is_namespace_indent_item = (
+ len(nesting_state.stack) > 1 and
+ nesting_state.stack[-1].check_namespace_indentation and
+ isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and
+ nesting_state.previous_stack_top == nesting_state.stack[-2])
+
+ if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item,
+ clean_lines.elided, line):
+ CheckItemIndentationInNamespace(filename, clean_lines.elided,
+ line, error)
+
+
+def CheckForFunctionLengths(filename, clean_lines, linenum,
+ function_state, error):
+ """Reports for long function bodies.
+
+ For an overview why this is done, see:
+ https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions
+
+ Uses a simplistic algorithm assuming other style guidelines
+ (especially spacing) are followed.
+ Only checks unindented functions, so class members are unchecked.
+ Trivial bodies are unchecked, so constructors with huge initializer lists
+ may be missed.
+ Blank/comment lines are not counted so as to avoid encouraging the removal
+ of vertical space and comments just to get through a lint check.
+ NOLINT *on the last line of a function* disables this check.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ function_state: Current function name and lines in body so far.
+ error: The function to call with any errors found.
+ """
+ lines = clean_lines.lines
+ line = lines[linenum]
+ joined_line = ''
+
+ starting_func = False
+ regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ...
+ match_result = Match(regexp, line)
+ if match_result:
+ # If the name is all caps and underscores, figure it's a macro and
+ # ignore it, unless it's TEST or TEST_F.
+ function_name = match_result.group(1).split()[-1]
+ if function_name == 'TEST' or function_name == 'TEST_F' or (
+ not Match(r'[A-Z_]+$', function_name)):
+ starting_func = True
+
+ if starting_func:
+ body_found = False
+ for start_linenum in xrange(linenum, clean_lines.NumLines()):
+ start_line = lines[start_linenum]
+ joined_line += ' ' + start_line.lstrip()
+ if Search(r'(;|})', start_line): # Declarations and trivial functions
+ body_found = True
+ break # ... ignore
+ elif Search(r'{', start_line):
+ body_found = True
+ function = Search(r'((\w|:)*)\(', line).group(1)
+ if Match(r'TEST', function): # Handle TEST... macros
+ parameter_regexp = Search(r'(\(.*\))', joined_line)
+ if parameter_regexp: # Ignore bad syntax
+ function += parameter_regexp.group(1)
+ else:
+ function += '()'
+ function_state.Begin(function)
+ break
+ if not body_found:
+ # No body for the function (or evidence of a non-function) was found.
+ error(filename, linenum, 'readability/fn_size', 5,
+ 'Lint failed to find start of function body.')
+ elif Match(r'^\}\s*$', line): # function end
+ function_state.Check(error, filename, linenum)
+ function_state.End()
+ elif not Match(r'^\s*$', line):
+ function_state.Count() # Count non-blank/non-comment lines.
+
+
+_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?')
+
+
+def CheckComment(line, filename, linenum, next_line_start, error):
+ """Checks for common mistakes in comments.
+
+ Args:
+ line: The line in question.
+ filename: The name of the current file.
+ linenum: The number of the line to check.
+ next_line_start: The first non-whitespace column of the next line.
+ error: The function to call with any errors found.
+ """
+ commentpos = line.find('//')
+ if commentpos != -1:
+ # Check if the // may be in quotes. If so, ignore it
+ if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0:
+ # Allow one space for new scopes, two spaces otherwise:
+ if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and
+ ((commentpos >= 1 and
+ line[commentpos-1] not in string.whitespace) or
+ (commentpos >= 2 and
+ line[commentpos-2] not in string.whitespace))):
+ error(filename, linenum, 'whitespace/comments', 2,
+ 'At least two spaces is best between code and comments')
+
+ # Checks for common mistakes in TODO comments.
+ comment = line[commentpos:]
+ match = _RE_PATTERN_TODO.match(comment)
+ if match:
+ # One whitespace is correct; zero whitespace is handled elsewhere.
+ leading_whitespace = match.group(1)
+ if len(leading_whitespace) > 1:
+ error(filename, linenum, 'whitespace/todo', 2,
+ 'Too many spaces before TODO')
+
+ username = match.group(2)
+ if not username:
+ error(filename, linenum, 'readability/todo', 2,
+ 'Missing username in TODO; it should look like '
+ '"// TODO(my_username): Stuff."')
+
+ middle_whitespace = match.group(3)
+ # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison
+ if middle_whitespace != ' ' and middle_whitespace != '':
+ error(filename, linenum, 'whitespace/todo', 2,
+ 'TODO(my_username) should be followed by a space')
+
+ # If the comment contains an alphanumeric character, there
+ # should be a space somewhere between it and the // unless
+ # it's a /// or //! Doxygen comment.
+ if (Match(r'//[^ ]*\w', comment) and
+ not Match(r'(///|//\!)(\s+|$)', comment)):
+ error(filename, linenum, 'whitespace/comments', 4,
+ 'Should have a space between // and comment')
+
+
+def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
+ """Checks for the correctness of various spacing issues in the code.
+
+ Things we check for: spaces around operators, spaces after
+ if/for/while/switch, no spaces around parens in function calls, two
+ spaces between code and comment, don't start a block with a blank
+ line, don't end a function with a blank line, don't add a blank line
+ after public/protected/private, don't have too many blank lines in a row.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+
+ # Don't use "elided" lines here, otherwise we can't check commented lines.
+ # Don't want to use "raw" either, because we don't want to check inside C++11
+ # raw strings,
+ raw = clean_lines.lines_without_raw_strings
+ line = raw[linenum]
+
+ # Before nixing comments, check if the line is blank for no good
+ # reason. This includes the first line after a block is opened, and
+ # blank lines at the end of a function (ie, right before a line like '}'
+ #
+ # Skip all the blank line checks if we are immediately inside a
+ # namespace body. In other words, don't issue blank line warnings
+ # for this block:
+ # namespace {
+ #
+ # }
+ #
+ # A warning about missing end of namespace comments will be issued instead.
+ #
+ # Also skip blank line checks for 'extern "C"' blocks, which are formatted
+ # like namespaces.
+ if (IsBlankLine(line) and
+ not nesting_state.InNamespaceBody() and
+ not nesting_state.InExternC()):
+ elided = clean_lines.elided
+ prev_line = elided[linenum - 1]
+ prevbrace = prev_line.rfind('{')
+ # TODO(unknown): Don't complain if line before blank line, and line after,
+ # both start with alnums and are indented the same amount.
+ # This ignores whitespace at the start of a namespace block
+ # because those are not usually indented.
+ if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1:
+ # OK, we have a blank line at the start of a code block. Before we
+ # complain, we check if it is an exception to the rule: The previous
+ # non-empty line has the parameters of a function header that are indented
+ # 4 spaces (because they did not fit in a 80 column line when placed on
+ # the same line as the function name). We also check for the case where
+ # the previous line is indented 6 spaces, which may happen when the
+ # initializers of a constructor do not fit into a 80 column line.
+ exception = False
+ if Match(r' {6}\w', prev_line): # Initializer list?
+ # We are looking for the opening column of initializer list, which
+ # should be indented 4 spaces to cause 6 space indentation afterwards.
+ search_position = linenum-2
+ while (search_position >= 0
+ and Match(r' {6}\w', elided[search_position])):
+ search_position -= 1
+ exception = (search_position >= 0
+ and elided[search_position][:5] == ' :')
+ else:
+ # Search for the function arguments or an initializer list. We use a
+ # simple heuristic here: If the line is indented 4 spaces; and we have a
+ # closing paren, without the opening paren, followed by an opening brace
+ # or colon (for initializer lists) we assume that it is the last line of
+ # a function header. If we have a colon indented 4 spaces, it is an
+ # initializer list.
+ exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',
+ prev_line)
+ or Match(r' {4}:', prev_line))
+
+ if not exception:
+ error(filename, linenum, 'whitespace/blank_line', 2,
+ 'Redundant blank line at the start of a code block '
+ 'should be deleted.')
+ # Ignore blank lines at the end of a block in a long if-else
+ # chain, like this:
+ # if (condition1) {
+ # // Something followed by a blank line
+ #
+ # } else if (condition2) {
+ # // Something else
+ # }
+ if linenum + 1 < clean_lines.NumLines():
+ next_line = raw[linenum + 1]
+ if (next_line
+ and Match(r'\s*}', next_line)
+ and next_line.find('} else ') == -1):
+ error(filename, linenum, 'whitespace/blank_line', 3,
+ 'Redundant blank line at the end of a code block '
+ 'should be deleted.')
+
+ matched = Match(r'\s*(public|protected|private):', prev_line)
+ if matched:
+ error(filename, linenum, 'whitespace/blank_line', 3,
+ 'Do not leave a blank line after "%s:"' % matched.group(1))
+
+ # Next, check comments
+ next_line_start = 0
+ if linenum + 1 < clean_lines.NumLines():
+ next_line = raw[linenum + 1]
+ next_line_start = len(next_line) - len(next_line.lstrip())
+ CheckComment(line, filename, linenum, next_line_start, error)
+
+ # get rid of comments and strings
+ line = clean_lines.elided[linenum]
+
+ # You shouldn't have spaces before your brackets, except maybe after
+ # 'delete []' or 'return []() {};'
+ if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line):
+ error(filename, linenum, 'whitespace/braces', 5,
+ 'Extra space before [')
+
+ # In range-based for, we wanted spaces before and after the colon, but
+ # not around "::" tokens that might appear.
+ if (Search(r'for *\(.*[^:]:[^: ]', line) or
+ Search(r'for *\(.*[^: ]:[^:]', line)):
+ error(filename, linenum, 'whitespace/forcolon', 2,
+ 'Missing space around colon in range-based for loop')
+
+
+def CheckOperatorSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing around operators.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Don't try to do spacing checks for operator methods. Do this by
+ # replacing the troublesome characters with something else,
+ # preserving column position for all other characters.
+ #
+ # The replacement is done repeatedly to avoid false positives from
+ # operators that call operators.
+ while True:
+ match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line)
+ if match:
+ line = match.group(1) + ('_' * len(match.group(2))) + match.group(3)
+ else:
+ break
+
+ # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )".
+ # Otherwise not. Note we only check for non-spaces on *both* sides;
+ # sometimes people put non-spaces on one side when aligning ='s among
+ # many lines (not that this is behavior that I approve of...)
+ if ((Search(r'[\w.]=', line) or
+ Search(r'=[\w.]', line))
+ and not Search(r'\b(if|while|for) ', line)
+ # Operators taken from [lex.operators] in C++11 standard.
+ and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line)
+ and not Search(r'operator=', line)):
+ error(filename, linenum, 'whitespace/operators', 4,
+ 'Missing spaces around =')
+
+ # It's ok not to have spaces around binary operators like + - * /, but if
+ # there's too little whitespace, we get concerned. It's hard to tell,
+ # though, so we punt on this one for now. TODO.
+
+ # You should always have whitespace around binary operators.
+ #
+ # Check <= and >= first to avoid false positives with < and >, then
+ # check non-include lines for spacing around < and >.
+ #
+ # If the operator is followed by a comma, assume it's be used in a
+ # macro context and don't do any checks. This avoids false
+ # positives.
+ #
+ # Note that && is not included here. This is because there are too
+ # many false positives due to RValue references.
+ match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line)
+ if match:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around %s' % match.group(1))
+ elif not Match(r'#.*include', line):
+ # Look for < that is not surrounded by spaces. This is only
+ # triggered if both sides are missing spaces, even though
+ # technically should should flag if at least one side is missing a
+ # space. This is done to avoid some false positives with shifts.
+ match = Match(r'^(.*[^\s<])<[^\s=<,]', line)
+ if match:
+ (_, _, end_pos) = CloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ if end_pos <= -1:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around <')
+
+ # Look for > that is not surrounded by spaces. Similar to the
+ # above, we only trigger if both sides are missing spaces to avoid
+ # false positives with shifts.
+ match = Match(r'^(.*[^-\s>])>[^\s=>,]', line)
+ if match:
+ (_, _, start_pos) = ReverseCloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ if start_pos <= -1:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around >')
+
+ # We allow no-spaces around << when used like this: 10<<20, but
+ # not otherwise (particularly, not when used as streams)
+ #
+ # We also allow operators following an opening parenthesis, since
+ # those tend to be macros that deal with operators.
+ match = Search(r'(operator|[^\s(<])(?:L|UL|LL|ULL|l|ul|ll|ull)?<<([^\s,=<])', line)
+ if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and
+ not (match.group(1) == 'operator' and match.group(2) == ';')):
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around <<')
+
+ # We allow no-spaces around >> for almost anything. This is because
+ # C++11 allows ">>" to close nested templates, which accounts for
+ # most cases when ">>" is not followed by a space.
+ #
+ # We still warn on ">>" followed by alpha character, because that is
+ # likely due to ">>" being used for right shifts, e.g.:
+ # value >> alpha
+ #
+ # When ">>" is used to close templates, the alphanumeric letter that
+ # follows would be part of an identifier, and there should still be
+ # a space separating the template type and the identifier.
+ # type<type<type>> alpha
+ match = Search(r'>>[a-zA-Z_]', line)
+ if match:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around >>')
+
+ # There shouldn't be space around unary operators
+ match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line)
+ if match:
+ error(filename, linenum, 'whitespace/operators', 4,
+ 'Extra space for operator %s' % match.group(1))
+
+
+def CheckParenthesisSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing around parentheses.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # No spaces after an if, while, switch, or for
+ match = Search(r' (if\(|for\(|while\(|switch\()', line)
+ if match:
+ error(filename, linenum, 'whitespace/parens', 5,
+ 'Missing space before ( in %s' % match.group(1))
+
+ # For if/for/while/switch, the left and right parens should be
+ # consistent about how many spaces are inside the parens, and
+ # there should either be zero or one spaces inside the parens.
+ # We don't want: "if ( foo)" or "if ( foo )".
+ # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed.
+ match = Search(r'\b(if|for|while|switch)\s*'
+ r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$',
+ line)
+ if match:
+ if len(match.group(2)) != len(match.group(4)):
+ if not (match.group(3) == ';' and
+ len(match.group(2)) == 1 + len(match.group(4)) or
+ not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)):
+ error(filename, linenum, 'whitespace/parens', 5,
+ 'Mismatching spaces inside () in %s' % match.group(1))
+ if len(match.group(2)) not in [0, 1]:
+ error(filename, linenum, 'whitespace/parens', 5,
+ 'Should have zero or one spaces inside ( and ) in %s' %
+ match.group(1))
+
+
+def CheckCommaSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing near commas and semicolons.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ raw = clean_lines.lines_without_raw_strings
+ line = clean_lines.elided[linenum]
+
+ # You should always have a space after a comma (either as fn arg or operator)
+ #
+ # This does not apply when the non-space character following the
+ # comma is another comma, since the only time when that happens is
+ # for empty macro arguments.
+ #
+ # We run this check in two passes: first pass on elided lines to
+ # verify that lines contain missing whitespaces, second pass on raw
+ # lines to confirm that those missing whitespaces are not due to
+ # elided comments.
+ if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and
+ Search(r',[^,\s]', raw[linenum])):
+ error(filename, linenum, 'whitespace/comma', 3,
+ 'Missing space after ,')
+
+ # You should always have a space after a semicolon
+ # except for few corner cases
+ # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more
+ # space after ;
+ if Search(r';[^\s};\\)/]', line):
+ error(filename, linenum, 'whitespace/semicolon', 3,
+ 'Missing space after ;')
+
+
+def _IsType(clean_lines, nesting_state, expr):
+ """Check if expression looks like a type name, returns true if so.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ expr: The expression to check.
+ Returns:
+ True, if token looks like a type.
+ """
+ # Keep only the last token in the expression
+ last_word = Match(r'^.*(\b\S+)$', expr)
+ if last_word:
+ token = last_word.group(1)
+ else:
+ token = expr
+
+ # Match native types and stdint types
+ if _TYPES.match(token):
+ return True
+
+ # Try a bit harder to match templated types. Walk up the nesting
+ # stack until we find something that resembles a typename
+ # declaration for what we are looking for.
+ typename_pattern = (r'\b(?:typename|class|struct)\s+' + re.escape(token) +
+ r'\b')
+ block_index = len(nesting_state.stack) - 1
+ while block_index >= 0:
+ if isinstance(nesting_state.stack[block_index], _NamespaceInfo):
+ return False
+
+ # Found where the opening brace is. We want to scan from this
+ # line up to the beginning of the function, minus a few lines.
+ # template <typename Type1, // stop scanning here
+ # ...>
+ # class C
+ # : public ... { // start scanning here
+ last_line = nesting_state.stack[block_index].starting_linenum
+
+ next_block_start = 0
+ if block_index > 0:
+ next_block_start = nesting_state.stack[block_index - 1].starting_linenum
+ first_line = last_line
+ while first_line >= next_block_start:
+ if clean_lines.elided[first_line].find('template') >= 0:
+ break
+ first_line -= 1
+ if first_line < next_block_start:
+ # Didn't find any "template" keyword before reaching the next block,
+ # there are probably no template things to check for this block
+ block_index -= 1
+ continue
+
+ # Look for typename in the specified range
+ for i in xrange(first_line, last_line + 1, 1):
+ if Search(typename_pattern, clean_lines.elided[i]):
+ return True
+ block_index -= 1
+
+ return False
+
+
+def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error):
+ """Checks for horizontal spacing near commas.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Except after an opening paren, or after another opening brace (in case of
+ # an initializer list, for instance), you should have spaces before your
+ # braces when they are delimiting blocks, classes, namespaces etc.
+ # And since you should never have braces at the beginning of a line,
+ # this is an easy test. Except that braces used for initialization don't
+ # follow the same rule; we often don't want spaces before those.
+ match = Match(r'^(.*[^ ({>]){', line)
+
+ if match:
+ # Try a bit harder to check for brace initialization. This
+ # happens in one of the following forms:
+ # Constructor() : initializer_list_{} { ... }
+ # Constructor{}.MemberFunction()
+ # Type variable{};
+ # FunctionCall(type{}, ...);
+ # LastArgument(..., type{});
+ # LOG(INFO) << type{} << " ...";
+ # map_of_type[{...}] = ...;
+ # ternary = expr ? new type{} : nullptr;
+ # OuterTemplate<InnerTemplateConstructor<Type>{}>
+ #
+ # We check for the character following the closing brace, and
+ # silence the warning if it's one of those listed above, i.e.
+ # "{.;,)<>]:".
+ #
+ # To account for nested initializer list, we allow any number of
+ # closing braces up to "{;,)<". We can't simply silence the
+ # warning on first sight of closing brace, because that would
+ # cause false negatives for things that are not initializer lists.
+ # Silence this: But not this:
+ # Outer{ if (...) {
+ # Inner{...} if (...){ // Missing space before {
+ # }; }
+ #
+ # There is a false negative with this approach if people inserted
+ # spurious semicolons, e.g. "if (cond){};", but we will catch the
+ # spurious semicolon with a separate check.
+ leading_text = match.group(1)
+ (endline, endlinenum, endpos) = CloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ trailing_text = ''
+ if endpos > -1:
+ trailing_text = endline[endpos:]
+ for offset in xrange(endlinenum + 1,
+ min(endlinenum + 3, clean_lines.NumLines() - 1)):
+ trailing_text += clean_lines.elided[offset]
+ # We also suppress warnings for `uint64_t{expression}` etc., as the style
+ # guide recommends brace initialization for integral types to avoid
+ # overflow/truncation.
+ if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text)
+ and not _IsType(clean_lines, nesting_state, leading_text)):
+ error(filename, linenum, 'whitespace/braces', 5,
+ 'Missing space before {')
+
+ # Make sure '} else {' has spaces.
+ if Search(r'}else', line):
+ error(filename, linenum, 'whitespace/braces', 5,
+ 'Missing space before else')
+
+ # You shouldn't have a space before a semicolon at the end of the line.
+ # There's a special case for "for" since the style guide allows space before
+ # the semicolon there.
+ if Search(r':\s*;\s*$', line):
+ error(filename, linenum, 'whitespace/semicolon', 5,
+ 'Semicolon defining empty statement. Use {} instead.')
+ elif Search(r'^\s*;\s*$', line):
+ error(filename, linenum, 'whitespace/semicolon', 5,
+ 'Line contains only semicolon. If this should be an empty statement, '
+ 'use {} instead.')
+ elif (Search(r'\s+;\s*$', line) and
+ not Search(r'\bfor\b', line)):
+ error(filename, linenum, 'whitespace/semicolon', 5,
+ 'Extra space before last semicolon. If this should be an empty '
+ 'statement, use {} instead.')
+
+
+def IsDecltype(clean_lines, linenum, column):
+ """Check if the token ending on (linenum, column) is decltype().
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: the number of the line to check.
+ column: end column of the token to check.
+ Returns:
+ True if this token is decltype() expression, False otherwise.
+ """
+ (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column)
+ if start_col < 0:
+ return False
+ if Search(r'\bdecltype\s*$', text[0:start_col]):
+ return True
+ return False
+
+
+def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error):
+ """Checks for additional blank line issues related to sections.
+
+ Currently the only thing checked here is blank line before protected/private.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ class_info: A _ClassInfo objects.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ # Skip checks if the class is small, where small means 25 lines or less.
+ # 25 lines seems like a good cutoff since that's the usual height of
+ # terminals, and any class that can't fit in one screen can't really
+ # be considered "small".
+ #
+ # Also skip checks if we are on the first line. This accounts for
+ # classes that look like
+ # class Foo { public: ... };
+ #
+ # If we didn't find the end of the class, last_line would be zero,
+ # and the check will be skipped by the first condition.
+ if (class_info.last_line - class_info.starting_linenum <= 24 or
+ linenum <= class_info.starting_linenum):
+ return
+
+ matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum])
+ if matched:
+ # Issue warning if the line before public/protected/private was
+ # not a blank line, but don't do this if the previous line contains
+ # "class" or "struct". This can happen two ways:
+ # - We are at the beginning of the class.
+ # - We are forward-declaring an inner class that is semantically
+ # private, but needed to be public for implementation reasons.
+ # Also ignores cases where the previous line ends with a backslash as can be
+ # common when defining classes in C macros.
+ prev_line = clean_lines.lines[linenum - 1]
+ if (not IsBlankLine(prev_line) and
+ not Search(r'\b(class|struct)\b', prev_line) and
+ not Search(r'\\$', prev_line)):
+ # Try a bit harder to find the beginning of the class. This is to
+ # account for multi-line base-specifier lists, e.g.:
+ # class Derived
+ # : public Base {
+ end_class_head = class_info.starting_linenum
+ for i in range(class_info.starting_linenum, linenum):
+ if Search(r'\{\s*$', clean_lines.lines[i]):
+ end_class_head = i
+ break
+ if end_class_head < linenum - 1:
+ error(filename, linenum, 'whitespace/blank_line', 3,
+ '"%s:" should be preceded by a blank line' % matched.group(1))
+
+
+def GetPreviousNonBlankLine(clean_lines, linenum):
+ """Return the most recent non-blank line and its line number.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file contents.
+ linenum: The number of the line to check.
+
+ Returns:
+ A tuple with two elements. The first element is the contents of the last
+ non-blank line before the current line, or the empty string if this is the
+ first non-blank line. The second is the line number of that line, or -1
+ if this is the first non-blank line.
+ """
+
+ prevlinenum = linenum - 1
+ while prevlinenum >= 0:
+ prevline = clean_lines.elided[prevlinenum]
+ if not IsBlankLine(prevline): # if not a blank line...
+ return (prevline, prevlinenum)
+ prevlinenum -= 1
+ return ('', -1)
+
+
+def CheckBraces(filename, clean_lines, linenum, error):
+ """Looks for misplaced braces (e.g. at the end of line).
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ line = clean_lines.elided[linenum] # get rid of comments and strings
+
+ if Match(r'\s*{\s*$', line):
+ # We allow an open brace to start a line in the case where someone is using
+ # braces in a block to explicitly create a new scope, which is commonly used
+ # to control the lifetime of stack-allocated variables. Braces are also
+ # used for brace initializers inside function calls. We don't detect this
+ # perfectly: we just don't complain if the last non-whitespace character on
+ # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the
+ # previous line starts a preprocessor block. We also allow a brace on the
+ # following line if it is part of an array initialization and would not fit
+ # within the 80 character limit of the preceding line.
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ if (not Search(r'[,;:}{(]\s*$', prevline) and
+ not Match(r'\s*#', prevline) and
+ not (GetLineWidth(prevline) > _line_length - 2 and '[]' in prevline)):
+ error(filename, linenum, 'whitespace/braces', 4,
+ '{ should almost always be at the end of the previous line')
+
+ # An else clause should be on the same line as the preceding closing brace.
+ if Match(r'\s*else\b\s*(?:if\b|\{|$)', line):
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ if Match(r'\s*}\s*$', prevline):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'An else should appear on the same line as the preceding }')
+
+ # If braces come on one side of an else, they should be on both.
+ # However, we have to worry about "else if" that spans multiple lines!
+ if Search(r'else if\s*\(', line): # could be multi-line if
+ brace_on_left = bool(Search(r'}\s*else if\s*\(', line))
+ # find the ( after the if
+ pos = line.find('else if')
+ pos = line.find('(', pos)
+ if pos > 0:
+ (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos)
+ brace_on_right = endline[endpos:].find('{') != -1
+ if brace_on_left != brace_on_right: # must be brace after if
+ error(filename, linenum, 'readability/braces', 5,
+ 'If an else has a brace on one side, it should have it on both')
+ elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line):
+ error(filename, linenum, 'readability/braces', 5,
+ 'If an else has a brace on one side, it should have it on both')
+
+ # Likewise, an else should never have the else clause on the same line
+ if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'Else clause should never be on same line as else (use 2 lines)')
+
+ # In the same way, a do/while should never be on one line
+ if Match(r'\s*do [^\s{]', line):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'do/while clauses should not be on a single line')
+
+ # Check single-line if/else bodies. The style guide says 'curly braces are not
+ # required for single-line statements'. We additionally allow multi-line,
+ # single statements, but we reject anything with more than one semicolon in
+ # it. This means that the first semicolon after the if should be at the end of
+ # its line, and the line after that should have an indent level equal to or
+ # lower than the if. We also check for ambiguous if/else nesting without
+ # braces.
+ if_else_match = Search(r'\b(if\s*\(|else\b)', line)
+ if if_else_match and not Match(r'\s*#', line):
+ if_indent = GetIndentLevel(line)
+ endline, endlinenum, endpos = line, linenum, if_else_match.end()
+ if_match = Search(r'\bif\s*\(', line)
+ if if_match:
+ # This could be a multiline if condition, so find the end first.
+ pos = if_match.end() - 1
+ (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos)
+ # Check for an opening brace, either directly after the if or on the next
+ # line. If found, this isn't a single-statement conditional.
+ if (not Match(r'\s*{', endline[endpos:])
+ and not (Match(r'\s*$', endline[endpos:])
+ and endlinenum < (len(clean_lines.elided) - 1)
+ and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))):
+ while (endlinenum < len(clean_lines.elided)
+ and ';' not in clean_lines.elided[endlinenum][endpos:]):
+ endlinenum += 1
+ endpos = 0
+ if endlinenum < len(clean_lines.elided):
+ endline = clean_lines.elided[endlinenum]
+ # We allow a mix of whitespace and closing braces (e.g. for one-liner
+ # methods) and a single \ after the semicolon (for macros)
+ endpos = endline.find(';')
+ if not Match(r';[\s}]*(\\?)$', endline[endpos:]):
+ # Semicolon isn't the last character, there's something trailing.
+ # Output a warning if the semicolon is not contained inside
+ # a lambda expression.
+ if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$',
+ endline):
+ error(filename, linenum, 'readability/braces', 4,
+ 'If/else bodies with multiple statements require braces')
+ elif endlinenum < len(clean_lines.elided) - 1:
+ # Make sure the next line is dedented
+ next_line = clean_lines.elided[endlinenum + 1]
+ next_indent = GetIndentLevel(next_line)
+ # With ambiguous nested if statements, this will error out on the
+ # if that *doesn't* match the else, regardless of whether it's the
+ # inner one or outer one.
+ if (if_match and Match(r'\s*else\b', next_line)
+ and next_indent != if_indent):
+ error(filename, linenum, 'readability/braces', 4,
+ 'Else clause should be indented at the same level as if. '
+ 'Ambiguous nested if/else chains require braces.')
+ elif next_indent > if_indent:
+ error(filename, linenum, 'readability/braces', 4,
+ 'If/else bodies with multiple statements require braces')
+
+
+def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
+ """Looks for redundant trailing semicolon.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ line = clean_lines.elided[linenum]
+
+ # Block bodies should not be followed by a semicolon. Due to C++11
+ # brace initialization, there are more places where semicolons are
+ # required than not, so we use a whitelist approach to check these
+ # rather than a blacklist. These are the places where "};" should
+ # be replaced by just "}":
+ # 1. Some flavor of block following closing parenthesis:
+ # for (;;) {};
+ # while (...) {};
+ # switch (...) {};
+ # Function(...) {};
+ # if (...) {};
+ # if (...) else if (...) {};
+ #
+ # 2. else block:
+ # if (...) else {};
+ #
+ # 3. const member function:
+ # Function(...) const {};
+ #
+ # 4. Block following some statement:
+ # x = 42;
+ # {};
+ #
+ # 5. Block at the beginning of a function:
+ # Function(...) {
+ # {};
+ # }
+ #
+ # Note that naively checking for the preceding "{" will also match
+ # braces inside multi-dimensional arrays, but this is fine since
+ # that expression will not contain semicolons.
+ #
+ # 6. Block following another block:
+ # while (true) {}
+ # {};
+ #
+ # 7. End of namespaces:
+ # namespace {};
+ #
+ # These semicolons seems far more common than other kinds of
+ # redundant semicolons, possibly due to people converting classes
+ # to namespaces. For now we do not warn for this case.
+ #
+ # Try matching case 1 first.
+ match = Match(r'^(.*\)\s*)\{', line)
+ if match:
+ # Matched closing parenthesis (case 1). Check the token before the
+ # matching opening parenthesis, and don't warn if it looks like a
+ # macro. This avoids these false positives:
+ # - macro that defines a base class
+ # - multi-line macro that defines a base class
+ # - macro that defines the whole class-head
+ #
+ # But we still issue warnings for macros that we know are safe to
+ # warn, specifically:
+ # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P
+ # - TYPED_TEST
+ # - INTERFACE_DEF
+ # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED:
+ #
+ # We implement a whitelist of safe macros instead of a blacklist of
+ # unsafe macros, even though the latter appears less frequently in
+ # google code and would have been easier to implement. This is because
+ # the downside for getting the whitelist wrong means some extra
+ # semicolons, while the downside for getting the blacklist wrong
+ # would result in compile errors.
+ #
+ # In addition to macros, we also don't want to warn on
+ # - Compound literals
+ # - Lambdas
+ # - alignas specifier with anonymous structs
+ # - decltype
+ closing_brace_pos = match.group(1).rfind(')')
+ opening_parenthesis = ReverseCloseExpression(
+ clean_lines, linenum, closing_brace_pos)
+ if opening_parenthesis[2] > -1:
+ line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]]
+ macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix)
+ func = Match(r'^(.*\])\s*$', line_prefix)
+ if ((macro and
+ macro.group(1) not in (
+ 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST',
+ 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED',
+ 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or
+ (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or
+ Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or
+ Search(r'\bdecltype$', line_prefix) or
+ Search(r'\s+=\s*$', line_prefix)):
+ match = None
+ if (match and
+ opening_parenthesis[1] > 1 and
+ Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])):
+ # Multi-line lambda-expression
+ match = None
+
+ else:
+ # Try matching cases 2-3.
+ match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line)
+ if not match:
+ # Try matching cases 4-6. These are always matched on separate lines.
+ #
+ # Note that we can't simply concatenate the previous line to the
+ # current line and do a single match, otherwise we may output
+ # duplicate warnings for the blank line case:
+ # if (cond) {
+ # // blank line
+ # }
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ if prevline and Search(r'[;{}]\s*$', prevline):
+ match = Match(r'^(\s*)\{', line)
+
+ # Check matching closing brace
+ if match:
+ (endline, endlinenum, endpos) = CloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ if endpos > -1 and Match(r'^\s*;', endline[endpos:]):
+ # Current {} pair is eligible for semicolon check, and we have found
+ # the redundant semicolon, output warning here.
+ #
+ # Note: because we are scanning forward for opening braces, and
+ # outputting warnings for the matching closing brace, if there are
+ # nested blocks with trailing semicolons, we will get the error
+ # messages in reversed order.
+
+ # We need to check the line forward for NOLINT
+ raw_lines = clean_lines.raw_lines
+ ParseNolintSuppressions(filename, raw_lines[endlinenum-1], endlinenum-1,
+ error)
+ ParseNolintSuppressions(filename, raw_lines[endlinenum], endlinenum,
+ error)
+
+ error(filename, endlinenum, 'readability/braces', 4,
+ "You don't need a ; after a }")
+
+
+def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
+ """Look for empty loop/conditional body with only a single semicolon.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ # Search for loop keywords at the beginning of the line. Because only
+ # whitespaces are allowed before the keywords, this will also ignore most
+ # do-while-loops, since those lines should start with closing brace.
+ #
+ # We also check "if" blocks here, since an empty conditional block
+ # is likely an error.
+ line = clean_lines.elided[linenum]
+ matched = Match(r'\s*(for|while|if)\s*\(', line)
+ if matched:
+ # Find the end of the conditional expression.
+ (end_line, end_linenum, end_pos) = CloseExpression(
+ clean_lines, linenum, line.find('('))
+
+ # Output warning if what follows the condition expression is a semicolon.
+ # No warning for all other cases, including whitespace or newline, since we
+ # have a separate check for semicolons preceded by whitespace.
+ if end_pos >= 0 and Match(r';', end_line[end_pos:]):
+ if matched.group(1) == 'if':
+ error(filename, end_linenum, 'whitespace/empty_conditional_body', 5,
+ 'Empty conditional bodies should use {}')
+ else:
+ error(filename, end_linenum, 'whitespace/empty_loop_body', 5,
+ 'Empty loop bodies should use {} or continue')
+
+ # Check for if statements that have completely empty bodies (no comments)
+ # and no else clauses.
+ if end_pos >= 0 and matched.group(1) == 'if':
+ # Find the position of the opening { for the if statement.
+ # Return without logging an error if it has no brackets.
+ opening_linenum = end_linenum
+ opening_line_fragment = end_line[end_pos:]
+ # Loop until EOF or find anything that's not whitespace or opening {.
+ while not Search(r'^\s*\{', opening_line_fragment):
+ if Search(r'^(?!\s*$)', opening_line_fragment):
+ # Conditional has no brackets.
+ return
+ opening_linenum += 1
+ if opening_linenum == len(clean_lines.elided):
+ # Couldn't find conditional's opening { or any code before EOF.
+ return
+ opening_line_fragment = clean_lines.elided[opening_linenum]
+ # Set opening_line (opening_line_fragment may not be entire opening line).
+ opening_line = clean_lines.elided[opening_linenum]
+
+ # Find the position of the closing }.
+ opening_pos = opening_line_fragment.find('{')
+ if opening_linenum == end_linenum:
+ # We need to make opening_pos relative to the start of the entire line.
+ opening_pos += end_pos
+ (closing_line, closing_linenum, closing_pos) = CloseExpression(
+ clean_lines, opening_linenum, opening_pos)
+ if closing_pos < 0:
+ return
+
+ # Now construct the body of the conditional. This consists of the portion
+ # of the opening line after the {, all lines until the closing line,
+ # and the portion of the closing line before the }.
+ if (clean_lines.raw_lines[opening_linenum] !=
+ CleanseComments(clean_lines.raw_lines[opening_linenum])):
+ # Opening line ends with a comment, so conditional isn't empty.
+ return
+ if closing_linenum > opening_linenum:
+ # Opening line after the {. Ignore comments here since we checked above.
+ body = list(opening_line[opening_pos+1:])
+ # All lines until closing line, excluding closing line, with comments.
+ body.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum])
+ # Closing line before the }. Won't (and can't) have comments.
+ body.append(clean_lines.elided[closing_linenum][:closing_pos-1])
+ body = '\n'.join(body)
+ else:
+ # If statement has brackets and fits on a single line.
+ body = opening_line[opening_pos+1:closing_pos-1]
+
+ # Check if the body is empty
+ if not _EMPTY_CONDITIONAL_BODY_PATTERN.search(body):
+ return
+ # The body is empty. Now make sure there's not an else clause.
+ current_linenum = closing_linenum
+ current_line_fragment = closing_line[closing_pos:]
+ # Loop until EOF or find anything that's not whitespace or else clause.
+ while Search(r'^\s*$|^(?=\s*else)', current_line_fragment):
+ if Search(r'^(?=\s*else)', current_line_fragment):
+ # Found an else clause, so don't log an error.
+ return
+ current_linenum += 1
+ if current_linenum == len(clean_lines.elided):
+ break
+ current_line_fragment = clean_lines.elided[current_linenum]
+
+ # The body is empty and there's no else clause until EOF or other code.
+ error(filename, end_linenum, 'whitespace/empty_if_body', 4,
+ ('If statement had no body and no else clause'))
+
+
+def FindCheckMacro(line):
+ """Find a replaceable CHECK-like macro.
+
+ Args:
+ line: line to search on.
+ Returns:
+ (macro name, start position), or (None, -1) if no replaceable
+ macro is found.
+ """
+ for macro in _CHECK_MACROS:
+ i = line.find(macro)
+ if i >= 0:
+ # Find opening parenthesis. Do a regular expression match here
+ # to make sure that we are matching the expected CHECK macro, as
+ # opposed to some other macro that happens to contain the CHECK
+ # substring.
+ matched = Match(r'^(.*\b' + macro + r'\s*)\(', line)
+ if not matched:
+ continue
+ return (macro, len(matched.group(1)))
+ return (None, -1)
+
+
+def CheckCheck(filename, clean_lines, linenum, error):
+ """Checks the use of CHECK and EXPECT macros.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ # Decide the set of replacement macros that should be suggested
+ lines = clean_lines.elided
+ (check_macro, start_pos) = FindCheckMacro(lines[linenum])
+ if not check_macro:
+ return
+
+ # Find end of the boolean expression by matching parentheses
+ (last_line, end_line, end_pos) = CloseExpression(
+ clean_lines, linenum, start_pos)
+ if end_pos < 0:
+ return
+
+ # If the check macro is followed by something other than a
+ # semicolon, assume users will log their own custom error messages
+ # and don't suggest any replacements.
+ if not Match(r'\s*;', last_line[end_pos:]):
+ return
+
+ if linenum == end_line:
+ expression = lines[linenum][start_pos + 1:end_pos - 1]
+ else:
+ expression = lines[linenum][start_pos + 1:]
+ for i in xrange(linenum + 1, end_line):
+ expression += lines[i]
+ expression += last_line[0:end_pos - 1]
+
+ # Parse expression so that we can take parentheses into account.
+ # This avoids false positives for inputs like "CHECK((a < 4) == b)",
+ # which is not replaceable by CHECK_LE.
+ lhs = ''
+ rhs = ''
+ operator = None
+ while expression:
+ matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||'
+ r'==|!=|>=|>|<=|<|\()(.*)$', expression)
+ if matched:
+ token = matched.group(1)
+ if token == '(':
+ # Parenthesized operand
+ expression = matched.group(2)
+ (end, _) = FindEndOfExpressionInLine(expression, 0, ['('])
+ if end < 0:
+ return # Unmatched parenthesis
+ lhs += '(' + expression[0:end]
+ expression = expression[end:]
+ elif token in ('&&', '||'):
+ # Logical and/or operators. This means the expression
+ # contains more than one term, for example:
+ # CHECK(42 < a && a < b);
+ #
+ # These are not replaceable with CHECK_LE, so bail out early.
+ return
+ elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'):
+ # Non-relational operator
+ lhs += token
+ expression = matched.group(2)
+ else:
+ # Relational operator
+ operator = token
+ rhs = matched.group(2)
+ break
+ else:
+ # Unparenthesized operand. Instead of appending to lhs one character
+ # at a time, we do another regular expression match to consume several
+ # characters at once if possible. Trivial benchmark shows that this
+ # is more efficient when the operands are longer than a single
+ # character, which is generally the case.
+ matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression)
+ if not matched:
+ matched = Match(r'^(\s*\S)(.*)$', expression)
+ if not matched:
+ break
+ lhs += matched.group(1)
+ expression = matched.group(2)
+
+ # Only apply checks if we got all parts of the boolean expression
+ if not (lhs and operator and rhs):
+ return
+
+ # Check that rhs do not contain logical operators. We already know
+ # that lhs is fine since the loop above parses out && and ||.
+ if rhs.find('&&') > -1 or rhs.find('||') > -1:
+ return
+
+ # At least one of the operands must be a constant literal. This is
+ # to avoid suggesting replacements for unprintable things like
+ # CHECK(variable != iterator)
+ #
+ # The following pattern matches decimal, hex integers, strings, and
+ # characters (in that order).
+ lhs = lhs.strip()
+ rhs = rhs.strip()
+ match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$'
+ if Match(match_constant, lhs) or Match(match_constant, rhs):
+ # Note: since we know both lhs and rhs, we can provide a more
+ # descriptive error message like:
+ # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42)
+ # Instead of:
+ # Consider using CHECK_EQ instead of CHECK(a == b)
+ #
+ # We are still keeping the less descriptive message because if lhs
+ # or rhs gets long, the error message might become unreadable.
+ error(filename, linenum, 'readability/check', 2,
+ 'Consider using %s instead of %s(a %s b)' % (
+ _CHECK_REPLACEMENT[check_macro][operator],
+ check_macro, operator))
+
+
+def CheckAltTokens(filename, clean_lines, linenum, error):
+ """Check alternative keywords being used in boolean expressions.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Avoid preprocessor lines
+ if Match(r'^\s*#', line):
+ return
+
+ # Last ditch effort to avoid multi-line comments. This will not help
+ # if the comment started before the current line or ended after the
+ # current line, but it catches most of the false positives. At least,
+ # it provides a way to workaround this warning for people who use
+ # multi-line comments in preprocessor macros.
+ #
+ # TODO(unknown): remove this once cpplint has better support for
+ # multi-line comments.
+ if line.find('/*') >= 0 or line.find('*/') >= 0:
+ return
+
+ for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line):
+ error(filename, linenum, 'readability/alt_tokens', 2,
+ 'Use operator %s instead of %s' % (
+ _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1)))
+
+
+def GetLineWidth(line):
+ """Determines the width of the line in column positions.
+
+ Args:
+ line: A string, which may be a Unicode string.
+
+ Returns:
+ The width of the line in column positions, accounting for Unicode
+ combining characters and wide characters.
+ """
+ if isinstance(line, unicode):
+ width = 0
+ for uc in unicodedata.normalize('NFC', line):
+ if unicodedata.east_asian_width(uc) in ('W', 'F'):
+ width += 2
+ elif not unicodedata.combining(uc):
+ width += 1
+ return width
+ else:
+ return len(line)
+
+
+def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
+ error):
+ """Checks rules from the 'C++ style rules' section of cppguide.html.
+
+ Most of these rules are hard to test (naming, comment style), but we
+ do what we can. In particular we check for 2-space indents, line lengths,
+ tab usage, spaces inside code, etc.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ file_extension: The extension (without the dot) of the filename.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+
+ # Don't use "elided" lines here, otherwise we can't check commented lines.
+ # Don't want to use "raw" either, because we don't want to check inside C++11
+ # raw strings,
+ raw_lines = clean_lines.lines_without_raw_strings
+ line = raw_lines[linenum]
+ prev = raw_lines[linenum - 1] if linenum > 0 else ''
+
+ if line.find('\t') != -1:
+ error(filename, linenum, 'whitespace/tab', 1,
+ 'Tab found; better to use spaces')
+
+ # One or three blank spaces at the beginning of the line is weird; it's
+ # hard to reconcile that with 2-space indents.
+ # NOTE: here are the conditions rob pike used for his tests. Mine aren't
+ # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces
+ # if(RLENGTH > 20) complain = 0;
+ # if(match($0, " +(error|private|public|protected):")) complain = 0;
+ # if(match(prev, "&& *$")) complain = 0;
+ # if(match(prev, "\\|\\| *$")) complain = 0;
+ # if(match(prev, "[\",=><] *$")) complain = 0;
+ # if(match($0, " <<")) complain = 0;
+ # if(match(prev, " +for \\(")) complain = 0;
+ # if(prevodd && match(prevprev, " +for \\(")) complain = 0;
+ scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$'
+ classinfo = nesting_state.InnermostClass()
+ initial_spaces = 0
+ cleansed_line = clean_lines.elided[linenum]
+ while initial_spaces < len(line) and line[initial_spaces] == ' ':
+ initial_spaces += 1
+ # There are certain situations we allow one space, notably for
+ # section labels, and also lines containing multi-line raw strings.
+ # We also don't check for lines that look like continuation lines
+ # (of lines ending in double quotes, commas, equals, or angle brackets)
+ # because the rules for how to indent those are non-trivial.
+ if (not Search(r'[",=><] *$', prev) and
+ (initial_spaces == 1 or initial_spaces == 3) and
+ not Match(scope_or_label_pattern, cleansed_line) and
+ not (clean_lines.raw_lines[linenum] != line and
+ Match(r'^\s*""', line))):
+ error(filename, linenum, 'whitespace/indent', 3,
+ 'Weird number of spaces at line-start. '
+ 'Are you using a 2-space indent?')
+
+ if line and line[-1].isspace():
+ error(filename, linenum, 'whitespace/end_of_line', 4,
+ 'Line ends in whitespace. Consider deleting these extra spaces.')
+
+ # Check if the line is a header guard.
+ is_header_guard = False
+ if IsHeaderExtension(file_extension):
+ cppvar = GetHeaderGuardCPPVariable(filename)
+ if (line.startswith('#ifndef %s' % cppvar) or
+ line.startswith('#define %s' % cppvar) or
+ line.startswith('#endif // %s' % cppvar)):
+ is_header_guard = True
+ # #include lines and header guards can be long, since there's no clean way to
+ # split them.
+ #
+ # URLs can be long too. It's possible to split these, but it makes them
+ # harder to cut&paste.
+ #
+ # The "$Id:...$" comment may also get very long without it being the
+ # developers fault.
+ if (not line.startswith('#include') and not is_header_guard and
+ not Match(r'^\s*//.*http(s?)://\S*$', line) and
+ not Match(r'^\s*//\s*[^\s]*$', line) and
+ not Match(r'^// \$Id:.*#[0-9]+ \$$', line)):
+ line_width = GetLineWidth(line)
+ if line_width > _line_length:
+ error(filename, linenum, 'whitespace/line_length', 2,
+ 'Lines should be <= %i characters long' % _line_length)
+
+ if (cleansed_line.count(';') > 1 and
+ # for loops are allowed two ;'s (and may run over two lines).
+ cleansed_line.find('for') == -1 and
+ (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or
+ GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and
+ # It's ok to have many commands in a switch case that fits in 1 line
+ not ((cleansed_line.find('case ') != -1 or
+ cleansed_line.find('default:') != -1) and
+ cleansed_line.find('break;') != -1)):
+ error(filename, linenum, 'whitespace/newline', 0,
+ 'More than one command on the same line')
+
+ # Some more style checks
+ CheckBraces(filename, clean_lines, linenum, error)
+ CheckTrailingSemicolon(filename, clean_lines, linenum, error)
+ CheckEmptyBlockBody(filename, clean_lines, linenum, error)
+ CheckSpacing(filename, clean_lines, linenum, nesting_state, error)
+ CheckOperatorSpacing(filename, clean_lines, linenum, error)
+ CheckParenthesisSpacing(filename, clean_lines, linenum, error)
+ CheckCommaSpacing(filename, clean_lines, linenum, error)
+ CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error)
+ CheckSpacingForFunctionCall(filename, clean_lines, linenum, error)
+ CheckCheck(filename, clean_lines, linenum, error)
+ CheckAltTokens(filename, clean_lines, linenum, error)
+ classinfo = nesting_state.InnermostClass()
+ if classinfo:
+ CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error)
+
+
+_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$')
+# Matches the first component of a filename delimited by -s and _s. That is:
+# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo'
+# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo'
+# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo'
+# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo'
+_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+')
+
+
+def _DropCommonSuffixes(filename):
+ """Drops common suffixes like _test.cc or -inl.h from filename.
+
+ For example:
+ >>> _DropCommonSuffixes('foo/foo-inl.h')
+ 'foo/foo'
+ >>> _DropCommonSuffixes('foo/bar/foo.cc')
+ 'foo/bar/foo'
+ >>> _DropCommonSuffixes('foo/foo_internal.h')
+ 'foo/foo'
+ >>> _DropCommonSuffixes('foo/foo_unusualinternal.h')
+ 'foo/foo_unusualinternal'
+
+ Args:
+ filename: The input filename.
+
+ Returns:
+ The filename with the common suffix removed.
+ """
+ for suffix in ('test.cc', 'regtest.cc', 'unittest.cc',
+ 'inl.h', 'impl.h', 'internal.h'):
+ if (filename.endswith(suffix) and len(filename) > len(suffix) and
+ filename[-len(suffix) - 1] in ('-', '_')):
+ return filename[:-len(suffix) - 1]
+ return os.path.splitext(filename)[0]
+
+
+def _ClassifyInclude(fileinfo, include, is_system):
+ """Figures out what kind of header 'include' is.
+
+ Args:
+ fileinfo: The current file cpplint is running over. A FileInfo instance.
+ include: The path to a #included file.
+ is_system: True if the #include used <> rather than "".
+
+ Returns:
+ One of the _XXX_HEADER constants.
+
+ For example:
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True)
+ _C_SYS_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True)
+ _CPP_SYS_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False)
+ _LIKELY_MY_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'),
+ ... 'bar/foo_other_ext.h', False)
+ _POSSIBLE_MY_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False)
+ _OTHER_HEADER
+ """
+ # This is a list of all standard c++ header files, except
+ # those already checked for above.
+ is_cpp_h = include in _CPP_HEADERS
+
+ if is_system:
+ if is_cpp_h:
+ return _CPP_SYS_HEADER
+ else:
+ return _C_SYS_HEADER
+
+ # If the target file and the include we're checking share a
+ # basename when we drop common extensions, and the include
+ # lives in . , then it's likely to be owned by the target file.
+ target_dir, target_base = (
+ os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName())))
+ include_dir, include_base = os.path.split(_DropCommonSuffixes(include))
+ if target_base == include_base and (
+ include_dir == target_dir or
+ include_dir == os.path.normpath(target_dir + '/../public')):
+ return _LIKELY_MY_HEADER
+
+ # If the target and include share some initial basename
+ # component, it's possible the target is implementing the
+ # include, so it's allowed to be first, but we'll never
+ # complain if it's not there.
+ target_first_component = _RE_FIRST_COMPONENT.match(target_base)
+ include_first_component = _RE_FIRST_COMPONENT.match(include_base)
+ if (target_first_component and include_first_component and
+ target_first_component.group(0) ==
+ include_first_component.group(0)):
+ return _POSSIBLE_MY_HEADER
+
+ return _OTHER_HEADER
+
+
+
+def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
+ """Check rules that are applicable to #include lines.
+
+ Strings on #include lines are NOT removed from elided line, to make
+ certain tasks easier. However, to prevent false positives, checks
+ applicable to #include lines in CheckLanguage must be put here.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ include_state: An _IncludeState instance in which the headers are inserted.
+ error: The function to call with any errors found.
+ """
+ fileinfo = FileInfo(filename)
+ line = clean_lines.lines[linenum]
+
+ # "include" should use the new style "foo/bar.h" instead of just "bar.h"
+ # Only do this check if the included header follows google naming
+ # conventions. If not, assume that it's a 3rd party API that
+ # requires special include conventions.
+ #
+ # We also make an exception for Lua headers, which follow google
+ # naming convention but not the include convention.
+ match = Match(r'#include\s*"([^/]+\.h)"', line)
+ if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)):
+ error(filename, linenum, 'build/include', 4,
+ 'Include the directory when naming .h files')
+
+ # we shouldn't include a file more than once. actually, there are a
+ # handful of instances where doing so is okay, but in general it's
+ # not.
+ match = _RE_PATTERN_INCLUDE.search(line)
+ if match:
+ include = match.group(2)
+ is_system = (match.group(1) == '<')
+ duplicate_line = include_state.FindHeader(include)
+ if duplicate_line >= 0:
+ error(filename, linenum, 'build/include', 4,
+ '"%s" already included at %s:%s' %
+ (include, filename, duplicate_line))
+ elif (include.endswith('.cc') and
+ os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)):
+ error(filename, linenum, 'build/include', 4,
+ 'Do not include .cc files from other packages')
+ elif not _THIRD_PARTY_HEADERS_PATTERN.match(include):
+ include_state.include_list[-1].append((include, linenum))
+
+ # We want to ensure that headers appear in the right order:
+ # 1) for foo.cc, foo.h (preferred location)
+ # 2) c system files
+ # 3) cpp system files
+ # 4) for foo.cc, foo.h (deprecated location)
+ # 5) other google headers
+ #
+ # We classify each include statement as one of those 5 types
+ # using a number of techniques. The include_state object keeps
+ # track of the highest type seen, and complains if we see a
+ # lower type after that.
+ error_message = include_state.CheckNextIncludeOrder(
+ _ClassifyInclude(fileinfo, include, is_system))
+ if error_message:
+ error(filename, linenum, 'build/include_order', 4,
+ '%s. Should be: %s.h, c system, c++ system, other.' %
+ (error_message, fileinfo.BaseName()))
+ canonical_include = include_state.CanonicalizeAlphabeticalOrder(include)
+ if not include_state.IsInAlphabeticalOrder(
+ clean_lines, linenum, canonical_include):
+ error(filename, linenum, 'build/include_alpha', 4,
+ 'Include "%s" not in alphabetical order' % include)
+ include_state.SetLastHeader(canonical_include)
+
+
+
+def _GetTextInside(text, start_pattern):
+ r"""Retrieves all the text between matching open and close parentheses.
+
+ Given a string of lines and a regular expression string, retrieve all the text
+ following the expression and between opening punctuation symbols like
+ (, [, or {, and the matching close-punctuation symbol. This properly nested
+ occurrences of the punctuations, so for the text like
+ printf(a(), b(c()));
+ a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'.
+ start_pattern must match string having an open punctuation symbol at the end.
+
+ Args:
+ text: The lines to extract text. Its comments and strings must be elided.
+ It can be single line and can span multiple lines.
+ start_pattern: The regexp string indicating where to start extracting
+ the text.
+ Returns:
+ The extracted text.
+ None if either the opening string or ending punctuation could not be found.
+ """
+ # TODO(unknown): Audit cpplint.py to see what places could be profitably
+ # rewritten to use _GetTextInside (and use inferior regexp matching today).
+
+ # Give opening punctuations to get the matching close-punctuations.
+ matching_punctuation = {'(': ')', '{': '}', '[': ']'}
+ closing_punctuation = set(matching_punctuation.itervalues())
+
+ # Find the position to start extracting text.
+ match = re.search(start_pattern, text, re.M)
+ if not match: # start_pattern not found in text.
+ return None
+ start_position = match.end(0)
+
+ assert start_position > 0, (
+ 'start_pattern must ends with an opening punctuation.')
+ assert text[start_position - 1] in matching_punctuation, (
+ 'start_pattern must ends with an opening punctuation.')
+ # Stack of closing punctuations we expect to have in text after position.
+ punctuation_stack = [matching_punctuation[text[start_position - 1]]]
+ position = start_position
+ while punctuation_stack and position < len(text):
+ if text[position] == punctuation_stack[-1]:
+ punctuation_stack.pop()
+ elif text[position] in closing_punctuation:
+ # A closing punctuation without matching opening punctuations.
+ return None
+ elif text[position] in matching_punctuation:
+ punctuation_stack.append(matching_punctuation[text[position]])
+ position += 1
+ if punctuation_stack:
+ # Opening punctuations left without matching close-punctuations.
+ return None
+ # punctuations match.
+ return text[start_position:position - 1]
+
+
+# Patterns for matching call-by-reference parameters.
+#
+# Supports nested templates up to 2 levels deep using this messy pattern:
+# < (?: < (?: < [^<>]*
+# >
+# | [^<>] )*
+# >
+# | [^<>] )*
+# >
+_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]*
+_RE_PATTERN_TYPE = (
+ r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?'
+ r'(?:\w|'
+ r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|'
+ r'::)+')
+# A call-by-reference parameter ends with '& identifier'.
+_RE_PATTERN_REF_PARAM = re.compile(
+ r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*'
+ r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]')
+# A call-by-const-reference parameter either ends with 'const& identifier'
+# or looks like 'const type& identifier' when 'type' is atomic.
+_RE_PATTERN_CONST_REF_PARAM = (
+ r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT +
+ r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')')
+# Stream types.
+_RE_PATTERN_REF_STREAM_PARAM = (
+ r'(?:.*stream\s*&\s*' + _RE_PATTERN_IDENT + r')')
+
+
+def CheckLanguage(filename, clean_lines, linenum, file_extension,
+ include_state, nesting_state, error):
+ """Checks rules from the 'C++ language rules' section of cppguide.html.
+
+ Some of these rules are hard to test (function overloading, using
+ uint32 inappropriately), but we do the best we can.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ file_extension: The extension (without the dot) of the filename.
+ include_state: An _IncludeState instance in which the headers are inserted.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ # If the line is empty or consists of entirely a comment, no need to
+ # check it.
+ line = clean_lines.elided[linenum]
+ if not line:
+ return
+
+ match = _RE_PATTERN_INCLUDE.search(line)
+ if match:
+ CheckIncludeLine(filename, clean_lines, linenum, include_state, error)
+ return
+
+ # Reset include state across preprocessor directives. This is meant
+ # to silence warnings for conditional includes.
+ match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line)
+ if match:
+ include_state.ResetSection(match.group(1))
+
+ # Make Windows paths like Unix.
+ fullname = os.path.abspath(filename).replace('\\', '/')
+
+ # Perform other checks now that we are sure that this is not an include line
+ CheckCasts(filename, clean_lines, linenum, error)
+ CheckGlobalStatic(filename, clean_lines, linenum, error)
+ CheckPrintf(filename, clean_lines, linenum, error)
+
+ if IsHeaderExtension(file_extension):
+ # TODO(unknown): check that 1-arg constructors are explicit.
+ # How to tell it's a constructor?
+ # (handled in CheckForNonStandardConstructs for now)
+ # TODO(unknown): check that classes declare or disable copy/assign
+ # (level 1 error)
+ pass
+
+ # Check if people are using the verboten C basic types. The only exception
+ # we regularly allow is "unsigned short port" for port.
+ if Search(r'\bshort port\b', line):
+ if not Search(r'\bunsigned short port\b', line):
+ error(filename, linenum, 'runtime/int', 4,
+ 'Use "unsigned short" for ports, not "short"')
+ else:
+ match = Search(r'\b(short|long(?! +double)|long long)\b', line)
+ if match:
+ error(filename, linenum, 'runtime/int', 4,
+ 'Use int16/int64/etc, rather than the C type %s' % match.group(1))
+
+ # Check if some verboten operator overloading is going on
+ # TODO(unknown): catch out-of-line unary operator&:
+ # class X {};
+ # int operator&(const X& x) { return 42; } // unary operator&
+ # The trick is it's hard to tell apart from binary operator&:
+ # class Y { int operator&(const Y& x) { return 23; } }; // binary operator&
+ if Search(r'\boperator\s*&\s*\(\s*\)', line):
+ error(filename, linenum, 'runtime/operator', 4,
+ 'Unary operator& is dangerous. Do not use it.')
+
+ # Check for suspicious usage of "if" like
+ # } if (a == b) {
+ if Search(r'\}\s*if\s*\(', line):
+ error(filename, linenum, 'readability/braces', 4,
+ 'Did you mean "else if"? If not, start a new line for "if".')
+
+ # Check for potential format string bugs like printf(foo).
+ # We constrain the pattern not to pick things like DocidForPrintf(foo).
+ # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str())
+ # TODO(unknown): Catch the following case. Need to change the calling
+ # convention of the whole function to process multiple line to handle it.
+ # printf(
+ # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line);
+ printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(')
+ if printf_args:
+ match = Match(r'([\w.\->()]+)$', printf_args)
+ if match and match.group(1) != '__VA_ARGS__':
+ function_name = re.search(r'\b((?:string)?printf)\s*\(',
+ line, re.I).group(1)
+ error(filename, linenum, 'runtime/printf', 4,
+ 'Potential format string bug. Do %s("%%s", %s) instead.'
+ % (function_name, match.group(1)))
+
+ # Check for potential memset bugs like memset(buf, sizeof(buf), 0).
+ match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line)
+ if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)):
+ error(filename, linenum, 'runtime/memset', 4,
+ 'Did you mean "memset(%s, 0, %s)"?'
+ % (match.group(1), match.group(2)))
+
+ if Search(r'\busing namespace\b', line):
+ error(filename, linenum, 'build/namespaces', 5,
+ 'Do not use namespace using-directives. '
+ 'Use using-declarations instead.')
+
+ # Detect variable-length arrays.
+ match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line)
+ if (match and match.group(2) != 'return' and match.group(2) != 'delete' and
+ match.group(3).find(']') == -1):
+ # Split the size using space and arithmetic operators as delimiters.
+ # If any of the resulting tokens are not compile time constants then
+ # report the error.
+ tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3))
+ is_const = True
+ skip_next = False
+ for tok in tokens:
+ if skip_next:
+ skip_next = False
+ continue
+
+ if Search(r'sizeof\(.+\)', tok): continue
+ if Search(r'arraysize\(\w+\)', tok): continue
+
+ tok = tok.lstrip('(')
+ tok = tok.rstrip(')')
+ if not tok: continue
+ if Match(r'\d+', tok): continue
+ if Match(r'0[xX][0-9a-fA-F]+', tok): continue
+ if Match(r'k[A-Z0-9]\w*', tok): continue
+ if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue
+ if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue
+ # A catch all for tricky sizeof cases, including 'sizeof expression',
+ # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)'
+ # requires skipping the next token because we split on ' ' and '*'.
+ if tok.startswith('sizeof'):
+ skip_next = True
+ continue
+ is_const = False
+ break
+ if not is_const:
+ error(filename, linenum, 'runtime/arrays', 1,
+ 'Do not use variable-length arrays. Use an appropriately named '
+ "('k' followed by CamelCase) compile-time constant for the size.")
+
+ # Check for use of unnamed namespaces in header files. Registration
+ # macros are typically OK, so we allow use of "namespace {" on lines
+ # that end with backslashes.
+ if (IsHeaderExtension(file_extension)
+ and Search(r'\bnamespace\s*{', line)
+ and line[-1] != '\\'):
+ error(filename, linenum, 'build/namespaces', 4,
+ 'Do not use unnamed namespaces in header files. See '
+ 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'
+ ' for more information.')
+
+
+def CheckGlobalStatic(filename, clean_lines, linenum, error):
+ """Check for unsafe global or static objects.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Match two lines at a time to support multiline declarations
+ if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line):
+ line += clean_lines.elided[linenum + 1].strip()
+
+ # Check for people declaring static/global STL strings at the top level.
+ # This is dangerous because the C++ language does not guarantee that
+ # globals with constructors are initialized before the first access, and
+ # also because globals can be destroyed when some threads are still running.
+ # TODO(unknown): Generalize this to also find static unique_ptr instances.
+ # TODO(unknown): File bugs for clang-tidy to find these.
+ match = Match(
+ r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +'
+ r'([a-zA-Z0-9_:]+)\b(.*)',
+ line)
+
+ # Remove false positives:
+ # - String pointers (as opposed to values).
+ # string *pointer
+ # const string *pointer
+ # string const *pointer
+ # string *const pointer
+ #
+ # - Functions and template specializations.
+ # string Function<Type>(...
+ # string Class<Type>::Method(...
+ #
+ # - Operators. These are matched separately because operator names
+ # cross non-word boundaries, and trying to match both operators
+ # and functions at the same time would decrease accuracy of
+ # matching identifiers.
+ # string Class::operator*()
+ if (match and
+ not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and
+ not Search(r'\boperator\W', line) and
+ not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))):
+ if Search(r'\bconst\b', line):
+ error(filename, linenum, 'runtime/string', 4,
+ 'For a static/global string constant, use a C style string '
+ 'instead: "%schar%s %s[]".' %
+ (match.group(1), match.group(2) or '', match.group(3)))
+ else:
+ error(filename, linenum, 'runtime/string', 4,
+ 'Static/global string variables are not permitted.')
+
+ if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or
+ Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)):
+ error(filename, linenum, 'runtime/init', 4,
+ 'You seem to be initializing a member variable with itself.')
+
+
+def CheckPrintf(filename, clean_lines, linenum, error):
+ """Check for printf related issues.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # When snprintf is used, the second argument shouldn't be a literal.
+ match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line)
+ if match and match.group(2) != '0':
+ # If 2nd arg is zero, snprintf is used to calculate size.
+ error(filename, linenum, 'runtime/printf', 3,
+ 'If you can, use sizeof(%s) instead of %s as the 2nd arg '
+ 'to snprintf.' % (match.group(1), match.group(2)))
+
+ # Check if some verboten C functions are being used.
+ if Search(r'\bsprintf\s*\(', line):
+ error(filename, linenum, 'runtime/printf', 5,
+ 'Never use sprintf. Use snprintf instead.')
+ match = Search(r'\b(strcpy|strcat)\s*\(', line)
+ if match:
+ error(filename, linenum, 'runtime/printf', 4,
+ 'Almost always, snprintf is better than %s' % match.group(1))
+
+
+def IsDerivedFunction(clean_lines, linenum):
+ """Check if current line contains an inherited function.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ Returns:
+ True if current line contains a function with "override"
+ virt-specifier.
+ """
+ # Scan back a few lines for start of current function
+ for i in xrange(linenum, max(-1, linenum - 10), -1):
+ match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i])
+ if match:
+ # Look for "override" after the matching closing parenthesis
+ line, _, closing_paren = CloseExpression(
+ clean_lines, i, len(match.group(1)))
+ return (closing_paren >= 0 and
+ Search(r'\boverride\b', line[closing_paren:]))
+ return False
+
+
+def IsOutOfLineMethodDefinition(clean_lines, linenum):
+ """Check if current line contains an out-of-line method definition.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ Returns:
+ True if current line contains an out-of-line method definition.
+ """
+ # Scan back a few lines for start of current function
+ for i in xrange(linenum, max(-1, linenum - 10), -1):
+ if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]):
+ return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None
+ return False
+
+
+def IsInitializerList(clean_lines, linenum):
+ """Check if current line is inside constructor initializer list.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ Returns:
+ True if current line appears to be inside constructor initializer
+ list, False otherwise.
+ """
+ for i in xrange(linenum, 1, -1):
+ line = clean_lines.elided[i]
+ if i == linenum:
+ remove_function_body = Match(r'^(.*)\{\s*$', line)
+ if remove_function_body:
+ line = remove_function_body.group(1)
+
+ if Search(r'\s:\s*\w+[({]', line):
+ # A lone colon tend to indicate the start of a constructor
+ # initializer list. It could also be a ternary operator, which
+ # also tend to appear in constructor initializer lists as
+ # opposed to parameter lists.
+ return True
+ if Search(r'\}\s*,\s*$', line):
+ # A closing brace followed by a comma is probably the end of a
+ # brace-initialized member in constructor initializer list.
+ return True
+ if Search(r'[{};]\s*$', line):
+ # Found one of the following:
+ # - A closing brace or semicolon, probably the end of the previous
+ # function.
+ # - An opening brace, probably the start of current class or namespace.
+ #
+ # Current line is probably not inside an initializer list since
+ # we saw one of those things without seeing the starting colon.
+ return False
+
+ # Got to the beginning of the file without seeing the start of
+ # constructor initializer list.
+ return False
+
+
+def CheckForNonConstReference(filename, clean_lines, linenum,
+ nesting_state, error):
+ """Check for non-const references.
+
+ Separate from CheckLanguage since it scans backwards from current
+ line, instead of scanning forward.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ # Do nothing if there is no '&' on current line.
+ line = clean_lines.elided[linenum]
+ if '&' not in line:
+ return
+
+ # If a function is inherited, current function doesn't have much of
+ # a choice, so any non-const references should not be blamed on
+ # derived function.
+ if IsDerivedFunction(clean_lines, linenum):
+ return
+
+ # Don't warn on out-of-line method definitions, as we would warn on the
+ # in-line declaration, if it isn't marked with 'override'.
+ if IsOutOfLineMethodDefinition(clean_lines, linenum):
+ return
+
+ # Long type names may be broken across multiple lines, usually in one
+ # of these forms:
+ # LongType
+ # ::LongTypeContinued &identifier
+ # LongType::
+ # LongTypeContinued &identifier
+ # LongType<
+ # ...>::LongTypeContinued &identifier
+ #
+ # If we detected a type split across two lines, join the previous
+ # line to current line so that we can match const references
+ # accordingly.
+ #
+ # Note that this only scans back one line, since scanning back
+ # arbitrary number of lines would be expensive. If you have a type
+ # that spans more than 2 lines, please use a typedef.
+ if linenum > 1:
+ previous = None
+ if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line):
+ # previous_line\n + ::current_line
+ previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$',
+ clean_lines.elided[linenum - 1])
+ elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line):
+ # previous_line::\n + current_line
+ previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$',
+ clean_lines.elided[linenum - 1])
+ if previous:
+ line = previous.group(1) + line.lstrip()
+ else:
+ # Check for templated parameter that is split across multiple lines
+ endpos = line.rfind('>')
+ if endpos > -1:
+ (_, startline, startpos) = ReverseCloseExpression(
+ clean_lines, linenum, endpos)
+ if startpos > -1 and startline < linenum:
+ # Found the matching < on an earlier line, collect all
+ # pieces up to current line.
+ line = ''
+ for i in xrange(startline, linenum + 1):
+ line += clean_lines.elided[i].strip()
+
+ # Check for non-const references in function parameters. A single '&' may
+ # found in the following places:
+ # inside expression: binary & for bitwise AND
+ # inside expression: unary & for taking the address of something
+ # inside declarators: reference parameter
+ # We will exclude the first two cases by checking that we are not inside a
+ # function body, including one that was just introduced by a trailing '{'.
+ # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare].
+ if (nesting_state.previous_stack_top and
+ not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or
+ isinstance(nesting_state.previous_stack_top, _NamespaceInfo))):
+ # Not at toplevel, not within a class, and not within a namespace
+ return
+
+ # Avoid initializer lists. We only need to scan back from the
+ # current line for something that starts with ':'.
+ #
+ # We don't need to check the current line, since the '&' would
+ # appear inside the second set of parentheses on the current line as
+ # opposed to the first set.
+ if linenum > 0:
+ for i in xrange(linenum - 1, max(0, linenum - 10), -1):
+ previous_line = clean_lines.elided[i]
+ if not Search(r'[),]\s*$', previous_line):
+ break
+ if Match(r'^\s*:\s+\S', previous_line):
+ return
+
+ # Avoid preprocessors
+ if Search(r'\\\s*$', line):
+ return
+
+ # Avoid constructor initializer lists
+ if IsInitializerList(clean_lines, linenum):
+ return
+
+ # We allow non-const references in a few standard places, like functions
+ # called "swap()" or iostream operators like "<<" or ">>". Do not check
+ # those function parameters.
+ #
+ # We also accept & in static_assert, which looks like a function but
+ # it's actually a declaration expression.
+ whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|'
+ r'operator\s*[<>][<>]|'
+ r'static_assert|COMPILE_ASSERT'
+ r')\s*\(')
+ if Search(whitelisted_functions, line):
+ return
+ elif not Search(r'\S+\([^)]*$', line):
+ # Don't see a whitelisted function on this line. Actually we
+ # didn't see any function name on this line, so this is likely a
+ # multi-line parameter list. Try a bit harder to catch this case.
+ for i in xrange(2):
+ if (linenum > i and
+ Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])):
+ return
+
+ decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body
+ for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls):
+ if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and
+ not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)):
+ error(filename, linenum, 'runtime/references', 2,
+ 'Is this a non-const reference? '
+ 'If so, make const or use a pointer: ' +
+ ReplaceAll(' *<', '<', parameter))
+
+
+def CheckCasts(filename, clean_lines, linenum, error):
+ """Various cast related checks.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Check to see if they're using an conversion function cast.
+ # I just try to capture the most common basic types, though there are more.
+ # Parameterless conversion functions, such as bool(), are allowed as they are
+ # probably a member operator declaration or default constructor.
+ match = Search(
+ r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b'
+ r'(int|float|double|bool|char|int32|uint32|int64|uint64)'
+ r'(\([^)].*)', line)
+ expecting_function = ExpectingFunctionArgs(clean_lines, linenum)
+ if match and not expecting_function:
+ matched_type = match.group(2)
+
+ # matched_new_or_template is used to silence two false positives:
+ # - New operators
+ # - Template arguments with function types
+ #
+ # For template arguments, we match on types immediately following
+ # an opening bracket without any spaces. This is a fast way to
+ # silence the common case where the function type is the first
+ # template argument. False negative with less-than comparison is
+ # avoided because those operators are usually followed by a space.
+ #
+ # function<double(double)> // bracket + no space = false positive
+ # value < double(42) // bracket + space = true positive
+ matched_new_or_template = match.group(1)
+
+ # Avoid arrays by looking for brackets that come after the closing
+ # parenthesis.
+ if Match(r'\([^()]+\)\s*\[', match.group(3)):
+ return
+
+ # Other things to ignore:
+ # - Function pointers
+ # - Casts to pointer types
+ # - Placement new
+ # - Alias declarations
+ matched_funcptr = match.group(3)
+ if (matched_new_or_template is None and
+ not (matched_funcptr and
+ (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(',
+ matched_funcptr) or
+ matched_funcptr.startswith('(*)'))) and
+ not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and
+ not Search(r'new\(\S+\)\s*' + matched_type, line)):
+ error(filename, linenum, 'readability/casting', 4,
+ 'Using deprecated casting style. '
+ 'Use static_cast<%s>(...) instead' %
+ matched_type)
+
+ if not expecting_function:
+ CheckCStyleCast(filename, clean_lines, linenum, 'static_cast',
+ r'\((int|float|double|bool|char|u?int(16|32|64))\)', error)
+
+ # This doesn't catch all cases. Consider (const char * const)"hello".
+ #
+ # (char *) "foo" should always be a const_cast (reinterpret_cast won't
+ # compile).
+ if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast',
+ r'\((char\s?\*+\s?)\)\s*"', error):
+ pass
+ else:
+ # Check pointer casts for other than string constants
+ CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast',
+ r'\((\w+\s?\*+\s?)\)', error)
+
+ # In addition, we look for people taking the address of a cast. This
+ # is dangerous -- casts can assign to temporaries, so the pointer doesn't
+ # point where you think.
+ #
+ # Some non-identifier character is required before the '&' for the
+ # expression to be recognized as a cast. These are casts:
+ # expression = &static_cast<int*>(temporary());
+ # function(&(int*)(temporary()));
+ #
+ # This is not a cast:
+ # reference_type&(int* function_param);
+ match = Search(
+ r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|'
+ r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line)
+ if match:
+ # Try a better error message when the & is bound to something
+ # dereferenced by the casted pointer, as opposed to the casted
+ # pointer itself.
+ parenthesis_error = False
+ match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line)
+ if match:
+ _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1)))
+ if x1 >= 0 and clean_lines.elided[y1][x1] == '(':
+ _, y2, x2 = CloseExpression(clean_lines, y1, x1)
+ if x2 >= 0:
+ extended_line = clean_lines.elided[y2][x2:]
+ if y2 < clean_lines.NumLines() - 1:
+ extended_line += clean_lines.elided[y2 + 1]
+ if Match(r'\s*(?:->|\[)', extended_line):
+ parenthesis_error = True
+
+ if parenthesis_error:
+ error(filename, linenum, 'readability/casting', 4,
+ ('Are you taking an address of something dereferenced '
+ 'from a cast? Wrapping the dereferenced expression in '
+ 'parentheses will make the binding more obvious'))
+ else:
+ error(filename, linenum, 'runtime/casting', 4,
+ ('Are you taking an address of a cast? '
+ 'This is dangerous: could be a temp var. '
+ 'Take the address before doing the cast, rather than after'))
+
+
+def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error):
+ """Checks for a C-style cast by looking for the pattern.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ cast_type: The string for the C++ cast to recommend. This is either
+ reinterpret_cast, static_cast, or const_cast, depending.
+ pattern: The regular expression used to find C-style casts.
+ error: The function to call with any errors found.
+
+ Returns:
+ True if an error was emitted.
+ False otherwise.
+ """
+ line = clean_lines.elided[linenum]
+ match = Search(pattern, line)
+ if not match:
+ return False
+
+ # Exclude lines with keywords that tend to look like casts
+ context = line[0:match.start(1) - 1]
+ if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context):
+ return False
+
+ # Try expanding current context to see if we one level of
+ # parentheses inside a macro.
+ if linenum > 0:
+ for i in xrange(linenum - 1, max(0, linenum - 5), -1):
+ context = clean_lines.elided[i] + context
+ if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context):
+ return False
+
+ # operator++(int) and operator--(int)
+ if context.endswith(' operator++') or context.endswith(' operator--'):
+ return False
+
+ # A single unnamed argument for a function tends to look like old style cast.
+ # If we see those, don't issue warnings for deprecated casts.
+ remainder = line[match.end(0):]
+ if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)',
+ remainder):
+ return False
+
+ # At this point, all that should be left is actual casts.
+ error(filename, linenum, 'readability/casting', 4,
+ 'Using C-style cast. Use %s<%s>(...) instead' %
+ (cast_type, match.group(1)))
+
+ return True
+
+
+def ExpectingFunctionArgs(clean_lines, linenum):
+ """Checks whether where function type arguments are expected.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+
+ Returns:
+ True if the line at 'linenum' is inside something that expects arguments
+ of function types.
+ """
+ line = clean_lines.elided[linenum]
+ return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or
+ (linenum >= 2 and
+ (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$',
+ clean_lines.elided[linenum - 1]) or
+ Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$',
+ clean_lines.elided[linenum - 2]) or
+ Search(r'\bstd::m?function\s*\<\s*$',
+ clean_lines.elided[linenum - 1]))))
+
+
+_HEADERS_CONTAINING_TEMPLATES = (
+ ('<deque>', ('deque',)),
+ ('<functional>', ('unary_function', 'binary_function',
+ 'plus', 'minus', 'multiplies', 'divides', 'modulus',
+ 'negate',
+ 'equal_to', 'not_equal_to', 'greater', 'less',
+ 'greater_equal', 'less_equal',
+ 'logical_and', 'logical_or', 'logical_not',
+ 'unary_negate', 'not1', 'binary_negate', 'not2',
+ 'bind1st', 'bind2nd',
+ 'pointer_to_unary_function',
+ 'pointer_to_binary_function',
+ 'ptr_fun',
+ 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t',
+ 'mem_fun_ref_t',
+ 'const_mem_fun_t', 'const_mem_fun1_t',
+ 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t',
+ 'mem_fun_ref',
+ )),
+ ('<limits>', ('numeric_limits',)),
+ ('<list>', ('list',)),
+ ('<map>', ('map', 'multimap',)),
+ ('<memory>', ('allocator', 'make_shared', 'make_unique', 'shared_ptr',
+ 'unique_ptr', 'weak_ptr')),
+ ('<queue>', ('queue', 'priority_queue',)),
+ ('<set>', ('set', 'multiset',)),
+ ('<stack>', ('stack',)),
+ ('<string>', ('char_traits', 'basic_string',)),
+ ('<tuple>', ('tuple',)),
+ ('<unordered_map>', ('unordered_map', 'unordered_multimap')),
+ ('<unordered_set>', ('unordered_set', 'unordered_multiset')),
+ ('<utility>', ('pair',)),
+ ('<vector>', ('vector',)),
+
+ # gcc extensions.
+ # Note: std::hash is their hash, ::hash is our hash
+ ('<hash_map>', ('hash_map', 'hash_multimap',)),
+ ('<hash_set>', ('hash_set', 'hash_multiset',)),
+ ('<slist>', ('slist',)),
+ )
+
+_HEADERS_MAYBE_TEMPLATES = (
+ ('<algorithm>', ('copy', 'max', 'min', 'min_element', 'sort',
+ 'transform',
+ )),
+ ('<utility>', ('forward', 'make_pair', 'move', 'swap')),
+ )
+
+_RE_PATTERN_STRING = re.compile(r'\bstring\b')
+
+_re_pattern_headers_maybe_templates = []
+for _header, _templates in _HEADERS_MAYBE_TEMPLATES:
+ for _template in _templates:
+ # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or
+ # type::max().
+ _re_pattern_headers_maybe_templates.append(
+ (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'),
+ _template,
+ _header))
+
+# Other scripts may reach in and modify this pattern.
+_re_pattern_templates = []
+for _header, _templates in _HEADERS_CONTAINING_TEMPLATES:
+ for _template in _templates:
+ _re_pattern_templates.append(
+ (re.compile(r'(\<|\b)' + _template + r'\s*\<'),
+ _template + '<>',
+ _header))
+
+
+def FilesBelongToSameModule(filename_cc, filename_h):
+ """Check if these two filenames belong to the same module.
+
+ The concept of a 'module' here is a as follows:
+ foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the
+ same 'module' if they are in the same directory.
+ some/path/public/xyzzy and some/path/internal/xyzzy are also considered
+ to belong to the same module here.
+
+ If the filename_cc contains a longer path than the filename_h, for example,
+ '/absolute/path/to/base/sysinfo.cc', and this file would include
+ 'base/sysinfo.h', this function also produces the prefix needed to open the
+ header. This is used by the caller of this function to more robustly open the
+ header file. We don't have access to the real include paths in this context,
+ so we need this guesswork here.
+
+ Known bugs: tools/base/bar.cc and base/bar.h belong to the same module
+ according to this implementation. Because of this, this function gives
+ some false positives. This should be sufficiently rare in practice.
+
+ Args:
+ filename_cc: is the path for the .cc file
+ filename_h: is the path for the header path
+
+ Returns:
+ Tuple with a bool and a string:
+ bool: True if filename_cc and filename_h belong to the same module.
+ string: the additional prefix needed to open the header file.
+ """
+
+ fileinfo = FileInfo(filename_cc)
+ if not fileinfo.IsSource():
+ return (False, '')
+ filename_cc = filename_cc[:-len(fileinfo.Extension())]
+ matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo.BaseName())
+ if matched_test_suffix:
+ filename_cc = filename_cc[:-len(matched_test_suffix.group(1))]
+ filename_cc = filename_cc.replace('/public/', '/')
+ filename_cc = filename_cc.replace('/internal/', '/')
+
+ if not filename_h.endswith('.h'):
+ return (False, '')
+ filename_h = filename_h[:-len('.h')]
+ if filename_h.endswith('-inl'):
+ filename_h = filename_h[:-len('-inl')]
+ filename_h = filename_h.replace('/public/', '/')
+ filename_h = filename_h.replace('/internal/', '/')
+
+ files_belong_to_same_module = filename_cc.endswith(filename_h)
+ common_path = ''
+ if files_belong_to_same_module:
+ common_path = filename_cc[:-len(filename_h)]
+ return files_belong_to_same_module, common_path
+
+
+def UpdateIncludeState(filename, include_dict, io=codecs):
+ """Fill up the include_dict with new includes found from the file.
+
+ Args:
+ filename: the name of the header to read.
+ include_dict: a dictionary in which the headers are inserted.
+ io: The io factory to use to read the file. Provided for testability.
+
+ Returns:
+ True if a header was successfully added. False otherwise.
+ """
+ headerfile = None
+ try:
+ headerfile = io.open(filename, 'r', 'utf8', 'replace')
+ except IOError:
+ return False
+ linenum = 0
+ for line in headerfile:
+ linenum += 1
+ clean_line = CleanseComments(line)
+ match = _RE_PATTERN_INCLUDE.search(clean_line)
+ if match:
+ include = match.group(2)
+ include_dict.setdefault(include, linenum)
+ return True
+
+
+def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
+ io=codecs):
+ """Reports for missing stl includes.
+
+ This function will output warnings to make sure you are including the headers
+ necessary for the stl containers and functions that you use. We only give one
+ reason to include a header. For example, if you use both equal_to<> and
+ less<> in a .h file, only one (the latter in the file) of these will be
+ reported as a reason to include the <functional>.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ include_state: An _IncludeState instance.
+ error: The function to call with any errors found.
+ io: The IO factory to use to read the header file. Provided for unittest
+ injection.
+ """
+ required = {} # A map of header name to linenumber and the template entity.
+ # Example of required: { '<functional>': (1219, 'less<>') }
+
+ for linenum in xrange(clean_lines.NumLines()):
+ line = clean_lines.elided[linenum]
+ if not line or line[0] == '#':
+ continue
+
+ # String is special -- it is a non-templatized type in STL.
+ matched = _RE_PATTERN_STRING.search(line)
+ if matched:
+ # Don't warn about strings in non-STL namespaces:
+ # (We check only the first match per line; good enough.)
+ prefix = line[:matched.start()]
+ if prefix.endswith('std::') or not prefix.endswith('::'):
+ required['<string>'] = (linenum, 'string')
+
+ for pattern, template, header in _re_pattern_headers_maybe_templates:
+ if pattern.search(line):
+ required[header] = (linenum, template)
+
+ # The following function is just a speed up, no semantics are changed.
+ if not '<' in line: # Reduces the cpu time usage by skipping lines.
+ continue
+
+ for pattern, template, header in _re_pattern_templates:
+ matched = pattern.search(line)
+ if matched:
+ # Don't warn about IWYU in non-STL namespaces:
+ # (We check only the first match per line; good enough.)
+ prefix = line[:matched.start()]
+ if prefix.endswith('std::') or not prefix.endswith('::'):
+ required[header] = (linenum, template)
+
+ # The policy is that if you #include something in foo.h you don't need to
+ # include it again in foo.cc. Here, we will look at possible includes.
+ # Let's flatten the include_state include_list and copy it into a dictionary.
+ include_dict = dict([item for sublist in include_state.include_list
+ for item in sublist])
+
+ # Did we find the header for this file (if any) and successfully load it?
+ header_found = False
+
+ # Use the absolute path so that matching works properly.
+ abs_filename = FileInfo(filename).FullName()
+
+ # For Emacs's flymake.
+ # If cpplint is invoked from Emacs's flymake, a temporary file is generated
+ # by flymake and that file name might end with '_flymake.cc'. In that case,
+ # restore original file name here so that the corresponding header file can be
+ # found.
+ # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h'
+ # instead of 'foo_flymake.h'
+ abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename)
+
+ # include_dict is modified during iteration, so we iterate over a copy of
+ # the keys.
+ header_keys = include_dict.keys()
+ for header in header_keys:
+ (same_module, common_path) = FilesBelongToSameModule(abs_filename, header)
+ fullpath = common_path + header
+ if same_module and UpdateIncludeState(fullpath, include_dict, io):
+ header_found = True
+
+ # If we can't find the header file for a .cc, assume it's because we don't
+ # know where to look. In that case we'll give up as we're not sure they
+ # didn't include it in the .h file.
+ # TODO(unknown): Do a better job of finding .h files so we are confident that
+ # not having the .h file means there isn't one.
+ if filename.endswith('.cc') and not header_found:
+ return
+
+ # All the lines have been processed, report the errors found.
+ for required_header_unstripped in required:
+ template = required[required_header_unstripped][1]
+ if required_header_unstripped.strip('<>"') not in include_dict:
+ error(filename, required[required_header_unstripped][0],
+ 'build/include_what_you_use', 4,
+ 'Add #include ' + required_header_unstripped + ' for ' + template)
+
+
+_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<')
+
+
+def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):
+ """Check that make_pair's template arguments are deduced.
+
+ G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are
+ specified explicitly, and such use isn't intended in any case.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line)
+ if match:
+ error(filename, linenum, 'build/explicit_make_pair',
+ 4, # 4 = high confidence
+ 'For C++11-compatibility, omit template arguments from make_pair'
+ ' OR use pair directly OR if appropriate, construct a pair directly')
+
+
+def CheckRedundantVirtual(filename, clean_lines, linenum, error):
+ """Check if line contains a redundant "virtual" function-specifier.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ # Look for "virtual" on current line.
+ line = clean_lines.elided[linenum]
+ virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line)
+ if not virtual: return
+
+ # Ignore "virtual" keywords that are near access-specifiers. These
+ # are only used in class base-specifier and do not apply to member
+ # functions.
+ if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or
+ Match(r'^\s+(public|protected|private)\b', virtual.group(3))):
+ return
+
+ # Ignore the "virtual" keyword from virtual base classes. Usually
+ # there is a column on the same line in these cases (virtual base
+ # classes are rare in google3 because multiple inheritance is rare).
+ if Match(r'^.*[^:]:[^:].*$', line): return
+
+ # Look for the next opening parenthesis. This is the start of the
+ # parameter list (possibly on the next line shortly after virtual).
+ # TODO(unknown): doesn't work if there are virtual functions with
+ # decltype() or other things that use parentheses, but csearch suggests
+ # that this is rare.
+ end_col = -1
+ end_line = -1
+ start_col = len(virtual.group(2))
+ for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())):
+ line = clean_lines.elided[start_line][start_col:]
+ parameter_list = Match(r'^([^(]*)\(', line)
+ if parameter_list:
+ # Match parentheses to find the end of the parameter list
+ (_, end_line, end_col) = CloseExpression(
+ clean_lines, start_line, start_col + len(parameter_list.group(1)))
+ break
+ start_col = 0
+
+ if end_col < 0:
+ return # Couldn't find end of parameter list, give up
+
+ # Look for "override" or "final" after the parameter list
+ # (possibly on the next few lines).
+ for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())):
+ line = clean_lines.elided[i][end_col:]
+ match = Search(r'\b(override|final)\b', line)
+ if match:
+ error(filename, linenum, 'readability/inheritance', 4,
+ ('"virtual" is redundant since function is '
+ 'already declared as "%s"' % match.group(1)))
+
+ # Set end_col to check whole lines after we are done with the
+ # first line.
+ end_col = 0
+ if Search(r'[^\w]\s*$', line):
+ break
+
+
+def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error):
+ """Check if line contains a redundant "override" or "final" virt-specifier.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ # Look for closing parenthesis nearby. We need one to confirm where
+ # the declarator ends and where the virt-specifier starts to avoid
+ # false positives.
+ line = clean_lines.elided[linenum]
+ declarator_end = line.rfind(')')
+ if declarator_end >= 0:
+ fragment = line[declarator_end:]
+ else:
+ if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0:
+ fragment = line
+ else:
+ return
+
+ # Check that at most one of "override" or "final" is present, not both
+ if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment):
+ error(filename, linenum, 'readability/inheritance', 4,
+ ('"override" is redundant since function is '
+ 'already declared as "final"'))
+
+
+
+
+# Returns true if we are at a new block, and it is directly
+# inside of a namespace.
+def IsBlockInNameSpace(nesting_state, is_forward_declaration):
+ """Checks that the new block is directly in a namespace.
+
+ Args:
+ nesting_state: The _NestingState object that contains info about our state.
+ is_forward_declaration: If the class is a forward declared class.
+ Returns:
+ Whether or not the new block is directly in a namespace.
+ """
+ if is_forward_declaration:
+ if len(nesting_state.stack) >= 1 and (
+ isinstance(nesting_state.stack[-1], _NamespaceInfo)):
+ return True
+ else:
+ return False
+
+ return (len(nesting_state.stack) > 1 and
+ nesting_state.stack[-1].check_namespace_indentation and
+ isinstance(nesting_state.stack[-2], _NamespaceInfo))
+
+
+def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item,
+ raw_lines_no_comments, linenum):
+ """This method determines if we should apply our namespace indentation check.
+
+ Args:
+ nesting_state: The current nesting state.
+ is_namespace_indent_item: If we just put a new class on the stack, True.
+ If the top of the stack is not a class, or we did not recently
+ add the class, False.
+ raw_lines_no_comments: The lines without the comments.
+ linenum: The current line number we are processing.
+
+ Returns:
+ True if we should apply our namespace indentation check. Currently, it
+ only works for classes and namespaces inside of a namespace.
+ """
+
+ is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments,
+ linenum)
+
+ if not (is_namespace_indent_item or is_forward_declaration):
+ return False
+
+ # If we are in a macro, we do not want to check the namespace indentation.
+ if IsMacroDefinition(raw_lines_no_comments, linenum):
+ return False
+
+ return IsBlockInNameSpace(nesting_state, is_forward_declaration)
+
+
+# Call this method if the line is directly inside of a namespace.
+# If the line above is blank (excluding comments) or the start of
+# an inner namespace, it cannot be indented.
+def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum,
+ error):
+ line = raw_lines_no_comments[linenum]
+ if Match(r'^\s+', line):
+ error(filename, linenum, 'runtime/indentation_namespace', 4,
+ 'Do not indent within a namespace')
+
+
+def ProcessLine(filename, file_extension, clean_lines, line,
+ include_state, function_state, nesting_state, error,
+ extra_check_functions=[]):
+ """Processes a single line in the file.
+
+ Args:
+ filename: Filename of the file that is being processed.
+ file_extension: The extension (dot not included) of the file.
+ clean_lines: An array of strings, each representing a line of the file,
+ with comments stripped.
+ line: Number of line being processed.
+ include_state: An _IncludeState instance in which the headers are inserted.
+ function_state: A _FunctionState instance which counts function lines, etc.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: A callable to which errors are reported, which takes 4 arguments:
+ filename, line number, error level, and message
+ extra_check_functions: An array of additional check functions that will be
+ run on each source line. Each function takes 4
+ arguments: filename, clean_lines, line, error
+ """
+ raw_lines = clean_lines.raw_lines
+ ParseNolintSuppressions(filename, raw_lines[line], line, error)
+ nesting_state.Update(filename, clean_lines, line, error)
+ CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line,
+ error)
+ if nesting_state.InAsmBlock(): return
+ CheckForFunctionLengths(filename, clean_lines, line, function_state, error)
+ CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)
+ CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error)
+ CheckLanguage(filename, clean_lines, line, file_extension, include_state,
+ nesting_state, error)
+ CheckForNonConstReference(filename, clean_lines, line, nesting_state, error)
+ CheckForNonStandardConstructs(filename, clean_lines, line,
+ nesting_state, error)
+ CheckVlogArguments(filename, clean_lines, line, error)
+ CheckPosixThreading(filename, clean_lines, line, error)
+ CheckInvalidIncrement(filename, clean_lines, line, error)
+ CheckMakePairUsesDeduction(filename, clean_lines, line, error)
+ CheckRedundantVirtual(filename, clean_lines, line, error)
+ CheckRedundantOverrideOrFinal(filename, clean_lines, line, error)
+ for check_fn in extra_check_functions:
+ check_fn(filename, clean_lines, line, error)
+
+def FlagCxx11Features(filename, clean_lines, linenum, error):
+ """Flag those c++11 features that we only allow in certain places.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line)
+
+ # Flag unapproved C++ TR1 headers.
+ if include and include.group(1).startswith('tr1/'):
+ error(filename, linenum, 'build/c++tr1', 5,
+ ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1))
+
+ # Flag unapproved C++11 headers.
+ if include and include.group(1) in ('cfenv',
+ 'condition_variable',
+ 'fenv.h',
+ 'future',
+ 'mutex',
+ 'thread',
+ 'chrono',
+ 'ratio',
+ 'regex',
+ 'system_error',
+ ):
+ error(filename, linenum, 'build/c++11', 5,
+ ('<%s> is an unapproved C++11 header.') % include.group(1))
+
+ # The only place where we need to worry about C++11 keywords and library
+ # features in preprocessor directives is in macro definitions.
+ if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return
+
+ # These are classes and free functions. The classes are always
+ # mentioned as std::*, but we only catch the free functions if
+ # they're not found by ADL. They're alphabetical by header.
+ for top_name in (
+ # type_traits
+ 'alignment_of',
+ 'aligned_union',
+ ):
+ if Search(r'\bstd::%s\b' % top_name, line):
+ error(filename, linenum, 'build/c++11', 5,
+ ('std::%s is an unapproved C++11 class or function. Send c-style '
+ 'an example of where it would make your code more readable, and '
+ 'they may let you use it.') % top_name)
+
+
+def FlagCxx14Features(filename, clean_lines, linenum, error):
+ """Flag those C++14 features that we restrict.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line)
+
+ # Flag unapproved C++14 headers.
+ if include and include.group(1) in ('scoped_allocator', 'shared_mutex'):
+ error(filename, linenum, 'build/c++14', 5,
+ ('<%s> is an unapproved C++14 header.') % include.group(1))
+
+
+def ProcessFileData(filename, file_extension, lines, error,
+ extra_check_functions=[]):
+ """Performs lint checks and reports any errors to the given error function.
+
+ Args:
+ filename: Filename of the file that is being processed.
+ file_extension: The extension (dot not included) of the file.
+ lines: An array of strings, each representing a line of the file, with the
+ last element being empty if the file is terminated with a newline.
+ error: A callable to which errors are reported, which takes 4 arguments:
+ filename, line number, error level, and message
+ extra_check_functions: An array of additional check functions that will be
+ run on each source line. Each function takes 4
+ arguments: filename, clean_lines, line, error
+ """
+ lines = (['// marker so line numbers and indices both start at 1'] + lines +
+ ['// marker so line numbers end in a known way'])
+
+ include_state = _IncludeState()
+ function_state = _FunctionState()
+ nesting_state = NestingState()
+
+ ResetNolintSuppressions()
+
+ CheckForCopyright(filename, lines, error)
+ ProcessGlobalSuppresions(lines)
+ RemoveMultiLineComments(filename, lines, error)
+ clean_lines = CleansedLines(lines)
+
+ if IsHeaderExtension(file_extension):
+ CheckForHeaderGuard(filename, clean_lines, error)
+
+ for line in xrange(clean_lines.NumLines()):
+ ProcessLine(filename, file_extension, clean_lines, line,
+ include_state, function_state, nesting_state, error,
+ extra_check_functions)
+ FlagCxx11Features(filename, clean_lines, line, error)
+ nesting_state.CheckCompletedBlocks(filename, error)
+
+ CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)
+
+ # Check that the .cc file has included its header if it exists.
+ if _IsSourceExtension(file_extension):
+ CheckHeaderFileIncluded(filename, include_state, error)
+
+ # We check here rather than inside ProcessLine so that we see raw
+ # lines rather than "cleaned" lines.
+ CheckForBadCharacters(filename, lines, error)
+
+ CheckForNewlineAtEOF(filename, lines, error)
+
+def ProcessConfigOverrides(filename):
+ """ Loads the configuration files and processes the config overrides.
+
+ Args:
+ filename: The name of the file being processed by the linter.
+
+ Returns:
+ False if the current |filename| should not be processed further.
+ """
+
+ abs_filename = os.path.abspath(filename)
+ cfg_filters = []
+ keep_looking = True
+ while keep_looking:
+ abs_path, base_name = os.path.split(abs_filename)
+ if not base_name:
+ break # Reached the root directory.
+
+ cfg_file = os.path.join(abs_path, "CPPLINT.cfg")
+ abs_filename = abs_path
+ if not os.path.isfile(cfg_file):
+ continue
+
+ try:
+ with open(cfg_file) as file_handle:
+ for line in file_handle:
+ line, _, _ = line.partition('#') # Remove comments.
+ if not line.strip():
+ continue
+
+ name, _, val = line.partition('=')
+ name = name.strip()
+ val = val.strip()
+ if name == 'set noparent':
+ keep_looking = False
+ elif name == 'filter':
+ cfg_filters.append(val)
+ elif name == 'exclude_files':
+ # When matching exclude_files pattern, use the base_name of
+ # the current file name or the directory name we are processing.
+ # For example, if we are checking for lint errors in /foo/bar/baz.cc
+ # and we found the .cfg file at /foo/CPPLINT.cfg, then the config
+ # file's "exclude_files" filter is meant to be checked against "bar"
+ # and not "baz" nor "bar/baz.cc".
+ if base_name:
+ pattern = re.compile(val)
+ if pattern.match(base_name):
+ sys.stderr.write('Ignoring "%s": file excluded by "%s". '
+ 'File path component "%s" matches '
+ 'pattern "%s"\n' %
+ (filename, cfg_file, base_name, val))
+ return False
+ elif name == 'linelength':
+ global _line_length
+ try:
+ _line_length = int(val)
+ except ValueError:
+ sys.stderr.write('Line length must be numeric.')
+ elif name == 'root':
+ global _root
+ _root = val
+ elif name == 'headers':
+ ProcessHppHeadersOption(val)
+ else:
+ sys.stderr.write(
+ 'Invalid configuration option (%s) in file %s\n' %
+ (name, cfg_file))
+
+ except IOError:
+ sys.stderr.write(
+ "Skipping config file '%s': Can't open for reading\n" % cfg_file)
+ keep_looking = False
+
+ # Apply all the accumulated filters in reverse order (top-level directory
+ # config options having the least priority).
+ for filter in reversed(cfg_filters):
+ _AddFilters(filter)
+
+ return True
+
+
+def ProcessFile(filename, vlevel, extra_check_functions=[]):
+ """Does google-lint on a single file.
+
+ Args:
+ filename: The name of the file to parse.
+
+ vlevel: The level of errors to report. Every error of confidence
+ >= verbose_level will be reported. 0 is a good default.
+
+ extra_check_functions: An array of additional check functions that will be
+ run on each source line. Each function takes 4
+ arguments: filename, clean_lines, line, error
+ """
+
+ _SetVerboseLevel(vlevel)
+ _BackupFilters()
+
+ if not ProcessConfigOverrides(filename):
+ _RestoreFilters()
+ return
+
+ lf_lines = []
+ crlf_lines = []
+ try:
+ # Support the UNIX convention of using "-" for stdin. Note that
+ # we are not opening the file with universal newline support
+ # (which codecs doesn't support anyway), so the resulting lines do
+ # contain trailing '\r' characters if we are reading a file that
+ # has CRLF endings.
+ # If after the split a trailing '\r' is present, it is removed
+ # below.
+ if filename == '-':
+ lines = codecs.StreamReaderWriter(sys.stdin,
+ codecs.getreader('utf8'),
+ codecs.getwriter('utf8'),
+ 'replace').read().split('\n')
+ else:
+ lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
+
+ # Remove trailing '\r'.
+ # The -1 accounts for the extra trailing blank line we get from split()
+ for linenum in range(len(lines) - 1):
+ if lines[linenum].endswith('\r'):
+ lines[linenum] = lines[linenum].rstrip('\r')
+ crlf_lines.append(linenum + 1)
+ else:
+ lf_lines.append(linenum + 1)
+
+ except IOError:
+ sys.stderr.write(
+ "Skipping input '%s': Can't open for reading\n" % filename)
+ _RestoreFilters()
+ return
+
+ # Note, if no dot is found, this will give the entire filename as the ext.
+ file_extension = filename[filename.rfind('.') + 1:]
+
+ # When reading from stdin, the extension is unknown, so no cpplint tests
+ # should rely on the extension.
+ if filename != '-' and file_extension not in _valid_extensions:
+ sys.stderr.write('Ignoring %s; not a valid file name '
+ '(%s)\n' % (filename, ', '.join(_valid_extensions)))
+ else:
+ ProcessFileData(filename, file_extension, lines, Error,
+ extra_check_functions)
+
+ # If end-of-line sequences are a mix of LF and CR-LF, issue
+ # warnings on the lines with CR.
+ #
+ # Don't issue any warnings if all lines are uniformly LF or CR-LF,
+ # since critique can handle these just fine, and the style guide
+ # doesn't dictate a particular end of line sequence.
+ #
+ # We can't depend on os.linesep to determine what the desired
+ # end-of-line sequence should be, since that will return the
+ # server-side end-of-line sequence.
+ if lf_lines and crlf_lines:
+ # Warn on every line with CR. An alternative approach might be to
+ # check whether the file is mostly CRLF or just LF, and warn on the
+ # minority, we bias toward LF here since most tools prefer LF.
+ for linenum in crlf_lines:
+ Error(filename, linenum, 'whitespace/newline', 1,
+ 'Unexpected \\r (^M) found; better to use only \\n')
+
+ sys.stdout.write('Done processing %s\n' % filename)
+ _RestoreFilters()
+
+
+def PrintUsage(message):
+ """Prints a brief usage string and exits, optionally with an error message.
+
+ Args:
+ message: The optional error message.
+ """
+ sys.stderr.write(_USAGE)
+ if message:
+ sys.exit('\nFATAL ERROR: ' + message)
+ else:
+ sys.exit(1)
+
+
+def PrintCategories():
+ """Prints a list of all the error-categories used by error messages.
+
+ These are the categories used to filter messages via --filter.
+ """
+ sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES))
+ sys.exit(0)
+
+
+def ParseArguments(args):
+ """Parses the command line arguments.
+
+ This may set the output format and verbosity level as side-effects.
+
+ Args:
+ args: The command line arguments:
+
+ Returns:
+ The list of filenames to lint.
+ """
+ try:
+ (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',
+ 'counting=',
+ 'filter=',
+ 'root=',
+ 'linelength=',
+ 'extensions=',
+ 'headers='])
+ except getopt.GetoptError:
+ PrintUsage('Invalid arguments.')
+
+ verbosity = _VerboseLevel()
+ output_format = _OutputFormat()
+ filters = ''
+ counting_style = ''
+
+ for (opt, val) in opts:
+ if opt == '--help':
+ PrintUsage(None)
+ elif opt == '--output':
+ if val not in ('emacs', 'vs7', 'eclipse'):
+ PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.')
+ output_format = val
+ elif opt == '--verbose':
+ verbosity = int(val)
+ elif opt == '--filter':
+ filters = val
+ if not filters:
+ PrintCategories()
+ elif opt == '--counting':
+ if val not in ('total', 'toplevel', 'detailed'):
+ PrintUsage('Valid counting options are total, toplevel, and detailed')
+ counting_style = val
+ elif opt == '--root':
+ global _root
+ _root = val
+ elif opt == '--linelength':
+ global _line_length
+ try:
+ _line_length = int(val)
+ except ValueError:
+ PrintUsage('Line length must be digits.')
+ elif opt == '--extensions':
+ global _valid_extensions
+ try:
+ _valid_extensions = set(val.split(','))
+ except ValueError:
+ PrintUsage('Extensions must be comma seperated list.')
+ elif opt == '--headers':
+ ProcessHppHeadersOption(val)
+
+ if not filenames:
+ PrintUsage('No files were specified.')
+
+ _SetOutputFormat(output_format)
+ _SetVerboseLevel(verbosity)
+ _SetFilters(filters)
+ _SetCountingStyle(counting_style)
+
+ return filenames
+
+
+def main():
+ filenames = ParseArguments(sys.argv[1:])
+
+ # Change stderr to write with replacement characters so we don't die
+ # if we try to print something containing non-ASCII characters.
+ sys.stderr = codecs.StreamReaderWriter(sys.stderr,
+ codecs.getreader('utf8'),
+ codecs.getwriter('utf8'),
+ 'replace')
+
+ _cpplint_state.ResetErrorCounts()
+ for filename in filenames:
+ ProcessFile(filename, _cpplint_state.verbose_level)
+ _cpplint_state.PrintErrorCounts()
+
+ sys.exit(_cpplint_state.error_count > 0)
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+PROJECT(esplusplayer)
+
+SET(fw_name "${PROJECT_NAME}")
+SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
+SET(${fw_name}_LDFLAGS)
+
+IF(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+SET(ADD_LIBS
+ "espplayer-core"
+ "trackrenderer"
+ "mixer"
+)
+ELSE(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+SET(ADD_LIBS
+ "espplayer-core"
+ "trackrenderer"
+)
+ENDIF(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+
+SET(${fw_name}_CXXFLAGS "-Wall -Werror -std=c++17 -fPIC -fno-lto -Wl,-z,relro -fstack-protector -DEFL_BETA_API_SUPPORT")
+
+SET(dependents "gstreamer-1.0 glib-2.0 dlog"
+ "boost"
+ "tv-resource-manager"
+ "elementary ecore ecore-wl2"
+ "jsoncpp")
+
+IF(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+SET(dependents ${dependents} "libavoc")
+ELSE(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+SET(dependents ${dependents} "libavoc-av")
+ENDIF(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+
+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(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
+ ${PARENT_DIR}/plusplayer-core/include_internal
+)
+
+SET(CC_SRCS
+ ${PROJECT_SOURCE_DIR}/src/espacket_logger.cpp
+ ${PROJECT_SOURCE_DIR}/src/esplayer_drm.cpp
+ ${PROJECT_SOURCE_DIR}/src/esplusplayer.cpp
+ ${PROJECT_SOURCE_DIR}/src/esplayer.cpp
+ ${PROJECT_SOURCE_DIR}/src/elementary_stream.cpp
+ ${PROJECT_SOURCE_DIR}/src/espacket.cpp
+ ${PROJECT_SOURCE_DIR}/src/esplusplayer_capi.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 [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_DECODED_PACKET_LIST_H__
+#define __ESPLUSPLAYER_SRC_ESPLAYER_DECODED_PACKET_LIST_H__
+
+#include <tbm_surface.h>
+
+#include <algorithm>
+#include <functional>
+#include <list>
+#include <memory>
+
+#include "core/utils/plusplayer_log.h"
+#include "esplusplayer/types/buffer.h"
+
+namespace {
+// LCOV_EXCL_START
+void DecodedPacketDeleter(esplusplayer_decoded_video_packet* packet) {
+ if (packet == nullptr || packet->surface_data == nullptr) return;
+ // LOG_DEBUG("packet[%p] deleted", packet);
+ tbm_surface_destroy(static_cast<tbm_surface_h>(packet->surface_data));
+ packet->surface_data = NULL;
+// LCOV_EXCL_STOP
+}
+} // namespace
+
+namespace esplusplayer {
+struct DecodedPacketManagerInterface {
+ virtual ~DecodedPacketManagerInterface() = default;
+ virtual bool TryToAdd(esplusplayer_decoded_video_packet* packet) = 0;
+ virtual void Remove(esplusplayer_decoded_video_packet* packet) = 0;
+ virtual void Clear() = 0;
+ virtual void GetFreeTbmSurface(void** ptr, bool is_scale_change) = 0;
+};
+struct CmaBufferInfo {
+ void* tbm_surf = nullptr;
+ bool tbm_is_free = true;
+ bool destroy_flag = false;
+};
+
+// LCOV_EXCL_START
+class AbstractDecodedPacketList : public DecodedPacketManagerInterface {
+ public:
+ explicit AbstractDecodedPacketList() = default;
+ virtual ~AbstractDecodedPacketList() = default;
+ using DecodedPacketPtr =
+ std::unique_ptr<esplusplayer_decoded_video_packet,
+ std::function<void(esplusplayer_decoded_video_packet*)>>;
+
+ public:
+ virtual bool TryToAdd(esplusplayer_decoded_video_packet* packet) override {
+ if (packet == nullptr) {
+ LOG_ERROR("packet is nullptr");
+ return false;
+ }
+ std::unique_lock<std::mutex> lk(mtx_);
+ auto pkt_ptr = DecodedPacketPtr(
+ packet, [this](esplusplayer_decoded_video_packet* pkt) {
+ DecodedPacketDeleter(pkt);
+ delete pkt;
+ });
+ if (IsAvailableInternal() == false) {
+ LOG_ERROR("not available to add a packet");
+ return false;
+ }
+ // LOG_DEBUG("packet[%p] added", packet);
+ list_.emplace_back(std::move(pkt_ptr));
+ return true;
+ }
+ virtual void Remove(esplusplayer_decoded_video_packet* packet) override {
+ if (packet == nullptr) return;
+ std::unique_lock<std::mutex> lk(mtx_);
+ list_.remove_if([&packet](const DecodedPacketPtr& cur) {
+ if (cur.get() == packet) return true;
+ return false;
+ });
+ }
+ virtual void Clear() override {
+ std::unique_lock<std::mutex> lk(mtx_);
+ LOG_DEBUG("all packets are cleared");
+ list_.clear();
+ }
+
+ virtual void GetFreeTbmSurface(void** ptr, bool is_scale_change) override {
+ return;
+ }
+
+ protected:
+ const std::list<DecodedPacketPtr>& GetList() { return list_; }
+
+ protected:
+ virtual bool IsAvailableInternal() = 0;
+ virtual void DecodedPacketDeleter(
+ esplusplayer_decoded_video_packet* packet) = 0;
+
+ protected:
+ std::mutex mtx_;
+ std::list<DecodedPacketPtr> list_;
+};
+
+class DecodedReferencePacketList : public AbstractDecodedPacketList {
+ public:
+ explicit DecodedReferencePacketList() { LOG_DEBUG("created"); }
+ virtual ~DecodedReferencePacketList() { LOG_DEBUG("destroyed"); }
+
+ protected:
+ virtual bool IsAvailableInternal() override {
+ if (GetList().size() > kMaxAvailableSize_) return false;
+ return true;
+ }
+ virtual void DecodedPacketDeleter(
+ esplusplayer_decoded_video_packet* packet) override {
+ ::DecodedPacketDeleter(packet);
+ }
+
+ private:
+ const std::uint32_t kMaxAvailableSize_ = 5;
+};
+
+class DecodedCopiedPacketList : public AbstractDecodedPacketList {
+ public:
+ explicit DecodedCopiedPacketList() { LOG_DEBUG("created"); }
+ virtual ~DecodedCopiedPacketList() { LOG_DEBUG("destroyed"); }
+
+ protected:
+ virtual bool IsAvailableInternal() override { return true; }
+ virtual void DecodedPacketDeleter(
+ esplusplayer_decoded_video_packet* packet) override {
+ ::DecodedPacketDeleter(packet);
+ }
+};
+
+class ManualDecodedCopiedPacketList : public DecodedPacketManagerInterface {
+ public:
+ using Handler = std::function<bool(esplusplayer_decoded_video_packet*)>;
+ explicit ManualDecodedCopiedPacketList(Handler handler) : handler_(handler) {
+ LOG_DEBUG("created");
+ }
+ virtual ~ManualDecodedCopiedPacketList() { LOG_DEBUG("destroyed"); }
+
+ protected:
+ virtual bool TryToAdd(esplusplayer_decoded_video_packet* packet) {
+ return false;
+ }
+ virtual void Clear() {}
+ virtual void GetFreeTbmSurface(void** ptr, bool is_scale_change) {}
+
+ virtual void Remove(esplusplayer_decoded_video_packet* packet) {
+ if (handler_(packet) == false) {
+ LOG_ERROR("packet return failed");
+ }
+ }
+
+ private:
+ Handler handler_;
+};
+// LCOV_EXCL_STOP
+
+class DecodedScaledPacketList : public AbstractDecodedPacketList {
+ public:
+ explicit DecodedScaledPacketList() { LOG_DEBUG("created"); }
+ virtual ~DecodedScaledPacketList() { LOG_DEBUG("destroyed"); }
+// LCOV_EXCL_START
+ virtual void GetFreeTbmSurface(void** ptr, bool is_scale_change) {
+ std::unique_lock<std::mutex> lk(mtx_);
+ if (is_scale_change) {
+ for (std::vector<CmaBufferInfo>::iterator it = tbm_mgr_.begin();
+ it != tbm_mgr_.end();) {
+ if (it->tbm_is_free == true) {
+ LOG_ERROR("scale size changed, destroy the free tbm %p",
+ it->tbm_surf);
+ tbm_surface_destroy(static_cast<tbm_surface_h>(it->tbm_surf));
+ it = tbm_mgr_.erase(it);
+ } else {
+ LOG_ERROR("scale size changed, using tbm will be destroy later%p",
+ it->tbm_surf);
+ it->destroy_flag = true;
+ it++;
+ }
+ }
+ *ptr = nullptr;
+ } else {
+ auto tbm_is_free = [](const CmaBufferInfo& item) -> bool {
+ return item.tbm_is_free == true;
+ };
+ auto target = std::find_if(tbm_mgr_.begin(), tbm_mgr_.end(), tbm_is_free);
+ if (target != tbm_mgr_.end()) {
+ *ptr = target->tbm_surf;
+ }
+ }
+ return;
+ }
+
+ virtual bool TryToAdd(esplusplayer_decoded_video_packet* packet) override {
+ if (packet == nullptr) {
+ LOG_ERROR("packet is nullptr");
+ return false;
+ }
+ std::unique_lock<std::mutex> lk(mtx_);
+ auto pkt_ptr = DecodedPacketPtr(
+ packet, [this](esplusplayer_decoded_video_packet* pkt) {
+ DecodedPacketDeleter(pkt);
+ delete pkt;
+ });
+ if (IsAvailableInternal() == false) {
+ LOG_ERROR("not available to add a packet");
+ return false;
+ }
+ // traverse tbm_mgr to find the same tbm surf,if find, change that,if not,
+ // new one and add to vector.
+ void* tbm_ptr = packet->surface_data;
+ auto has_tbm = [tbm_ptr](const CmaBufferInfo& item) -> bool {
+ return item.tbm_surf == tbm_ptr;
+ };
+ auto target = std::find_if(tbm_mgr_.begin(), tbm_mgr_.end(), has_tbm);
+ if (target == tbm_mgr_.end()) {
+ CmaBufferInfo tmp_tbm_buffer;
+ tmp_tbm_buffer.tbm_surf = packet->surface_data;
+ tmp_tbm_buffer.tbm_is_free = false;
+ tmp_tbm_buffer.destroy_flag = false;
+ tbm_mgr_.push_back(tmp_tbm_buffer);
+ LOG_ERROR("add tbm surface %p to list", tmp_tbm_buffer.tbm_surf);
+ } else {
+ target->tbm_is_free = false;
+ }
+ list_.emplace_back(std::move(pkt_ptr));
+ return true;
+ }
+
+ virtual void Remove(esplusplayer_decoded_video_packet* packet) override {
+ if (packet == nullptr) return;
+ std::unique_lock<std::mutex> lk(mtx_);
+ // LOG_ERROR("Remove pkt %p ", packet->surface_data);
+ // before remove packet, set the tbm surf free.
+ void* tbm_ptr = packet->surface_data;
+ auto has_tbm = [tbm_ptr](const CmaBufferInfo& item) -> bool {
+ return item.tbm_surf == tbm_ptr;
+ };
+ auto target = std::find_if(tbm_mgr_.begin(), tbm_mgr_.end(), has_tbm);
+ if (target != tbm_mgr_.end()) {
+ if (target->destroy_flag == true) {
+ LOG_ERROR("destroy tbm surface %p", target->tbm_surf);
+ tbm_surface_destroy(static_cast<tbm_surface_h>(target->tbm_surf));
+ tbm_mgr_.erase(target);
+ } else {
+ target->tbm_is_free = true;
+ }
+ }
+ list_.remove_if([&packet](const DecodedPacketPtr& cur) {
+ if (cur.get() == packet) return true;
+ return false;
+ });
+ }
+// LCOV_EXCL_STOP
+
+ virtual void Clear() override {
+ std::unique_lock<std::mutex> lk(mtx_);
+ LOG_DEBUG("all packets are cleared");
+ list_.clear();
+ for (auto& iter : tbm_mgr_) {
+ LOG_ERROR("destroy tbm buffer in list %p", iter.tbm_surf);
+ tbm_surface_destroy(static_cast<tbm_surface_h>(iter.tbm_surf));
+ }
+ std::vector<CmaBufferInfo> tmp;
+ tbm_mgr_.swap(tmp);
+ }
+
+ protected:
+// LCOV_EXCL_START
+ virtual bool IsAvailableInternal() override {
+ if (GetList().size() > kMaxAvailableSize_) return false;
+ return true;
+ }
+
+ virtual void DecodedPacketDeleter(
+ esplusplayer_decoded_video_packet* packet) override {
+ // redesign, when pkt list is full, which means new tbm ptr can't add, so
+ // destroy it after create in trackrender.
+ if (GetList().size() > kMaxAvailableSize_) {
+ if (packet == nullptr || packet->surface_data == nullptr) return;
+ // only destroy the tbm surface new created but not added in tbm list
+ void* tbm_ptr = packet->surface_data;
+ auto has_tbm = [tbm_ptr](const CmaBufferInfo& item) -> bool {
+ return item.tbm_surf == tbm_ptr;
+ };
+ auto target = std::find_if(tbm_mgr_.begin(), tbm_mgr_.end(), has_tbm);
+ if (target == tbm_mgr_.end()) {
+ LOG_ERROR("tbm_surface_destroy[%p] deleted", packet->surface_data);
+ tbm_surface_destroy(static_cast<tbm_surface_h>(packet->surface_data));
+ packet->surface_data = NULL;
+ }
+ }
+ }
+// LCOV_EXCL_STOP
+
+ private:
+ const std::uint32_t kMaxAvailableSize_ = 5;
+ std::vector<CmaBufferInfo> tbm_mgr_;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_ESPLAYER_DECODED_PACKET_LIST_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_ESPACEKT_LOGGER__H__
+#define __ESPLUSPLAYER_SRC_ESPLAYER_ESPACEKT_LOGGER__H__
+
+#include <mutex>
+#include <string>
+
+#include "esplusplayer/espacket.h"
+
+namespace esplusplayer {
+class EsPacketLogger {
+ public:
+ EsPacketLogger() = default;
+ virtual ~EsPacketLogger() = default;
+ EsPacketLogger(const EsPacketLogger&) = delete;
+ EsPacketLogger(EsPacketLogger&&) = delete;
+
+ void StorePacketInfo(const EsPacketPtr& packet);
+ void PrintStoredPacketInfo(const StreamType type, bool force = false);
+ void ResetLog(const StreamType type);
+
+ private:
+ constexpr static int kStreamTypeMax = static_cast<int>(StreamType::kMax);
+ constexpr static int kLogBufferThreshold = 60;
+ struct LightWeightEsPacketInfo {
+ bool valid = false;
+ std::uint32_t size = 0;
+ std::uint64_t pts = 0;
+ std::uint64_t duration = 0;
+ bool is_eos = false;
+ };
+ std::string GetStringFromLastPacketInfo_(const StreamType type) const;
+
+ LightWeightEsPacketInfo last_packet_info_[kStreamTypeMax];
+ std::uint64_t last_log_packet_count_[kStreamTypeMax] = {0};
+ std::mutex mtx_;
+};
+} // namespace esplusplayer
+
+#endif
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_ESPLAYER__H__
+#define __ESPLUSPLAYER_SRC_ESPLAYER_ESPLAYER__H__
+
+#include <boost/core/noncopyable.hpp>
+#include <future>
+#include <list>
+#include <map>
+#include <memory>
+#include <queue>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "core/decoderinputbuffer.h"
+#include "core/kpi.h"
+#include "core/trackrendereradapter.h"
+#ifndef IS_AUDIO_PRODUCT
+#include "mixer/mixerticket.h"
+#include "mixer/mixerticket_eventlistener.h"
+#endif
+#include "esplayer/espacket_logger.h"
+#include "esplayer/message.hpp"
+#include "esplayer/state_manager.hpp"
+#include "esplusplayer/drm.h"
+#include "esplusplayer/esplusplayer.h"
+#include "esplusplayer/track.h"
+#include "esplusplayer/types/buffer.h"
+#include "esplusplayer/types/display.h"
+#include "esplusplayer/types/error.h"
+#include "esplusplayer/types/stream.h"
+
+namespace esplusplayer {
+
+enum EosStatus {
+ kNone = 0x0000,
+ kAudioEos = 0x0010,
+ kVideoEos = 0x0100,
+ kAllEos = (kAudioEos | kVideoEos)
+};
+
+static constexpr unsigned long kNeedDataMaskNone = 0x00;
+static constexpr unsigned long kNeedDataMaskBySeek = 0x01;
+static constexpr unsigned long kNeedDataMaskByPrepare = 0x02;
+
+class EsPlayer : public EsPlusPlayer {
+ public:
+ EsPlayer();
+ ~EsPlayer();
+
+ bool Open() override;
+ bool Close() override;
+ bool PrepareAsync() override;
+ bool Deactivate(const StreamType type) override;
+ bool Activate(const StreamType type) override;
+ bool Start() override;
+ bool Stop() override;
+ bool Pause() override;
+ bool Resume() override;
+ bool Seek(const uint64_t time) override;
+ void SetAppInfo(const PlayerAppInfo& app_info) override;
+ void SetAppInfoEx(const PlayerAppInfoEx& app_info) override;
+ bool SetPlaybackRate(const double rate, const bool audio_mute) override;
+ bool SetDisplay(const DisplayType& type, void* obj) override;
+#ifndef IS_AUDIO_PRODUCT
+ bool SetDisplay(const DisplayType& type, MixerTicket* handle) override;
+#endif
+ bool SetDisplay(const DisplayType& type, void* ecore_wl2_window, const int x,
+ const int y, const int w, const int h) override;
+ bool SetDisplaySubsurface(const DisplayType& type, void* ecore_wl2_subsurface,
+ const int x, const int y, const int w,
+ const int h) override;
+ bool SetDisplay(const DisplayType& type, unsigned int surface_id, const int x,
+ const int y, const int w, const int h) override;
+ bool SetDisplayMode(const DisplayMode& mode) override;
+ bool SetStretchMode(const int& mode) override;
+ bool SetDisplayRoi(const Geometry& roi) override;
+ bool SetVideoRoi(const CropArea& area) override;
+ bool ResizeRenderRect(const RenderRect& rect) override;
+ bool SetDisplayRotate(const DisplayRotation& rotate) override;
+ bool GetDisplayRotate(DisplayRotation* rotate) override;
+ bool SetDisplayVisible(bool is_visible) override;
+ bool SetTrustZoneUse(bool is_using_tz) override;
+ bool SetSubmitDataType(SubmitDataType type) override;
+ bool SetStream(const AudioStreamPtr& stream) override;
+ bool SetStream(const VideoStreamPtr& stream) override;
+ PacketSubmitStatus SubmitPacket(const EsPacketPtr& packet) override;
+ PacketSubmitStatus SubmitTrustZonePacket(const EsPacketPtr& packet,
+ uint32_t tz_handle = 0) override;
+ PacketSubmitStatus SubmitEncryptedPacket(
+ const EsPacketPtr& packet,
+ const drm::EsPlayerEncryptedInfo& drm_info) override;
+ EsState GetState() override;
+ bool GetPlayingTime(uint64_t* time) override;
+ bool SetAudioMute(bool is_mute) override;
+ bool SetVideoFrameBufferType(DecodedVideoFrameBufferType type) override;
+ bool SetVideoFrameBufferScaleResolution(
+ const uint32_t& target_width, const uint32_t& target_height) override;
+ bool SetDecodedVideoFrameRate(const Rational& request_framerate) override;
+ void RegisterListener(EsEventListener* listener,
+ EsEventListener::UserData userdata) override;
+ bool GetAdaptiveInfo(void* padaptive_info,
+ const PlayerAdaptiveInfo& adaptive_type) override;
+ bool SetVolume(const int& volume) override;
+ bool GetVolume(int* volume) override;
+ bool Flush(const StreamType& type) override;
+ void SetBufferSize(const BufferOption& option, uint64_t size) override;
+ bool SetLowLatencyMode(const PlayerLowLatencyMode& mode) override;
+ bool SetVideoFramePeekMode() override;
+ bool RenderVideoFrame() override;
+ bool SetUnlimitedMaxBufferMode() override;
+ ErrorType SetFmmMode() override;
+ bool SetAudioCodecType(const PlayerAudioCodecType& type) override;
+ bool SetVideoCodecType(const PlayerVideoCodecType& type) override;
+ bool SetRenderTimeOffset(const StreamType type, int64_t offset) override;
+ bool GetRenderTimeOffset(const StreamType type, int64_t* offset) override;
+ bool SetAlternativeVideoResource(unsigned int rsc_type) override;
+ bool SetAlternativeAudioResource(
+ const PlayerAudioResourceType rsc_type) override;
+ bool SwitchAudioStreamOnTheFly(const AudioStreamPtr& stream) override;
+ bool SetAiFilter(void* aifilter) override;
+ bool SetCatchUpSpeed(const CatchUpSpeed& level) override;
+ bool GetVideoLatencyStatus(LatencyStatus* status) override;
+ bool GetAudioLatencyStatus(LatencyStatus* status) override;
+ bool SetVideoMidLatencyThreshold(const unsigned int threshold) override;
+ bool SetAudioMidLatencyThreshold(const unsigned int threshold) override;
+ bool SetVideoHighLatencyThreshold(const unsigned int threshold) override;
+ bool SetAudioHighLatencyThreshold(const unsigned int threshold) override;
+ bool InitAudioEasingInfo(const uint32_t init_volume,
+ const uint32_t init_elapsed_time,
+ const AudioEasingInfo& easing_info) override;
+ bool UpdateAudioEasingInfo(const AudioEasingInfo& easing_info) override;
+ bool GetAudioEasingInfo(uint32_t* current_volume, uint32_t* elapsed_time,
+ AudioEasingInfo* easing_info) override;
+ bool StartAudioEasing() override;
+ bool StopAudioEasing() override;
+ bool GetVirtualRscId(const RscType type, int* virtual_id) override;
+ bool SetAdvancedPictureQualityType(const AdvPictureQualityType type) override;
+ bool SetResourceAllocatePolicy(const RscAllocPolicy policy) override;
+ GetDecodedVideoFrameStatus GetDecodedPacket(
+ DecodedVideoPacket& packet) override;
+ bool ReturnDecodedPacket(const DecodedVideoPacket& packet) override;
+ bool SetAudioPreloading() override;
+ bool SetVideoScanType(const PlayerVideoScanType type) override;
+ bool SetTimeUnitType(const PlayerTimeUnitType type) override;
+ bool GetDecodingTime(StreamType type, int32_t* time_in_milliseconds) override;
+ bool SetVideoStreamRotationInfo(const VideoRotation& rotation) override;
+ bool GetVideoStreamRotationInfo(VideoRotation* rotation) override;
+ bool SetSimpleMixOutBufferLevel(const PlayerSimpleMixOutBufferLevel level) override;
+
+ private:
+ using SubmitPacketOperator =
+ std::function<PacketSubmitStatus(GstBuffer* buffer)>;
+
+ struct NeedData {
+ std::bitset<2> mask = kNeedDataMaskNone;
+ uint64_t seek_offset = 0;
+ };
+
+ struct ResumeTime {
+ bool is_set = false;
+ uint64_t time = 0;
+ };
+
+ enum class MakeBufferStatus {
+ kEos, // eos packet
+ kOutOfMemory, // out of memory
+ kSuccess // success
+ };
+ struct SrcQueueSize {
+ std::uint64_t kMaxByteOfVideoSrcQueue = 70 * 1024 * 1024; // 70 MB
+ std::uint64_t kMaxByteOfAudioSrcQueue = 10 * 1024 * 1024; // 10 MB
+ std::uint32_t kMinByteThresholdOfVideoSrcQueue = 0;
+ std::uint32_t kMinByteThresholdOfAudioSrcQueue = 0;
+
+ std::uint64_t kMaxTimeOfVideoSrcQueue = 0;
+ std::uint64_t kMaxTimeOfAudioSrcQueue = 0;
+ std::uint32_t kMinTimeThresholdOfVideoSrcQueue = 0;
+ std::uint32_t kMinTimeThresholdOfAudioSrcQueue = 0;
+ };
+
+ private:
+ void Init_();
+ void MsgTask_();
+ bool Prepare_();
+ void PrepareTask_();
+ void SetTrackRendererAttributes_();
+ GstBuffer* GetGstBuffer_(const EsPacketPtr& packet, MakeBufferStatus* status);
+ void UnsetTzQdata_(const DecoderInputBufferPtr& buffer);
+ void MakeGstBufferForTzHandle_(GstBuffer* gstbuffer, const TrackType& type,
+ const uint32_t& tz_handle,
+ const uint32_t& packet_size);
+ void MakeGstBufferForEncryptedPacket_(
+ GstBuffer* gstbuffer, const EsPacketPtr& packet,
+ const drm::EsPlayerEncryptedInfo& drm_info);
+ PacketSubmitStatus SubmitPacketCommon_(const EsPacketPtr& packet,
+ SubmitPacketOperator op);
+ PacketSubmitStatus SubmitEosPacket_(const TrackType& type);
+ PacketSubmitStatus SubmitDecoderInputBuffer_(
+ const DecoderInputBufferPtr& buffer);
+ bool SetTrack_(const Track& track);
+ bool ChangeStream_(const Track& track);
+ bool SetStream_(const Track& track);
+ void ResetContextForClose_();
+ void ResetContextForStop_();
+ void GetSrcQueueCurrentSize_(const TrackType& type, uint64_t* byte_size,
+ uint64_t* time_size);
+ kpi::EsCodecLoggerKeys MakeKpiKeys_();
+#ifndef IS_AUDIO_PRODUCT
+ bool PrepareVideoMixingMode_(std::vector<Track>* tracks);
+#endif
+
+ private: // private types
+ class TrackRendererEventListener
+ : public TrackRendererAdapter::EventListener {
+ public:
+ explicit TrackRendererEventListener(EsPlayer* handler) : handler_(handler) {
+ assert(handler);
+ }
+ void OnError(const ErrorType& error_code) override;
+ void OnErrorMsg(const ErrorType& error_code, char* error_msg) override;
+ void OnResourceConflicted() override;
+ void OnEos() override;
+ void OnSeekDone() override;
+ void OnBufferStatus(const TrackType& type,
+ const BufferStatus& status) override;
+ void OnSeekData(const TrackType& type, const uint64_t offset) override;
+ void OnClosedCaptionData(const char* data, const int size) override;
+ void OnFlushDone() override;
+ void OnEvent(const EventType& event_type,
+ const EventMsg& msg_data) override;
+ void OnFirstDecodingDone() override;
+ void OnVideoDecoderUnderrun() override;
+ void OnVideoLatencyStatus(const LatencyStatus& latency_status) override;
+ void OnAudioLatencyStatus(const LatencyStatus& latency_status) override;
+ void OnVideoHighLatency() override;
+ void OnAudioHighLatency() override;
+ void OnVideoFrameDropped(const uint64_t& count) override;
+#ifndef IS_AUDIO_PRODUCT
+ void OnMediaPacketVideoRawDecoded(
+ const DecodedVideoRawModePacket& packet) override;
+#endif
+
+ private:
+ void ReadyToPrepare_(const TrackType& type);
+ void ReadyToSeek_(const TrackType& type);
+ void BufferStatus_(const TrackType& type, const BufferStatus& status);
+ void OnMediaPacketVideoDecoded(const DecodedVideoPacket& packet);
+ void OnMediaPacketGetTbmBufPtr(void** ptr, bool is_scale_change);
+ void OnDecoderInputBufferTime(const TrackType& type, const DecoderBufferTime &time);
+ void OnDecoderOutputBufferTime(const TrackType& type, const DecoderBufferTime &time);
+
+ private:
+ EsPlayer* handler_ = nullptr;
+ }; // class TrackRendererEventListener
+
+#ifndef IS_AUDIO_PRODUCT
+ class MixerListener : public MixerTicketEventListener {
+ public:
+ explicit MixerListener(EsPlayer* handler) : handler_(handler) {
+ assert(handler);
+ }
+ bool OnAudioFocusChanged(bool active) override;
+ bool OnUpdateDisplayInfo(const DisplayInfo& cur_info,
+ DisplayInfo* new_info) override;
+
+ private:
+ EsPlayer* handler_{nullptr};
+ };
+#endif
+
+ private:
+ std::vector<Track> track_;
+ NeedData need_data_[kTrackTypeMax];
+ int eos_status_ = EosStatus::kAllEos;
+ std::mutex eos_mutex_;
+ EsEventListener* eventlistener_ = nullptr;
+ EsEventListener::UserData eventlistener_userdata_ = nullptr;
+
+ EsStateManager state_manager_;
+
+ SubmitDataType submit_data_type_ = SubmitDataType::kCleanData;
+ drm::Property drm_property_;
+ std::uint32_t low_latency_mode_ = 0;
+ std::uint32_t video_frame_peek_mode_ = 0;
+ std::uint32_t unlimited_max_buffer_mode_ = 0;
+ std::uint32_t accurate_seek_mode_ = 1;
+ std::uint32_t video_pre_display_mode_ = 1;
+ std::uint32_t fmm_mode_ = 0;
+ PlayerAudioCodecType audio_codec_type_ = kPlayerAudioCodecTypeHW;
+ PlayerVideoCodecType video_codec_type_ = kPlayerVideoCodecTypeHW;
+ bool force_audio_swdecoder_use_ = false;
+ std::uint32_t video_decoding_mode_ = 0x02; // seamless mode
+ std::uint32_t alternative_video_resource_ = 0;
+ RscAllocPolicy resource_alloc_policy_ = RscAllocPolicy::kRscAllocExclusive;
+ std::uint32_t set_video_progressive_ = 0;
+ ResumeTime resume_time_;
+
+ bool is_msg_task_stop_ = false;
+ std::mutex msg_task_mutex_;
+ std::mutex submit_mutex_;
+ std::condition_variable msg_task_cv_;
+ std::queue<es_msg::Base::Ptr> msg_queue_;
+ std::future<void> msg_handler_task_;
+
+ std::unique_ptr<TrackRendererEventListener> trackrenderer_event_listener_{
+ new TrackRendererEventListener(this)};
+#ifndef IS_AUDIO_PRODUCT
+ std::unique_ptr<MixerListener> mixer_event_listener_{new MixerListener(this)};
+#endif
+ std::unique_ptr<TrackRendererAdapter> trackrenderer_;
+ std::future<void> preparetask_;
+ PlayerAppInfo app_info_;
+ double current_playback_rate_ = 1.0;
+ bool current_audio_mute_ = false;
+ bool is_resource_conflicted_ = false;
+ bool is_stopped_ = false;
+ bool is_preparing_ = false;
+ bool is_seek_done_need_drop = false;
+ SrcQueueSize src_queue_size_;
+
+ std::string caf_unique_number;
+#ifndef IS_AUDIO_PRODUCT
+ std::unique_ptr<MixerTicket> mixer_ticket_;
+ bool is_audio_focused_ = true;
+ Geometry mixerticket_roi_;
+ bool is_visible_ = true;
+ std::mutex audio_focus_m_;
+ bool enable_audio_pipeline_handle_ = true;
+ bool enable_rsc_alloc_handle_ = true;
+#endif
+ DecodedVideoFrameBufferType vidoe_frame_buffer_type_ =
+ DecodedVideoFrameBufferType::kNone;
+ // for debugging
+ EsPacketLogger es_packet_logger_;
+ PlayerTimeUnitType time_unit_type = kPlayerTimeUnitTypeMs;
+ VideoRotation video_rotate_ = VideoRotation::kVideoRotateNone;
+};
+
+} // namespace esplusplayer
+#endif // __ESPLUSPLAYER_SRC_ESPLAYER_ESPLAYER__H__
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_ESPLAYER_DRM__H__
+#define __ESPLUSPLAYER_SRC_ESPLAYER_ESPLAYER_DRM__H__
+
+#include "core/gstobject_guard.h"
+#include "esplusplayer/espacket.h"
+#include "esplusplayer/external_drm.h"
+
+namespace esplusplayer {
+namespace esplayer_drm {
+using GBytesPtr = gstguard::GstGuardPtr<GBytes>;
+GBytesPtr Serialize(const EsPacketPtr &packet,
+ const drm::EsPlayerEncryptedInfo &drm_info);
+} // namespace esplayer_drm
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_ESPLAYER_ESPLAYER_DRM__H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_MESSAGE_H__
+#define __ESPLUSPLAYER_SRC_ESPLAYER_MESSAGE_H__
+
+#include <boost/core/noncopyable.hpp>
+#include <functional>
+#include <list>
+#include <memory>
+
+#include "core/utils/plusplayer_log.h"
+#include "esplusplayer/track.h"
+#include "esplusplayer/types/buffer.h"
+#include "esplusplayer/types/error.h"
+#include "esplusplayer/types/stream.h"
+
+// Restructuring points
+// - remove listener handler ptr from msg class member
+// - no RTTI
+// - no need to know message type in mskTask
+// - seperate configurations into each messages
+// Check point
+// - design issues? delivering op breaks encapsulation ?
+// -> related modules : pipeline , statemanager , msghandler.
+
+namespace esplusplayer {
+
+// messages
+namespace es_msg {
+
+struct Base : private boost::noncopyable {
+ using Ptr = std::unique_ptr<Base>;
+ using UserData = void*;
+ virtual ~Base() {}
+ virtual void Execute() = 0;
+
+ protected:
+ Base() = default;
+ Base(const UserData userdata) : userdata_(userdata) {}
+ UserData userdata_ = nullptr;
+}; // class Msg
+
+struct Simple : Base {
+ using Ptr = std::unique_ptr<Simple>;
+ using Operator = std::function<void(Base::UserData)>;
+
+ static Base::Ptr Make(const Operator& _op, const Base::UserData userdata) {
+ return Base::Ptr(new Simple(_op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(userdata_);
+ }
+ const Operator op;
+
+ private:
+ explicit Simple(const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), op(_op) {}
+};
+
+// LCOV_EXCL_START
+struct Error : Base {
+ using Ptr = std::unique_ptr<Error>;
+ using Operator = std::function<void(const ErrorType&, Base::UserData)>;
+
+ static Base::Ptr Make(const ErrorType& error_type, const Operator& op,
+ const Base::UserData userdata) {
+ return Base::Ptr(new Error(error_type, op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(type, userdata_);
+ }
+
+ const ErrorType type = ErrorType::kNone;
+ const Operator op;
+
+ private:
+ explicit Error(const ErrorType& _type, const Operator& _op,
+ const Base::UserData userdata)
+ : Base(userdata), type(_type), op(_op) {}
+};
+
+struct ErrorMsg : Base {
+ using Ptr = std::unique_ptr<ErrorMsg>;
+ using Operator =
+ std::function<void(const ErrorType&, const char*, Base::UserData)>;
+
+ static Base::Ptr Make(const ErrorType& error_type, const char* error_msg,
+ size_t size, const Operator& op,
+ const Base::UserData userdata) {
+ return Base::Ptr(new ErrorMsg(error_type, error_msg, size, op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(type, message, userdata_);
+ }
+
+ const ErrorType type = ErrorType::kNone;
+ const char* message = nullptr;
+ const Operator op;
+
+ private:
+ explicit ErrorMsg(const ErrorType& _type, const char* _msg, size_t _size,
+ const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), type(_type), op(_op) {
+ message = new const char[_size + 1]{0};
+ memcpy((void*)message, _msg, _size);
+ }
+
+ ~ErrorMsg() {
+ delete[] message;
+ message = nullptr;
+ }
+};
+// LCOV_EXCL_STOP
+
+struct Bufferstatus : Base {
+ using Ptr = std::unique_ptr<Bufferstatus>;
+ using Operator = std::function<void(StreamType, BufferStatus, uint64_t,
+ uint64_t, Base::UserData)>;
+
+ static Base::Ptr Make(const StreamType type, const BufferStatus status,
+ const uint64_t byte_size, const uint64_t time_size,
+ const Operator& op, const Base::UserData userdata) {
+ return Base::Ptr(
+ new Bufferstatus(type, status, byte_size, time_size, op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(type, status, byte_size, time_size, userdata_);
+ }
+
+ const StreamType type = StreamType::kMax;
+ const BufferStatus status = BufferStatus::kUnderrun;
+ const uint64_t byte_size;
+ const uint64_t time_size;
+ const Operator op;
+
+ private:
+ explicit Bufferstatus(const StreamType _type, const BufferStatus _status,
+ const uint64_t _byte_size, const uint64_t _time_size,
+ const Operator& _op, const Base::UserData userdata)
+ : Base(userdata),
+ type(_type),
+ status(_status),
+ byte_size(_byte_size),
+ time_size(_time_size),
+ op(_op) {}
+};
+
+struct ReadyToPrepare : Base {
+ using Ptr = std::unique_ptr<ReadyToPrepare>;
+ using Operator = std::function<void(StreamType, Base::UserData)>;
+
+ static Base::Ptr Make(const StreamType type, const Operator& op,
+ const Base::UserData userdata) {
+ return Base::Ptr(new ReadyToPrepare(type, op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(type, userdata_);
+ }
+
+ const StreamType type = StreamType::kMax;
+ const Operator op;
+
+ private:
+ explicit ReadyToPrepare(const StreamType _type, const Operator& _op,
+ const Base::UserData userdata)
+ : Base(userdata), type(_type), op(_op) {}
+};
+
+struct ReadyToSeek : Base {
+ using Ptr = std::unique_ptr<ReadyToSeek>;
+ using Operator = std::function<void(StreamType, uint64_t, Base::UserData)>;
+
+ static Base::Ptr Make(const StreamType type, const uint64_t offset,
+ const Operator& op, const Base::UserData userdata) {
+ return Base::Ptr(new ReadyToSeek(type, offset, op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(type, offset, userdata_);
+ }
+
+ const StreamType type = StreamType::kMax;
+ const uint64_t offset = 0;
+ const Operator op;
+
+ private:
+ explicit ReadyToSeek(const StreamType _type, const uint64_t _offset,
+ const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), type(_type), offset(_offset), op(_op) {}
+};
+
+// LCOV_EXCL_START
+struct ResourceConflict : Base {
+ using Ptr = std::unique_ptr<ResourceConflict>;
+ using Operator = std::function<void(Base::UserData)>;
+
+ static Base::Ptr Make(const Operator& _op, const Base::UserData userdata) {
+ return Base::Ptr(new ResourceConflict(_op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(userdata_);
+ }
+ const Operator op;
+
+ private:
+ explicit ResourceConflict(const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), op(_op) {}
+};
+
+struct Eos : Base {
+ using Ptr = std::unique_ptr<Eos>;
+ using Operator = std::function<void(Base::UserData)>;
+
+ static Base::Ptr Make(const Operator& _op, const Base::UserData userdata) {
+ return Base::Ptr(new Eos(_op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(userdata_);
+ }
+
+ const Operator op;
+
+ private:
+ explicit Eos(const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), op(_op) {}
+};
+
+struct SeekDone : Base {
+ using Ptr = std::unique_ptr<SeekDone>;
+ using Operator = std::function<void(Base::UserData)>;
+
+ static Base::Ptr Make(const Operator& _op, const Base::UserData userdata) {
+ return Base::Ptr(new SeekDone(_op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(userdata_);
+ }
+
+ const Operator op;
+
+ private:
+ explicit SeekDone(const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), op(_op) {}
+};
+
+struct FlushDone : Base {
+ using Ptr = std::unique_ptr<FlushDone>;
+ using Operator = std::function<void(Base::UserData)>;
+
+ static Base::Ptr Make(const Operator& _op, const Base::UserData userdata) {
+ return Base::Ptr(new FlushDone(_op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(userdata_);
+ }
+
+ const Operator op;
+
+ private:
+ explicit FlushDone(const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), op(_op) {}
+};
+
+struct OnEvent : Base {
+ using Ptr = std::unique_ptr<OnEvent>;
+ using Operator =
+ std::function<void(const EventType&, const EventMsg&, Base::UserData)>;
+
+ static Base::Ptr Make(const EventType& event, const EventMsg& msg_data,
+ const Operator& op, const Base::UserData userdata) {
+ return Base::Ptr(new OnEvent(event, msg_data, op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(event, msg_data, userdata_);
+ }
+
+ const EventType event = EventType::kNone;
+ EventMsg msg_data;
+ const Operator op;
+
+ private:
+ explicit OnEvent(const EventType& _event, const EventMsg& _msg_data,
+ const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), event(_event), msg_data(_msg_data), op(_op) {}
+};
+// LCOV_EXCL_STOP
+
+struct FirstDecodingDone : Base {
+ using Ptr = std::unique_ptr<FirstDecodingDone>;
+ using Operator = std::function<void(Base::UserData)>;
+
+ static Base::Ptr Make(const Operator& _op, const Base::UserData userdata) {
+ return Base::Ptr(new FirstDecodingDone(_op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(userdata_);
+ }
+
+ const Operator op;
+
+ private:
+ explicit FirstDecodingDone(const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), op(_op) {}
+};
+
+// LCOV_EXCL_START
+struct ClosedCaption : Base {
+ using Ptr = std::unique_ptr<ClosedCaption>;
+ using Operator =
+ std::function<void(std::unique_ptr<char[]>, int, Base::UserData)>;
+
+ static Base::Ptr Make(const char* _data, const int _size, const Operator& _op,
+ const Base::UserData userdata) {
+ return Base::Ptr(new ClosedCaption(_data, _size, _op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(std::move(data), size, userdata_);
+ }
+
+ std::unique_ptr<char[]> data;
+ const int size = 0;
+ const Operator op;
+
+ private:
+ explicit ClosedCaption(const char* _data, const int _size,
+ const Operator& _op, const Base::UserData userdata)
+ : Base(userdata), size(_size), op(_op) {
+ data.reset(new char[size]);
+ memcpy(data.get(), _data, size);
+ }
+};
+// LCOV_EXCL_STOP
+
+struct PacketLatencyStatus : Base {
+ using Ptr = std::unique_ptr<PacketLatencyStatus>;
+ using Operator = std::function<void(LatencyStatus, Base::UserData)>;
+
+ static Base::Ptr Make(const LatencyStatus latency_status, const Operator& op,
+ const Base::UserData userdata) {
+ return Base::Ptr(new PacketLatencyStatus(latency_status, op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(latency_status, userdata_);
+ }
+
+ const LatencyStatus latency_status = LatencyStatus::kLow;
+ const Operator op;
+
+ private:
+ explicit PacketLatencyStatus(const LatencyStatus _latency_status,
+ const Operator& _op,
+ const Base::UserData userdata)
+ : Base(userdata), latency_status(_latency_status), op(_op) {}
+};
+
+// LCOV_EXCL_START
+struct FrameDroppedCount : Base {
+ using Ptr = std::unique_ptr<FrameDroppedCount>;
+ using Operator = std::function<void(uint64_t, Base::UserData)>;
+
+ static Base::Ptr Make(const uint64_t count, const Operator& op,
+ const Base::UserData userdata) {
+ return Base::Ptr(new FrameDroppedCount(count, op, userdata));
+ }
+ void Execute() override {
+ if (!op) return;
+ op(count, userdata_);
+ }
+
+ const uint64_t count = 0;
+ const Operator op;
+
+ private:
+ explicit FrameDroppedCount(const uint64_t _count,
+ const Operator& _op,
+ const Base::UserData userdata)
+ : Base(userdata), count(_count), op(_op) {}
+};
+// LCOV_EXCL_STOP
+
+
+} // namespace es_msg
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_ESPLAYER_MESSAGE_H__
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+//
+// StateMachine using boost::msm (meta state machine)
+// <Quick Guide>
+// * Sequence
+// 1. generate Event (by user)
+// 2. call process_event(Event)
+// 3. check transition_table
+// 3-1. return if no transition defined
+// 3-2. process_event() return if other event is being processed(deferred)
+// 4. check Guard(Event const&)
+// 5. do Action(Event const&) if Guard succeeded
+// 6. Transition updated(to "next")
+// (4-6) is atomically serialized in boost::msm
+//
+// * Return value of process_event()
+// . failed(HANDLED_FALSE=0) if no transition defined in transition_table
+// . HANDLED_GUARD_REJECT if Guard(Event const&) returned false
+//
+
+#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_STATE_MANAGER_H__
+#define __ESPLUSPLAYER_SRC_ESPLAYER_STATE_MANAGER_H__
+
+#include <bitset>
+#include <boost/core/noncopyable.hpp>
+#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
+// max lines of transition table
+#define BOOST_MPL_LIMIT_VECTOR_SIZE 50 // or whatever you need
+#define BOOST_MPL_LIMIT_MAP_SIZE 50 // or whatever you need
+// max number of fsm states
+#define FUSION_MAX_VECTOR_SIZE 20 // or whatever you need
+#include <boost/msm/back/state_machine.hpp> // back-end
+#include <boost/msm/front/functor_row.hpp> // functors
+#include <boost/msm/front/state_machine_def.hpp> // front-end
+#include <mutex>
+#include <vector>
+
+#include "core/utils/plusplayer_log.h"
+#include "esplusplayer/esplusplayer.h"
+
+#define DEBUG_STATE_MANAGER
+#ifdef DEBUG_STATE_MANAGER
+#define STATE_TRACE(fmt, arg...) LOG_DEBUG(fmt, ##arg)
+#define STATE_TRACE_P(id, fmt, arg...) LOG_DEBUG_P(id, fmt, ##arg)
+#else
+#define STATE_TRACE(fmt, arg...)
+#endif
+
+namespace esplusplayer {
+
+namespace msm = boost::msm;
+namespace mpl = boost::mpl;
+// to make transition_table cleaner
+// to use 'Row' 'Defer'
+using namespace msm::front;
+
+namespace es_event {
+
+using Operator = std::function<bool(void)>;
+struct Open {
+ Open() = default;
+ explicit Open(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct Close {
+ Close() = default;
+ explicit Close(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct Start {
+ Start() = default;
+ explicit Start(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct Stop {
+ Stop() = default;
+ explicit Stop(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct SetStream {
+ SetStream() = default;
+ explicit SetStream(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct Prepare {
+ Prepare() = default;
+ explicit Prepare(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct ResConflict {
+ ResConflict() = default;
+ explicit ResConflict(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct Pause {
+ Pause() = default;
+ explicit Pause(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct Resume {
+ Resume() = default;
+ explicit Resume(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct Seek {
+ Seek() = default;
+ explicit Seek(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+struct PlaybackRate {
+ PlaybackRate() = default;
+ explicit PlaybackRate(const Operator _op) : op(_op) {}
+ const Operator op;
+};
+
+} // namespace es_event
+
+//
+// transition actions
+//
+struct ResetMaskStop {
+ template <class EVT, class FSM, class SourceState, class TargetState>
+ void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) {
+ STATE_TRACE("entering Action : ResetMaskStop");
+ fsm.event_stop_mask = false;
+ }
+};
+
+using namespace es_event;
+struct EsStateMachine : msm::front::state_machine_def<EsStateMachine> {
+ //
+ // Customizing a state machine / Getting more speed
+ // -http://www.boost.org/doc/libs/1_57_0/libs/msm/doc/HTML/ch03s02.html#d0e1147
+ //
+ typedef int no_exception_thrown; // no need for exception handling
+ // typedef int no_message_queue; // no need for message queue
+ // we need this, check ut.
+ typedef int activate_deferred_events; // manually enable deferred events
+
+ // when a transition is about to be taken, we already update our currently
+ // active state(s)
+ typedef msm::active_state_switch_before_transition active_state_switch_policy;
+
+ template <class Event, class FSM>
+ void on_entry(Event const&, FSM&) {
+ STATE_TRACE("entering: EsPlayer StateMachine");
+ }
+ template <class Event, class FSM>
+ void on_exit(Event const&, FSM&) {
+ STATE_TRACE("leaving: EsPlayer StateMachine");
+ }
+
+ //
+ // The list of FSM states
+ //
+ struct None : public msm::front::state<> {};
+ struct Idle : public msm::front::state<> {};
+ struct StreamReady : public msm::front::state<> {};
+ struct Ready : public msm::front::state<> {};
+ struct Playing : public msm::front::state<> {};
+ struct Paused : public msm::front::state<> {};
+
+ //
+ // guard conditions
+ //
+ struct AlwaysTrue {
+ template <class EVT, class FSM, class SourceState, class TargetState>
+ bool operator()(EVT const& evt, FSM&, SourceState& from, TargetState& to) {
+ return true;
+ }
+ };
+
+ struct CheckOp {
+ template <class EVT, class FSM, class SourceState, class TargetState>
+ bool operator()(EVT const& evt, FSM& fsm, SourceState& from,
+ TargetState& to) {
+ if (fsm.event_stop_mask) return false;
+ return evt.op ? evt.op() : true;
+ }
+ };
+
+ struct AlwaysRun {
+ template <class EVT, class FSM, class SourceState, class TargetState>
+ bool operator()(EVT const& evt, FSM& fsm, SourceState& from,
+ TargetState& to) {
+ return evt.op ? evt.op() : true;
+ }
+ };
+
+ //
+ // the initial state of the StateMachine. Must be defined
+ //
+ typedef None initial_state;
+
+ //
+ // Transition table
+ //
+ struct transition_table : mpl::vector<
+ // Start Event Next Action Guard
+ // +------------+----------------+-------------+------+--------+
+ Row< None , Open , Idle , ResetMaskStop, AlwaysRun >,
+ Row< Idle , Close , None , none, AlwaysRun >,
+ // Start Event Next Action Guard
+ // +------------+----------------+-------------+------------+--------+
+ Row< Idle , SetStream , StreamReady , ResetMaskStop, CheckOp >,
+ Row< StreamReady , SetStream , StreamReady , ResetMaskStop, CheckOp >,
+ Row< StreamReady , Prepare , Ready , none, CheckOp >,
+ // Start Event Next Action Guard
+ // +------------+----------------+-------------+-----+--------+
+ Row< Ready , Start , Playing , none, CheckOp >,
+ // Start Event Next Action Guard
+ // +------------+----------------+-------------+-----+---------+
+ Row< Ready , Pause , Paused , none, CheckOp >,
+ Row< Playing , Pause , Paused , none, CheckOp >,
+ Row< Paused , Resume , Playing , none, CheckOp >,
+ // Start Event Next Action Guard
+ // +------------+----------------+-------------+-----+--------+
+ Row< None , Close , None , none, AlwaysTrue >,
+ Row< Paused , Pause , Paused , none, AlwaysTrue >,
+ Row< Playing , Resume , Playing , none, AlwaysTrue >,
+ Row< Idle , Stop , Idle , none, AlwaysTrue >,
+ // Start Event Next Action Guard
+ // +-------------+--------------+--------------+-----+--------+
+ Row< StreamReady , Stop , Idle , none , none >,
+ Row< Ready , Stop , Idle , none , none >,
+ Row< Playing , Stop , Idle , none , none >,
+ Row< Paused , Stop , Idle , none , none >,
+ // Start Event Next Action Guard
+ // +-------------+----------------+----------+---------+-------+
+ Row< Ready , Seek , Ready , none, CheckOp >,
+ Row< Playing , Seek , Playing , none, CheckOp >,
+ Row< Paused , Seek , Paused , none, CheckOp >,
+ // Start Event Next Action Guard
+ // +-------------+----------------+----------+---------+-------+
+ Row< Ready , PlaybackRate , Ready , none , CheckOp >,
+ Row< Paused , PlaybackRate , Paused , none , CheckOp >,
+ Row< Playing , PlaybackRate , Playing , none , CheckOp >
+ // +-------------+----------------+--------------+---------+-------+
+ > {};
+
+ // Replaces the default no-transition response.
+ template <class FSM, class Event>
+ void no_transition(Event const& e, FSM& fsm, int state) {
+ LOG_ERROR("no transition on event[%s], check transition_table current[%d]",
+ typeid(e).name(), *fsm.current_state());
+ }
+
+ // Additional data section for customization
+ bool event_stop_mask = false;
+ bool is_preparing = false;
+}; // struct StateMachine
+
+class EsStateManager : private boost::noncopyable {
+ public:
+ EsStateManager() noexcept {}
+ ~EsStateManager() {}
+ void Start() {
+ std::lock_guard<std::mutex> lock(control_m_);
+ msm_.start();
+ stopped_ = false;
+ }
+ void* log_id_ = nullptr;
+
+ void Stop() {
+ // TODO(js4716.chun) :
+ // - this is for ut_meta_state_machine.cpp MessageQueueTest.
+ // - find out better solution.
+ while (GetState() != EsState::kNone) {
+ LOG_ERROR_P(this,
+ "waiting the state to none before stopping state-machine");
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ {
+ std::lock_guard<std::mutex> lock(control_m_);
+ stopped_ = true;
+ // need to check & wait message queue is empty
+ msm_.stop();
+ }
+ }
+
+ template <typename T>
+ bool ProcessEvent(const T& es_event) {
+ std::lock_guard<std::mutex> lock(control_m_);
+ if (stopped_) return false;
+
+ ProcessEventRet ret = msm::back::HANDLED_FALSE;
+ STATE_TRACE_P(this, "Transition Request, Current[%d]", GetStateEnum());
+ if (!(ret = msm_.process_event(es_event))) {
+ STATE_TRACE_P(this, "process_event failed ret[%d], Current[%d]", ret,
+ GetStateEnum());
+ return false;
+ }
+ if (ret == msm::back::HANDLED_GUARD_REJECT) {
+ STATE_TRACE_P(this,
+ "Guard Rejected, probably event.op failed Current[%d]",
+ GetStateEnum());
+ return false;
+ } else if (ret == msm::back::HANDLED_DEFERRED) {
+ STATE_TRACE_P(this, "Process_event Deffered");
+ // TODO(js4716.chun) : return값을 어떻게 해야하는가.
+ // - deferred된 이후 gaurd fail시 return값이 잘못 될 수 있다.
+ // - 이게 중요한가? 꼭 처리해야 하는가?
+ }
+ STATE_TRACE_P(this, "Process_event done Current[%d],ret[%d]",
+ GetStateEnum(), ret);
+ return true;
+ }
+
+ bool ProcessEventStop(const es_event::Stop& stop_event) {
+ std::unique_lock<std::mutex> lock(control_m_, std::defer_lock);
+ lock.try_lock();
+ if (stopped_) return false;
+ STATE_TRACE_P(this, "Transition Stop Requested, Current[%d]",
+ GetStateEnum());
+ msm_.event_stop_mask = true;
+ msm_.is_preparing = false;
+ if (!stop_event.op()) {
+ LOG_ERROR("Stop Operation failed");
+ return false;
+ }
+ ProcessEventRet ret = msm::back::HANDLED_FALSE;
+ STATE_TRACE_P(this, "Transition Request, Current[%d]", GetStateEnum());
+ if (!lock.owns_lock()) lock.lock();
+ if (!(ret = msm_.process_event(stop_event))) {
+ STATE_TRACE_P(this, "process_event_stop failed ret[%d], current[%d]", ret,
+ GetStateEnum());
+ return false;
+ }
+ STATE_TRACE_P(this, "Process_event done Current[%d],ret[%d]",
+ GetStateEnum(), ret);
+ return true;
+ }
+
+ EsState GetState() {
+ if (stopped_) return EsState::kNone;
+
+ unsigned int index = *msm_.current_state();
+ if (index >= state_vector_.size()) {
+ assert(0 && "invalid state id returned, something went wrong");
+ return EsState::kNone;
+ }
+ return state_vector_[index];
+ }
+
+ int GetStateEnum() {
+ if (stopped_) return static_cast<int>(EsState::kNone);
+
+ unsigned int index = *msm_.current_state();
+ if (index >= state_vector_.size()) {
+ assert(0 && "invalid state id returned, something went wrong");
+ return static_cast<int>(EsState::kNone);
+ }
+ return static_cast<int>(state_vector_[index]);
+ }
+
+ void SetPreparingState(bool val) { msm_.is_preparing = val; }
+
+ bool GetPreparingState() { return msm_.is_preparing; }
+
+ private:
+ std::mutex control_m_; // TODO(js4716.chun) : remove this if possible
+ bool stopped_ = true; // TODO(js4716.chun) : remove this if possible
+
+ // typedef enum {
+ // HANDLED_FALSE = 0,
+ // HANDLED_TRUE = 1,
+ // HANDLED_GUARD_REJECT = 2,
+ // HANDLED_DEFERRED = 4
+ // } HandledEnum;
+ using ProcessEventRet = msm::back::HandledEnum;
+ // Generating rules of state ids
+ // http://www.boost.org/doc/libs/1_65_1/libs/msm/doc/HTML/ch06s03.html#internals-state-id
+ const std::vector<EsState> state_vector_ = {EsState::kNone, // None
+ EsState::kIdle, // Idle
+ EsState::kIdle, // StreamReady
+ EsState::kReady, // Ready
+ EsState::kPlaying, // Playing
+ EsState::kPaused}; // Paused
+ msm::back::state_machine<EsStateMachine> msm_;
+}; // class StateManager
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_ESPLAYER_STATE_MANAGER_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "esplusplayer/elementary_stream.h"
+
+namespace {
+
+constexpr int kLittleEdian = 1234;
+constexpr int kBigEdian = 4321;
+
+} // namespace
+
+namespace esplusplayer {
+
+AudioStream::AudioStream() noexcept {
+ track_.type = kTrackTypeAudio;
+ track_.index = 0;
+ track_.active = true;
+}
+
+void AudioStream::SetMimeType(AudioMimeType mimetype) {
+ mimetype_ = mimetype;
+ SetMimeType_(mimetype);
+}
+
+
+void AudioStream::SetMimeType_(AudioMimeType mimetype) {
+ mimetype_ = mimetype;
+// LCOV_EXCL_START
+ switch (mimetype) {
+ case AudioMimeType::kAAC: {
+ track_.mimetype = "audio/mpeg";
+ track_.version = 2;
+ break;
+ }
+ case AudioMimeType::kMP2: {
+ track_.mimetype = "audio/mpeg";
+ track_.version = 1;
+ track_.layer = 2;
+ break;
+ }
+ case AudioMimeType::kMP3: {
+ track_.mimetype = "audio/mpeg";
+ track_.version = 1;
+ track_.layer = 3;
+ break;
+ }
+ case AudioMimeType::kAC3: {
+ track_.mimetype = "audio/x-ac3";
+ break;
+ }
+ case AudioMimeType::kEAC3: {
+ track_.mimetype = "audio/x-eac3";
+ break;
+ }
+ case AudioMimeType::kVORBIS: {
+ track_.mimetype = "audio/x-vorbis";
+ break;
+ }
+ case AudioMimeType::kOPUS: {
+ track_.mimetype = "audio/x-opus";
+ break;
+ }
+ case AudioMimeType::kPCM_S16LE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = true;
+ track_.endianness = kLittleEdian;
+ track_.sample_format = 16;
+ break;
+ }
+ case AudioMimeType::kPCM_S16BE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = true;
+ track_.endianness = kBigEdian;
+ track_.sample_format = 16;
+ break;
+ }
+ case AudioMimeType::kPCM_U16LE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = false;
+ track_.endianness = kLittleEdian;
+ track_.sample_format = 16;
+ break;
+ }
+ case AudioMimeType::kPCM_U16BE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = false;
+ track_.endianness = kBigEdian;
+ track_.sample_format = 16;
+ break;
+ }
+ case AudioMimeType::kPCM_S24LE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = true;
+ track_.endianness = kLittleEdian;
+ track_.sample_format = 24;
+ break;
+ }
+ case AudioMimeType::kPCM_S24BE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = true;
+ track_.endianness = kBigEdian;
+ track_.sample_format = 24;
+ break;
+ }
+ case AudioMimeType::kPCM_U24LE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = false;
+ track_.endianness = kLittleEdian;
+ track_.sample_format = 24;
+ break;
+ }
+ case AudioMimeType::kPCM_U24BE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = false;
+ track_.endianness = kBigEdian;
+ track_.sample_format = 24;
+ break;
+ }
+ case AudioMimeType::kPCM_S32LE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = true;
+ track_.endianness = kLittleEdian;
+ track_.sample_format = 32;
+ break;
+ }
+ case AudioMimeType::kPCM_S32BE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = true;
+ track_.endianness = kBigEdian;
+ track_.sample_format = 32;
+ break;
+ }
+ case AudioMimeType::kPCM_U32LE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = false;
+ track_.endianness = kLittleEdian;
+ track_.sample_format = 32;
+ break;
+ }
+ case AudioMimeType::kPCM_U32BE: {
+ track_.mimetype = "audio/x-raw";
+ track_.is_signed = false;
+ track_.endianness = kBigEdian;
+ track_.sample_format = 32;
+ break;
+ }
+ case AudioMimeType::kG711_MULAW: {
+ track_.mimetype = "audio/x-mulaw";
+ force_swdecoder_use_ = true;
+ break;
+ }
+ case AudioMimeType::kAC4: {
+ track_.mimetype = "audio/x-ac4";
+ break;
+ }
+ case AudioMimeType::kMpegH: {
+ track_.mimetype = "audio/x-gst-fourcc-mhm1";
+ break;
+ }
+ default:
+ track_.mimetype = "unknown";
+ break;
+ }
+// LCOV_EXCL_STOP
+}
+
+
+VideoStream::VideoStream() noexcept {
+ track_.type = kTrackTypeVideo;
+ track_.index = 0;
+ track_.active = true;
+}
+
+void VideoStream::SetMimeType(VideoMimeType mimetype) {
+ mimetype_ = mimetype;
+ SetMimeType_(mimetype);
+}
+
+void VideoStream::SetMimeType_(VideoMimeType mimetype) {
+ switch (mimetype) {
+// LCOV_EXCL_START
+ case VideoMimeType::kH263:
+ track_.mimetype = "video/x-h263";
+ break;
+ case VideoMimeType::kH264:
+ track_.mimetype = "video/x-h264";
+ break;
+ case VideoMimeType::kHEVC:
+ track_.mimetype = "video/x-h265";
+ break;
+ case VideoMimeType::kMPEG1: {
+ track_.mimetype = "video/mpeg";
+ track_.version = 1;
+ break;
+ }
+ case VideoMimeType::kMPEG2: {
+ track_.mimetype = "video/mpeg";
+ track_.version = 2;
+ break;
+ }
+ case VideoMimeType::kMPEG4: {
+ track_.mimetype = "video/mpeg";
+ track_.version = 4;
+ break;
+ }
+ case VideoMimeType::kVP8:
+ track_.mimetype = "video/x-vp8";
+ break;
+ case VideoMimeType::kVP9:
+ track_.mimetype = "video/x-vp9";
+ break;
+ case VideoMimeType::kWMV3:
+ track_.mimetype = "video/x-wmv";
+ track_.version = 3;
+ break;
+ case VideoMimeType::kAV1:
+ track_.mimetype = "video/x-av1";
+ break;
+ case VideoMimeType::kMJPEG:
+ track_.mimetype = "video/x-jpeg";
+ break;
+ default:
+ track_.mimetype = "unknown";
+ break;
+// LCOV_EXCL_STOP
+ }
+}
+
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include "esplusplayer/espacket.h"
+
+namespace esplusplayer {
+EsPacketPtr EsPacket::Create(const StreamType type,
+ std::shared_ptr<char> buffer,
+ const uint32_t buffer_size, const uint64_t pts,
+ const uint64_t duration,
+ const uint32_t hdr10p_size,
+ std::shared_ptr<char> hdr10p_data) {
+ return Ptr(new EsPacket(type, buffer, buffer_size, pts, duration, hdr10p_size,
+ hdr10p_data));
+}
+
+EsPacketPtr EsPacket::CreateEos(const StreamType type) {
+ return Ptr(new EsPacket(type, nullptr, 0, 0, 0, 0, nullptr));
+}
+
+EsPacket::EsPacket(const StreamType type, std::shared_ptr<char> buffer,
+ const uint32_t buffer_size, const uint64_t pts,
+ const uint64_t duration, const uint32_t hdr10p_size,
+ std::shared_ptr<char> hdr10p_data)
+ : type_(type),
+ buffer_size_(buffer_size),
+ pts_(pts),
+ duration_(duration),
+ hdr10p_metadata_size_(hdr10p_size) {
+ if (buffer) {
+ buffer_ = buffer;
+ }
+ if (hdr10p_data) {
+ hdr10p_metadata_ = hdr10p_data;
+ }
+}
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "esplayer/espacket_logger.h"
+
+#include <sstream>
+#include <thread>
+#include <utility>
+#include <cinttypes>
+
+#include "core/utils/plusplayer_log.h"
+
+namespace internal {
+enum class TizenBuiltImageType { Unknown, Debug, Release, Perf };
+static TizenBuiltImageType GetBuiltImageType() {
+ static TizenBuiltImageType image_type;
+ if (image_type != TizenBuiltImageType::Unknown) return image_type;
+// LCOV_EXCL_START
+ if (access("/etc/debug", F_OK) == 0) {
+ image_type = TizenBuiltImageType::Debug;
+ LOG_DEBUG("Debug Image");
+ } else if (access("/etc/release", F_OK) == 0) {
+ image_type = TizenBuiltImageType::Release;
+ LOG_DEBUG("Release Image");
+ } else if (access("/etc/perf", F_OK) == 0) {
+ image_type = TizenBuiltImageType::Perf;
+ LOG_DEBUG("Perf Image");
+ }
+// LCOV_EXCL_STOP
+ return image_type;
+}
+} // namespace internal
+
+namespace esplusplayer {
+std::string EsPacketLogger::GetStringFromLastPacketInfo_(
+ const StreamType type) const {
+ const int track_index = static_cast<int>(type);
+ const auto& pkt_info = last_packet_info_[track_index];
+ std::ostringstream oss;
+ oss << "valid? [" << (pkt_info.valid ? 'T' : 'F') << "], ";
+ oss << "size [" << (pkt_info.size) << "], ";
+ oss << "pts [" << (pkt_info.pts) << "], ";
+ oss << "duration [" << (pkt_info.duration) << "], ";
+ oss << "is_eos? [" << (pkt_info.is_eos ? 'T' : 'F') << "]";
+ return oss.str();
+}
+
+void EsPacketLogger::StorePacketInfo(const EsPacketPtr& packet) {
+ if (packet == nullptr) return;
+ const int track_index = static_cast<int>(packet->GetType());
+ if (track_index >= static_cast<int>(StreamType::kMax)) return;
+
+ std::lock_guard<std::mutex> lk(mtx_);
+
+ auto& count = last_log_packet_count_[track_index];
+ count++;
+
+ auto& last_pkt_info = last_packet_info_[track_index];
+ last_pkt_info.valid = true;
+ last_pkt_info.size = packet->GetSize();
+ last_pkt_info.pts = packet->GetPts();
+ last_pkt_info.duration = packet->GetDuration();
+ last_pkt_info.is_eos = packet->IsEosPacket();
+}
+
+void EsPacketLogger::PrintStoredPacketInfo(const StreamType type, bool force) {
+ const bool printable = ((force) || (internal::GetBuiltImageType() ==
+ internal::TizenBuiltImageType::Debug));
+ if (printable == false) return;
+
+ const int track_index = static_cast<int>(type);
+ if (track_index >= static_cast<int>(StreamType::kMax)) return;
+
+ std::lock_guard<std::mutex> lk(mtx_);
+
+ const auto& count = last_log_packet_count_[track_index];
+ const bool in_timing =
+ (force) || (count == 1) || (count % kLogBufferThreshold == 0);
+ if (in_timing) {
+ const auto logmsg = GetStringFromLastPacketInfo_(type);
+ LOG_INFO("<pktinfo - %" PRId64" %s> [%s] %s", count, (force ? ": last" : ""),
+ (type == StreamType::kAudio ? "AUDIO" : "VIDEO"), logmsg.c_str());
+ }
+}
+
+void EsPacketLogger::ResetLog(const StreamType type) {
+ std::lock_guard<std::mutex> lk(mtx_);
+
+ last_packet_info_[static_cast<int>(type)].valid = false;
+ last_log_packet_count_[static_cast<int>(type)] = 0;
+}
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+// to manipulate TrackRenderer objects
+// to provide optimized control of TrackRenderer
+
+#include "esplayer/esplayer.h"
+
+#include <boost/scope_exit.hpp>
+#include <cassert>
+#include <chrono>
+#include <fstream>
+#include <functional>
+#include <memory>
+#include <sstream>
+#include <thread>
+#include <cinttypes>
+
+#ifdef SOUNDBAR_PRODUCT
+#include "avoc_av_audio.h"
+#else
+#include "avoc.h"
+#endif
+#include "core/gst_utils.h"
+#include "core/track_util.h"
+#include "core/utils/caf_logger.h"
+#include "core/utils/performance_checker.h"
+#include "core/utils/plusplayer_cfg.h"
+#include "core/utils/plusplayer_log.h"
+#include "core/videoframetypestrategy.h"
+#include "esplayer/esplayer_drm.h"
+#include "json/json.h"
+
+namespace esplusplayer {
+
+namespace es_conf {
+
+// get values from /etc/multimedia/esplusplayer.ini
+static std::once_flag loaded;
+static std::map<std::string, bool> ini_property;
+
+bool LoadIniFile();
+void LoadIniProperty(const Json::Value& root);
+
+} // namespace es_conf
+
+namespace util {
+
+std::uint64_t ConvertMsToNs(std::uint64_t ms) {
+ constexpr std::uint64_t ns_unit = 1000000;
+ if (ms * ns_unit > G_MAXUINT64) return G_MAXUINT64;
+ return ms * ns_unit;
+}
+
+std::uint64_t ConvertNsToMs(std::uint64_t ns) {
+ constexpr std::uint64_t ms_unit = 1000000;
+ return ns / ms_unit;
+}
+
+std::int64_t ConvertMsToNs(std::int64_t ms) {
+ constexpr std::int64_t ns_unit = 1000000;
+ if (ms * ns_unit > G_MAXINT64) return G_MAXINT64;
+ return ms * ns_unit;
+}
+
+std::int64_t ConvertNsToMs(std::int64_t ns) {
+ constexpr std::int64_t ms_unit = 1000000;
+ return ns / ms_unit;
+}
+
+std::uint64_t ConvertUsToNs(std::uint64_t us) {
+ constexpr std::uint64_t ns_unit = 1000;
+ if (us * ns_unit > G_MAXUINT64) return G_MAXUINT64;
+ return us * ns_unit;
+}
+
+std::uint64_t ConvertNsToUs(std::uint64_t ns) {
+ constexpr std::uint64_t us_unit = 1000;
+ return ns / us_unit;
+}
+
+std::int64_t ConvertUsToNs(std::int64_t us) {
+ constexpr std::int64_t ns_unit = 1000;
+ if (us * ns_unit > G_MAXINT64) return G_MAXINT64;
+ return us * ns_unit;
+}
+
+std::int64_t ConvertNsToUs(std::int64_t ns) {
+ constexpr std::int64_t us_unit = 1000;
+ return ns / us_unit;
+}
+
+// LCOV_EXCL_START
+std::string GetStringFromMatroskaColor(const MatroskaColor& color_info) {
+ std::ostringstream oss;
+ oss << "matrixCoefficients:" << color_info.matrix_coefficients
+ << " bitsPerChannel:" << color_info.bits_per_channel
+ << " chromaSubsamplingHorz:" << color_info.chroma_subsampling_horizontal
+ << " chromaSubsamplingVert:" << color_info.chroma_subsampling_vertical
+ << " cbSubsamplingHorz:" << color_info.cb_subsampling_horizontal
+ << " cbSubsamplingVert:" << color_info.cb_subsampling_vertical
+ << " chromaSitingHorz:" << color_info.chroma_siting_horizontal
+ << " chromaSitingVert:" << color_info.chroma_siting_vertical
+ << " range:" << color_info.range
+ << " transferCharacteristics:" << color_info.transfer_characteristics
+ << " primaries:" << color_info.primaries
+ << " maxCLL:" << color_info.max_cll << " maxFALL:" << color_info.max_fall
+ << " RX:" << color_info.metadata.primary_r_chromaticity_x
+ << " RY:" << color_info.metadata.primary_r_chromaticity_y
+ << " GX:" << color_info.metadata.primary_g_chromaticity_x
+ << " GY:" << color_info.metadata.primary_g_chromaticity_y
+ << " BX:" << color_info.metadata.primary_b_chromaticity_x
+ << " BY:" << color_info.metadata.primary_b_chromaticity_y
+ << " wX:" << color_info.metadata.white_point_chromaticity_x
+ << " wY:" << color_info.metadata.white_point_chromaticity_y
+ << " luminanceMax:" << color_info.metadata.luminance_max
+ << " luminanceMin:" << color_info.metadata.luminance_min
+ << " isHDR10p:" << color_info.is_hdr_10p;
+ return oss.str();
+}
+// LCOV_EXCL_STOP
+
+} // namespace util
+
+namespace internal {
+// const std::uint64_t kMaxByteOfVideoSrcQueue = 70 * 1024 * 1024; // 70 MB
+// const std::uint64_t kMaxByteOfAudioSrcQueue = 10 * 1024 * 1024; // 10 MB
+// const std::uint64_t kMaxTimeOfVideoSrcQueue = 10000000000; // 10 s
+// const std::uint64_t kMaxTimeOfAudioSrcQueue = 10000000000; // 10 s
+
+constexpr uint32_t kNdecodingMode = 0x04;
+
+enum VolumeLevel { kVolumeMin = 0, kVolumeMax = 100 };
+constexpr int kMaxFhdWidth = 1920;
+constexpr int kMaxFhdHeight = 1080;
+
+inline bool IsPcmMimeType(const std::string& mimetype) {
+ return (mimetype.find("audio/x-raw") != std::string::npos);
+}
+inline bool IsForcedUnsetTz(const Track& track, const std::string& id) {
+ if ((track.type == kTrackTypeAudio) && strstr(id.c_str(), "netflix")) {
+ return true;
+ }
+ return false;
+}
+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 IsAvailableCodecSwitch(const Track& track) {
+ if (internal::IsAacCodec(track) || internal::IsAc3Codec(track.mimetype) ||
+ internal::IsEac3Codec(track.mimetype))
+ return true;
+ return false;
+}
+
+int ResetEosStatus(const TrackType& type, int eos_status) {
+ if (type == kTrackTypeVideo)
+ eos_status &= ~EosStatus::kVideoEos;
+ else if (type == kTrackTypeAudio)
+ eos_status &= ~EosStatus::kAudioEos;
+ return eos_status;
+}
+
+// LCOV_EXCL_START
+void MakeTrustZoneTracks(std::vector<Track>& tracks,
+ const std::string& app_id) {
+ for (auto& track : tracks) {
+ const bool is_already_tz_type =
+ (track.mimetype.find("_tz", track.mimetype.length() - 3) !=
+ std::string::npos);
+ if (is_already_tz_type) {
+ continue;
+ }
+ track.streamtype = track.mimetype;
+ if (track.use_swdecoder || IsPcmMimeType(track.mimetype) ||
+ IsForcedUnsetTz(track, app_id))
+ continue;
+ else
+ track.mimetype = track.mimetype + "_tz";
+ }
+}
+// LCOV_EXCL_STOP
+
+void UpdateCodecTypeTracks(std::vector<Track>& tracks,
+ const PlayerAudioCodecType& audio_codec_type,
+ const PlayerVideoCodecType& video_codec_type,
+ bool force_audio_swdecoder_use) {
+ for (auto& track : tracks) {
+ switch (track.type) {
+ case kTrackTypeAudio: {
+ if (audio_codec_type == kPlayerAudioCodecTypeSW ||
+ force_audio_swdecoder_use)
+ track.use_swdecoder = true;
+ else
+ track.use_swdecoder = false;
+ break;
+ }
+ case kTrackTypeVideo: {
+ if (video_codec_type == kPlayerVideoCodecTypeSW)
+ track.use_swdecoder = true;
+ else
+ track.use_swdecoder = false;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+inline bool IsSupportedDrmType(const drm::Type& drm_type) {
+ static const std::map<drm::Type, bool> kSupportedDrmType = {
+ {drm::Type::kPlayready, true},
+ };
+ if (kSupportedDrmType.count(drm_type) == 0) return false;
+ return kSupportedDrmType.at(drm_type);
+}
+inline void ResetDrmProperty(drm::Property& drm_property) {
+ drm_property = drm::Property();
+}
+inline StreamType ConvertToStreamType(TrackType type) {
+ return (type == kTrackTypeAudio) ? StreamType::kAudio : StreamType::kVideo;
+}
+struct AppsrcQueueSizeOption {
+ std::uint64_t current_size = 0;
+ std::uint64_t max_size = 0;
+ std::uint32_t threshold = 0;
+};
+inline bool IsUnderRun(AppsrcQueueSizeOption& byte_based,
+ AppsrcQueueSizeOption& time_based) {
+ bool need_data_by_byte = false, need_data_by_time = false;
+ need_data_by_byte = (byte_based.max_size > 0) &&
+ (byte_based.current_size * 100 / byte_based.max_size <=
+ byte_based.threshold);
+ need_data_by_time = (time_based.max_size > 0) &&
+ (time_based.current_size * 100 / time_based.max_size <=
+ time_based.threshold);
+ if (need_data_by_byte || need_data_by_time)
+ return true;
+ else
+ return false;
+}
+inline bool IsLowLatencyModeDisableAVSync(std::uint32_t mode) {
+ constexpr std::uint32_t kAVSync = kLowLatencyModeDisableAVSync;
+ return (mode & kAVSync) ? true : false;
+}
+inline bool IsLowLatencyModeDisablePreroll(std::uint32_t mode) {
+ constexpr std::uint32_t kPreroll = kLowLatencyModeDisablePreroll;
+ return (mode & kPreroll) ? true : false;
+}
+inline bool IsLowLatencyMode(std::uint32_t mode) {
+ return (mode != static_cast<std::uint32_t>(kLowLatencyModeNone)) ? true
+ : false;
+}
+inline bool IsSupportedTsOffset(std::uint32_t mode) {
+ return IsLowLatencyMode(mode) && !IsLowLatencyModeDisableAVSync(mode) ? true
+ : false;
+}
+
+inline bool IsLowLatencyModeEnableGameMode(std::uint32_t mode) {
+ constexpr std::uint32_t kGameMode = kLowLatencyModeEnableGameMode ^
+ kLowLatencyModeAudio ^
+ kLowLatencyModeVideo;
+ return (mode & kGameMode) ? true : false;
+}
+
+inline bool IsLowLatencyModeForCatchUp(std::uint32_t mode) {
+ return IsLowLatencyModeDisableAVSync(mode) &&
+ IsLowLatencyModeEnableGameMode(mode)
+ ? true
+ : false;
+}
+
+inline bool IsExclusiveLowLatencyMode(std::uint32_t current_mode,
+ std::uint32_t set_mode) {
+ std::uint32_t exclusive_mode = 0;
+ exclusive_mode |= static_cast<std::uint32_t>(kLowLatencyModeEnableGameMode);
+ exclusive_mode |=
+ static_cast<std::uint32_t>(kLowLatencyModeDisableVideoQuality);
+
+ std::uint32_t new_mode = current_mode | set_mode;
+ return (exclusive_mode == (new_mode & exclusive_mode)) ? true : false;
+}
+} // namespace internal
+
+EsPlayer::EsPlayer() {
+ std::call_once(es_conf::loaded, [this]() { es_conf::LoadIniFile(); });
+ if (CafLogger::Initialize() != true) {
+ LOG_INFO("CAF Dbus not connect.");
+ }
+}
+
+EsPlayer::~EsPlayer() {
+ LOG_ENTER_P(this);
+ Close();
+ LOG_LEAVE_P(this);
+}
+
+bool EsPlayer::Open() {
+ LOG_INFO_P(this, "state manager > %p", &state_manager_);
+ Init_();
+ state_manager_.Start();
+ auto op = [this]() noexcept -> bool {
+ const auto start = performance_checker::Start();
+ if (trackrenderer_) {
+ assert(0 && "trackrenderer already exist");
+ }
+ msg_handler_task_ =
+ std::async(std::launch::async, &EsPlayer::MsgTask_, this);
+ trackrenderer_ = TrackRendererAdapter::Create();
+ assert(trackrenderer_);
+
+ trackrenderer_->RegisterListenerForEsplayer(
+ trackrenderer_event_listener_.get());
+ performance_checker::End(start, "Open");
+ return true;
+ };
+ CafLogger::SetUniqueNumber();
+ caf_unique_number = CafLogger::GetUniqueNumber();
+ CafLogger::LogMessage(CafEventType::kIdle, caf_unique_number);
+
+ es_event::Open event{op};
+ return state_manager_.ProcessEvent(event);
+}
+
+bool EsPlayer::Close() {
+ LOG_ENTER_P(this);
+ std::lock_guard<std::mutex> lk(submit_mutex_);
+ if (state_manager_.GetState() >= EsState::kIdle) {
+ Stop();
+ }
+ auto op = [this]() noexcept {
+ if (is_msg_task_stop_ == false) {
+ {
+ std::unique_lock<std::mutex> msg_mutex(msg_task_mutex_);
+ is_msg_task_stop_ = true;
+ }
+ msg_task_cv_.notify_one();
+ if (msg_handler_task_.valid()) msg_handler_task_.wait();
+ }
+
+ if (trackrenderer_) trackrenderer_.reset();
+ ResetContextForClose_();
+ LOG_LEAVE_P(this);
+ return true;
+ };
+ es_event::Close event{op};
+ state_manager_.ProcessEvent(event);
+ state_manager_.Stop();
+ return true;
+}
+
+bool EsPlayer::Deactivate(const StreamType type) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ if (!enable_audio_pipeline_handle_ && type == StreamType::kAudio) {
+ LOG_ERROR_P(
+ this, "can't deactivate audio stream, mixer will control audio stream");
+ return false;
+ }
+#endif
+ if (!trackrenderer_->Deactivate(static_cast<TrackType>(type))) {
+ return false;
+ }
+ {
+ std::lock_guard<std::mutex> lock(eos_mutex_);
+ switch (type) {
+ case StreamType::kAudio:
+ eos_status_ |= EosStatus::kAudioEos;
+ break;
+ case StreamType::kVideo:
+ eos_status_ |= EosStatus::kVideoEos;
+ break;
+ default:
+ break;
+ }
+ }
+ for (auto& track : track_) {
+ if (track.type == static_cast<TrackType>(type)) {
+ track.active = false;
+ }
+ }
+ return true;
+}
+
+bool EsPlayer::Activate(const StreamType type) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ if (!enable_audio_pipeline_handle_ && type == StreamType::kAudio) {
+ LOG_ERROR_P(this,
+ "can't activate audio stream, mixer will control audio stream");
+ return false;
+ }
+#endif
+ if (!track_.empty()) {
+ auto has_track = [type](const Track& item) -> bool {
+ return item.type == static_cast<TrackType>(type);
+ };
+ auto target = std::find_if(track_.begin(), track_.end(), has_track);
+ if (target == track_.end()) {
+ LOG_ERROR_P(this, "there is no track to activate");
+ return false;
+ }
+ if (target->active != false) {
+ LOG_ERROR_P(this, "The track should be deactivated in advance.");
+ return false;
+ }
+ target->active = true;
+ internal::UpdateCodecTypeTracks(track_, audio_codec_type_,
+ video_codec_type_,
+ force_audio_swdecoder_use_);
+ if (drm_property_.external_decryption) {
+ internal::MakeTrustZoneTracks(track_, app_info_.id);
+ }
+ SetTrackRendererAttributes_();
+#ifndef IS_AUDIO_PRODUCT
+ if (type == StreamType::kVideo) {
+ if (mixer_ticket_)
+ trackrenderer_->SetVideoFrameBufferType(
+ VideoFrameTypeStrategyPtr(new RawVideoFrameTypeStrategy()));
+ else
+ trackrenderer_->SetVideoFrameBufferType(VideoFrameTypeStrategyPtr(
+ new DefaultVideoFrameTypeStrategy(vidoe_frame_buffer_type_)));
+ }
+#endif
+ if (!trackrenderer_->Activate(target->type, *target)) {
+ target->active = false;
+ return false;
+ }
+ eos_status_ =
+ internal::ResetEosStatus(static_cast<TrackType>(type), eos_status_);
+ return true;
+ }
+ return false;
+}
+
+bool EsPlayer::Start() {
+ LOG_ENTER_P(this);
+ if (is_stopped_) {
+ LOG_ERROR_P(this, "Stop already, no need to Start,leave...");
+ return false;
+ }
+ auto op = [this]() noexcept {
+ if (!trackrenderer_->Start()) {
+ return false;
+ }
+ return true;
+ };
+
+ CafLogger::LogMessage(CafEventType::kPlaying, caf_unique_number);
+
+ es_event::Start event{op};
+ return state_manager_.ProcessEvent(event);
+}
+
+bool EsPlayer::Stop() {
+ LOG_ENTER_P(this);
+ is_stopped_ = true;
+ auto stop = [this]() noexcept -> bool {
+ const auto start = performance_checker::Start();
+ if (trackrenderer_) trackrenderer_->Stop();
+ ResetContextForStop_();
+#ifndef IS_AUDIO_PRODUCT
+ if (mixer_ticket_) mixer_ticket_.reset();
+#endif
+ performance_checker::End(start, "Stop");
+ return true;
+ };
+ for (const auto& track : track_) {
+ es_packet_logger_.PrintStoredPacketInfo(
+ internal::ConvertToStreamType(track.type), true);
+ }
+ es_event::Stop event{stop};
+ bool res = state_manager_.ProcessEventStop(event);
+
+ if (preparetask_.valid()) {
+ LOG_INFO_P(this, "Stopped , Wait Prepare() finish...");
+ preparetask_.wait();
+ LOG_INFO_P(this, "Wait , Wait Prepare() Done...");
+ }
+
+ CafLogger::LogMessage(CafEventType::kIdle, caf_unique_number);
+ CafLogger::StopLoggingThread();
+
+ return res;
+}
+
+void EsPlayer::SetTrackRendererAttributes_() {
+ if (trackrenderer_ == nullptr) return;
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoQueueMaxByte,
+ src_queue_size_.kMaxByteOfVideoSrcQueue);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kAudioQueueMaxByte,
+ src_queue_size_.kMaxByteOfAudioSrcQueue);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoMinByteThreshold,
+ src_queue_size_.kMinByteThresholdOfVideoSrcQueue);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kAudioMinByteThreshold,
+ src_queue_size_.kMinByteThresholdOfAudioSrcQueue);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoQueueMaxTime,
+ util::ConvertMsToNs(src_queue_size_.kMaxTimeOfVideoSrcQueue));
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kAudioQueueMaxTime,
+ util::ConvertMsToNs(src_queue_size_.kMaxTimeOfAudioSrcQueue));
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoMinTimeThreshold,
+ src_queue_size_.kMinTimeThresholdOfVideoSrcQueue);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kAudioMinTimeThreshold,
+ src_queue_size_.kMinTimeThresholdOfAudioSrcQueue);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kUnlimitedMaxBufferMode,
+ unlimited_max_buffer_mode_);
+ trackrenderer_->SetAttribute(TrackRendererAdapter::Attribute::kLowLatencyMode,
+ low_latency_mode_);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoFramePeekMode,
+ video_frame_peek_mode_);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kAccurateSeekMode, accurate_seek_mode_);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoPreDisplayMode,
+ video_pre_display_mode_);
+ if (resume_time_.is_set) {
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kStartRenderingTime,
+ resume_time_.time);
+ resume_time_.is_set = false;
+ }
+ trackrenderer_->SetAttribute(TrackRendererAdapter::Attribute::kFmmMode,
+ fmm_mode_);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kAlternativeVideoResource,
+ alternative_video_resource_);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoDecodingMode,
+ video_decoding_mode_);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoProgressiveMode,
+ set_video_progressive_);
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kPlayerTimeUnitType,
+ static_cast<std::uint32_t>(time_unit_type));
+}
+
+// LCOV_EXCL_START
+#ifndef IS_AUDIO_PRODUCT
+bool EsPlayer::PrepareVideoMixingMode_(std::vector<Track>* tracks) {
+ LOG_ENTER_P(this);
+ mixer_ticket_->Prepare();
+ if (enable_rsc_alloc_handle_) return true;
+ ResourceType type = ResourceType::kHwMain;
+ if (!mixer_ticket_->GetAvailableResourceType(ResourceCategory::kVideoDecoder,
+ &type)) {
+ LOG_ERROR_P(this, "no available resource");
+ return false;
+ }
+ if (!mixer_ticket_->Alloc(ResourceCategory::kVideoDecoder, type)) {
+ LOG_ERROR_P(this, "fail to alloc resource [%d]", static_cast<int>(type));
+ return false;
+ }
+
+ if (type == ResourceType::kHwSub) {
+ alternative_video_resource_ = 1;
+ } else if (type == ResourceType::kSw) {
+ for (auto it = tracks->begin(); it != tracks->end(); ++it) {
+ if (it->type == kTrackTypeVideo) {
+ it->use_swdecoder = true;
+ break;
+ }
+ }
+ } else if (type == ResourceType::kNdecoder) {
+ video_decoding_mode_ = internal::kNdecodingMode;
+ }
+ return true;
+}
+#endif
+// LCOV_EXCL_START
+
+bool EsPlayer::Prepare_() {
+ LOG_ENTER_P(this);
+ if (is_stopped_) {
+ LOG_ERROR_P(this, "Stop already, no need to prepare,leave...");
+ return false;
+ }
+ auto op = [this]() noexcept -> bool {
+ const auto start = performance_checker::Start();
+
+ internal::UpdateCodecTypeTracks(track_, audio_codec_type_,
+ video_codec_type_,
+ force_audio_swdecoder_use_);
+ if (drm_property_.external_decryption) {
+ internal::MakeTrustZoneTracks(track_, app_info_.id);
+ }
+ std::vector<Track> active_track;
+ if (!track_util::GetActiveTrackList(track_, active_track)) {
+ return false;
+ }
+ trackrenderer_->SetIniProperty(es_conf::ini_property);
+#ifndef IS_AUDIO_PRODUCT
+ if (mixer_ticket_) {
+ trackrenderer_->SetVideoFrameBufferType(
+ VideoFrameTypeStrategyPtr(new RawVideoFrameTypeStrategy()));
+ if (!PrepareVideoMixingMode_(&active_track)) {
+ LOG_ERROR_P(this, "fail to prepare mixing mode");
+ return false;
+ }
+ }
+ std::unique_lock<std::mutex> lock(audio_focus_m_);
+ if (!enable_audio_pipeline_handle_ && !is_audio_focused_) {
+ for (auto it = active_track.begin(); it != active_track.end(); ++it) {
+ if (it->type == kTrackTypeAudio) {
+ active_track.erase(it);
+ LOG_INFO_P(this, "erase audio track is_audio_focused_ [%d]",
+ is_audio_focused_);
+ break;
+ }
+ }
+ }
+#endif
+ for (const auto& track : active_track) {
+ switch (track.type) {
+ case kTrackTypeAudio: {
+ std::lock_guard<std::mutex> lock2(eos_mutex_);
+ eos_status_ = internal::ResetEosStatus(kTrackTypeAudio, eos_status_);
+ need_data_[track.type].mask |= kNeedDataMaskByPrepare;
+ break;
+ }
+ case kTrackTypeVideo: {
+ std::lock_guard<std::mutex> lock2(eos_mutex_);
+ eos_status_ = internal::ResetEosStatus(kTrackTypeVideo, eos_status_);
+ need_data_[track.type].mask |= kNeedDataMaskByPrepare;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ SetTrackRendererAttributes_();
+ trackrenderer_->SetTrack(active_track);
+ if (!trackrenderer_->Prepare()) {
+ return false;
+ }
+ performance_checker::End(start, "Prepare");
+ return true;
+ };
+
+ CafLogger::StartLoggingThread();
+ CafLogger::LogMessage(CafEventType::kReady, caf_unique_number);
+
+ es_event::Prepare event{op};
+ if (!state_manager_.ProcessEvent(event)) {
+ return false;
+ }
+ LOG_LEAVE_P(this);
+ return true;
+}
+
+void EsPlayer::PrepareTask_() {
+ bool ret = Prepare_();
+
+ state_manager_.SetPreparingState(false);
+ if (eventlistener_) {
+ LOG_INFO_P(this, "Prepare completely, call OnPrepareDone(%d)", ret);
+ eventlistener_->OnPrepareDone(ret, eventlistener_userdata_);
+ }
+
+ kpi::CodecLogger logger;
+ kpi::EsCodecLoggerKeys event_keys = MakeKpiKeys_();
+ logger.SendKpi(ret, event_keys);
+ LOG_LEAVE_P(this);
+}
+
+bool EsPlayer::PrepareAsync() {
+ LOG_ENTER_P(this);
+ state_manager_.SetPreparingState(true);
+ preparetask_ = std::async(std::launch::async, &EsPlayer::PrepareTask_, this);
+ if (!preparetask_.valid()) {
+ state_manager_.SetPreparingState(false);
+ return false;
+ }
+ return true;
+}
+
+bool EsPlayer::Pause() {
+ LOG_ENTER_P(this);
+ if (is_stopped_) {
+ LOG_ERROR_P(this, "Stop already, no need to pause,leave...");
+ return false;
+ }
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ auto op = [this]() noexcept -> bool {
+ if (!trackrenderer_) return false;
+ if (!trackrenderer_->Pause()) {
+ return false;
+ }
+ return true;
+ };
+ for (const auto& track : track_) {
+ es_packet_logger_.PrintStoredPacketInfo(
+ internal::ConvertToStreamType(track.type), true);
+ }
+
+ CafLogger::LogMessage(CafEventType::kPaused, caf_unique_number);
+
+ es_event::Pause event{op};
+ return state_manager_.ProcessEvent(event);
+}
+
+bool EsPlayer::Resume() {
+ LOG_ENTER_P(this);
+ if (is_stopped_) {
+ LOG_ERROR_P(this, "Stop already, no need to Resume,leave...");
+ return false;
+ }
+ if (state_manager_.GetState() <= EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ if (is_resource_conflicted_) {
+ LOG_ERROR_P(this, "Resuem fail resource conflicted");
+ return false;
+ }
+ auto op = [this]() noexcept -> bool {
+ if (!trackrenderer_) return false;
+ if (!trackrenderer_->Resume()) {
+ return false;
+ }
+ return true;
+ };
+ for (const auto& track : track_) {
+ es_packet_logger_.PrintStoredPacketInfo(
+ internal::ConvertToStreamType(track.type), true);
+ }
+
+ CafLogger::LogMessage(CafEventType::kPlaying, caf_unique_number);
+
+ es_event::Resume event{op};
+ return state_manager_.ProcessEvent(event);
+}
+
+bool EsPlayer::Seek(const uint64_t time) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ if (state_manager_.GetState() == EsState::kIdle) {
+ if (state_manager_.GetPreparingState()) {
+ LOG_ERROR_P(this, "Invalid State , during preparing");
+ return false;
+ }
+ LOG_ERROR("%p resume time [%" PRId64 " ]", this, time);
+ resume_time_.is_set = true;
+ resume_time_.time = time;
+ return true;
+ }
+ is_seek_done_need_drop = true;
+ if (time_unit_type == kPlayerTimeUnitTypeMs)
+ LOG_DEBUG("%p [ENTER] seek time [%" PRId64 " ms]", this, time);
+ else if (time_unit_type == kPlayerTimeUnitTypeUs)
+ LOG_DEBUG("%p [ENTER] seek time [%" PRId64 " us]", this, time);
+ for (const auto& track : track_) {
+ eos_status_ = internal::ResetEosStatus(track.type, eos_status_);
+ es_packet_logger_.PrintStoredPacketInfo(
+ internal::ConvertToStreamType(track.type), true);
+ }
+ auto op = [this, time]() -> bool {
+ if (!trackrenderer_->Seek(time, current_playback_rate_,
+ current_audio_mute_)) {
+ return false;
+ }
+ return true;
+ };
+ es_event::Seek event{op};
+ bool ret = state_manager_.ProcessEvent(event);
+ is_seek_done_need_drop = false;
+
+ if (eventlistener_) {
+ if (internal::IsLowLatencyModeDisableAVSync(low_latency_mode_) ||
+ internal::IsLowLatencyModeDisablePreroll(low_latency_mode_)) {
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnSeekDone,
+ eventlistener_, std::placeholders::_1);
+ auto msg = es_msg::Simple::Make(listener, eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(msg_task_mutex_);
+ msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ msg_task_cv_.notify_one();
+ }
+ }
+ LOG_DEBUG("%p, [LEAVE] seek end ", this);
+ return ret;
+}
+
+void EsPlayer::SetAppInfo(const PlayerAppInfo& app_info) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return;
+ }
+ app_info_ = app_info;
+ trackrenderer_->SetAppInfo(app_info);
+ LOG_INFO("Appid [%s]", app_info.id.c_str());
+ CafLogger::SetAppId(app_info.id);
+}
+
+void EsPlayer::SetAppInfoEx(const PlayerAppInfoEx& app_info) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return;
+ }
+ app_info_.id = app_info.id;
+ app_info_.version = app_info.version;
+ app_info_.type = app_info.type;
+ trackrenderer_->SetAppInfoEx(app_info);
+ LOG_INFO("Appid [%s]", app_info_.id.c_str());
+ CafLogger::SetAppId(app_info_.id);
+}
+
+bool EsPlayer::SetPlaybackRate(const double rate, const bool audio_mute) {
+ LOG_ENTER_P(this);
+
+ if (rate <= 0 || rate > 2.0) {
+ LOG_ERROR_P(this, "Not a valid PlaybackRate");
+ return false;
+ }
+
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+
+ auto op = [this, rate, audio_mute]() -> bool {
+ if (!trackrenderer_->SetPlaybackRate(rate, audio_mute)) {
+ return false;
+ }
+ current_playback_rate_ = rate;
+ current_audio_mute_ = audio_mute;
+ return true;
+ };
+ es_event::PlaybackRate event{op};
+ return state_manager_.ProcessEvent(event);
+
+ LOG_LEAVE_P(this);
+ return true;
+}
+
+bool EsPlayer::SetDisplay(const DisplayType& type, void* obj) {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ if (mixer_ticket_) mixer_ticket_.reset();
+#endif
+ return trackrenderer_->SetDisplay(type, obj);
+}
+
+// LCOV_EXCL_START
+#ifndef IS_AUDIO_PRODUCT
+bool EsPlayer::SetDisplay(const DisplayType& type, MixerTicket* handle) {
+ if (type == DisplayType::kMixer) {
+ LOG_INFO_P(this, "Create MixerTicket");
+ mixer_ticket_.reset(handle);
+ mixer_ticket_->RegisterListener(mixer_event_listener_.get());
+ if (mixer_ticket_->IsAudioFocusHandler())
+ enable_audio_pipeline_handle_ = false;
+ if (mixer_ticket_->IsRscAllocHandler()) enable_rsc_alloc_handle_ = false;
+ trackrenderer_->SetDisplay(DisplayType::kNone, nullptr);
+ }
+ return true;
+}
+#endif
+
+bool EsPlayer::SetDisplay(const DisplayType& type, void* ecore_wl2_window,
+ const int x, const int y, const int w, const int h) {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ if (mixer_ticket_) mixer_ticket_.reset();
+#endif
+ return trackrenderer_->SetDisplay(type, ecore_wl2_window, x, y, w, h);
+}
+
+bool EsPlayer::SetDisplaySubsurface(const DisplayType& type,
+ void* ecore_wl2_subsurface, const int x,
+ const int y, const int w, const int h) {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ if (mixer_ticket_) mixer_ticket_.reset();
+#endif
+ return trackrenderer_->SetDisplaySubsurface(type, ecore_wl2_subsurface, x, y,
+ w, h);
+}
+
+bool EsPlayer::SetDisplay(const DisplayType& type, unsigned int surface_id,
+ const int x, const int y, const int w, const int h) {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ if (mixer_ticket_) mixer_ticket_.reset();
+#endif
+ return trackrenderer_->SetDisplay(type, surface_id, x, y, w, h);
+}
+// LCOV_EXCL_START
+
+bool EsPlayer::SetDisplayMode(const DisplayMode& mode) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ trackrenderer_->SetDisplayMode(mode);
+ return true;
+}
+
+bool EsPlayer::SetStretchMode(const int& mode) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ trackrenderer_->SetStretchMode(mode);
+ return true;
+}
+
+
+bool EsPlayer::SetDisplayRoi(const Geometry& roi) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ mixerticket_roi_ = roi;
+ if (mixer_ticket_) return true;
+#endif
+ return trackrenderer_->SetDisplayRoi(roi);
+}
+
+bool EsPlayer::SetVideoRoi(const CropArea& area) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->SetVideoRoi(area);
+}
+
+bool EsPlayer::ResizeRenderRect(const RenderRect& rect) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->ResizeRenderRect(rect);
+}
+
+bool EsPlayer::SetDisplayRotate(const DisplayRotation& rotate) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->SetDisplayRotate(rotate);
+}
+
+bool EsPlayer::GetDisplayRotate(DisplayRotation* rotate) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ if (trackrenderer_->GetDisplayRotate(rotate)) {
+ return true;
+ }
+ return false;
+}
+
+bool EsPlayer::SetDisplayVisible(bool is_visible) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ if (mixer_ticket_) {
+ LOG_INFO_P(this, "mixed player is_visible [%d] -> [%d]", is_visible_,
+ is_visible);
+ is_visible_ = is_visible;
+ return true;
+ }
+#endif
+ return trackrenderer_->SetDisplayVisible(is_visible);
+}
+
+bool EsPlayer::SetTrustZoneUse(bool is_using_tz) {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ if (drm_property_.type != drm::Type::kNone) {
+ LOG_ERROR_P(this, "drm type is already set for sending encrypted packets");
+ return false;
+ }
+ LOG_INFO_P(this, "set trust zone use [%d]", is_using_tz);
+ drm_property_.external_decryption = is_using_tz;
+
+ drm::Property drm_property = drm_property_;
+ drm_property.type = drm::Type::kPlayready;
+ trackrenderer_->SetDrm(drm_property);
+ return true;
+}
+
+bool EsPlayer::SetSubmitDataType(SubmitDataType type) {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ LOG_INFO_P(this, "set submit data type [%d]", static_cast<int>(type));
+
+ if (type == SubmitDataType::kCleanData) return true;
+ submit_data_type_ = type;
+ drm_property_.type = drm::Type::kPlayready;
+ // TODO: following SubmitDataType, need to set external_decryption
+ drm_property_.external_decryption = true;
+ drm::Property drm_property = drm_property_;
+ trackrenderer_->SetDrm(drm_property);
+ return true;
+}
+
+bool EsPlayer::SetTrack_(const Track& track) {
+ LOG_ENTER_P(this);
+ track_util::ShowTrackInfo(track);
+ track_.push_back(std::move(track));
+ return true;
+}
+
+bool EsPlayer::ChangeStream_(const Track& track) {
+ TrackType type = track.type;
+ if (track_.empty()) return false;
+ auto has_track = [type](const Track& item) -> bool {
+ return item.type == type;
+ };
+ std::lock_guard<std::mutex> lk(submit_mutex_);
+ auto target = std::find_if(track_.begin(), track_.end(), has_track);
+ if (target == track_.end()) {
+ LOG_ERROR_P(this, "Add a new stream.");
+ SetTrack_(track);
+ return true;
+ }
+ if (target->active != false) {
+ LOG_ERROR_P(this, "The track should be deactivated in advance.");
+ return false;
+ }
+ track_.erase(target);
+ LOG_ERROR_P(this, "previously added %s stream is deleted",
+ (type == kTrackTypeAudio) ? "audio" : "video");
+ return SetTrack_(track);
+}
+
+bool EsPlayer::SetStream_(const Track& track) {
+ TrackType type = track.type;
+ if (!track_.empty()) {
+ auto has_track = [type](const Track& item) -> bool {
+ return item.type == type;
+ };
+ auto target = std::find_if(track_.begin(), track_.end(), has_track);
+ if (target != track_.end()) {
+ LOG_ERROR_P(this, "Stream is already exist");
+ return false;
+ }
+ }
+ auto op = [this, track]() noexcept {
+ if (!SetTrack_(track)) {
+ return false;
+ }
+ return true;
+ };
+
+ CafLogger::LogMessage(CafEventType::kStreamReady, caf_unique_number);
+
+ es_event::SetStream event{op};
+ return state_manager_.ProcessEvent(event);
+}
+
+bool EsPlayer::SetStream(const AudioStreamPtr& stream) {
+ LOG_ENTER_P(this);
+ bool ret = false;
+ BOOST_SCOPE_EXIT(&ret, &stream, &force_audio_swdecoder_use_) {
+ if (ret) force_audio_swdecoder_use_ = stream->GetForceSwDecoderUse();
+ }
+ BOOST_SCOPE_EXIT_END
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return ret;
+ }
+ Track track = stream->GetTrack_();
+ if (state_manager_.GetState() >= EsState::kReady) {
+ track.active = false;
+ ret = ChangeStream_(track);
+ return ret;
+ }
+ ret = SetStream_(track);
+ return ret;
+}
+
+bool EsPlayer::SetStream(const VideoStreamPtr& stream) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ Track track = stream->GetTrack_();
+ if (state_manager_.GetState() >= EsState::kReady) {
+ track.active = false;
+ return ChangeStream_(track);
+ }
+ return SetStream_(track);
+}
+
+bool EsPlayer::SwitchAudioStreamOnTheFly(const AudioStreamPtr& stream) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ Track track = stream->GetTrack_();
+
+ if (!internal::IsAvailableCodecSwitch(track)) {
+ LOG_ERROR_P(this, "Invalid new mimetype [%s][%d]", track.mimetype.c_str(),
+ track.version);
+ return false;
+ }
+ for (auto& old_track : track_) {
+ if (old_track.type == TrackType::kTrackTypeAudio) {
+ if (!internal::IsAvailableCodecSwitch(old_track)) {
+ LOG_ERROR_P(this, "Invalid previous mimetype [%s][%d]",
+ old_track.mimetype.c_str(), old_track.version);
+ return false;
+ }
+ if (!Flush(StreamType::kAudio)) return false;
+ old_track.active = false;
+ break;
+ }
+ }
+ if (!ChangeStream_(track)) return false;
+
+ trackrenderer_->SetTrack(track_);
+ return true;
+}
+
+bool EsPlayer::SetAdvancedPictureQualityType(const AdvPictureQualityType type) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ trackrenderer_->SetAdvancedPictureQualityType(type);
+ return true;
+}
+
+bool EsPlayer::SetResourceAllocatePolicy(const RscAllocPolicy policy) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ resource_alloc_policy_ = policy;
+ trackrenderer_->SetResourceAllocatePolicy(policy);
+ return true;
+}
+
+// LCOV_EXCL_START
+GetDecodedVideoFrameStatus EsPlayer::GetDecodedPacket(
+ DecodedVideoPacket& packet) {
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return GetDecodedVideoFrameStatus::kUnknown;
+ }
+ return trackrenderer_->GetDecodedPacket(packet);
+}
+
+bool EsPlayer::ReturnDecodedPacket(const DecodedVideoPacket& packet) {
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->ReturnDecodedPacket(packet);
+}
+// LCOV_EXCL_STOP
+
+void EsPlayer::ResetContextForClose_() {
+ internal::ResetDrmProperty(drm_property_);
+ track_.clear();
+ submit_data_type_ = SubmitDataType::kCleanData;
+ low_latency_mode_ = 0;
+ resume_time_.is_set = false;
+ video_frame_peek_mode_ = 0;
+ unlimited_max_buffer_mode_ = 0;
+ fmm_mode_ = 0;
+ audio_codec_type_ = kPlayerAudioCodecTypeHW;
+ video_codec_type_ = kPlayerVideoCodecTypeHW;
+ is_resource_conflicted_ = false;
+ app_info_ = PlayerAppInfo();
+ src_queue_size_ = SrcQueueSize();
+ is_msg_task_stop_ = false;
+}
+
+void EsPlayer::ResetContextForStop_() {
+ for (int i = 0; i < kTrackTypeMax; ++i) {
+ need_data_[i].mask = kNeedDataMaskNone;
+ need_data_[i].seek_offset = 0;
+ }
+ {
+ std::lock_guard<std::mutex> lock(eos_mutex_);
+ eos_status_ = EosStatus::kAllEos;
+ }
+ current_playback_rate_ = 1.0;
+ current_audio_mute_ = false;
+}
+
+void EsPlayer::GetSrcQueueCurrentSize_(const TrackType& type,
+ uint64_t* byte_size,
+ uint64_t* time_size) {
+ boost::any byte_size_, time_size_;
+ if (type == TrackType::kTrackTypeVideo) {
+ trackrenderer_->GetAttribute(
+ TrackRendererAdapter::Attribute::kVideoQueueCurrentLevelByte,
+ &byte_size_);
+ trackrenderer_->GetAttribute(
+ TrackRendererAdapter::Attribute::kVideoQueueCurrentLevelTime,
+ &time_size_);
+ } else if (type == TrackType::kTrackTypeAudio) {
+ trackrenderer_->GetAttribute(
+ TrackRendererAdapter::Attribute::kAudioQueueCurrentLevelByte,
+ &byte_size_);
+ trackrenderer_->GetAttribute(
+ TrackRendererAdapter::Attribute::kAudioQueueCurrentLevelTime,
+ &time_size_);
+ } else
+ return;
+ *byte_size = boost::any_cast<std::uint64_t>(byte_size_);
+ *time_size = boost::any_cast<std::uint64_t>(time_size_);
+ if (time_unit_type == kPlayerTimeUnitTypeMs)
+ *time_size = util::ConvertNsToMs(*time_size);
+ else if (time_unit_type == kPlayerTimeUnitTypeUs)
+ *time_size = util::ConvertNsToUs(*time_size);
+}
+
+GstBuffer* EsPlayer::GetGstBuffer_(const EsPacketPtr& packet,
+ MakeBufferStatus* status) {
+ std::shared_ptr<char> buffer = packet->GetBuffer();
+ uint32_t size = packet->GetSize();
+ if (packet->IsEosPacket()) {
+ *status = MakeBufferStatus::kEos;
+ return nullptr;
+ }
+ GstBuffer* gstbuffer = gst_buffer_new_and_alloc(size);
+ if (!gstbuffer) {
+ *status = MakeBufferStatus::kOutOfMemory;
+ return nullptr;
+ }
+ if (buffer != nullptr) {
+ GstMapInfo map;
+ gst_buffer_map(gstbuffer, &map, GST_MAP_WRITE);
+ memcpy(map.data, buffer.get(), size);
+ gst_buffer_unmap(gstbuffer, &map);
+ }
+
+ uint64_t pts = packet->GetPts();
+ uint64_t duration = packet->GetDuration();
+ /* if pts or duration are -1(=GST_CLOCK_TIME_NONE), some of the elements don't
+ * adjust the buffer. */
+ if (time_unit_type == kPlayerTimeUnitTypeMs) {
+ GST_BUFFER_PTS(gstbuffer) = (pts == GST_CLOCK_TIME_NONE)
+ ? GST_CLOCK_TIME_NONE
+ : (GstClockTime)util::ConvertMsToNs(pts);
+ GST_BUFFER_DURATION(gstbuffer) =
+ (duration == GST_CLOCK_TIME_NONE)
+ ? GST_CLOCK_TIME_NONE
+ : (GstClockTime)util::ConvertMsToNs(duration);
+ } else if (time_unit_type == kPlayerTimeUnitTypeUs) {
+ GST_BUFFER_PTS(gstbuffer) = (pts == GST_CLOCK_TIME_NONE)
+ ? GST_CLOCK_TIME_NONE
+ : (GstClockTime)util::ConvertUsToNs(pts);
+ GST_BUFFER_DURATION(gstbuffer) =
+ (duration == GST_CLOCK_TIME_NONE)
+ ? GST_CLOCK_TIME_NONE
+ : (GstClockTime)util::ConvertUsToNs(duration);
+ }
+ uint32_t hdr10p_size = packet->GetHdr10pSize();
+ std::shared_ptr<char> hdr10p_metadata = packet->GetHdr10pData();
+
+ if (hdr10p_size > 0 && hdr10p_metadata != nullptr) {
+ guint32* blockadditional_size = (guint32*)g_malloc(sizeof(uint32_t));
+ *blockadditional_size = hdr10p_size;
+ gst_mini_object_set_qdata(
+ GST_MINI_OBJECT(gstbuffer),
+ g_quark_from_static_string("matroska_blockadditional_size"),
+ blockadditional_size, g_free);
+
+ /* blockadditiona_data : the data sent to omx, size (4 bytes) + metadata */
+ guint8* blockadditional_data =
+ (guint8*)g_malloc(((*blockadditional_size) + 4) * sizeof(guint8));
+ memcpy(blockadditional_data, blockadditional_size, sizeof(uint32_t));
+ memcpy(blockadditional_data + 4, hdr10p_metadata.get(),
+ (*blockadditional_size));
+ gst_mini_object_set_qdata(
+ GST_MINI_OBJECT(gstbuffer),
+ g_quark_from_static_string("matroska_blockadditional_info"),
+ blockadditional_data, g_free);
+ }
+ *status = MakeBufferStatus::kSuccess;
+ return gstbuffer;
+}
+
+PacketSubmitStatus EsPlayer::SubmitEosPacket_(const TrackType& type) {
+ PacketSubmitStatus submitstate = PacketSubmitStatus::kSuccess;
+ {
+ std::lock_guard<std::mutex> lock(eos_mutex_);
+ switch (type) {
+ case kTrackTypeAudio:
+ eos_status_ |= EosStatus::kAudioEos;
+ break;
+ case kTrackTypeVideo:
+ eos_status_ |= EosStatus::kVideoEos;
+ break;
+ default:
+ break;
+ }
+ if (eos_status_ != EosStatus::kAllEos) {
+ return submitstate;
+ }
+ }
+ for (int tracktype = kTrackTypeAudio; tracktype < kTrackTypeMax;
+ ++tracktype) {
+ auto inbuffer =
+ DecoderInputBuffer::Create(static_cast<TrackType>(tracktype));
+ if (!trackrenderer_->SubmitPacket2(inbuffer, nullptr)) {
+ std::lock_guard<std::mutex> lock(eos_mutex_);
+ eos_status_ = EosStatus::kAllEos;
+ submitstate = PacketSubmitStatus::kNotPrepared;
+ return submitstate;
+ }
+ }
+ return submitstate;
+}
+
+void EsPlayer::UnsetTzQdata_(const DecoderInputBufferPtr& buffer) {
+ const GstBuffer* gstbuf = buffer->Get();
+ if (!gstbuf) return;
+ GstStructure* tzqdata = GST_STRUCTURE(gst_mini_object_steal_qdata(
+ GST_MINI_OBJECT(gstbuf), g_quark_from_static_string("GstTzHandleData")));
+ if (tzqdata) gst_structure_free(tzqdata);
+}
+
+PacketSubmitStatus EsPlayer::SubmitDecoderInputBuffer_(
+ const DecoderInputBufferPtr& buffer) {
+ PacketSubmitStatus status = PacketSubmitStatus::kSuccess;
+ TrackRendererAdapter::SubmitStatus submitstate =
+ TrackRendererAdapter::SubmitStatus::kSuccess;
+ trackrenderer_->SubmitPacket2(buffer, &submitstate);
+
+ switch (submitstate) {
+ case TrackRendererAdapter::SubmitStatus::kSuccess:
+ case TrackRendererAdapter::SubmitStatus::kDrop:
+ status = PacketSubmitStatus::kSuccess;
+ break;
+ case TrackRendererAdapter::SubmitStatus::kFull:
+ UnsetTzQdata_(buffer);
+ trackrenderer_event_listener_->OnBufferStatus(buffer->GetType(),
+ BufferStatus::kOverrun);
+ status = PacketSubmitStatus::kFull;
+ break;
+ default:
+ UnsetTzQdata_(buffer);
+ status = PacketSubmitStatus::kNotPrepared;
+ break;
+ }
+ return status;
+}
+
+// LCOV_EXCL_START
+void EsPlayer::MakeGstBufferForTzHandle_(GstBuffer* gstbuffer,
+ const TrackType& type,
+ const uint32_t& tz_handle,
+ const uint32_t& packet_size) {
+ GstStructure* gst_tz_handle_data_structure =
+ gst_structure_new("GstTzHandleData", "packet_handle", G_TYPE_UINT,
+ static_cast<guint32>(tz_handle), "packet_size",
+ G_TYPE_UINT, static_cast<guint32>(packet_size),
+ "secure", G_TYPE_BOOLEAN, true, nullptr);
+ gst_mini_object_set_qdata(
+ GST_MINI_OBJECT(gstbuffer), g_quark_from_string("GstTzHandleData"),
+ gst_tz_handle_data_structure, (GDestroyNotify)gst_structure_free);
+
+ if (type == kTrackTypeAudio) {
+ Track audio_track;
+ bool has_active_audio_track =
+ track_util::GetActiveTrack(track_, kTrackTypeAudio, &audio_track);
+ if (has_active_audio_track) {
+ GstStructure* audio_info_structure =
+ gst_structure_new("AudioInfo", "mime_type", G_TYPE_STRING,
+ audio_track.mimetype.c_str(), nullptr);
+ gst_mini_object_set_qdata(
+ GST_MINI_OBJECT(gstbuffer), g_quark_from_string("AudioInfo"),
+ audio_info_structure, (GDestroyNotify)gst_structure_free);
+ }
+ }
+}
+
+void EsPlayer::MakeGstBufferForEncryptedPacket_(
+ GstBuffer* gstbuffer, const EsPacketPtr& packet,
+ const drm::EsPlayerEncryptedInfo& drm_info) {
+ if (drm_info.handle == 0) return;
+ auto serialized_drm_info_ptr = esplayer_drm::Serialize(packet, drm_info);
+ GstStructure* gst_drm_info_structure =
+ gst_structure_new("drm_info", "drm_specific_info", G_TYPE_BYTES,
+ serialized_drm_info_ptr.get(), nullptr);
+ if (gst_drm_info_structure) {
+ gst_mini_object_set_qdata(
+ GST_MINI_OBJECT(gstbuffer), g_quark_from_static_string("drm_info"),
+ gst_drm_info_structure, (GDestroyNotify)gst_structure_free);
+ }
+}
+// LCOV_EXCL_STOP
+
+PacketSubmitStatus EsPlayer::SubmitPacketCommon_(const EsPacketPtr& packet,
+ SubmitPacketOperator op) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ return PacketSubmitStatus::kNotPrepared;
+ }
+ if (!packet) return PacketSubmitStatus::kInvalidPacket;
+
+ TrackType type = static_cast<TrackType>(packet->GetType());
+ Track activated_track;
+ if (!track_util::GetActiveTrack(track_, type, &activated_track))
+ return PacketSubmitStatus::kInvalidPacket;
+
+ if (state_manager_.GetState() == EsState::kPaused ||
+ state_manager_.GetState() == EsState::kReady) {
+ internal::AppsrcQueueSizeOption byte_based, time_based;
+ switch (type) {
+ case kTrackTypeAudio:
+ byte_based.max_size = src_queue_size_.kMaxByteOfAudioSrcQueue;
+ time_based.max_size = src_queue_size_.kMaxTimeOfAudioSrcQueue;
+ byte_based.threshold = src_queue_size_.kMinByteThresholdOfAudioSrcQueue;
+ time_based.threshold = src_queue_size_.kMinTimeThresholdOfAudioSrcQueue;
+ break;
+ case kTrackTypeVideo:
+ byte_based.max_size = src_queue_size_.kMaxByteOfVideoSrcQueue;
+ time_based.max_size = src_queue_size_.kMaxTimeOfVideoSrcQueue;
+ byte_based.threshold = src_queue_size_.kMinByteThresholdOfVideoSrcQueue;
+ time_based.threshold = src_queue_size_.kMinTimeThresholdOfVideoSrcQueue;
+ break;
+ default:
+ break;
+ }
+ GetSrcQueueCurrentSize_(type, &(byte_based.current_size),
+ &(time_based.current_size));
+ if (internal::IsUnderRun(byte_based, time_based))
+ trackrenderer_event_listener_->OnBufferStatus(type,
+ BufferStatus::kUnderrun);
+ }
+
+ es_packet_logger_.StorePacketInfo(packet);
+ es_packet_logger_.PrintStoredPacketInfo(packet->GetType());
+
+ MakeBufferStatus make_buffer_status;
+ GstBuffer* gstbuffer = GetGstBuffer_(packet, &make_buffer_status);
+ if (!gstbuffer) {
+ if (make_buffer_status == MakeBufferStatus::kEos)
+ return SubmitEosPacket_(type);
+ else if (make_buffer_status == MakeBufferStatus::kOutOfMemory)
+ return PacketSubmitStatus::kOutOfMemory;
+ }
+ if (op != nullptr) {
+ PacketSubmitStatus op_status = op(gstbuffer);
+ if (op_status != PacketSubmitStatus::kSuccess) {
+ return op_status;
+ }
+ }
+
+ auto inbuffer = DecoderInputBuffer::Create(type, 0, gstbuffer);
+ gst_buffer_unref(gstbuffer);
+
+ if (packet->HasMatroskaColorInfo()) {
+ std::string color_info_str =
+ util::GetStringFromMatroskaColor(packet->GetMatroskaColorInfo());
+ if (trackrenderer_->SetMatroskaColorInfo(color_info_str) == false)
+ return PacketSubmitStatus::kNotPrepared;
+ }
+
+ return SubmitDecoderInputBuffer_(inbuffer);
+}
+
+PacketSubmitStatus EsPlayer::SubmitPacket(const EsPacketPtr& packet) {
+ std::lock_guard<std::mutex> lk(submit_mutex_);
+ return SubmitPacketCommon_(packet, nullptr);
+}
+
+// LCOV_EXCL_START
+PacketSubmitStatus EsPlayer::SubmitTrustZonePacket(const EsPacketPtr& packet,
+ uint32_t tz_handle) {
+ std::lock_guard<std::mutex> lk(submit_mutex_);
+ if (submit_data_type_ != SubmitDataType::kTrustZoneData)
+ return PacketSubmitStatus::kInvalidPacket;
+ auto submitpacket_op = [this, &tz_handle,
+ &packet](GstBuffer* gstbuffer) -> PacketSubmitStatus {
+ if (tz_handle > 0) {
+ TrackType type = static_cast<TrackType>(packet->GetType());
+ uint32_t packet_size = packet->GetSize();
+ MakeGstBufferForTzHandle_(gstbuffer, type, tz_handle, packet_size);
+ }
+ return PacketSubmitStatus::kSuccess;
+ };
+ return SubmitPacketCommon_(packet, submitpacket_op);
+}
+
+PacketSubmitStatus EsPlayer::SubmitEncryptedPacket(
+ const EsPacketPtr& packet, const drm::EsPlayerEncryptedInfo& drm_info) {
+ std::lock_guard<std::mutex> lk(submit_mutex_);
+ if (submit_data_type_ != SubmitDataType::kEncryptedData)
+ return PacketSubmitStatus::kInvalidPacket;
+ auto submitpacket_op =
+ [this, &packet, &drm_info](GstBuffer* gstbuffer) -> PacketSubmitStatus {
+ MakeGstBufferForEncryptedPacket_(gstbuffer, packet, drm_info);
+ return PacketSubmitStatus::kSuccess;
+ };
+ return SubmitPacketCommon_(packet, submitpacket_op);
+}
+// LCOV_EXCL_STOP
+
+EsState EsPlayer::GetState() { return state_manager_.GetState(); }
+
+bool EsPlayer::GetPlayingTime(uint64_t* time) {
+ if (!time) return false;
+ if (state_manager_.GetState() <= EsState::kReady) {
+ *time = 0;
+ return false;
+ }
+ return trackrenderer_->GetPlayingTime(time);
+}
+
+bool EsPlayer::SetAudioMute(bool is_mute) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->SetAudioMute(is_mute);
+}
+
+bool EsPlayer::SetVideoFrameBufferType(DecodedVideoFrameBufferType type) {
+ if ((state_manager_.GetState() != EsState::kIdle &&
+ type != DecodedVideoFrameBufferType::kScale) ||
+ (state_manager_.GetState() < EsState::kIdle &&
+ type == DecodedVideoFrameBufferType::kScale)) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ if (type == DecodedVideoFrameBufferType::kScale &&
+ video_codec_type_ == kPlayerVideoCodecTypeSW) {
+ LOG_ERROR_P(this, "kScale is not supportted when using sw video decoder");
+ return false;
+ }
+ trackrenderer_->SetVideoFrameBufferType(
+ VideoFrameTypeStrategyPtr(new DefaultVideoFrameTypeStrategy(type)));
+ vidoe_frame_buffer_type_ = type;
+ return true;
+}
+
+bool EsPlayer::SetVideoFrameBufferScaleResolution(
+ const uint32_t& target_width, const uint32_t& target_height) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+
+ return trackrenderer_->SetVideoFrameBufferScaleResolution(target_width,
+ target_height);
+}
+
+bool EsPlayer::SetDecodedVideoFrameRate(const Rational& request_framerate) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR("Invalid State , current %d", state_manager_.GetStateEnum());
+ return false;
+ }
+
+ return trackrenderer_->SetDecodedVideoFrameRate(request_framerate);
+}
+
+void EsPlayer::RegisterListener(EsEventListener* listener,
+ EsEventListener::UserData userdata) {
+ // assert(listener); // allow unregister by setting nullptr
+ assert(!eventlistener_);
+ eventlistener_ = listener;
+ eventlistener_userdata_ = userdata;
+}
+
+bool EsPlayer::GetAdaptiveInfo(void* padaptive_info,
+ const PlayerAdaptiveInfo& adaptive_type) {
+ if (!padaptive_info || adaptive_type <= PlayerAdaptiveInfo::kMinType ||
+ adaptive_type >= PlayerAdaptiveInfo::kMaxType)
+ return false;
+ switch (adaptive_type) {
+ case PlayerAdaptiveInfo::kVideoDroppedFrames:
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Wrong state, we aren't started yet");
+ return false;
+ }
+ return trackrenderer_->GetDroppedFrames(padaptive_info);
+ case PlayerAdaptiveInfo::kDroppedVideoFramesForCatchup:
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Wrong state, we aren't started yet");
+ return false;
+ }
+ return trackrenderer_->GetDroppedFramesForCatchup(kTrackTypeVideo,
+ padaptive_info);
+ case PlayerAdaptiveInfo::kDroppedAudioFramesForCatchup:
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Wrong state, we aren't started yet");
+ return false;
+ }
+ return trackrenderer_->GetDroppedFramesForCatchup(kTrackTypeAudio,
+ padaptive_info);
+ default:
+ break;
+ }
+ return false;
+}
+
+bool EsPlayer::SetVolume(const int& volume) {
+ if (volume < internal::kVolumeMin || volume > internal::kVolumeMax) {
+ LOG_ERROR_P(this, "Invalid volume level %d", volume);
+ return false;
+ }
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->SetVolume(volume);
+}
+
+bool EsPlayer::GetVolume(int* volume) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->GetVolume(volume);
+}
+
+bool EsPlayer::Flush(const StreamType& type) {
+ if (state_manager_.GetState() <= EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ eos_status_ =
+ internal::ResetEosStatus(static_cast<TrackType>(type), eos_status_);
+ es_packet_logger_.ResetLog(type);
+ return trackrenderer_->Flush(type);
+}
+
+void EsPlayer::SetBufferSize(const BufferOption& option, uint64_t size) {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return;
+ }
+ switch (option) {
+ case BufferOption::kBufferAudioMaxByteSize:
+ src_queue_size_.kMaxByteOfAudioSrcQueue = size;
+ break;
+ case BufferOption::kBufferVideoMaxByteSize:
+ src_queue_size_.kMaxByteOfVideoSrcQueue = size;
+ break;
+ case BufferOption::kBufferAudioMinByteThreshold:
+ src_queue_size_.kMinByteThresholdOfAudioSrcQueue =
+ static_cast<uint32_t>(size);
+ break;
+ case BufferOption::kBufferVideoMinByteThreshold:
+ src_queue_size_.kMinByteThresholdOfVideoSrcQueue =
+ static_cast<uint32_t>(size);
+ break;
+ case BufferOption::kBufferAudioMaxTimeSize:
+ src_queue_size_.kMaxTimeOfAudioSrcQueue = size;
+ break;
+ case BufferOption::kBufferVideoMaxTimeSize:
+ src_queue_size_.kMaxTimeOfVideoSrcQueue = size;
+ break;
+ case BufferOption::kBufferAudioMinTimeThreshold:
+ src_queue_size_.kMinTimeThresholdOfAudioSrcQueue =
+ static_cast<uint32_t>(size);
+ break;
+ case BufferOption::kBufferVideoMinTimeThreshold:
+ src_queue_size_.kMinTimeThresholdOfVideoSrcQueue =
+ static_cast<uint32_t>(size);
+ break;
+ default:
+ LOG_ERROR_P(this, "Invalid option!!!");
+ break;
+ }
+}
+
+bool EsPlayer::SetLowLatencyMode(const PlayerLowLatencyMode& mode) {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ if (true == internal::IsExclusiveLowLatencyMode(low_latency_mode_, mode)) {
+ LOG_ERROR_P(this, "Invalid Mode , current 0x%x, set 0x%x",
+ static_cast<std::uint32_t>(low_latency_mode_),
+ static_cast<std::uint32_t>(mode));
+ return false;
+ }
+ low_latency_mode_ |= static_cast<std::uint32_t>(mode);
+ return true;
+}
+
+bool EsPlayer::SetRenderTimeOffset(const StreamType type, int64_t offset) {
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ if (!internal::IsSupportedTsOffset(low_latency_mode_)) {
+ LOG_ERROR_P(this,
+ "low latency mode have to be set except disable_sync mode");
+ return false;
+ }
+ std::int64_t ns_unit = 0;
+ if (time_unit_type == kPlayerTimeUnitTypeMs)
+ ns_unit = 1000000;
+ else if (time_unit_type == kPlayerTimeUnitTypeUs)
+ ns_unit = 1000;
+ if ((offset * ns_unit > G_MAXINT64) || (offset * ns_unit < G_MININT64)) {
+ LOG_ERROR("%p, wrong value : G_MAXINT64 < offset[%" PRId64
+ "] * 1000000 < G_MAXINT64",
+ this, offset);
+ return false;
+ }
+ if (type == StreamType::kMax) return false;
+ if (time_unit_type == kPlayerTimeUnitTypeMs) {
+ if (type == StreamType::kAudio)
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kAudioRenderTimeOffset,
+ util::ConvertMsToNs(offset));
+ else if (type == StreamType::kVideo)
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoRenderTimeOffset,
+ util::ConvertMsToNs(offset));
+ } else if (time_unit_type == kPlayerTimeUnitTypeUs) {
+ if (type == StreamType::kAudio)
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kAudioRenderTimeOffset,
+ util::ConvertUsToNs(offset));
+ else if (type == StreamType::kVideo)
+ trackrenderer_->SetAttribute(
+ TrackRendererAdapter::Attribute::kVideoRenderTimeOffset,
+ util::ConvertUsToNs(offset));
+ }
+ return true;
+}
+
+bool EsPlayer::GetRenderTimeOffset(const StreamType type, int64_t* offset) {
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ if (!internal::IsSupportedTsOffset(low_latency_mode_)) {
+ LOG_ERROR_P(this, "low latency mode have to be set");
+ return false;
+ }
+ if (type == StreamType::kMax) return false;
+ boost::any off_set;
+ if (type == StreamType::kAudio)
+ trackrenderer_->GetAttribute(
+ TrackRendererAdapter::Attribute::kAudioRenderTimeOffset, &off_set);
+ else if (type == StreamType::kVideo)
+ trackrenderer_->GetAttribute(
+ TrackRendererAdapter::Attribute::kVideoRenderTimeOffset, &off_set);
+
+ *offset = boost::any_cast<std::int64_t>(off_set);
+ if (time_unit_type == kPlayerTimeUnitTypeMs)
+ *offset = util::ConvertNsToMs(*offset);
+ else if (time_unit_type == kPlayerTimeUnitTypeUs)
+ *offset = util::ConvertNsToUs(*offset);
+ return true;
+}
+
+bool EsPlayer::SetVideoFramePeekMode() {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ constexpr std::uint32_t peek_mode_on = 1;
+ video_frame_peek_mode_ = peek_mode_on;
+ return true;
+}
+
+bool EsPlayer::RenderVideoFrame() {
+ if (!video_frame_peek_mode_) return false;
+ if (state_manager_.GetState() == EsState::kReady ||
+ state_manager_.GetState() == EsState::kPaused) {
+ trackrenderer_->RenderVideoFrame();
+ return true;
+ }
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+}
+
+bool EsPlayer::SetUnlimitedMaxBufferMode() {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ constexpr std::uint32_t unlimited_max_buffer_mode_on = 1;
+ unlimited_max_buffer_mode_ = unlimited_max_buffer_mode_on;
+ return true;
+}
+
+ErrorType EsPlayer::SetFmmMode() {
+ EsState state = state_manager_.GetState();
+ LOG_ENTER_P(this);
+ if (state < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return ErrorType::kInvalidState;
+ }
+
+ int onoff = 0;
+#ifdef SOUNDBAR_PRODUCT
+ return ErrorType::kInvalidState;
+#else
+ avoc_error_e avoc_ret = avoc_get_filmmaker_auto_mode(&onoff);
+ LOG_ERROR_P(this, "avoc get fmm ret [%d] onoff[%d]", avoc_ret, onoff);
+ if (avoc_ret == AVOC_EXIT_FAILURE) return ErrorType::kInvalidState;
+#endif
+ ErrorType ret = ErrorType::kNone;
+
+ constexpr int fmm_auto_off = 0;
+ if (onoff == fmm_auto_off) ret = ErrorType::kInvalidOperation;
+
+ constexpr std::uint32_t fmm_mode_on = 1;
+ fmm_mode_ = fmm_mode_on;
+
+ if (state < EsState::kReady) return ret;
+
+ trackrenderer_->SetAttribute(TrackRendererAdapter::Attribute::kFmmMode,
+ fmm_mode_);
+ return ret;
+}
+
+bool EsPlayer::SetAudioCodecType(const PlayerAudioCodecType& type) {
+ Track activated_track;
+ bool is_existed =
+ track_util::GetActiveTrack(track_, kTrackTypeAudio, &activated_track);
+ EsState state = state_manager_.GetState();
+ if ((state < EsState::kIdle) || (state > EsState::kIdle && is_existed)) {
+ LOG_ERROR_P(this,
+ "Invalid State [state:%d] or audio stream already exists[%d],",
+ state_manager_.GetStateEnum(), is_existed);
+ return false;
+ }
+ if (force_audio_swdecoder_use_ && type == kPlayerAudioCodecTypeHW) {
+ LOG_ERROR_P(this, "Not support hw decoder");
+ return false;
+ }
+ LOG_INFO_P(this, "PlayerAudioCodecType [%s]",
+ (type == kPlayerAudioCodecTypeHW) ? "hardware" : "software");
+ audio_codec_type_ = type;
+ return true;
+}
+
+bool EsPlayer::SetVideoCodecType(const PlayerVideoCodecType& type) {
+ Track activated_track;
+ bool is_existed =
+ track_util::GetActiveTrack(track_, kTrackTypeVideo, &activated_track);
+ EsState state = state_manager_.GetState();
+ if ((state < EsState::kIdle) || (state > EsState::kIdle && is_existed)) {
+ LOG_ERROR_P(this,
+ "Invalid State [state:%d] or video stream already exists[%d],",
+ state_manager_.GetStateEnum(), is_existed);
+ return false;
+ }
+ if (type == kPlayerVideoCodecTypeSW &&
+ vidoe_frame_buffer_type_ == DecodedVideoFrameBufferType::kScale) {
+ LOG_ERROR_P(this,
+ "sw video decoder is not supportted when video frame buffer "
+ "type is scale");
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ if (!enable_rsc_alloc_handle_) {
+ LOG_ERROR_P(this, "player can't control resource type, mixer will do it");
+ return false;
+ }
+ if (type == kPlayerVideoCodecTypeHWNdecoding) {
+ LOG_INFO_P(this, "PlayerVideoCodecType HW N-decoding");
+ video_decoding_mode_ = internal::kNdecodingMode;
+ return true;
+ }
+#endif
+ LOG_INFO_P(this, "PlayerVideoCodecType [%s]",
+ (type == kPlayerVideoCodecTypeHW) ? "hardware" : "software");
+ video_codec_type_ = type;
+ return true;
+}
+
+bool EsPlayer::SetAiFilter(void* aifilter) {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ trackrenderer_->SetAiFilter(aifilter);
+ return true;
+}
+
+bool EsPlayer::InitAudioEasingInfo(const uint32_t init_volume,
+ const uint32_t init_elapsed_time,
+ const AudioEasingInfo& easing_info) {
+ if (init_volume > internal::kVolumeMax ||
+ easing_info.target_volume > internal::kVolumeMax) {
+ LOG_ERROR_P(this, "Invalid volume: init [%d] target [%d]", init_volume,
+ easing_info.target_volume);
+ return false;
+ }
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->InitAudioEasingInfo(init_volume, init_elapsed_time,
+ easing_info);
+}
+
+bool EsPlayer::UpdateAudioEasingInfo(const AudioEasingInfo& easing_info) {
+ if (easing_info.target_volume > internal::kVolumeMax) {
+ LOG_ERROR_P(this, "Invalid volume level %d", easing_info.target_volume);
+ return false;
+ }
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->UpdateAudioEasingInfo(easing_info);
+}
+
+bool EsPlayer::GetAudioEasingInfo(uint32_t* current_volume,
+ uint32_t* elapsed_time,
+ AudioEasingInfo* easing_info) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->GetAudioEasingInfo(current_volume, elapsed_time,
+ easing_info);
+}
+
+bool EsPlayer::StartAudioEasing() {
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->StartAudioEasing();
+}
+
+bool EsPlayer::StopAudioEasing() {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->StopAudioEasing();
+}
+
+bool EsPlayer::SetAlternativeVideoResource(unsigned int rsc_type) {
+ if (state_manager_.GetState() == EsState::kNone) {
+ LOG_ERROR_P(this, "Invalid State");
+ return false;
+ }
+#ifndef IS_AUDIO_PRODUCT
+ if (!enable_rsc_alloc_handle_) {
+ LOG_ERROR_P(this, "player can't control resource type, mixer will do it");
+ return false;
+ }
+#endif
+ if (resource_alloc_policy_ > RscAllocPolicy::kRscAllocExclusive) {
+ LOG_ERROR_P(this,
+ "has set resource allocate policy, the alternative "
+ "resource setting will be wrong");
+ return false;
+ }
+ alternative_video_resource_ = static_cast<std::uint32_t>(rsc_type);
+ return true;
+}
+
+bool EsPlayer::SetAlternativeAudioResource(
+ const PlayerAudioResourceType rsc_type) {
+ if (state_manager_.GetState() == EsState::kNone) {
+ LOG_ERROR_P(this, "Invalid State");
+ return false;
+ }
+
+ if (resource_alloc_policy_ > RscAllocPolicy::kRscAllocExclusive) {
+ LOG_ERROR_P(this,
+ "has set resource allocate policy, the alternative "
+ "resource setting will be wrong");
+ return false;
+ }
+ return trackrenderer_->SetAlternativeAudioResource(rsc_type);
+}
+
+bool EsPlayer::SetCatchUpSpeed(const CatchUpSpeed& level) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+
+ if (false == internal::IsLowLatencyModeForCatchUp(low_latency_mode_)) {
+ LOG_ERROR_P(this, "Do not set low latency mode, current[0x%x]",
+ static_cast<std::uint32_t>(low_latency_mode_));
+ return false;
+ }
+
+ if (trackrenderer_->SetCatchUpSpeed(level)) {
+ return true;
+ }
+ return false;
+}
+
+bool EsPlayer::GetVideoLatencyStatus(LatencyStatus* status) {
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+
+ if (trackrenderer_->GetVideoLatencyStatus(status)) {
+ return true;
+ }
+ return false;
+}
+
+bool EsPlayer::GetAudioLatencyStatus(LatencyStatus* status) {
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+
+ if (trackrenderer_->GetAudioLatencyStatus(status)) {
+ return true;
+ }
+ return false;
+}
+
+bool EsPlayer::SetVideoMidLatencyThreshold(const unsigned int threshold) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+
+ if (false == internal::IsLowLatencyModeForCatchUp(low_latency_mode_)) {
+ LOG_ERROR_P(this, "Don't set low latency mode, current[0x%x]",
+ static_cast<std::uint32_t>(low_latency_mode_));
+ return false;
+ }
+
+ LOG_INFO_P(this, "video_mid_latency_threshold : [%u]", threshold);
+
+ trackrenderer_->SetVideoMidLatencyThreshold(threshold);
+ return true;
+}
+
+bool EsPlayer::SetAudioMidLatencyThreshold(const unsigned int threshold) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+
+ if (false == internal::IsLowLatencyModeForCatchUp(low_latency_mode_)) {
+ LOG_ERROR_P(this, "Don't set low latency mode, current[0x%x]",
+ static_cast<std::uint32_t>(low_latency_mode_));
+ return false;
+ }
+
+ LOG_INFO_P(this, "audio_mid_latency_threshold : [%u]", threshold);
+
+ trackrenderer_->SetAudioMidLatencyThreshold(threshold);
+ return true;
+}
+
+bool EsPlayer::SetVideoHighLatencyThreshold(const unsigned int threshold) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+
+ if (false == internal::IsLowLatencyModeForCatchUp(low_latency_mode_)) {
+ LOG_ERROR_P(this, "Don't set low latency mode, current[0x%x]",
+ static_cast<std::uint32_t>(low_latency_mode_));
+ return false;
+ }
+
+ LOG_INFO_P(this, "video_high_latency_threshold : [%u]", threshold);
+
+ trackrenderer_->SetVideoHighLatencyThreshold(threshold);
+ return true;
+}
+
+bool EsPlayer::SetAudioHighLatencyThreshold(const unsigned int threshold) {
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+
+ if (false == internal::IsLowLatencyModeForCatchUp(low_latency_mode_)) {
+ LOG_ERROR_P(this, "Don't set low latency mode, current[0x%x]",
+ static_cast<std::uint32_t>(low_latency_mode_));
+ return false;
+ }
+
+ LOG_INFO_P(this, "audio_high_latency_threshold : [%u]", threshold);
+
+ trackrenderer_->SetAudioHighLatencyThreshold(threshold);
+ return true;
+}
+
+bool EsPlayer::GetVirtualRscId(const RscType type, int* virtual_id) {
+ if (virtual_id == nullptr) return false;
+ if (state_manager_.GetState() < EsState::kReady) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ *virtual_id = -1;
+ return false;
+ }
+ return trackrenderer_->GetVirtualRscId(type, virtual_id);
+}
+
+bool EsPlayer::SetAudioPreloading() {
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR("Invalid State , current %d", state_manager_.GetStateEnum());
+ return false;
+ }
+ return trackrenderer_->SetAudioPreloading();
+}
+
+void EsPlayer::Init_() {
+ track_.clear();
+ is_stopped_ = false;
+ LOG_LEAVE_P(this);
+}
+
+void EsPlayer::MsgTask_() {
+ std::unique_lock<std::mutex> msg_mutex(msg_task_mutex_);
+ while (!is_msg_task_stop_) {
+ if (msg_queue_.empty()) {
+ msg_task_cv_.wait(msg_mutex);
+ } else {
+ msg_mutex.unlock();
+ msg_queue_.front()->Execute();
+ msg_mutex.lock();
+ msg_queue_.pop();
+ }
+ }
+ while (!msg_queue_.empty()) {
+ msg_queue_.pop();
+ }
+ LOG_INFO_P(this, "Stop MsgTask");
+}
+
+void EsPlayer::TrackRendererEventListener::OnEos() {
+ LOG_ENTER_P(handler_);
+ if (!handler_->eventlistener_) return;
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnEos,
+ handler_->eventlistener_, std::placeholders::_1);
+ auto msg = es_msg::Simple::Make(listener, handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+// LCOV_EXCL_START
+void EsPlayer::TrackRendererEventListener::OnSeekDone() {
+ LOG_ENTER_P(handler_);
+ if (!handler_->eventlistener_) return;
+ if (handler_->is_seek_done_need_drop == true) return;
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnSeekDone,
+ handler_->eventlistener_, std::placeholders::_1);
+ auto msg = es_msg::Simple::Make(listener, handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+void EsPlayer::TrackRendererEventListener::OnResourceConflicted() {
+ LOG_ENTER_P(handler_);
+ if (handler_->is_stopped_) {
+ LOG_INFO_P(handler_, "LEAVE ~ Stop is called already");
+ return;
+ }
+ LOG_INFO_P(handler_, "Handling resource conflict...");
+ handler_->is_resource_conflicted_ = true;
+ handler_->trackrenderer_->Stop();
+ if (!handler_->eventlistener_ || handler_->is_stopped_) return;
+ auto listener =
+ std::bind(&esplusplayer::EsEventListener::OnResourceConflicted,
+ handler_->eventlistener_, std::placeholders::_1);
+ auto msg = es_msg::Simple::Make(listener, handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ if (handler_->is_stopped_) {
+ LOG_LEAVE_P(handler_);
+ return;
+ }
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+void EsPlayer::TrackRendererEventListener::OnError(
+ const ErrorType& error_code) {
+ if (!handler_->eventlistener_) return;
+ if (error_code == ErrorType::kResourceLimit) return;
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnError,
+ handler_->eventlistener_, std::placeholders::_1,
+ std::placeholders::_2);
+ auto msg = es_msg::Error::Make(error_code, listener,
+ handler_->eventlistener_userdata_);
+
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+}
+
+void EsPlayer::TrackRendererEventListener::OnErrorMsg(
+ const ErrorType& error_code, char* error_msg) {
+ if (!handler_->eventlistener_) return;
+ if (error_code == ErrorType::kResourceLimit) return;
+
+ std::vector<Track> activeTracks;
+ track_util::GetActiveTrackList(handler_->track_, activeTracks);
+
+ Json::Value message;
+ message["error_code"] = (int)error_code;
+
+ switch (error_code) {
+ case ErrorType::kNotSupportedVideoCodec:
+ for (const auto& track : activeTracks) {
+ if (track.type == kTrackTypeVideo) {
+ message["codec"] = track.mimetype.c_str();
+ message["demux"] = track.container_type.c_str();
+ char json_string[20] = {0};
+ int nLen = 19;
+ strncat(json_string, std::to_string(track.width).c_str(), nLen);
+ nLen = sizeof(json_string) - strlen(json_string) - 1;
+ if (nLen < 0) nLen = 0;
+ strncat(json_string, "*", nLen);
+ nLen = sizeof(json_string) - strlen(json_string) - 1;
+ if (nLen < 0) nLen = 0;
+ strncat(json_string, std::to_string(track.height).c_str(), nLen);
+ message["resolution"] = json_string;
+ memset(json_string, 0, sizeof(json_string));
+ nLen = 19;
+ strncat(json_string, std::to_string(track.framerate_num).c_str(),
+ nLen);
+ nLen = sizeof(json_string) - strlen(json_string) - 1;
+ if (nLen < 0) nLen = 0;
+ strncat(json_string, "/", nLen);
+ nLen = sizeof(json_string) - strlen(json_string) - 1;
+ if (nLen < 0) nLen = 0;
+ strncat(json_string, std::to_string(track.framerate_den).c_str(),
+ nLen);
+ message["fps"] = json_string;
+ message["detail_info"] = error_msg;
+ break;
+ }
+ }
+ break;
+ case ErrorType::kNotSupportedAudioCodec:
+
+ break;
+ default:
+ break;
+ }
+
+ Json::FastWriter writer;
+ std::string str = writer.write(message);
+ LOG_INFO_P(handler_, "error message: %s", str.c_str());
+
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnErrorMsg,
+ handler_->eventlistener_, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3);
+ auto msg =
+ es_msg::ErrorMsg::Make(error_code, str.c_str(), str.size(), listener,
+ handler_->eventlistener_userdata_);
+
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+}
+// LCOV_EXCL_STOP
+
+void EsPlayer::TrackRendererEventListener::ReadyToPrepare_(
+ const TrackType& type) {
+ LOG_INFO_P(handler_, "OnReadyToPrepare [%s]",
+ (type == kTrackTypeAudio) ? "audio" : "video");
+
+ handler_->need_data_[type].mask &= ~kNeedDataMaskByPrepare;
+
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnReadyToPrepare,
+ handler_->eventlistener_, std::placeholders::_1,
+ std::placeholders::_2);
+ StreamType stream_type = internal::ConvertToStreamType(type);
+ auto msg = es_msg::ReadyToPrepare::Make(stream_type, listener,
+ handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+}
+
+void EsPlayer::TrackRendererEventListener::ReadyToSeek_(const TrackType& type) {
+ uint64_t offset = handler_->need_data_[type].seek_offset;
+
+ LOG_INFO("%p, OnReadyToSeek [%s] offset [%" PRId64 "]", handler_,
+ (type == kTrackTypeAudio) ? "audio" : "video", offset);
+
+ handler_->need_data_[type].mask &= ~kNeedDataMaskBySeek;
+ handler_->need_data_[type].seek_offset = 0;
+
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnReadyToSeek,
+ handler_->eventlistener_, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3);
+ StreamType stream_type = internal::ConvertToStreamType(type);
+ handler_->es_packet_logger_.ResetLog(stream_type);
+ auto msg = es_msg::ReadyToSeek::Make(stream_type, offset, listener,
+ handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+}
+
+void EsPlayer::TrackRendererEventListener::BufferStatus_(
+ const TrackType& type, const BufferStatus& status) {
+ uint64_t byte_size, time_size;
+ // LOG_INFO_P(handler_, "OnBufferStatus [%s] [%s]",
+ // (type == kTrackTypeAudio) ? "audio" : "video",
+ // (status == BufferStatus::kUnderrun) ? "underrun" : "overrun");
+ handler_->GetSrcQueueCurrentSize_(type, &byte_size, &time_size);
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnBufferStatus,
+ handler_->eventlistener_, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3,
+ std::placeholders::_4, std::placeholders::_5);
+ StreamType stream_type = internal::ConvertToStreamType(type);
+ auto msg =
+ es_msg::Bufferstatus::Make(stream_type, status, byte_size, time_size,
+ listener, handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+}
+
+void EsPlayer::TrackRendererEventListener::OnBufferStatus(
+ const TrackType& type, const BufferStatus& status) {
+ if (!handler_->eventlistener_) return;
+ if (internal::IsLowLatencyModeDisableAVSync(handler_->low_latency_mode_))
+ return;
+
+ if (handler_->need_data_[type].mask == kNeedDataMaskByPrepare &&
+ status == BufferStatus::kUnderrun) {
+ ReadyToPrepare_(type);
+ } else if (handler_->need_data_[type].mask == kNeedDataMaskBySeek &&
+ status == BufferStatus::kUnderrun) {
+ ReadyToSeek_(type);
+ } else {
+ BufferStatus_(type, status);
+ }
+}
+
+// LCOV_EXCL_START
+void EsPlayer::TrackRendererEventListener::OnMediaPacketGetTbmBufPtr(
+ void** tbm_ptr, bool is_scale_change) {
+ if (!handler_->eventlistener_) return;
+
+ handler_->eventlistener_->OnMediaPacketGetTbmBufPtr(tbm_ptr, is_scale_change);
+}
+
+void EsPlayer::TrackRendererEventListener::OnMediaPacketVideoDecoded(
+ const DecodedVideoPacket& packet) {
+ if (!handler_->eventlistener_) return;
+
+ handler_->eventlistener_->OnMediaPacketVideoDecoded(packet);
+}
+
+#ifndef IS_AUDIO_PRODUCT
+void EsPlayer::TrackRendererEventListener::OnMediaPacketVideoRawDecoded(
+ const DecodedVideoRawModePacket& packet) {
+ if (handler_->mixer_ticket_ == nullptr) return;
+ const auto& data = packet.data;
+ if (packet.type == DecodedVideoRawModePacketType::kPhysicalAddress) {
+ DecodedRawInfo info;
+ info.width = packet.width;
+ info.height = packet.height;
+ info.y_info.phyaddr = data.raw.y_phyaddr;
+ info.y_info.viraddr = data.raw.y_viraddr;
+ info.y_info.linesize = data.raw.y_linesize;
+ info.uv_info.phyaddr = data.raw.uv_phyaddr;
+ info.uv_info.viraddr = data.raw.uv_viraddr;
+ info.uv_info.linesize = data.raw.uv_linesize;
+ handler_->mixer_ticket_->Render(info);
+ } else if (packet.type == DecodedVideoRawModePacketType::kTizenBuffer) {
+ DecodedVideoKeyTypeInfo info;
+ info.width = packet.width;
+ info.height = packet.height;
+ info.key = packet.data.tbm.key;
+ handler_->mixer_ticket_->Render(info);
+ }
+}
+
+bool EsPlayer::MixerListener::OnAudioFocusChanged(bool active) {
+ LOG_INFO_P(handler_, "focused [%d]", active);
+ std::unique_lock<std::mutex> lock(handler_->audio_focus_m_);
+ if (handler_->state_manager_.GetState() < EsState::kReady) {
+ handler_->is_audio_focused_ = active;
+ return true;
+ }
+ if (active) {
+ Track activated_track;
+ track_util::GetActiveTrack(handler_->track_, kTrackTypeAudio,
+ &activated_track);
+ LOG_INFO_P(handler_, "Activate audio track");
+ {
+ std::lock_guard<std::mutex> lock2(handler_->eos_mutex_);
+ handler_->eos_status_ =
+ internal::ResetEosStatus(kTrackTypeAudio, handler_->eos_status_);
+ }
+ return handler_->trackrenderer_->Activate(kTrackTypeAudio, activated_track);
+ }
+ LOG_INFO_P(handler_, "Deactivate audio track");
+ handler_->is_audio_focused_ = false;
+ {
+ std::lock_guard<std::mutex> lock2(handler_->eos_mutex_);
+ handler_->eos_status_ |= EosStatus::kAudioEos;
+ }
+ return handler_->trackrenderer_->Deactivate(kTrackTypeAudio);
+}
+
+bool EsPlayer::MixerListener::OnUpdateDisplayInfo(const DisplayInfo& cur_info,
+ DisplayInfo* new_info) {
+ new_info->geometry = handler_->mixerticket_roi_;
+ new_info->visible_status =
+ handler_->is_visible_ ? VisibleStatus::kVisible : VisibleStatus::kHide;
+ return true;
+}
+#endif
+// LCOV_EXCL_STOP
+
+void EsPlayer::TrackRendererEventListener::OnSeekData(const TrackType& type,
+ const uint64_t offset) {
+ if (!handler_->eventlistener_) return;
+
+ if (handler_->need_data_[type].mask != kNeedDataMaskByPrepare) {
+ handler_->need_data_[type].mask |= kNeedDataMaskBySeek;
+ handler_->need_data_[type].seek_offset = offset;
+ }
+}
+
+// LCOV_EXCL_START
+void EsPlayer::TrackRendererEventListener::OnClosedCaptionData(const char* data,
+ const int size) {
+ if (size <= 0) return;
+ if (!handler_->eventlistener_) return;
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnClosedCaptionData,
+ handler_->eventlistener_, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3);
+ auto msg = es_msg::ClosedCaption::Make(data, size, listener,
+ handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+}
+
+void EsPlayer::TrackRendererEventListener::OnFlushDone() {
+ LOG_ENTER_P(handler_);
+ if (!handler_->eventlistener_) return;
+
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnFlushDone,
+ handler_->eventlistener_, std::placeholders::_1);
+ auto msg =
+ es_msg::FlushDone::Make(listener, handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+void EsPlayer::TrackRendererEventListener::OnEvent(const EventType& event,
+ const EventMsg& msg_data) {
+ if (!handler_->eventlistener_) return;
+
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnEvent,
+ handler_->eventlistener_, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3);
+ auto msg = es_msg::OnEvent::Make(event, msg_data, listener,
+ handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+// LCOV_EXCL_STOP
+
+void EsPlayer::TrackRendererEventListener::OnFirstDecodingDone() {
+ LOG_ENTER_P(handler_);
+ if (!handler_->eventlistener_) return;
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnFirstDecodingDone,
+ handler_->eventlistener_, std::placeholders::_1);
+ auto msg = es_msg::FirstDecodingDone::Make(listener,
+ handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+void EsPlayer::TrackRendererEventListener::OnVideoDecoderUnderrun() {
+ LOG_ENTER_P(handler_);
+ if (!handler_->eventlistener_) return;
+
+ if (handler_->state_manager_.GetState() != EsState::kPlaying &&
+ handler_->state_manager_.GetState() != EsState::kPaused) {
+ return;
+ }
+
+ auto listener =
+ std::bind(&esplusplayer::EsEventListener::OnVideoDecoderUnderrun,
+ handler_->eventlistener_, std::placeholders::_1);
+ auto msg = es_msg::Simple::Make(listener, handler_->eventlistener_userdata_);
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+void EsPlayer::TrackRendererEventListener::OnVideoLatencyStatus(
+ const LatencyStatus& latency_status) {
+ LOG_ENTER_P(handler_);
+ if (!handler_->eventlistener_) return;
+
+ if (handler_->state_manager_.GetState() != EsState::kPlaying &&
+ handler_->state_manager_.GetState() != EsState::kPaused) {
+ return;
+ }
+
+ auto listener = std::bind(
+ &esplusplayer::EsEventListener::OnVideoLatencyStatus,
+ handler_->eventlistener_, std::placeholders::_1, std::placeholders::_2);
+
+ auto msg = es_msg::PacketLatencyStatus::Make(
+ latency_status, listener, handler_->eventlistener_userdata_);
+
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+void EsPlayer::TrackRendererEventListener::OnAudioLatencyStatus(
+ const LatencyStatus& latency_status) {
+ LOG_ENTER_P(handler_);
+ if (!handler_->eventlistener_) return;
+
+ if (handler_->state_manager_.GetState() != EsState::kPlaying &&
+ handler_->state_manager_.GetState() != EsState::kPaused) {
+ return;
+ }
+
+ auto listener = std::bind(
+ &esplusplayer::EsEventListener::OnAudioLatencyStatus,
+ handler_->eventlistener_, std::placeholders::_1, std::placeholders::_2);
+
+ auto msg = es_msg::PacketLatencyStatus::Make(
+ latency_status, listener, handler_->eventlistener_userdata_);
+
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+void EsPlayer::TrackRendererEventListener::OnVideoHighLatency() {
+ LOG_ENTER_P(handler_);
+ if (!handler_->eventlistener_) return;
+
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnVideoHighLatency,
+ handler_->eventlistener_, std::placeholders::_1);
+
+ auto msg = es_msg::Simple::Make(listener, handler_->eventlistener_userdata_);
+
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+// LCOV_EXCL_START
+void EsPlayer::TrackRendererEventListener::OnAudioHighLatency() {
+ LOG_ENTER_P(handler_);
+ if (!handler_->eventlistener_) return;
+
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnAudioHighLatency,
+ handler_->eventlistener_, std::placeholders::_1);
+
+ auto msg = es_msg::Simple::Make(listener, handler_->eventlistener_userdata_);
+
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+}
+
+void EsPlayer::TrackRendererEventListener::OnVideoFrameDropped(
+ const uint64_t& count) {
+ if (!handler_->eventlistener_) return;
+
+ handler_->eventlistener_->OnVideoFrameDropped(
+ count, handler_->eventlistener_userdata_);
+
+ #if 0
+
+ if (handler_->state_manager_.GetState() != EsState::kPlaying &&
+ handler_->state_manager_.GetState() != EsState::kPaused) {
+ return;
+ }
+
+ auto listener = std::bind(&esplusplayer::EsEventListener::OnVideoFrameDropped,
+ handler_->eventlistener_, std::placeholders::_1,
+ std::placeholders::_2);
+
+ auto msg = es_msg::FrameDroppedCount::Make(
+ count, listener, handler_->eventlistener_userdata_);
+
+ std::unique_lock<std::mutex> msg_mutex(handler_->msg_task_mutex_);
+ handler_->msg_queue_.push(std::move(msg));
+ msg_mutex.unlock();
+ handler_->msg_task_cv_.notify_one();
+ LOG_LEAVE_P(handler_);
+ #endif
+}
+
+void EsPlayer::TrackRendererEventListener::OnDecoderInputBufferTime
+ (const TrackType& type, const DecoderBufferTime &time){
+ if (!handler_->eventlistener_) return;
+
+ StreamType stream_type = internal::ConvertToStreamType(type);
+ handler_->eventlistener_->OnDecoderInputBufferTime(stream_type, time);
+}
+
+void EsPlayer::TrackRendererEventListener::OnDecoderOutputBufferTime
+ (const TrackType& type, const DecoderBufferTime &time){
+ if (!handler_->eventlistener_) return;
+
+ StreamType stream_type = internal::ConvertToStreamType(type);
+ handler_->eventlistener_->OnDecoderOutputBufferTime(stream_type, time);
+}
+
+// LCOV_EXCL_STOP
+
+bool EsPlayer::SetVideoScanType(const PlayerVideoScanType type) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ if (type == kPlayerVideoScanTypeProgressive) set_video_progressive_ = 1;
+ return true;
+}
+bool EsPlayer::SetTimeUnitType(const PlayerTimeUnitType type) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() != EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ LOG_INFO_P(this, "PlayerTimeUnitType [%s]",
+ (type == kPlayerTimeUnitTypeMs) ? "Ms" : "Us");
+ time_unit_type = type;
+ return true;
+}
+
+bool EsPlayer::GetDecodingTime(StreamType type, int32_t* time_in_milliseconds) {
+ if (!time_in_milliseconds) return false;
+ if (state_manager_.GetState() <= EsState::kReady) {
+ *time_in_milliseconds = 0;
+ return false;
+ }
+ return trackrenderer_->GetDecodingTime(type, time_in_milliseconds);
+}
+
+bool EsPlayer::SetVideoStreamRotationInfo(const VideoRotation& rotation) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ video_rotate_ = rotation;
+ return trackrenderer_->SetVideoStreamRotationInfo(rotation);
+}
+
+bool EsPlayer::GetVideoStreamRotationInfo(VideoRotation* rotation) {
+ LOG_ENTER_P(this);
+ if (state_manager_.GetState() < EsState::kIdle) {
+ LOG_ERROR_P(this, "Invalid State , current %d",
+ state_manager_.GetStateEnum());
+ return false;
+ }
+ *rotation = video_rotate_;
+ return true;
+}
+
+bool EsPlayer::SetSimpleMixOutBufferLevel(
+ const PlayerSimpleMixOutBufferLevel level) {
+ if (state_manager_.GetState() == EsState::kNone) {
+ LOG_ERROR_P(this, "Invalid State");
+ return false;
+ }
+ int converted_level = 1;
+ if (level == kPlayerSimpleMixOutBufferLow) {
+ converted_level = 0;
+ }
+ else if (level == kPlayerSimpleMixOutBufferMid){
+ converted_level = 1;
+ }
+ else if (level == kPlayerSimpleMixOutBufferHigh){
+ converted_level = 2;
+ }
+
+ return trackrenderer_->SetSimpleMixOutBufferLevel(converted_level);
+}
+
+kpi::EsCodecLoggerKeys EsPlayer::MakeKpiKeys_() {
+ kpi::EsCodecLoggerKeys event_info;
+ event_info.app_id = app_info_.id;
+ if (submit_data_type_ == SubmitDataType::kCleanData) {
+ event_info.is_clean = true;
+ } else {
+ event_info.is_clean = false;
+ }
+
+ for (const auto& track : track_) {
+ if (track.type == kTrackTypeVideo) {
+ event_info.v_codec = track.mimetype;
+ event_info.v_codec_version = track.version;
+ event_info.width = track.maxwidth;
+ event_info.height = track.maxheight;
+ } else if (track.type == kTrackTypeAudio) {
+ event_info.a_codec = track.mimetype;
+ }
+ }
+ return event_info;
+}
+
+namespace es_conf {
+
+void LoadIniProperty(const Json::Value& root) {
+ gst_util::GstInit(root);
+ std::string key = "generate_dot";
+ es_conf::ini_property[key] = root.get(key, "").asBool();
+ LOG_DEBUG("[%s] : [%d]", key.c_str(), es_conf::ini_property[key]);
+}
+
+bool LoadIniFile() {
+ const char* path = esplusplayer_cfg::GetIniPath();
+ LOG_INFO("path : %s", path);
+ std::streampos size;
+ char* buf = nullptr;
+ std::ifstream file(path, std::ios::binary | std::ios::ate);
+ if (file.is_open() == false) {
+ gst_util::GstInit();
+ LOG_ERROR("Can't open file !!");
+ return false;
+ }
+ BOOST_SCOPE_EXIT(&file) { file.close(); }
+ BOOST_SCOPE_EXIT_END
+
+ size = file.tellg();
+ if (size <= 0) {
+ LOG_ERROR("Wrong file size");
+ return false;
+ }
+ size += 1;
+ buf = static_cast<char*>(calloc(size, sizeof(char)));
+ if (buf == nullptr) {
+ LOG_ERROR("Fail to calloc buf");
+ return false;
+ }
+ BOOST_SCOPE_EXIT(&buf) {
+ if (buf) free(buf);
+ }
+ BOOST_SCOPE_EXIT_END
+
+ file.seekg(0, std::ios::beg);
+ file.read(buf, size);
+
+ std::string config = buf;
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(config, root)) {
+ LOG_ERROR("Fail to parse configuration file %s",
+ (reader.getFormatedErrorMessages()).c_str());
+ return false;
+ }
+
+ es_conf::LoadIniProperty(root);
+ return true;
+}
+
+} // namespace es_conf
+
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "esplayer/esplayer_drm.h"
+
+#include <cstdint>
+#include <cstring>
+#include <sstream>
+#include <type_traits>
+
+#include "core/serializer.h"
+// LCOV_EXCL_START
+namespace esplusplayer {
+namespace esplayer_drm {
+GBytesPtr Serialize(const EsPacketPtr &packet,
+ const drm::EsPlayerEncryptedInfo &drm_info) {
+ Serializer s;
+
+ // box
+ s.Put<uint32_t>(0); // box_size : (lazy)
+ s.Put<uint32_t>(true); // secure
+ s.Put<int32_t>(drm_info.handle); // handle
+ s.Put<uint32_t>(0); // session_id
+ s.Put<uint32_t>(0); // psshinfo_size
+ auto psa_size_offset = s.Put<uint32_t>(0); // psa_size : (lazy)
+
+ // box.pPSAParam
+ s.Put(drm_info.algorithm); // algorithm
+ s.Put(drm_info.format); // format
+ s.Put(drm_info.phase); // phase
+ s.Put<uint32_t>(drm_info.use_out_buffer); // bUseOutBuf
+ s.Put<uint32_t>(drm_info.kid.size()); // uKIDLen
+ s.Put(drm_info.kid); // pKID
+ s.Put<uint32_t>(0); // uDataLen
+ s.Put<uint32_t>(0); // uOutBufLen
+ s.Put<uint32_t>(drm_info.initialization_vector.size()); // uIVLen
+ s.Put(drm_info.initialization_vector); // pIV
+
+ // box.pPSAParam->pSubData
+ drm::DrmbEsFragmentedMp4Data *sub_data =
+ reinterpret_cast<drm::DrmbEsFragmentedMp4Data *>(drm_info.sub_data);
+ if (sub_data != nullptr && sub_data->sub_sample_info_vector.size() != 0) {
+ uint32_t subdata_size = static_cast<unsigned int>(
+ sizeof(uint32_t) + sub_data->sub_sample_info_vector.size() *
+ sizeof(drm::DrmbEsSubSampleInfo));
+ s.Put<uint32_t>(subdata_size); // subData_size
+ const size_t sub_sample_count = sub_data->sub_sample_info_vector.size();
+ s.Put<uint32_t>(sub_sample_count); // uSubSampleCount
+ for (const auto &value : sub_data->sub_sample_info_vector) {
+ s.Put<uint32_t>(value.bytes_of_clear_data); // uBytesOfClearData
+ s.Put<uint32_t>(value.bytes_of_encrypted_data); // uBytesOfEncryptedData
+ }
+ } else {
+ s.Put<uint32_t>(0); // subData_size
+ }
+
+ // box.pPSAParam
+ s.Put(
+ reinterpret_cast<const Serializer::Byte *>(drm_info.split_offsets.data()),
+ sizeof(int) * drm_info.split_offsets.max_size()); // split_offsets
+ s.Put<uint8_t>(drm_info.use_pattern); // use_pattern
+ s.Put<uint32_t>(drm_info.crypt_byte_block); // crypt_byte_block
+ s.Put<uint32_t>(drm_info.skip_byte_block); // skip_byte_block
+
+ // box
+ auto reconfigure_pssh_offset = s.Put<uint32_t>(0); // reconfigure_pssh
+
+ const auto total_size = s.GetSize();
+ const auto psa_size = reconfigure_pssh_offset - psa_size_offset;
+
+ std::unique_ptr<Serializer::Byte, std::function<void(Serializer::Byte *)>>
+ raw_data_ptr(new Serializer::Byte[total_size],
+ [](Serializer::Byte *data) { delete[] data; });
+ s.Serialize(raw_data_ptr.get());
+ auto raw_data = raw_data_ptr.get();
+
+ // fill size field
+ Serializer::Put<uint32_t>(raw_data, total_size); // box_size
+ Serializer::Put<uint32_t>(raw_data + psa_size_offset, psa_size); // psa_size
+
+ return gstguard::make_guard(g_bytes_new(raw_data, total_size));
+}
+// LCOV_EXCL_STOP
+
+} // namespace esplayer_drm
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "esplusplayer/esplusplayer.h"
+
+#include "core/utils/plusplayer_log.h"
+#include "esplayer/esplayer.h"
+
+namespace esplusplayer {
+
+std::unique_ptr<EsPlusPlayer> EsPlusPlayer::Create() {
+ auto instance = std::unique_ptr<EsPlayer>(new EsPlayer);
+ LOG_INFO("Create Es Player [%p]", instance.get());
+ return instance;
+}
+
+} // namespace esplusplayer
--- /dev/null
+#include "esplusplayer_capi/esplusplayer_capi.h"
+
+#include "esplusplayer/esplusplayer.h"
+
+using esplusplayer::EsPlusPlayer;
+using esplusplayer::Geometry;
+
+#include <inttypes.h>
+#include <tbm_surface.h>
+
+#include <functional>
+#include <iostream>
+#include <fstream>
+#include <mutex>
+#include <unordered_map>
+#include <filesystem>
+
+#include "core/utils/plusplayer_log.h"
+#include "esplayer/decoded_pkt_list.h"
+#include "esplusplayer_capi/esplusplayer_internal.h"
+#ifndef IS_TOMATO
+#include "mixer_capi/mixer_capi.h"
+#endif
+#include "esplusplayer/appinfo.h"
+#include "esplusplayer/audioeasinginfo.h"
+#include "esplusplayer/drm.h"
+#include "esplusplayer/elementary_stream.h"
+#include "esplusplayer/espacket.h"
+#include "esplusplayer/esplusplayer.h"
+#include "esplusplayer/track.h"
+#include "esplusplayer/types/buffer.h"
+#include "esplusplayer/types/display.h"
+#include "esplusplayer/types/error.h"
+#include "esplusplayer/types/latency.h"
+#include "esplusplayer/types/picturequality.h"
+#include "esplusplayer/types/resource.h"
+#include "esplusplayer/types/stream.h"
+
+using esplusplayer::AdvPictureQualityType;
+using esplusplayer::AudioEasingInfo;
+using esplusplayer::AudioEasingType;
+using esplusplayer::AudioMimeType;
+using esplusplayer::AudioStream;
+using esplusplayer::AudioStreamPtr;
+using esplusplayer::BufferStatus;
+using esplusplayer::CatchUpSpeed;
+using esplusplayer::CropArea;
+using esplusplayer::DecodedPacketManagerInterface;
+using esplusplayer::DisplayMode;
+using esplusplayer::DisplayRotation;
+using esplusplayer::DisplayType;
+using esplusplayer::ErrorType;
+using esplusplayer::EsPacket;
+using esplusplayer::EsPacketPtr;
+using esplusplayer::EsState;
+using esplusplayer::LatencyStatus;
+using esplusplayer::MatroskaColor;
+using esplusplayer::PlayerAdaptiveInfo;
+using esplusplayer::PlayerAppInfo;
+using esplusplayer::PlayerAppInfoEx;
+using esplusplayer::PlayerAudioCodecType;
+using esplusplayer::PlayerAudioResourceType;
+using esplusplayer::PlayerLowLatencyMode;
+using esplusplayer::PlayerVideoCodecType;
+using esplusplayer::PlayerVideoScanType;
+using esplusplayer::PlayerTimeUnitType;
+using esplusplayer::Rational;
+using esplusplayer::RenderRect;
+using esplusplayer::RscAllocPolicy;
+using esplusplayer::RscType;
+using esplusplayer::StreamType;
+using esplusplayer::SubmitDataType;
+using esplusplayer::Track;
+using esplusplayer::TrackType;
+using esplusplayer::VideoMimeType;
+using esplusplayer::VideoStream;
+using esplusplayer::VideoStreamPtr;
+using esplusplayer::drm::EsPlayerEncryptedInfo;
+using esplusplayer::drm::Type;
+using esplusplayer::DecoderBufferTime;
+using esplusplayer::PlayerSimpleMixOutBufferLevel;
+using std::filesystem::exists;
+using esplusplayer::VideoRotation;
+
+namespace util {
+const std::unordered_map<esplusplayer_error_type, std::string> kErrorStringMap =
+ {{ESPLUSPLAYER_ERROR_TYPE_NONE, "ESPLUSPLAYER_ERROR_TYPE_NONE"},
+ {ESPLUSPLAYER_ERROR_TYPE_OUT_OF_MEMORY,
+ "ESPLUSPLAYER_ERROR_TYPE_OUT_OF_MEMORY"},
+ {ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER,
+ "ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER"},
+ {ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION,
+ "ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION"},
+ {ESPLUSPLAYER_ERROR_TYPE_INVALID_STATE,
+ "ESPLUSPLAYER_ERROR_TYPE_INVALID_STATE"},
+ {ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_AUDIO_CODEC,
+ "ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_AUDIO_CODEC"},
+ {ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_VIDEO_CODEC,
+ "ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_VIDEO_CODEC"},
+ {ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FILE,
+ "ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FILE"},
+ {ESPLUSPLAYER_ERROR_TYPE_CONNECTION_FAILED,
+ "ESPLUSPLAYER_ERROR_TYPE_CONNECTION_FAILED"},
+ {ESPLUSPLAYER_ERROR_TYPE_DRM_EXPIRED,
+ "ESPLUSPLAYER_ERROR_TYPE_DRM_EXPIRED"},
+ {ESPLUSPLAYER_ERROR_TYPE_DRM_NO_LICENSE,
+ "ESPLUSPLAYER_ERROR_TYPE_DRM_NO_LICENSE"},
+ {ESPLUSPLAYER_ERROR_TYPE_DRM_FUTURE_USE,
+ "ESPLUSPLAYER_ERROR_TYPE_DRM_FUTURE_USE"},
+ {ESPLUSPLAYER_ERROR_TYPE_NOT_PERMITTED,
+ "ESPLUSPLAYER_ERROR_TYPE_NOT_PERMITTED"},
+ {ESPLUSPLAYER_ERROR_TYPE_DRM_DECRYPTION_FAILED,
+ "ESPLUSPLAYER_ERROR_TYPE_DRM_DECRYPTION_FAILED"},
+ {ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FORMAT,
+ "ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FORMAT"},
+ {ESPLUSPLAYER_ERROR_TYPE_UNKNOWN, "ESPLUSPLAYER_ERROR_TYPE_UNKNOWN"}};
+
+const std::string kUnhandledErrorString = "Unhandled Error Type";
+static const std::string& ConvertErrorTypeToString(
+ esplusplayer_error_type type) {
+ return kErrorStringMap.count(type) > 0 ? kErrorStringMap.at(type)
+ : kUnhandledErrorString;
+}
+
+// LCOV_EXCL_START
+esplusplayer_error_type ConvertErrorCode(const ErrorType& error_code) {
+ esplusplayer_error_type type = ESPLUSPLAYER_ERROR_TYPE_NONE;
+ switch (error_code) {
+ case ErrorType::kOutOfMemory:
+ type = ESPLUSPLAYER_ERROR_TYPE_OUT_OF_MEMORY;
+ break;
+ case ErrorType::kInvalidParameter:
+ type = ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ break;
+ case ErrorType::kInvalidOperation:
+ type = ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION;
+ break;
+ case ErrorType::kInvalidState:
+ type = ESPLUSPLAYER_ERROR_TYPE_INVALID_STATE;
+ break;
+ case ErrorType::kNotSupportedAudioCodec:
+ type = ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_AUDIO_CODEC;
+ break;
+ case ErrorType::kNotSupportedVideoCodec:
+ type = ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_VIDEO_CODEC;
+ break;
+ case ErrorType::kNotSupportedFile:
+ type = ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FILE;
+ break;
+ case ErrorType::kConnectionFailed:
+ type = ESPLUSPLAYER_ERROR_TYPE_CONNECTION_FAILED;
+ break;
+ case ErrorType::kDrmExpired:
+ type = ESPLUSPLAYER_ERROR_TYPE_DRM_EXPIRED;
+ break;
+ case ErrorType::kDrmNoLicense:
+ type = ESPLUSPLAYER_ERROR_TYPE_DRM_NO_LICENSE;
+ break;
+ case ErrorType::kDrmFutureUse:
+ type = ESPLUSPLAYER_ERROR_TYPE_DRM_FUTURE_USE;
+ break;
+ case ErrorType::kDrmNotPermitted:
+ type = ESPLUSPLAYER_ERROR_TYPE_NOT_PERMITTED;
+ break;
+ case ErrorType::kDrmInfo:
+ type = ESPLUSPLAYER_ERROR_TYPE_DRM_DECRYPTION_FAILED;
+ break;
+ case ErrorType::kNotSupportedFormat:
+ type = ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FORMAT;
+ break;
+ case ErrorType::kNone:
+ type = ESPLUSPLAYER_ERROR_TYPE_NONE;
+ break;
+ default:
+ LOG_ERROR("not defined error %x", static_cast<int>(error_code));
+ type = ESPLUSPLAYER_ERROR_TYPE_UNKNOWN;
+ break;
+ }
+ return type;
+}
+// LCOV_EXCL_STOP
+
+} // namespace util
+
+struct EsPlusPlayerPriv;
+
+class listener_bridge : public esplusplayer::EsEventListener {
+ public:
+ listener_bridge() { LOG_ENTER }
+ ~listener_bridge() { LOG_ENTER }
+
+ void ResetPacketList() {
+ if (decoded_pkt_mgr_) decoded_pkt_mgr_->Clear();
+ }
+
+ void ResetMultiSeekControl() {
+ std::unique_lock<std::mutex> lock(multi_seek_control.lock);
+ multi_seek_control.is_offset_valid = false;
+ multi_seek_control.offset = 0;
+ }
+
+ void Reset() {
+ LOG_ENTER
+ ResetPacketList();
+ ResetMultiSeekControl();
+ LOG_LEAVE
+ }
+
+
+
+ virtual void OnError(const ErrorType& error_code, UserData userdata) {
+ LOG_ENTER
+ LOG_INFO("error code : %x", static_cast<int>(error_code));
+ if (this->error_cb_)
+ this->error_cb_(util::ConvertErrorCode(error_code), error_cb_userdata_);
+ }
+
+ virtual void OnBufferStatus(const StreamType& type,
+ const BufferStatus& status,
+ const uint64_t byte_size,
+ const uint64_t time_size, UserData userdata) {
+ // LOG_ENTER
+ // LOG_INFO("stream type : %d, buffer status : %d", static_cast<int>(type),
+ // static_cast<int>(status));
+
+ if (this->buffer_status_cb_)
+ this->buffer_status_cb_(static_cast<esplusplayer_stream_type>(type),
+ static_cast<esplusplayer_buffer_status>(status),
+ buffer_status_cb_userdata_);
+ if (this->buffer_byte_status_cb_)
+ this->buffer_byte_status_cb_(
+ static_cast<esplusplayer_stream_type>(type),
+ static_cast<esplusplayer_buffer_status>(status), byte_size,
+ buffer_byte_status_cb_userdata_);
+ if (this->buffer_time_status_cb_)
+ this->buffer_time_status_cb_(
+ static_cast<esplusplayer_stream_type>(type),
+ static_cast<esplusplayer_buffer_status>(status), time_size,
+ buffer_time_status_cb_userdata_);
+ }
+
+ virtual void OnResourceConflicted(UserData userdata) {
+ LOG_ENTER
+ this->Reset();
+ if (this->resource_conflicted_cb_)
+ this->resource_conflicted_cb_(resource_conflicted_cb_userdata_);
+ }
+
+ virtual void OnEos(UserData userdata) {
+ LOG_ENTER
+ if (this->eos_cb_) this->eos_cb_(eos_cb_userdata_);
+ }
+
+ virtual void OnPrepareDone(bool result, UserData userdata) {
+ LOG_ENTER
+ LOG_INFO("prepare done. result : %s", result ? "true" : "false");
+ if (this->prepare_async_done_cb_)
+ this->prepare_async_done_cb_(result, prepare_async_done_cb_userdata_);
+ }
+
+ virtual void OnReadyToPrepare(const StreamType& type, UserData userdata) {
+ LOG_ENTER
+ LOG_INFO("stream type : %d", static_cast<int>(type));
+ if (this->ready_to_prepare_cb_)
+ this->ready_to_prepare_cb_(static_cast<esplusplayer_stream_type>(type),
+ ready_to_prepare_cb_userdata_);
+ }
+
+ virtual void OnSeekDone(UserData userdata) {
+ LOG_ENTER
+ if (this->seek_done_cb_) this->seek_done_cb_(seek_done_cb_userdata_);
+ }
+
+ virtual void OnReadyToSeek(const StreamType& type, const uint64_t offset,
+ UserData userdata) {
+ LOG_ENTER
+ LOG_INFO("offset : %" PRId64"", offset);
+ std::unique_lock<std::mutex> lock(this->multi_seek_control.lock);
+ if (this->multi_seek_control.is_offset_valid == false ||
+ this->multi_seek_control.offset != offset) {
+ LOG_ERROR("Invalid offset:%" PRId64"", this->multi_seek_control.offset);
+ return;
+ }
+ if (this->ready_to_seek_cb_)
+ this->ready_to_seek_cb_(static_cast<esplusplayer_stream_type>(type),
+ offset, ready_to_seek_cb_userdata_);
+ }
+
+ void SetDecodedPacketManager(
+ std::shared_ptr<DecodedPacketManagerInterface>& mgr) {
+ decoded_pkt_mgr_ = mgr;
+ }
+
+ virtual void OnMediaPacketGetTbmBufPtr(void** ptr, bool is_scale_change) {
+ // get one free point in current tbm list, send to trackrender, if can't
+ // find, set null
+ void* ptr1 = nullptr;
+ if (decoded_pkt_mgr_)
+ decoded_pkt_mgr_->GetFreeTbmSurface(&ptr1, is_scale_change);
+ *ptr = ptr1;
+ }
+
+ // LCOV_EXCL_START
+ virtual void OnMediaPacketVideoDecoded(
+ const esplusplayer::DecodedVideoPacket& packet) {
+ if (this->media_packet_video_decoded_cb_ == nullptr) return;
+
+ auto* _pkt = new esplusplayer_decoded_video_packet();
+ _pkt->pts = packet.pts;
+ _pkt->duration = packet.duration;
+ _pkt->surface_data = static_cast<void*>(packet.surface_data);
+ _pkt->private_data = packet.scaler_index;
+ if (decoded_pkt_mgr_ && decoded_pkt_mgr_->TryToAdd(_pkt)) {
+ this->media_packet_video_decoded_cb_(
+ _pkt, media_packet_video_decoded_cb_userdata_);
+ } else {
+ LOG_ERROR("Too many buffers are not released. packet(%p) will be drop.",
+ _pkt);
+ }
+ }
+
+ virtual void OnClosedCaptionData(std::unique_ptr<char[]> data, const int size,
+ UserData userdata) {
+ LOG_ENTER
+ if (this->closed_caption_cb_) {
+ this->closed_caption_cb_(data.get(), size, closed_caption_cb_userdata_);
+ }
+ }
+
+ virtual void OnFlushDone(UserData userdata) {
+ LOG_ENTER
+ if (this->flush_done_cb_) this->flush_done_cb_(flush_done_cb_userdata_);
+ }
+
+ virtual void OnEvent(const esplusplayer::EventType& event,
+ const esplusplayer::EventMsg& msg_data,
+ UserData userdata) {
+ LOG_INFO("event type [%d]", static_cast<int>(event));
+ esplusplayer_event_msg event_msg;
+ event_msg.data = const_cast<char*>(msg_data.data.c_str());
+ event_msg.len = msg_data.len;
+ if (this->event_cb_)
+ this->event_cb_(static_cast<esplusplayer_event_type>(event), event_msg,
+ event_cb_userdata_);
+ }
+ // LCOV_EXCL_STOP
+
+ virtual void OnFirstDecodingDone(UserData userdata) {
+ LOG_ENTER
+ if (this->first_video_decoding_done_cb_) {
+ this->first_video_decoding_done_cb_(
+ first_video_decoding_done_cb_userdata_);
+ }
+ }
+
+ virtual void OnVideoDecoderUnderrun(UserData userdata) {
+ LOG_ENTER
+ if (this->video_decoder_underrun_cb_)
+ this->video_decoder_underrun_cb_(video_decoder_underrun_cb_userdata_);
+ }
+
+ virtual void OnVideoLatencyStatus(const LatencyStatus& latency_status,
+ UserData userdata) {
+ LOG_ENTER
+ if (this->video_latency_status_cb_)
+ this->video_latency_status_cb_(
+ static_cast<esplusplayer_latency_status>(latency_status),
+ video_latency_status_cb_userdata_);
+ }
+
+ virtual void OnAudioLatencyStatus(const LatencyStatus& latency_status,
+ UserData userdata) {
+ LOG_ENTER
+ if (this->audio_latency_status_cb_)
+ this->audio_latency_status_cb_(
+ static_cast<esplusplayer_latency_status>(latency_status),
+ audio_latency_status_cb_userdata_);
+ }
+
+ virtual void OnVideoHighLatency(UserData userdata) {
+ LOG_ENTER
+ if (this->video_high_latency_cb_)
+ this->video_high_latency_cb_(video_high_latency_cb_userdata_);
+ }
+
+ virtual void OnAudioHighLatency(UserData userdata) {
+ LOG_ENTER
+ if (this->audio_high_latency_cb_)
+ this->audio_high_latency_cb_(audio_high_latency_cb_userdata_);
+ }
+
+ virtual void OnVideoFrameDropped(const uint64_t& count, UserData userdata) {
+ LOG_ERROR("count: %" PRId64"", count);
+ if (this->video_frame_dropped_cb_)
+ this->video_frame_dropped_cb_(count, video_frame_dropped_cb_userdata_);
+ }
+
+ virtual void OnDecoderInputBufferTime(const StreamType& type, const DecoderBufferTime& time) {
+ if (this->decoder_input_buffer_time_cb_) {
+ esplusplayer_decoder_buffer_time decoder_buffer_time;
+ decoder_buffer_time.pts = time.pts;
+ decoder_buffer_time.system_time = time.system_time;
+ this->decoder_input_buffer_time_cb_(
+ static_cast<esplusplayer_stream_type>(type), decoder_buffer_time, decoder_input_buffer_time_cb_userdata_);
+ }
+ }
+
+ virtual void OnDecoderOutputBufferTime(const StreamType& type, const DecoderBufferTime& time) {
+ if (this->decoder_output_buffer_time_cb_) {
+ esplusplayer_decoder_buffer_time decoder_buffer_time;
+ decoder_buffer_time.pts = time.pts;
+ decoder_buffer_time.system_time = time.system_time;
+ this->decoder_output_buffer_time_cb_(
+ static_cast<esplusplayer_stream_type>(type), decoder_buffer_time, decoder_output_buffer_time_cb_userdata_);
+ }
+ }
+
+ private:
+ static void DecodedPacketDeleter(esplusplayer_decoded_video_packet* packet) {
+ if (packet->surface_data != nullptr) {
+ tbm_surface_destroy(static_cast<tbm_surface_h>(packet->surface_data));
+ packet->surface_data = NULL;
+ }
+ delete packet;
+ }
+
+ private:
+ esplusplayer_error_cb error_cb_ = nullptr;
+ void* error_cb_userdata_ = nullptr;
+ esplusplayer_buffer_status_cb buffer_status_cb_ = nullptr;
+ void* buffer_status_cb_userdata_ = nullptr;
+ esplusplayer_buffer_byte_status_cb buffer_byte_status_cb_ = nullptr;
+ void* buffer_byte_status_cb_userdata_ = nullptr;
+ esplusplayer_buffer_time_status_cb buffer_time_status_cb_ = nullptr;
+ void* buffer_time_status_cb_userdata_ = nullptr;
+ esplusplayer_resource_conflicted_cb resource_conflicted_cb_ = nullptr;
+ void* resource_conflicted_cb_userdata_ = nullptr;
+ esplusplayer_eos_cb eos_cb_ = nullptr;
+ void* eos_cb_userdata_ = nullptr;
+ esplusplayer_ready_to_prepare_cb ready_to_prepare_cb_ = nullptr;
+ void* ready_to_prepare_cb_userdata_ = nullptr;
+ esplusplayer_prepare_async_done_cb prepare_async_done_cb_ = nullptr;
+ void* prepare_async_done_cb_userdata_ = nullptr;
+ esplusplayer_seek_done_cb seek_done_cb_ = nullptr;
+ void* seek_done_cb_userdata_ = nullptr;
+ esplusplayer_ready_to_seek_cb ready_to_seek_cb_ = nullptr;
+ void* ready_to_seek_cb_userdata_ = nullptr;
+ esplusplayer_media_packet_video_decoded_cb media_packet_video_decoded_cb_ =
+ nullptr;
+ void* media_packet_video_decoded_cb_userdata_ = nullptr;
+ esplusplayer_closed_caption_cb closed_caption_cb_ = nullptr;
+ void* closed_caption_cb_userdata_ = nullptr;
+ esplusplayer_flush_done_cb flush_done_cb_ = nullptr;
+ void* flush_done_cb_userdata_ = nullptr;
+ esplusplayer_event_cb event_cb_ = nullptr;
+ void* event_cb_userdata_ = nullptr;
+ esplusplayer_first_video_decoding_done_cb first_video_decoding_done_cb_ =
+ nullptr;
+ void* first_video_decoding_done_cb_userdata_ = nullptr;
+ esplusplayer_decoder_underrun_cb video_decoder_underrun_cb_ = nullptr;
+ void* video_decoder_underrun_cb_userdata_ = nullptr;
+ esplusplayer_video_latency_status_cb video_latency_status_cb_ = nullptr;
+ esplusplayer_audio_latency_status_cb audio_latency_status_cb_ = nullptr;
+ void* video_latency_status_cb_userdata_ = nullptr;
+ void* audio_latency_status_cb_userdata_ = nullptr;
+ esplusplayer_video_high_latency_cb video_high_latency_cb_ = nullptr;
+ esplusplayer_audio_high_latency_cb audio_high_latency_cb_ = nullptr;
+ void* video_high_latency_cb_userdata_ = nullptr;
+ void* audio_high_latency_cb_userdata_ = nullptr;
+ esplusplayer_video_frame_dropped_cb video_frame_dropped_cb_ = nullptr;
+ void* video_frame_dropped_cb_userdata_ = nullptr;
+ esplusplayer_decoder_buffer_time_cb decoder_input_buffer_time_cb_ =
+ nullptr;
+ esplusplayer_decoder_buffer_time_cb decoder_output_buffer_time_cb_ =
+ nullptr;
+ void* decoder_input_buffer_time_cb_userdata_ = nullptr;
+ void* decoder_output_buffer_time_cb_userdata_ = nullptr;
+
+ std::shared_ptr<DecodedPacketManagerInterface> decoded_pkt_mgr_;
+
+ struct MultiSeekControl {
+ std::mutex lock;
+ bool is_offset_valid = false;
+ uint64_t offset = 0;
+ };
+ friend void update_ready_to_seek_callback(
+ esplusplayer_handle pp, esplusplayer_ready_to_seek_cb ready_to_seek_cb,
+ void* userdata);
+ friend void update_ready_to_seek_offset(esplusplayer_handle pp,
+ const uint64_t offset);
+ MultiSeekControl multi_seek_control;
+
+ friend int esplusplayer_set_error_cb(esplusplayer_handle pp,
+ esplusplayer_error_cb error_cb,
+ void* userdata);
+ friend int esplusplayer_set_buffer_status_cb(
+ esplusplayer_handle pp, esplusplayer_buffer_status_cb buffer_status_cb,
+ void* userdata);
+ friend int esplusplayer_set_buffer_byte_status_cb(
+ esplusplayer_handle pp,
+ esplusplayer_buffer_byte_status_cb buffer_status_cb, void* userdata);
+ friend int esplusplayer_set_buffer_time_status_cb(
+ esplusplayer_handle pp,
+ esplusplayer_buffer_time_status_cb buffer_status_cb, void* userdata);
+ friend int esplusplayer_set_resource_conflicted_cb(
+ esplusplayer_handle pp,
+ esplusplayer_resource_conflicted_cb resource_conflicted_cb,
+ void* userdata);
+ friend int esplusplayer_set_eos_cb(esplusplayer_handle pp,
+ esplusplayer_eos_cb eos_cb,
+ void* userdata);
+ friend int esplusplayer_set_ready_to_prepare_cb(
+ esplusplayer_handle pp,
+ esplusplayer_ready_to_prepare_cb ready_to_prepare_cb, void* userdata);
+ friend int esplusplayer_set_prepare_async_done_cb(
+ esplusplayer_handle pp,
+ esplusplayer_prepare_async_done_cb prepare_async_done_cb, void* userdata);
+ friend int esplusplayer_set_seek_done_cb(
+ esplusplayer_handle pp, esplusplayer_seek_done_cb seek_done_cb,
+ void* userdata);
+ friend int esplusplayer_set_ready_to_seek_cb(
+ esplusplayer_handle pp, esplusplayer_ready_to_seek_cb ready_to_seek_cb,
+ void* userdata);
+ friend int esplusplayer_set_media_packet_video_decoded_cb(
+ esplusplayer_handle pp,
+ esplusplayer_media_packet_video_decoded_cb media_packet_video_decoded_cb,
+ void* userdata);
+ friend int esplusplayer_set_closed_caption_cb(
+ esplusplayer_handle handle,
+ esplusplayer_closed_caption_cb closed_caption_cb, void* userdata);
+ friend int esplusplayer_set_flush_done_cb(
+ esplusplayer_handle pp, esplusplayer_flush_done_cb flush_done_cb,
+ void* userdata);
+ friend int esplusplayer_set_event_cb(esplusplayer_handle pp,
+ esplusplayer_event_cb event_cb,
+ void* userdata);
+ friend int esplusplayer_set_first_video_decoding_done_cb(
+ esplusplayer_handle handle,
+ esplusplayer_first_video_decoding_done_cb first_video_decoding_done_cb,
+ void* userdata);
+ friend int esplusplayer_set_video_decoder_underrun_cb(
+ esplusplayer_handle handle,
+ esplusplayer_decoder_underrun_cb video_decoder_underrun_cb,
+ void* userdata);
+ friend int esplusplayer_set_video_latency_status_cb(
+ esplusplayer_handle pp,
+ esplusplayer_video_latency_status_cb video_latency_status_cb,
+ void* userdata);
+ friend int esplusplayer_set_audio_latency_status_cb(
+ esplusplayer_handle pp,
+ esplusplayer_audio_latency_status_cb audio_latency_status_cb,
+ void* userdata);
+ friend int esplusplayer_set_video_high_latency_cb(
+ esplusplayer_handle pp,
+ esplusplayer_video_high_latency_cb video_high_latency_cb, void* userdata);
+ friend int esplusplayer_set_audio_high_latency_cb(
+ esplusplayer_handle pp,
+ esplusplayer_audio_high_latency_cb audio_high_latency_cb, void* userdata);
+ friend int esplusplayer_set_video_frame_dropped_cb(
+ esplusplayer_handle pp,
+ esplusplayer_video_frame_dropped_cb video_frame_dropped_cb,
+ void* userdata);
+ friend int esplusplayer_set_decoder_input_buffer_time_cb(
+ esplusplayer_handle handle,
+ esplusplayer_decoder_buffer_time_cb decoder_buffer_time_cb,
+ void* userdata);
+ friend int esplusplayer_set_decoder_output_buffer_time_cb(
+ esplusplayer_handle handle,
+ esplusplayer_decoder_buffer_time_cb decoder_buffer_time_cb,
+ void* userdata);
+};
+
+#define ES_DUMP 0
+struct EsPlusPlayerPriv {
+ std::unique_ptr<EsPlusPlayer> player;
+ std::unique_ptr<listener_bridge> listener{new listener_bridge()};
+ std::shared_ptr<DecodedPacketManagerInterface> decoded_pkt_mgr;
+
+ friend EsPlusPlayerPriv* EsPrivCreate();
+ friend void EsPrivDestroy(EsPlusPlayerPriv*& instance);
+
+#ifdef ES_DUMP
+ std::ofstream video_stream_;
+ std::ofstream audio_stream_;
+#endif
+
+ private:
+ EsPlusPlayerPriv() {}
+ ~EsPlusPlayerPriv() {}
+};
+
+EsPlusPlayerPriv* EsPrivCreate() {
+ EsPlusPlayerPriv* instance = new EsPlusPlayerPriv();
+ instance->player = EsPlusPlayer::Create();
+ instance->player->RegisterListener(instance->listener.get(),
+ instance->player.get());
+ return instance;
+}
+
+void EsPrivDestroy(EsPlusPlayerPriv*& instance) {
+ if (instance) delete instance;
+ instance = nullptr;
+}
+
+inline bool is_null_(void* object) { return object == nullptr; }
+
+inline EsPlusPlayer* cast_(esplusplayer_handle pp) {
+ auto priv = static_cast<EsPlusPlayerPriv*>(pp);
+ return priv ? priv->player.get() : nullptr;
+}
+
+inline listener_bridge* listener_cast_(esplusplayer_handle pp) {
+ auto priv = static_cast<EsPlusPlayerPriv*>(pp);
+ return priv->listener.get();
+}
+
+void update_ready_to_seek_callback(
+ esplusplayer_handle handle, esplusplayer_ready_to_seek_cb ready_to_seek_cb,
+ void* userdata) {
+ LOG_ENTER
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return;
+ }
+ std::unique_lock<std::mutex> lock(listener->multi_seek_control.lock);
+ listener->ready_to_seek_cb_ = ready_to_seek_cb;
+ listener->ready_to_seek_cb_userdata_ = userdata;
+ listener->multi_seek_control.is_offset_valid = false;
+}
+void update_ready_to_seek_offset(esplusplayer_handle handle,
+ const uint64_t offset) {
+ LOG_ENTER
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return;
+ }
+ std::unique_lock<std::mutex> lock(listener->multi_seek_control.lock);
+ listener->multi_seek_control.offset = offset;
+ listener->multi_seek_control.is_offset_valid = true;
+}
+
+// LCOV_EXCL_START
+inline void convert_matroska_color_info_(
+ const esplusplayer_matroska_color* from, MatroskaColor* to) {
+ to->matrix_coefficients = from->matrix_coefficients;
+ to->bits_per_channel = from->bits_per_channel;
+ to->chroma_subsampling_horizontal = from->chroma_subsampling_horizontal;
+ to->chroma_subsampling_vertical = from->chroma_subsampling_vertical;
+ to->cb_subsampling_horizontal = from->cb_subsampling_horizontal;
+ to->cb_subsampling_vertical = from->cb_subsampling_vertical;
+ to->chroma_siting_horizontal = from->chroma_siting_horizontal;
+ to->chroma_siting_vertical = from->chroma_siting_vertical;
+ to->range = from->range;
+ to->transfer_characteristics = from->transfer_characteristics;
+ to->primaries = from->primaries;
+ to->max_cll = from->max_cll;
+ to->max_fall = from->max_fall;
+ to->is_hdr_10p = from->isHDR10p;
+ to->metadata.primary_r_chromaticity_x =
+ from->metadata.primary_r_chromaticity_x;
+ to->metadata.primary_r_chromaticity_y =
+ from->metadata.primary_r_chromaticity_y;
+ to->metadata.primary_g_chromaticity_x =
+ from->metadata.primary_g_chromaticity_x;
+ to->metadata.primary_g_chromaticity_y =
+ from->metadata.primary_g_chromaticity_y;
+ to->metadata.primary_b_chromaticity_x =
+ from->metadata.primary_b_chromaticity_x;
+ to->metadata.primary_b_chromaticity_y =
+ from->metadata.primary_b_chromaticity_y;
+ to->metadata.white_point_chromaticity_x =
+ from->metadata.white_point_chromaticity_x;
+ to->metadata.white_point_chromaticity_y =
+ from->metadata.white_point_chromaticity_y;
+ to->metadata.luminance_max = from->metadata.luminance_max;
+ to->metadata.luminance_min = from->metadata.luminance_min;
+}
+// LCOV_EXCL_STOP
+
+inline EsPacketPtr convert_espacket_(esplusplayer_es_packet* from) {
+ std::shared_ptr<char> buffer = nullptr;
+ std::shared_ptr<char> hdr10p_metadata = nullptr;
+ if (from->buffer_size != 0 && from->buffer) {
+ buffer = std::shared_ptr<char>(new char[from->buffer_size],
+ std::default_delete<char[]>());
+ memcpy(buffer.get(), from->buffer, from->buffer_size);
+ }
+ if (from->hdr10p_metadata_size != 0 && from->hdr10p_metadata) {
+ hdr10p_metadata = std::shared_ptr<char>(
+ new char[from->hdr10p_metadata_size], std::default_delete<char[]>());
+ memcpy(hdr10p_metadata.get(), from->hdr10p_metadata,
+ from->hdr10p_metadata_size);
+ }
+ auto espacket = EsPacket::Create(static_cast<StreamType>(from->type), buffer,
+ from->buffer_size, from->pts, from->duration,
+ from->hdr10p_metadata_size, hdr10p_metadata);
+
+ if (from->matroska_color_info != nullptr) {
+ MatroskaColor color_info;
+ convert_matroska_color_info_(from->matroska_color_info, &color_info);
+ bool ret = espacket->SetMatroskaColorInfo(color_info);
+ if (ret == false) return nullptr;
+ }
+ return std::move(espacket);
+}
+
+// LCOV_EXCL_START
+using EncryptedInfoPtr =
+ std::unique_ptr<EsPlayerEncryptedInfo,
+ std::function<void(EsPlayerEncryptedInfo*)>>;
+inline EncryptedInfoPtr convert_es_drm_info_(esplusplayer_drm_info* from) {
+ auto custom_deleter = [](EsPlayerEncryptedInfo* drm_info) {
+ if (drm_info == nullptr) return;
+ if (drm_info->sub_data != nullptr) {
+ delete reinterpret_cast<esplusplayer::drm::DrmbEsFragmentedMp4Data*>(
+ drm_info->sub_data);
+ drm_info->sub_data = nullptr;
+ }
+ delete drm_info;
+ };
+
+ if (from == nullptr) return EncryptedInfoPtr(nullptr, custom_deleter);
+
+ EncryptedInfoPtr drm_info =
+ EncryptedInfoPtr(new EsPlayerEncryptedInfo(), custom_deleter);
+
+ drm_info->handle = from->handle;
+ drm_info->algorithm =
+ static_cast<esplusplayer::drm::DrmbEsCipherAlgorithm>(from->algorithm);
+ drm_info->format =
+ static_cast<esplusplayer::drm::DrmbEsMediaFormat>(from->format);
+ drm_info->phase =
+ static_cast<esplusplayer::drm::DrmbEsCipherPhase>(from->phase);
+
+ // kid
+ if (from->kid && from->kid_length > 0) {
+ drm_info->kid = std::move(
+ std::vector<unsigned char>(from->kid, from->kid + from->kid_length));
+ }
+
+ // initialization_vector
+ if (from->iv && from->iv_length > 0) {
+ drm_info->initialization_vector = std::move(
+ std::vector<unsigned char>(from->iv, from->iv + from->iv_length));
+ }
+
+ // sub_data
+ auto* from_sub_data =
+ reinterpret_cast<esplusplayer_drmb_es_fmp4_data*>(from->sub_data);
+ if (from_sub_data && from_sub_data->subsample_count > 0) {
+ drm_info->sub_data = new esplusplayer::drm::DrmbEsFragmentedMp4Data;
+ auto* sub_data =
+ reinterpret_cast<esplusplayer::drm::DrmbEsFragmentedMp4Data*>(
+ drm_info->sub_data);
+ for (uint32_t i = 0; i < from_sub_data->subsample_count; i++) {
+ auto& subsample_info = from_sub_data->subsample_infos[i];
+ sub_data->sub_sample_info_vector.emplace_back(
+ subsample_info.bytes_of_clear_data,
+ subsample_info.bytes_of_encrypted_data);
+ }
+ }
+
+ // split_offsets
+ if (from->split_offsets) {
+ const std::size_t kSplitOffsetMaxSize = 15 * sizeof(int);
+ std::memcpy(drm_info->split_offsets.data(), from->split_offsets,
+ kSplitOffsetMaxSize);
+ }
+
+ drm_info->use_out_buffer = from->use_out_buffer;
+ drm_info->use_pattern = from->use_pattern;
+ drm_info->crypt_byte_block = from->crypt_byte_block;
+ drm_info->skip_byte_block = from->skip_byte_block;
+
+ return std::move(drm_info);
+}
+// LCOV_EXCL_STOP
+
+inline AudioStreamPtr convert_stream_ptr_(
+ esplusplayer_audio_stream_info* from) {
+ LOG_INFO("mime type : %d", static_cast<int>(from->mime_type));
+ LOG_INFO("from->bitrate : %d", from->bitrate);
+ LOG_INFO("from->channels : %d", from->channels);
+ LOG_INFO("from->sample_rate : %d", from->sample_rate);
+ LOG_INFO("from->codec_data_length : %d", from->codec_data_length);
+
+ auto stream = AudioStream::Create();
+ std::shared_ptr<char> codec_data = nullptr;
+
+ if (from->codec_data_length != 0) {
+ codec_data = std::shared_ptr<char>(new char[from->codec_data_length],
+ std::default_delete<char[]>());
+ memcpy(codec_data.get(), from->codec_data, from->codec_data_length);
+ }
+
+ stream->SetCodecData(codec_data, from->codec_data_length);
+ stream->SetMimeType(
+ static_cast<esplusplayer::AudioMimeType>(from->mime_type));
+ stream->SetBitrate(from->bitrate);
+ stream->SetChannels(from->channels);
+ stream->SetSamplerate(from->sample_rate);
+
+ return std::move(stream);
+}
+
+inline int convert_return_type_(bool ret) {
+ return ret ? ESPLUSPLAYER_ERROR_TYPE_NONE
+ : ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION;
+}
+
+inline esplusplayer_get_decoded_video_frame_status_type
+convert_get_decoded_video_frame_status_(
+ const esplusplayer::GetDecodedVideoFrameStatus status) {
+ switch (status) {
+ case esplusplayer::GetDecodedVideoFrameStatus::kSuccess: {
+ return ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_SUCCESS;
+ }
+ case esplusplayer::GetDecodedVideoFrameStatus::kNoRemainingBuffer: {
+ return ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_NO_REMAINING_BUFFER;
+ }
+ case esplusplayer::GetDecodedVideoFrameStatus::kNoFilledBuffer: {
+ return ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_NO_FILLED_BUFFER;
+ }
+ case esplusplayer::GetDecodedVideoFrameStatus::kUnknown: {
+ return ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_UNKNOWN;
+ }
+ default: { return ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_UNKNOWN; }
+ }
+ return ESPLUSPLAYER_GET_DECVIDEOFRAME_STATUS_UNKNOWN;
+}
+
+inline VideoStreamPtr convert_stream_ptr_(
+ esplusplayer_video_stream_info* from) {
+ LOG_INFO("mime type : %u", static_cast<int>(from->mime_type));
+ LOG_INFO("from->width : %u", from->width);
+ LOG_INFO("from->height : %u", from->height);
+ LOG_INFO("from->max_width : %u", from->max_width);
+ LOG_INFO("from->max_height : %u", from->max_height);
+ LOG_INFO("from->framerate_num : %u", from->framerate_num);
+ LOG_INFO("from->framerate_den : %u", from->framerate_den);
+ LOG_INFO("from->codec_data_length : %u", from->codec_data_length);
+
+ auto stream = VideoStream::Create();
+ std::shared_ptr<char> codec_data = nullptr;
+
+ if (from->codec_data_length != 0) {
+ codec_data = std::shared_ptr<char>(new char[from->codec_data_length],
+ std::default_delete<char[]>());
+ memcpy(codec_data.get(), from->codec_data, from->codec_data_length);
+ }
+
+ stream->SetCodecData(codec_data, from->codec_data_length);
+ stream->SetMimeType(
+ static_cast<esplusplayer::VideoMimeType>(from->mime_type));
+ stream->SetWidth(from->width);
+ stream->SetHeight(from->height);
+ stream->SetMaxWidth(from->max_width);
+ stream->SetMaxHeight(from->max_height);
+ stream->SetFramerate(from->framerate_num, from->framerate_den);
+
+ return std::move(stream);
+}
+
+esplusplayer_handle esplusplayer_create() {
+ esplusplayer_handle player = static_cast<esplusplayer_handle>(EsPrivCreate());
+ LOG_INFO("capi handle > [%p], cpp handle > [%p]", player, cast_(player));
+ return player;
+}
+
+int esplusplayer_open(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ return convert_return_type_(cast_(handle)->Open());
+}
+
+int esplusplayer_close(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+#ifdef ES_DUMP
+ std::ofstream& vstream =
+ static_cast<EsPlusPlayerPriv*>(handle)->video_stream_;
+ if (vstream.is_open()) {
+ vstream.close();
+ LOG_DEBUG("Close video_stream_");
+ }
+ std::ofstream& astream =
+ static_cast<EsPlusPlayerPriv*>(handle)->audio_stream_;
+ if (astream.is_open()) {
+ astream.close();
+ LOG_DEBUG("Close audio_stream_");
+ }
+#endif
+
+ bool ret = cast_(handle)->Close();
+ listener->Reset();
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_destroy(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ esplusplayer_state state = esplusplayer_get_state(handle);
+ if (ESPLUSPLAYER_STATE_NONE != state) {
+ LOG_ERROR("state must be ESPLUSPLAYER_STATE_NONE, but %d now", state);
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_STATE;
+ }
+
+ auto priv = static_cast<EsPlusPlayerPriv*>(handle);
+ EsPrivDestroy(priv);
+
+ return ESPLUSPLAYER_ERROR_TYPE_NONE;
+}
+
+int esplusplayer_deactivate(esplusplayer_handle handle,
+ esplusplayer_stream_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(
+ cast_(handle)->Deactivate(static_cast<StreamType>(type)));
+}
+
+int esplusplayer_activate(esplusplayer_handle handle,
+ esplusplayer_stream_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(
+ cast_(handle)->Activate(static_cast<StreamType>(type)));
+}
+
+int esplusplayer_prepare_async(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->PrepareAsync());
+}
+
+int esplusplayer_start(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->Start());
+}
+
+int esplusplayer_stop(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->Stop());
+}
+
+int esplusplayer_pause(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->Pause());
+}
+
+int esplusplayer_resume(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->Resume());
+}
+
+int esplusplayer_set_playback_rate(esplusplayer_handle handle,
+ const double playback_rate,
+ const bool audio_mute) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "playback rate : %lf, audio mute : %d",
+ playback_rate, audio_mute);
+ return convert_return_type_(
+ cast_(handle)->SetPlaybackRate(playback_rate, audio_mute));
+}
+
+int esplusplayer_seek(esplusplayer_handle handle, uint64_t time) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO("%p time : %" PRId64 "", cast_(handle), time);
+ update_ready_to_seek_offset(handle, time);
+
+ return convert_return_type_(cast_(handle)->Seek(time));
+}
+
+int esplusplayer_set_app_info(esplusplayer_handle handle,
+ const esplusplayer_app_info* app_info) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ if (app_info == nullptr) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ LOG_INFO_P(cast_(handle), "app id : %s ", app_info->id);
+ LOG_INFO_P(cast_(handle), "app version : %s ", app_info->version);
+ LOG_INFO_P(cast_(handle), "app type : %s", app_info->type);
+
+ PlayerAppInfo info;
+ info.id = app_info->id;
+ info.version = app_info->version;
+ info.type = app_info->type;
+ cast_(handle)->SetAppInfo(info);
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_app_info_ex(esplusplayer_handle handle,
+ const esplusplayer_app_info_ex* app_info) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ if (app_info == nullptr) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ LOG_INFO_P(cast_(handle), "app id : %s ", app_info->id);
+ LOG_INFO_P(cast_(handle), "app version : %s ", app_info->version);
+ LOG_INFO_P(cast_(handle), "app type : %s", app_info->type);
+ LOG_INFO_P(cast_(handle), "app runtitle : %s", app_info->runtitle);
+
+ PlayerAppInfoEx info;
+ info.id = app_info->id;
+ info.version = app_info->version;
+ info.type = app_info->type;
+ info.runtitle = app_info->runtitle;
+ cast_(handle)->SetAppInfoEx(info);
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_display(esplusplayer_handle handle,
+ esplusplayer_display_type type, void* window) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "display type : %d, object : %p",
+ static_cast<int>(type), window);
+
+#if (!IS_AUDIO_PRODUCT) && (!IS_TOMATO)
+ if (type == ESPLUSPLAYER_DISPLAY_TYPE_MIXER) {
+ mixer_handle mixer_h = window;
+ esplusplayer::MixerTicket* ticket =
+ (esplusplayer::MixerTicket*)mixer_create_ticket(mixer_h, handle);
+ if (is_null_(ticket)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ return convert_return_type_(
+ cast_(handle)->SetDisplay(static_cast<DisplayType>(type), ticket));
+ }
+#endif
+ return convert_return_type_(
+ cast_(handle)->SetDisplay(static_cast<DisplayType>(type), window));
+}
+
+int esplusplayer_set_ecore_display(esplusplayer_handle handle,
+ esplusplayer_display_type type, void* window,
+ int x, int y, int width, int height) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(window))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "display type : %d, object : %p",
+ static_cast<int>(type), window);
+
+ return convert_return_type_(cast_(handle)->SetDisplay(
+ static_cast<DisplayType>(type), window, x, y, width, height));
+}
+
+int esplusplayer_set_display_ecore_subsurface(esplusplayer_handle handle,
+ esplusplayer_display_type type,
+ void* subsurface, int x, int y,
+ int width, int height) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(subsurface))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "display type : %d, object : %p",
+ static_cast<int>(type), subsurface);
+
+ return convert_return_type_(cast_(handle)->SetDisplaySubsurface(
+ static_cast<DisplayType>(type), subsurface, x, y, width, height));
+}
+
+int esplusplayer_set_surface_display(esplusplayer_handle handle,
+ esplusplayer_display_type type,
+ unsigned int surface_id, int x, int y,
+ int width, int height) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "display type : %d, object : %u",
+ static_cast<int>(type), surface_id);
+
+ return convert_return_type_(cast_(handle)->SetDisplay(
+ static_cast<DisplayType>(type), surface_id, x, y, width, height));
+}
+
+int esplusplayer_set_display_mode(esplusplayer_handle handle,
+ esplusplayer_display_mode mode) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "display mode : %d", static_cast<int>(mode));
+
+ return convert_return_type_(
+ cast_(handle)->SetDisplayMode(static_cast<DisplayMode>(mode)));
+}
+
+int esplusplayer_set_display_roi(esplusplayer_handle handle, int x, int y,
+ int width, int height) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "x : %d, y: %d, width : %d, height : %d", x, y,
+ width, height);
+
+ Geometry roi;
+ roi.x = x;
+ roi.y = y;
+ roi.w = width;
+ roi.h = height;
+
+ return convert_return_type_(cast_(handle)->SetDisplayRoi(roi));
+}
+
+int esplusplayer_set_stretch_mode(esplusplayer_handle handle,
+ int mode) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "stretch mode : %d", static_cast<int>(mode));
+
+ return convert_return_type_(
+ cast_(handle)->SetStretchMode(mode));
+}
+
+int esplusplayer_set_video_roi(esplusplayer_handle handle, double scale_x,
+ double scale_y, double scale_w, double scale_h) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle),
+ "scale-x : %lf, scale-y: %lf, scale-w : %lf, scale-h : %lf",
+ scale_x, scale_y, scale_w, scale_h);
+
+ CropArea rio_area;
+ rio_area.scale_x = scale_x;
+ rio_area.scale_y = scale_y;
+ rio_area.scale_w = scale_w;
+ rio_area.scale_h = scale_h;
+
+ return convert_return_type_(cast_(handle)->SetVideoRoi(rio_area));
+}
+
+int esplusplayer_resize_render_rect(esplusplayer_handle handle, int x, int y,
+ int width, int height) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "x : %d, y: %d, width : %d, height : %d", x, y,
+ width, height);
+
+ RenderRect rect;
+ rect.x = x;
+ rect.y = y;
+ rect.w = width;
+ rect.h = height;
+
+ return convert_return_type_(cast_(handle)->ResizeRenderRect(rect));
+}
+
+int esplusplayer_set_display_rotation(
+ esplusplayer_handle handle, esplusplayer_display_rotation_type rotation) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "display rotate angle : %d",
+ static_cast<int>(rotation));
+ return convert_return_type_(
+ cast_(handle)->SetDisplayRotate(static_cast<DisplayRotation>(rotation)));
+}
+
+int esplusplayer_get_display_rotation(
+ esplusplayer_handle handle, esplusplayer_display_rotation_type* rotation) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(rotation))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ return convert_return_type_(cast_(handle)->GetDisplayRotate(
+ reinterpret_cast<DisplayRotation*>(rotation)));
+}
+
+int esplusplayer_set_display_visible(esplusplayer_handle handle, bool visible) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "visible : %s", visible ? "true" : "false");
+ return convert_return_type_(cast_(handle)->SetDisplayVisible(visible));
+}
+
+int esplusplayer_set_tz_use(esplusplayer_handle handle, bool using_tz) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "using_tz : %s", using_tz ? "true" : "false");
+ return convert_return_type_(cast_(handle)->SetTrustZoneUse(using_tz));
+}
+
+int esplusplayer_set_submit_data_type(esplusplayer_handle handle,
+ esplusplayer_submit_data_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "type : %d", type);
+ return convert_return_type_(
+ cast_(handle)->SetSubmitDataType(static_cast<SubmitDataType>(type)));
+}
+
+int esplusplayer_set_audio_mute(esplusplayer_handle handle, bool mute) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "mute : %s", mute ? "true" : "false");
+ return convert_return_type_(cast_(handle)->SetAudioMute(mute));
+}
+
+esplusplayer_state esplusplayer_get_state(esplusplayer_handle handle) {
+ // LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return esplusplayer_state::ESPLUSPLAYER_STATE_NONE;
+ auto current_state =
+ static_cast<esplusplayer_state>(cast_(handle)->GetState());
+ // LOG_INFO_P(cast_(handle), "state : %d", static_cast<int>(current_state));
+
+ return current_state;
+}
+
+#ifdef ES_DUMP
+static void esdump(esplusplayer_handle handle, esplusplayer_es_packet* packet) {
+ std::ofstream& ostream =
+ (packet->type == ESPLUSPLAYER_STREAM_TYPE_AUDIO
+ ? static_cast<EsPlusPlayerPriv*>(handle)->audio_stream_
+ : static_cast<EsPlusPlayerPriv*>(handle)->video_stream_);
+
+ if (ostream.is_open() == false) return;
+
+ uint64_t tmp = packet->pts * 1000000;
+ ostream.write(reinterpret_cast<char*>(&tmp), sizeof(uint64_t));
+ tmp = packet->duration * 1000000;
+ ostream.write(reinterpret_cast<char*>(&tmp), sizeof(uint64_t));
+ std::uint64_t size = (std::uint64_t)packet->buffer_size;
+ ostream.write(reinterpret_cast<char*>(&size), sizeof(size));
+ ostream.write(reinterpret_cast<char*>(packet->buffer), packet->buffer_size);
+ LOG_DEBUG("DUMP type:%d pkt pts: %" PRId64 "duration: %" PRId64 "size: %d",
+ packet->type, packet->pts, packet->duration, packet->buffer_size);
+}
+#endif
+
+esplusplayer_submit_status esplusplayer_submit_packet(
+ esplusplayer_handle handle, esplusplayer_es_packet* packet) {
+ if (is_null_(handle)) return ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED;
+ auto packetptr = convert_espacket_(packet);
+ if (packetptr == nullptr) {
+ LOG_ERROR("packet converting failed");
+ return ESPLUSPLAYER_SUBMIT_STATUS_INVALID_PACKET;
+ }
+
+#ifdef ES_DUMP
+ esdump(handle, packet);
+#endif
+
+ auto status = cast_(handle)->SubmitPacket(packetptr);
+ if (status != esplusplayer::PacketSubmitStatus::kSuccess) {
+ LOG_ERROR("SubmitPacket status isn't SUCCESS [%d]",
+ static_cast<int>(status));
+ }
+ return static_cast<esplusplayer_submit_status>(status);
+}
+// LCOV_EXCL_START
+esplusplayer_submit_status esplusplayer_submit_trust_zone_packet(
+ esplusplayer_handle handle, esplusplayer_es_packet* packet,
+ uint32_t tz_handle) {
+ if (is_null_(handle)) return ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED;
+ auto packetptr = convert_espacket_(packet);
+ if (packetptr == nullptr) {
+ LOG_ERROR("packet converting failed");
+ return ESPLUSPLAYER_SUBMIT_STATUS_INVALID_PACKET;
+ }
+ auto status = cast_(handle)->SubmitTrustZonePacket(packetptr, tz_handle);
+ if (status != esplusplayer::PacketSubmitStatus::kSuccess) {
+ LOG_ERROR("SubmitPacket status isn't SUCCESS [%d]",
+ static_cast<int>(status));
+ }
+ return static_cast<esplusplayer_submit_status>(status);
+}
+
+esplusplayer_submit_status esplusplayer_submit_encrypted_packet(
+ esplusplayer_handle handle, esplusplayer_es_packet* packet,
+ esplusplayer_drm_info* drm_info) {
+ if (is_null_(handle)) return ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED;
+ auto packetptr = convert_espacket_(packet);
+ if (packetptr == nullptr) {
+ LOG_ERROR("packet converting failed");
+ return ESPLUSPLAYER_SUBMIT_STATUS_INVALID_PACKET;
+ }
+ auto status = esplusplayer::PacketSubmitStatus::kSuccess;
+ if (drm_info == nullptr) {
+ status = cast_(handle)->SubmitPacket(packetptr);
+ } else {
+ auto encrypted_info = convert_es_drm_info_(drm_info);
+ status = cast_(handle)->SubmitEncryptedPacket(packetptr, *encrypted_info);
+ }
+ if (status != esplusplayer::PacketSubmitStatus::kSuccess) {
+ LOG_ERROR("SubmitPacket status isn't SUCCESS [%d]",
+ static_cast<int>(status));
+ }
+ return static_cast<esplusplayer_submit_status>(status);
+}
+
+esplusplayer_submit_status esplusplayer_submit_eos_packet(
+ esplusplayer_handle handle, esplusplayer_stream_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED;
+
+ auto status = cast_(handle)->SubmitPacket(
+ std::move(EsPacket::CreateEos(static_cast<StreamType>(type))));
+ return static_cast<esplusplayer_submit_status>(status);
+}
+// LCOV_EXCL_STOP
+
+#ifdef ES_DUMP
+static void audio_es_dump(esplusplayer_handle handle,
+ esplusplayer_audio_stream_info* info) {
+ if (!exists("/tmp/asdump")) return;
+ if (!exists("/etc/debug") && !exists("/etc/perf")) return;
+
+ std::ofstream& ostream =
+ static_cast<EsPlusPlayerPriv*>(handle)->audio_stream_;
+ if (ostream.is_open()) ostream.close();
+ std::string dumppath = "/tmp/audiodump.ESP";
+ ostream.open(dumppath + ".es", std::ofstream::binary | std::ofstream::trunc);
+ if (ostream.is_open() == false) {
+ LOG_ERROR("Fail to open %s.es", dumppath.c_str());
+ return;
+ }
+ std::ofstream info_stream;
+ info_stream.open(dumppath + ".info", std::ofstream::trunc);
+ if (info_stream.is_open() == false) {
+ LOG_ERROR("Fail to open %s.info", dumppath.c_str());
+ return;
+ }
+ info_stream << static_cast<int>(info->mime_type) << std::endl;
+ info_stream << info->sample_rate << std::endl << info->channels << std::endl;
+ info_stream.close();
+ if (info->codec_data_length == 0) return;
+ std::ofstream codec_extradata_stream;
+ codec_extradata_stream.open(dumppath + ".codec_extradata",
+ std::ofstream::binary | std::ofstream::trunc);
+ if (codec_extradata_stream.is_open() == false) {
+ LOG_ERROR("Fail to open %s.codec_extradata", dumppath.c_str());
+ return;
+ }
+ codec_extradata_stream.write(
+ reinterpret_cast<char*>(&info->codec_data_length),
+ sizeof(info->codec_data_length));
+ codec_extradata_stream.write(info->codec_data, info->codec_data_length);
+ codec_extradata_stream.close();
+}
+
+static void video_es_dump(esplusplayer_handle handle,
+ esplusplayer_video_stream_info* info) {
+ // It requires to do "chsmack -a '_' /tmp/vsdump"
+ // after touch /tmp/vsdump on the shell
+ if (!exists("/tmp/vsdump")) return;
+ if (!exists("/etc/debug") && !exists("/etc/perf")) return;
+
+ std::ofstream& ostream =
+ static_cast<EsPlusPlayerPriv*>(handle)->video_stream_;
+ if (ostream.is_open()) ostream.close();
+ std::string dumppath = "/tmp/videodump.ESP";
+ ostream.open(dumppath + ".es", std::ofstream::binary | std::ofstream::trunc);
+ if (ostream.is_open() == false) {
+ LOG_ERROR("Fail to open %s.es", dumppath.c_str());
+ return;
+ }
+ std::ofstream info_stream;
+ info_stream.open(dumppath + ".info", std::ofstream::trunc);
+ if (info_stream.is_open() == false) {
+ LOG_ERROR("Fail to open %s.info", dumppath.c_str());
+ return;
+ }
+ info_stream << static_cast<int>(info->mime_type) << std::endl;
+ info_stream << info->width << std::endl << info->height << std::endl;
+ info_stream << info->max_width << std::endl << info->max_height << std::endl;
+ info_stream << info->framerate_num << std::endl
+ << info->framerate_den << std::endl;
+ info_stream.close();
+ if (info->codec_data_length == 0) return;
+ std::ofstream codec_extradata_stream;
+ codec_extradata_stream.open(dumppath + ".codec_extradata",
+ std::ofstream::binary | std::ofstream::trunc);
+ if (codec_extradata_stream.is_open() == false) {
+ LOG_ERROR("Fail to open %s.codec_extradata", dumppath.c_str());
+ return;
+ }
+ codec_extradata_stream.write(
+ reinterpret_cast<char*>(&info->codec_data_length),
+ sizeof(info->codec_data_length));
+ codec_extradata_stream.write(info->codec_data, info->codec_data_length);
+ codec_extradata_stream.close();
+}
+#endif
+
+int esplusplayer_set_audio_stream_info(esplusplayer_handle handle,
+ esplusplayer_audio_stream_info* info) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(info))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+#ifdef ES_DUMP
+ audio_es_dump(handle, info);
+#endif
+
+ auto stream = convert_stream_ptr_(info);
+ return convert_return_type_(cast_(handle)->SetStream(std::move(stream)));
+}
+
+int esplusplayer_set_video_stream_info(esplusplayer_handle handle,
+ esplusplayer_video_stream_info* info) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(info))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+#ifdef ES_DUMP
+ video_es_dump(handle, info);
+#endif
+
+ auto stream = convert_stream_ptr_(info);
+ return convert_return_type_(cast_(handle)->SetStream(std::move(stream)));
+}
+
+int esplusplayer_get_playing_time(esplusplayer_handle handle,
+ uint64_t* cur_time) {
+ if (is_null_(handle) || is_null_(cur_time))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto ret = cast_(handle)->GetPlayingTime(cur_time);
+ // LOG_INFO_P(cast_(handle), "playing time : %llu", *ms);
+ return convert_return_type_(ret);
+}
+
+namespace {
+std::shared_ptr<DecodedPacketManagerInterface> CreateDecodedPacketManager(
+ esplusplayer_handle handle,
+ esplusplayer_decoded_video_frame_buffer_type type) {
+ std::shared_ptr<DecodedPacketManagerInterface> mgr = nullptr;
+ if (type == ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_COPY)
+ mgr = std::make_shared<esplusplayer::DecodedCopiedPacketList>();
+ else if (type == ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_REFERENCE)
+ mgr = std::make_shared<esplusplayer::DecodedReferencePacketList>();
+ else if (type == ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE)
+ mgr = std::make_shared<esplusplayer::DecodedScaledPacketList>();
+ else if (type == ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_MANUAL_COPY)
+ mgr = std::make_shared<esplusplayer::ManualDecodedCopiedPacketList>(
+ [handle](esplusplayer_decoded_video_packet* pkt) {
+ esplusplayer::DecodedVideoPacket _pkt;
+ _pkt.pts = pkt->pts;
+ _pkt.duration = pkt->duration;
+ _pkt.surface_data = static_cast<tbm_surface_h>(pkt->surface_data);
+ _pkt.scaler_index = pkt->private_data;
+ return cast_(handle)->ReturnDecodedPacket(_pkt);
+ });
+ return mgr;
+}
+} // namespace
+
+int esplusplayer_set_video_frame_buffer_type(
+ esplusplayer_handle handle,
+ esplusplayer_decoded_video_frame_buffer_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(listener_cast_(handle)))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "decoded buffer type : %d", static_cast<int>(type));
+
+ auto priv = static_cast<EsPlusPlayerPriv*>(handle);
+ priv->decoded_pkt_mgr = ::CreateDecodedPacketManager(handle, type);
+ priv->listener->SetDecodedPacketManager(priv->decoded_pkt_mgr);
+
+ auto ret = cast_(handle)->SetVideoFrameBufferType(
+ static_cast<esplusplayer::DecodedVideoFrameBufferType>(type));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_video_frame_buffer_scale_resolution(
+ esplusplayer_handle handle, uint32_t target_width, uint32_t target_height) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || !target_width || !target_height)
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ LOG_INFO_P(cast_(handle), "target_width : %d, target_height: %d",
+ target_width, target_height);
+ return convert_return_type_(cast_(handle)->SetVideoFrameBufferScaleResolution(
+ target_width, target_height));
+}
+
+int esplusplayer_set_decoded_video_frame_rate(
+ esplusplayer_handle handle, esplusplayer_rational request_framerate) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO("request decoded video frame rate : %d/%d", request_framerate.num,
+ request_framerate.den);
+
+ Rational request_fps;
+ request_fps.num = request_framerate.num;
+ request_fps.den = request_framerate.den;
+ return convert_return_type_(
+ cast_(handle)->SetDecodedVideoFrameRate(request_fps));
+}
+
+int esplusplayer_get_adaptive_info(
+ esplusplayer_handle handle, void* padaptive_info,
+ esplusplayer_adaptive_info_type adaptive_type) {
+ // LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(padaptive_info))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto ret = cast_(handle)->GetAdaptiveInfo(
+ padaptive_info, static_cast<PlayerAdaptiveInfo>(adaptive_type));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_volume(esplusplayer_handle handle, const int volume) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto ret = cast_(handle)->SetVolume(volume);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_get_volume(esplusplayer_handle handle, int* volume) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(volume))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto ret = cast_(handle)->GetVolume(volume);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_flush(esplusplayer_handle handle,
+ esplusplayer_stream_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->Flush(static_cast<StreamType>(type));
+ return convert_return_type_(ret);
+}
+
+const char* esplusplayer_get_error_string(esplusplayer_error_type type) {
+ LOG_ENTER
+ return util::ConvertErrorTypeToString(type).c_str();
+}
+
+int esplusplayer_set_error_cb(esplusplayer_handle handle,
+ esplusplayer_error_cb error_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->error_cb_ = error_cb;
+ listener->error_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_buffer_status_cb(
+ esplusplayer_handle handle, esplusplayer_buffer_status_cb buffer_status_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->buffer_status_cb_ = buffer_status_cb;
+ listener->buffer_status_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_buffer_byte_status_cb(
+ esplusplayer_handle handle,
+ esplusplayer_buffer_byte_status_cb buffer_status_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->buffer_byte_status_cb_ = buffer_status_cb;
+ listener->buffer_byte_status_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_buffer_time_status_cb(
+ esplusplayer_handle handle,
+ esplusplayer_buffer_time_status_cb buffer_status_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->buffer_time_status_cb_ = buffer_status_cb;
+ listener->buffer_time_status_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_buffer_size(esplusplayer_handle handle,
+ esplusplayer_buffer_option option,
+ uint64_t size) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO("%p option: %d, size: %" PRId64 "", cast_(handle),
+ static_cast<int>(option), size);
+ cast_(handle)->SetBufferSize(static_cast<esplusplayer::BufferOption>(option),
+ size);
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_resource_conflicted_cb(
+ esplusplayer_handle handle,
+ esplusplayer_resource_conflicted_cb resource_conflicted_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->resource_conflicted_cb_ = resource_conflicted_cb;
+ listener->resource_conflicted_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_eos_cb(esplusplayer_handle handle,
+ esplusplayer_eos_cb eos_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->eos_cb_ = eos_cb;
+ listener->eos_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_ready_to_prepare_cb(
+ esplusplayer_handle handle,
+ esplusplayer_ready_to_prepare_cb ready_to_prepare_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->ready_to_prepare_cb_ = ready_to_prepare_cb;
+ listener->ready_to_prepare_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_prepare_async_done_cb(
+ esplusplayer_handle handle,
+ esplusplayer_prepare_async_done_cb prepare_async_done_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->prepare_async_done_cb_ = prepare_async_done_cb;
+ listener->prepare_async_done_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_seek_done_cb(esplusplayer_handle handle,
+ esplusplayer_seek_done_cb seek_done_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->seek_done_cb_ = seek_done_cb;
+ listener->seek_done_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_ready_to_seek_cb(
+ esplusplayer_handle handle, esplusplayer_ready_to_seek_cb ready_to_seek_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+ update_ready_to_seek_callback(handle, ready_to_seek_cb, userdata);
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_media_packet_video_decoded_cb(
+ esplusplayer_handle handle,
+ esplusplayer_media_packet_video_decoded_cb media_packet_video_decoded_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->media_packet_video_decoded_cb_ = media_packet_video_decoded_cb;
+ listener->media_packet_video_decoded_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_closed_caption_cb(
+ esplusplayer_handle handle,
+ esplusplayer_closed_caption_cb closed_caption_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->closed_caption_cb_ = closed_caption_cb;
+ listener->closed_caption_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_flush_done_cb(esplusplayer_handle handle,
+ esplusplayer_flush_done_cb flush_done_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->flush_done_cb_ = flush_done_cb;
+ listener->flush_done_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_event_cb(esplusplayer_handle handle,
+ esplusplayer_event_cb event_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+ listener->event_cb_ = event_cb;
+ listener->event_cb_userdata_ = userdata;
+
+ return convert_return_type_(true);
+}
+
+// LCOV_EXCL_START
+int esplusplayer_set_first_video_decoding_done_cb(
+ esplusplayer_handle handle,
+ esplusplayer_first_video_decoding_done_cb first_video_decoding_done_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+ listener->first_video_decoding_done_cb_ = first_video_decoding_done_cb;
+ listener->first_video_decoding_done_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_video_decoder_underrun_cb(
+ esplusplayer_handle handle,
+ esplusplayer_decoder_underrun_cb video_decoder_underrun_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+ listener->video_decoder_underrun_cb_ = video_decoder_underrun_cb;
+ listener->video_decoder_underrun_cb_userdata_ = userdata;
+
+ return convert_return_type_(true);
+}
+// LCOV_EXCL_STOP
+
+int esplusplayer_set_video_latency_status_cb(
+ esplusplayer_handle handle,
+ esplusplayer_video_latency_status_cb video_latency_status_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->video_latency_status_cb_ = video_latency_status_cb;
+ listener->video_latency_status_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_audio_latency_status_cb(
+ esplusplayer_handle handle,
+ esplusplayer_audio_latency_status_cb audio_latency_status_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->audio_latency_status_cb_ = audio_latency_status_cb;
+ listener->audio_latency_status_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_video_high_latency_cb(
+ esplusplayer_handle handle,
+ esplusplayer_video_high_latency_cb video_high_latency_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->video_high_latency_cb_ = video_high_latency_cb;
+ listener->video_high_latency_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_audio_high_latency_cb(
+ esplusplayer_handle handle,
+ esplusplayer_audio_high_latency_cb audio_high_latency_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->audio_high_latency_cb_ = audio_high_latency_cb;
+ listener->audio_high_latency_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_get_decoded_video_packet(
+ esplusplayer_handle handle, esplusplayer_decoded_video_packet* packet,
+ esplusplayer_get_decoded_video_frame_status_type* state) {
+ if (is_null_(handle) || is_null_(packet)) {
+ LOG_ERROR("handle[%p] or packet[%p] is nil.", handle, packet);
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+ esplusplayer::DecodedVideoPacket _packet;
+ bool ret = false;
+ auto _state = cast_(handle)->GetDecodedPacket(_packet);
+ if (_state != esplusplayer::GetDecodedVideoFrameStatus::kUnknown) {
+ ret = true;
+ }
+ if (_state == esplusplayer::GetDecodedVideoFrameStatus::kSuccess) {
+ packet->pts = _packet.pts;
+ packet->duration = _packet.duration;
+ packet->surface_data = static_cast<void*>(_packet.surface_data);
+ packet->private_data = _packet.scaler_index;
+ }
+ if (state) {
+ *state = convert_get_decoded_video_frame_status_(_state);
+ }
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_decoded_buffer_destroy(
+ esplusplayer_handle handle, esplusplayer_decoded_video_packet* packet) {
+ if (is_null_(handle)) {
+ LOG_ERROR("ESPlayer object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+ auto priv = static_cast<EsPlusPlayerPriv*>(handle);
+ auto& mgr = priv->decoded_pkt_mgr;
+ if (mgr == nullptr) {
+ LOG_ERROR("DecodedPacketManager object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+ mgr->Remove(packet);
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_low_latency_mode(esplusplayer_handle handle,
+ esplusplayer_low_latency_mode mode) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret =
+ cast_(handle)->SetLowLatencyMode(static_cast<PlayerLowLatencyMode>(mode));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_video_frame_peek_mode(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->SetVideoFramePeekMode();
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_render_video_frame(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->RenderVideoFrame();
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_unlimited_max_buffer_mode(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->SetUnlimitedMaxBufferMode();
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_fmm_mode(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->SetFmmMode();
+ return util::ConvertErrorCode(ret);
+}
+
+int esplusplayer_set_audio_codec_type(esplusplayer_handle handle,
+ esplusplayer_audio_codec_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret =
+ cast_(handle)->SetAudioCodecType(static_cast<PlayerAudioCodecType>(type));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_aifilter(esplusplayer_handle handle, void* aifilter) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(aifilter))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->SetAiFilter(aifilter);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_video_codec_type(esplusplayer_handle handle,
+ esplusplayer_video_codec_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret =
+ cast_(handle)->SetVideoCodecType(static_cast<PlayerVideoCodecType>(type));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_alternative_video_resource(esplusplayer_handle handle,
+ unsigned int rsc_type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->SetAlternativeVideoResource(rsc_type);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_alternative_audio_resource(
+ esplusplayer_handle handle, esplusplayer_audio_resource_type rsc_type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->SetAlternativeAudioResource(
+ static_cast<PlayerAudioResourceType>(rsc_type));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_render_time_offset(esplusplayer_handle handle,
+ esplusplayer_stream_type type,
+ int64_t offset) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret =
+ cast_(handle)->SetRenderTimeOffset(static_cast<StreamType>(type), offset);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_get_render_time_offset(esplusplayer_handle handle,
+ esplusplayer_stream_type type,
+ int64_t* offset) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(offset))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret =
+ cast_(handle)->GetRenderTimeOffset(static_cast<StreamType>(type), offset);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_switch_audio_stream_onthefly(
+ esplusplayer_handle handle, esplusplayer_audio_stream_info* info) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(info))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto stream = convert_stream_ptr_(info);
+ auto ret = cast_(handle)->SwitchAudioStreamOnTheFly(std::move(stream));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_init_audio_easing_info(
+ esplusplayer_handle handle, uint32_t init_volume, uint32_t elapsed_time,
+ const esplusplayer_target_audio_easing_info* easing_info) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ if (easing_info == nullptr) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ AudioEasingInfo info;
+ info.target_volume = easing_info->volume;
+ info.duration = easing_info->duration;
+ info.type = static_cast<AudioEasingType>(easing_info->type);
+ auto ret =
+ cast_(handle)->InitAudioEasingInfo(init_volume, elapsed_time, info);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_update_audio_easing_info(
+ esplusplayer_handle handle,
+ const esplusplayer_target_audio_easing_info* easing_info) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ if (easing_info == nullptr) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ AudioEasingInfo info;
+ info.target_volume = easing_info->volume;
+ info.duration = easing_info->duration;
+ info.type = static_cast<AudioEasingType>(easing_info->type);
+
+ auto ret = cast_(handle)->UpdateAudioEasingInfo(info);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_get_audio_easing_info(
+ esplusplayer_handle handle, uint32_t* current_volume,
+ uint32_t* elapsed_time,
+ esplusplayer_target_audio_easing_info* easing_info) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ if (is_null_(current_volume) || is_null_(elapsed_time) ||
+ is_null_(easing_info))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ AudioEasingInfo info;
+ auto ret =
+ cast_(handle)->GetAudioEasingInfo(current_volume, elapsed_time, &info);
+ easing_info->volume = info.target_volume;
+ easing_info->duration = info.duration;
+ easing_info->type = static_cast<esplusplayer_audio_easing_type>(info.type);
+
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_start_audio_easing(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->StartAudioEasing();
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_stop_audio_easing(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ auto ret = cast_(handle)->StopAudioEasing();
+ return convert_return_type_(ret);
+}
+
+// LCOV_EXCL_START
+int get_size_of_esplusplayer_app_info(void) {
+ return sizeof(esplusplayer_app_info);
+}
+
+int get_size_of_esplusplayer_es_packet(void) {
+ return sizeof(esplusplayer_es_packet);
+}
+
+int get_size_of_esplusplayer_es_tz_packet(void) {
+ return sizeof(esplusplayer_es_tz_packet);
+}
+
+int get_size_of_esplusplayer_audio_stream_info(void) {
+ return sizeof(esplusplayer_audio_stream_info);
+}
+
+int get_size_of_esplusplayer_video_stream_info(void) {
+ return sizeof(esplusplayer_video_stream_info);
+}
+
+int get_size_of_esplusplayer_drm_info(void) {
+ return sizeof(esplusplayer_drm_info);
+}
+// LCOV_EXCL_STOP
+
+int esplusplayer_set_catch_up_speed(esplusplayer_handle handle,
+ esplusplayer_catch_up_speed level) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ if (level < ESPLUSPLAYER_CATCH_UP_SPEED_NONE ||
+ level > ESPLUSPLAYER_CATCH_UP_SPEED_FAST) {
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+ auto ret = cast_(handle)->SetCatchUpSpeed(static_cast<CatchUpSpeed>(level));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_get_video_latency_status(esplusplayer_handle handle,
+ esplusplayer_latency_status* status) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(status))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ LatencyStatus current_status = LatencyStatus::kLow;
+ auto ret = cast_(handle)->GetVideoLatencyStatus(¤t_status);
+ *status = static_cast<esplusplayer_latency_status>(current_status);
+
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_get_audio_latency_status(esplusplayer_handle handle,
+ esplusplayer_latency_status* status) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(status))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ LatencyStatus current_status = LatencyStatus::kLow;
+ auto ret = cast_(handle)->GetAudioLatencyStatus(¤t_status);
+ *status = static_cast<esplusplayer_latency_status>(current_status);
+
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_video_mid_latency_threshold(esplusplayer_handle handle,
+ const unsigned int threshold) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto ret = cast_(handle)->SetVideoMidLatencyThreshold(threshold);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_audio_mid_latency_threshold(esplusplayer_handle handle,
+ const unsigned int threshold) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto ret = cast_(handle)->SetAudioMidLatencyThreshold(threshold);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_video_high_latency_threshold(
+ esplusplayer_handle handle, const unsigned int threshold,
+ esplusplayer_video_high_latency_cb video_high_latency_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ esplusplayer_set_video_high_latency_cb(handle, video_high_latency_cb,
+ userdata);
+
+ auto ret = cast_(handle)->SetVideoHighLatencyThreshold(threshold);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_audio_high_latency_threshold(
+ esplusplayer_handle handle, const unsigned int threshold,
+ esplusplayer_audio_high_latency_cb audio_high_latency_cb, void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ esplusplayer_set_audio_high_latency_cb(handle, audio_high_latency_cb,
+ userdata);
+
+ auto ret = cast_(handle)->SetAudioHighLatencyThreshold(threshold);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_get_virtual_rsc_id(esplusplayer_handle handle,
+ const esplusplayer_rsc_type type,
+ int* virtual_id) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(virtual_id))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto ret =
+ cast_(handle)->GetVirtualRscId(static_cast<RscType>(type), virtual_id);
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_advanced_picture_quality_type(
+ esplusplayer_handle handle,
+ esplusplayer_advanced_picture_quality_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto ret = cast_(handle)->SetAdvancedPictureQualityType(
+ static_cast<AdvPictureQualityType>(type));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_resource_allocate_policy(
+ esplusplayer_handle handle, esplusplayer_rsc_alloc_policy policy) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO("policy: %d", static_cast<int>(policy));
+
+ auto ret = cast_(handle)->SetResourceAllocatePolicy(
+ static_cast<RscAllocPolicy>(policy));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_audio_preloading(esplusplayer_handle handle) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ return convert_return_type_(cast_(handle)->SetAudioPreloading());
+}
+
+int esplusplayer_set_video_frame_dropped_cb(
+ esplusplayer_handle handle,
+ esplusplayer_video_frame_dropped_cb video_frame_dropped_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->video_frame_dropped_cb_ = video_frame_dropped_cb;
+ listener->video_frame_dropped_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_decoder_input_buffer_time_cb(
+ esplusplayer_handle handle,
+ esplusplayer_decoder_buffer_time_cb decoder_buffer_time_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->decoder_input_buffer_time_cb_ = decoder_buffer_time_cb;
+ listener->decoder_input_buffer_time_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_decoder_output_buffer_time_cb(
+ esplusplayer_handle handle,
+ esplusplayer_decoder_buffer_time_cb decoder_buffer_time_cb,
+ void* userdata) {
+ LOG_ENTER_P(cast_(handle))
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("ESPlayer or Listener object is nil.");
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+
+ listener->decoder_output_buffer_time_cb_ = decoder_buffer_time_cb;
+ listener->decoder_output_buffer_time_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
+
+int esplusplayer_set_video_scan_type(esplusplayer_handle handle,
+ esplusplayer_video_scan_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(listener_cast_(handle)))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO("scan type: %d", static_cast<int>(type));
+
+ auto ret =
+ cast_(handle)->SetVideoScanType(static_cast<PlayerVideoScanType>(type));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_get_decoding_time(esplusplayer_handle handle,
+ esplusplayer_stream_type type,
+ int32_t* time_in_milliseconds) {
+ if (is_null_(handle) || is_null_(time_in_milliseconds))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+
+ StreamType stream_type = static_cast<StreamType>(type);
+
+ auto ret = cast_(handle)->GetDecodingTime(stream_type, time_in_milliseconds);
+ LOG_INFO_I("decoding_time = %d", *time_in_milliseconds);
+ return convert_return_type_(ret);
+}
+int esplusplayer_set_timeunit_type(esplusplayer_handle handle,
+ esplusplayer_time_unit_type type) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(listener_cast_(handle)))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO("time unit type: %d", static_cast<int>(type));
+ auto ret =
+ cast_(handle)->SetTimeUnitType(static_cast<PlayerTimeUnitType>(type));
+ return convert_return_type_(ret);
+}
+
+int esplusplayer_set_video_stream_rotation_info(esplusplayer_handle handle,
+ const esplusplayer_video_stream_rotation_type rotation) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "video stream rotation type : %d",
+ static_cast<int>(rotation));
+ return convert_return_type_(
+ cast_(handle)->SetVideoStreamRotationInfo(static_cast<VideoRotation>(rotation)));
+}
+
+int esplusplayer_get_video_stream_rotation_info(esplusplayer_handle handle,
+ esplusplayer_video_stream_rotation_type* rotation) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle) || is_null_(rotation))
+ return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ return convert_return_type_(cast_(handle)->GetVideoStreamRotationInfo(
+ reinterpret_cast<VideoRotation*>(rotation)));
+}
+
+int esplusplayer_set_simple_mix_out_buffer_level(
+ esplusplayer_handle handle,
+ esplusplayer_simple_mix_out_buffer_level level) {
+ LOG_ENTER_P(cast_(handle))
+ if (is_null_(handle)) return ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO_P(cast_(handle), "buffer level : %d", static_cast<int>((level)));
+
+ return convert_return_type_(cast_(handle)->SetSimpleMixOutBufferLevel(
+ static_cast<PlayerSimpleMixOutBufferLevel>(level)));
+}
--- /dev/null
+PROJECT(mixer)
+
+SET(fw_name "${PROJECT_NAME}")
+SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
+SET(${fw_name}_LDFLAGS)
+
+SET(${fw_name}_CXXFLAGS "-Wall -Werror -std=c++11 -fPIC -Wl,-z,relro -fstack-protector")
+
+SET(dependents "dlog boost gstreamer-1.0 libtbm graphics-control")
+
+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(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
+ ${PARENT_DIR}/plusplayer-core/include_internal
+)
+
+SET(CC_SRCS
+ ${PROJECT_SOURCE_DIR}/src/mixer_capi.cpp
+ ${PROJECT_SOURCE_DIR}/src/mixer.cpp
+ ${PROJECT_SOURCE_DIR}/src/defaultmixer.cpp
+ ${PROJECT_SOURCE_DIR}/src/sys/tbminterface.cpp
+ ${PROJECT_SOURCE_DIR}/src/tizen/tizenaccessiblebufferobj.cpp
+ ${PROJECT_SOURCE_DIR}/src/tizen/tizenbufferkeyvideoframe.cpp
+ ${PROJECT_SOURCE_DIR}/src/tizen/tizendefaultphyaddraccessor.cpp
+ ${PROJECT_SOURCE_DIR}/src/tizen/tizenhwbufferobj.cpp
+ ${PROJECT_SOURCE_DIR}/src/tizen/tizenhwvideoframe.cpp
+ ${PROJECT_SOURCE_DIR}/src/tizen/tizenrenderableobj_factory.cpp
+ ${PROJECT_SOURCE_DIR}/src/tizen/tizensurfacevideoframe.cpp
+ ${PROJECT_SOURCE_DIR}/src/abs_videoframe.cpp
+ ${PROJECT_SOURCE_DIR}/src/mixedframe.cpp
+ ${PROJECT_SOURCE_DIR}/src/renderer.cpp
+ ${PROJECT_SOURCE_DIR}/src/videoplane.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})
+
+INSTALL(TARGETS ${fw_name} DESTINATION ${LIB_INSTALL_DIR})
+INSTALL(
+ DIRECTORY ${INC_DIR}/ DESTINATION include/
+)
--- /dev/null
+#ifndef __ESPLUSPLAYER_ABSTRACT_MIXER_VIDEO_FRAME_H__
+#define __ESPLUSPLAYER_ABSTRACT_MIXER_VIDEO_FRAME_H__
+
+#include <memory>
+
+#include "mixer/interfaces/videoplanecollection.h"
+#include "mixer/interfaces/videoplanemanipulable.h"
+
+namespace esplusplayer {
+
+class AbstractVideoFrame : public VideoPlaneCollection {
+ public:
+ using VideoPlaneManipulablePtr = std::unique_ptr<VideoPlaneManipulable>;
+
+ public:
+ explicit AbstractVideoFrame() = default;
+ virtual ~AbstractVideoFrame() = default;
+
+ public:
+ virtual const std::vector<VideoPlaneManipulableInfo> GetVideoPlaneManipInfo()
+ const override;
+
+ bool IsValid() const;
+ bool SetCropArea(const CropArea& croparea);
+ const std::uint32_t GetWidth() const;
+ const std::uint32_t GetHeight() const;
+
+ protected:
+ virtual bool IsValid_() const = 0;
+ virtual const std::uint32_t GetWidth_() const = 0;
+ virtual const std::uint32_t GetHeight_() const = 0;
+
+ protected:
+ void RegisterVideoPlaneManipulablePtr_(VideoPlaneManipulablePtr vpmanip);
+
+ private:
+ std::vector<VideoPlaneManipulablePtr> planes_;
+};
+} // namespace esplusplayer
+
+#endif
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_PLAYER_DEFAULTMIXER__H__
+#define __ESPLUSPLAYER_SRC_PLAYER_DEFAULTMIXER__H__
+
+#include <trackrenderer_capi/trackrenderer_capi.h>
+
+#include <cassert>
+#include <chrono>
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/interfaces/videoplanecollection.h"
+#include "mixer/mixer.h"
+#include "mixer/mixerticket.h"
+#include "mixer/renderer.h"
+#include "mixer/tizen/tizenbuffermgr.h"
+
+namespace esplusplayer {
+
+class DefaultMixer : public Mixer {
+ public:
+ using RendererPtr = std::unique_ptr<Renderer>;
+
+ public:
+ DefaultMixer();
+ ~DefaultMixer();
+
+ bool Start() override;
+ bool Stop() override;
+ int GetMaximumAllowedNumberOfPlayer() override;
+ bool SetDisplay(const DisplayType type, void* obj) override;
+ bool SetDisplay(const DisplayType type, const uint32_t surface_id,
+ const int x, const int y, const int w, const int h) override;
+ bool SetDisplayMode(const DisplayMode& mode) override;
+ bool SetDisplayRoi(const Geometry& geometry) override;
+ bool DisableAudioFocusSetting() override;
+ bool SetAlternativeVideoScaler() override;
+ bool SetAudioFocus(const void* player_instance) override;
+ bool SetRscAllocMode(const RscAllocMode& mode) override;
+ bool Commit() override;
+ bool SetResolution(const ResolutionInfo& info) override;
+ bool RegisterListener(MixerEventListener* listener) override;
+ MixerTicket* CreateTicket(const void* player_instance) override;
+
+ class Ticket : public MixerTicket {
+ public:
+ explicit Ticket(DefaultMixer* handler, const void* player_instance)
+ : handler_(handler), player_instance_(player_instance) {
+ assert(handler);
+ }
+ ~Ticket();
+ bool GetAvailableResourceType(const ResourceCategory& category,
+ ResourceType* type) override;
+ bool Alloc(const ResourceCategory& category,
+ const ResourceType& type) override;
+ bool Render(const DecodedRawInfo& info) override;
+ bool Render(const DecodedVideoKeyTypeInfo& info) override;
+ bool RegisterListener(MixerTicketEventListener* listener) override;
+ bool Prepare() override;
+ bool IsAudioFocusHandler() override;
+ bool IsRscAllocHandler() override;
+
+ // interface for defaultmixer
+ MixerTicketEventListener* GetListener();
+ bool IsAudioFocused();
+ void SetAudioFocus(bool active);
+ void GetDisplayInfo(DisplayInfo* info);
+ bool HasRenderedBefore();
+ void UpdateDisplayInfo(const DisplayInfo& info);
+ void DeallocResource();
+ void RecordRenderingTime();
+
+ private:
+ DefaultMixer* handler_ = nullptr;
+ const void* player_instance_ = nullptr;
+ MixerTicketEventListener* ticket_listener_ = nullptr;
+ bool is_audio_focus_ = false;
+ DisplayInfo each_display_info_;
+ bool has_rendered_ = false;
+ std::chrono::system_clock::time_point last_rendering_time_;
+ };
+
+ private:
+ struct Resource {
+ ResourceCategory category = ResourceCategory::kVideoDecoder;
+ ResourceType type = ResourceType::kHwMain;
+ const void* assignee = nullptr;
+ };
+
+ class MixerRendererEventListener : public RendererEventListener {
+ public:
+ explicit MixerRendererEventListener(TrackRendererHandle* trhandle_ptr);
+ virtual ~MixerRendererEventListener();
+ virtual bool OnRenderingRelease(const BufferKeyType& key) override;
+
+ private:
+ void* CreateGstBuffer_(const BufferKeyType& key) const;
+ void FillDecoderInputBuffer_(TrackRendererDecoderInputBuffer& buffer,
+ const BufferKeyType& key) const;
+
+ private:
+ TrackRendererHandle* trhandle_ptr_;
+ };
+
+ private:
+ bool Detach_(const void* player_instance);
+ bool GetAvailableResourceType_();
+ bool Render_(const void* player_instance,
+ const VideoPlaneCollection& vplanes);
+ bool RegisterListener_();
+ void InitResourceList_();
+ bool IsNeededToSkipRendering_(const DisplayInfo& display_info);
+
+ using UserData = void*;
+ static void ResourceConflictCb_(UserData userdata);
+ static void ErrorCb_(const TrackRendererErrorType error_code,
+ UserData userdata);
+
+ private:
+ std::map<const void*, Ticket*> player_map_;
+ std::mutex mixer_lock_;
+ std::mutex ticket_lock_;
+ bool is_started_ = false;
+ MixerEventListener* listener_ = nullptr;
+ TrackRendererHandle trackrenderer_handle_ = nullptr;
+ const void* audio_focused_player_ = nullptr;
+ ResolutionInfo whole_resolution_;
+ std::list<Resource> resource_list_;
+ bool enable_audio_focus_setting_ = true;
+ RscAllocMode resource_allocation_mode_ = RscAllocMode::kDefault;
+ bool use_sub_scaler_ = false;
+ RendererPtr renderer_;
+ MixerRendererEventListener renderer_listener_;
+ TizenBufferManager bufmgr_;
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_PLAYER_DEFAULTMIXER__H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_ACCESSIBLE_BUFFER_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_ACCESSIBLE_BUFFER_H__
+
+#include <memory>
+
+#include "mixer/interfaces/bufferobject.h"
+#include "mixer/interfaces/phyaddraccessor.h"
+
+namespace esplusplayer {
+struct AccessibleBuffer {
+ using PhyAddrAccessorPtr = std::unique_ptr<PhysicalAddressAccessor>;
+
+ virtual ~AccessibleBuffer() = default;
+ virtual PhyAddrAccessorPtr GetReadableAddress() const = 0;
+ virtual PhyAddrAccessorPtr GetWritableAddress() const = 0;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_ACCESSIBLE_BUFFER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_BUFFER_OBJECT_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_BUFFER_OBJECT_H__
+
+#include "mixer/types/buffertype.h"
+
+namespace esplusplayer {
+struct BufferObject {
+ virtual ~BufferObject() = default;
+
+ virtual BufferHandleType GetBufferHandle() const = 0;
+ virtual BufferKeyType Export() const = 0;
+ virtual std::uint32_t GetSize() const = 0;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_BUFFER_OBJECT_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_MEMORY_OPERATOR_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_MEMORY_OPERATOR_H__
+
+#include <cstdint>
+#include <memory>
+
+#include "mixer/interfaces/bufferobject.h"
+#include "mixer/types/buffertype.h"
+
+namespace esplusplayer {
+
+struct MemoryAllocator {
+ virtual ~MemoryAllocator() = default;
+ virtual BufferObject* Allocate(const std::uint32_t& size) const = 0;
+};
+} // namespace esplusplayer
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_MEMORY_OPERATOR_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_PHYSICAL_ADDRESS_ACCESSOR_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_PHYSICAL_ADDRESS_ACCESSOR_H__
+
+#include "mixer/types/buffertype.h"
+
+namespace esplusplayer {
+struct PhysicalAddressAccessor {
+ virtual ~PhysicalAddressAccessor() = default;
+ virtual BufferPhysicalAddrType GetAddress() = 0;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_PHYSICAL_ADDRESS_ACCESSOR_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_RENDERABLE_OBJECT_FACTORY_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_RENDERABLE_OBJECT_FACTORY_H__
+
+#include "mixer/interfaces/renderableobject.h"
+
+namespace esplusplayer {
+struct RenderableObjectFactory {
+ virtual ~RenderableObjectFactory() = default;
+ virtual RenderableObject* CreateRenderableObject(
+ const std::uint32_t width, const std::uint32_t height) const = 0;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_RENDERABLE_OBJECT_FACTORY_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_RENDERABLE_OBJECT_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_RENDERABLE_OBJECT_H__
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "mixer/interfaces/videoplanemanipulator.h"
+#include "mixer/types/buffertype.h"
+#include "mixer/types/planecomponent.h"
+#include "mixer/types/videoplanemanipinfo.h"
+#include "esplusplayer/types/display.h"
+
+namespace esplusplayer {
+struct RenderableObject {
+ using Ptr = std::unique_ptr<RenderableObject>;
+ virtual ~RenderableObject() = default;
+ virtual bool IsValid() const = 0;
+ virtual std::uint32_t GetWidth() const = 0;
+ virtual std::uint32_t GetHeight() const = 0;
+ virtual std::uint32_t GetSize() const = 0;
+ virtual bool Render(const VideoPlaneManipulator* const vpmanip,
+ const std::vector<VideoPlaneManipulableInfo>& planes,
+ const Geometry& geom) = 0;
+ virtual bool Fill(const VideoPlaneColorManipulator* const vpmanip,
+ const PlaneComponent& comp, const std::uint32_t& color,
+ const Geometry& geom) = 0;
+ virtual bool Export(BufferKeyType& key) const = 0;
+};
+using RenderableObjectPtr = RenderableObject::Ptr;
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_RENDERABLE_OBJECT_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLLECTION_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLLECTION_H__
+
+#include <vector>
+
+#include "mixer/types/videoplanemanipinfo.h"
+
+namespace esplusplayer {
+struct VideoPlaneCollection {
+ virtual ~VideoPlaneCollection() = default;
+ virtual const std::vector<VideoPlaneManipulableInfo> GetVideoPlaneManipInfo()
+ const = 0;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLLECTION_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLOR_FILLER_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLOR_FILLER_H__
+
+#include <memory>
+
+#include "mixer/interfaces/videoplanemanipulator.h"
+#include "mixer/types/planecomponent.h"
+
+namespace esplusplayer {
+struct VideoPlaneColorFiller {
+ virtual ~VideoPlaneColorFiller() = default;
+ virtual const VideoPlaneColorManipulator* const GetColorFillManipulator()
+ const = 0;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLOR_FILLER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COPIER_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COPIER_H__
+
+#include "mixer/interfaces/videoplanemanipulator.h"
+
+namespace esplusplayer {
+struct VideoPlaneCopier {
+ virtual ~VideoPlaneCopier() = default;
+ virtual const VideoPlaneManipulator* const GetCopyManipulator() const = 0;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COPIER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_MANIPULABLE_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_MANIPULABLE_H__
+
+#include "mixer/types/videoplanemanipinfo.h"
+#include "esplusplayer/types/display.h"
+
+namespace esplusplayer {
+
+struct VideoPlaneManipulable {
+ virtual ~VideoPlaneManipulable() = default;
+ virtual bool IsValid() const = 0;
+ virtual VideoPlaneManipulableInfo GetVideoPlaneManipulableInfo() const = 0;
+ virtual void SetCropArea(const CropArea& croparea) = 0;
+};
+
+} // namespace esplusplayer
+
+#endif //__ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_MANIPULABLE_H__
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_MANIPULATOR_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_MANIPULATOR_H__
+
+#include <cstdint>
+
+#include "mixer/types/planecomponent.h"
+#include "mixer/types/videoplanemanipinfo.h"
+
+namespace esplusplayer {
+
+template <typename T>
+struct VideoPlaneManipulatorWithType {
+ virtual bool Do(const T&, const VideoPlaneManipulableInfo&) const = 0;
+};
+
+using VideoPlaneManipulator =
+ VideoPlaneManipulatorWithType<VideoPlaneManipulableInfo>;
+
+using VideoPlaneColorManipulator = VideoPlaneManipulatorWithType<std::uint32_t>;
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_MANIPULATOR_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_SCALER_H__
+#define __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_SCALER_H__
+
+#include "mixer/interfaces/videoplanemanipulator.h"
+
+namespace esplusplayer {
+struct VideoPlaneScaler {
+ virtual ~VideoPlaneScaler() = default;
+ virtual const VideoPlaneManipulator* const GetScaleManipulator() const = 0;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_SCALER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_MIXED_FRAME_H__
+#define __ESPLUSPLAYER_MIXER_MIXED_FRAME_H__
+
+#include <cstdint>
+#include <memory>
+
+#include "mixer/interfaces/memoryallocator.h"
+#include "mixer/interfaces/renderableobject.h"
+#include "mixer/interfaces/videoplanecollection.h"
+#include "mixer/interfaces/videoplanemanipulator.h"
+#include "esplusplayer/types/display.h"
+
+namespace esplusplayer {
+class MixedFrame : public VideoPlaneCollection, public RenderableObject {
+ public:
+ using Ptr = std::unique_ptr<MixedFrame>;
+ using BufferObjectPtr = std::unique_ptr<BufferObject>;
+
+ public:
+ static Ptr Create(const MemoryAllocator* const memop,
+ const std::uint32_t width, const std::uint32_t height);
+
+ public:
+ explicit MixedFrame(const MemoryAllocator* const memop,
+ const std::uint32_t width, const std::uint32_t height);
+ virtual ~MixedFrame() = default;
+
+ public:
+ virtual const std::vector<VideoPlaneManipulableInfo> GetVideoPlaneManipInfo()
+ const override;
+
+ public:
+ virtual bool IsValid() const override;
+ virtual std::uint32_t GetWidth() const override;
+ virtual std::uint32_t GetHeight() const override;
+ virtual std::uint32_t GetSize() const override;
+ virtual bool Render(const VideoPlaneManipulator* const vpmanip,
+ const std::vector<VideoPlaneManipulableInfo>& planes,
+ const Geometry& geom) override;
+ virtual bool Fill(const VideoPlaneColorManipulator* const vpmanip,
+ const PlaneComponent& comp, const std::uint32_t& color,
+ const Geometry& geom) override;
+ virtual bool Export(BufferKeyType& key) const override;
+
+ private:
+ std::uint32_t CalculateBufferSize_(const std::uint32_t width,
+ const std::uint32_t height);
+ VideoPlaneManipulableInfo GetMixedFrameVideoPlaneManipulableInfo_(
+ const PlaneComponent component, const Geometry& geom);
+ VideoPlaneManipulableInfo GetYComponentVMInfo_(BufferHandleType handle) const;
+ VideoPlaneManipulableInfo GetUVComponentVMInfo_(
+ BufferHandleType handle) const;
+
+ private:
+ const std::uint32_t width_ = 0;
+ const std::uint32_t height_ = 0;
+ mutable std::uint32_t allocated_size_ = 0;
+ BufferObjectPtr buffer_ = nullptr;
+};
+using MixedFramePtr = MixedFrame::Ptr;
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_MIXED_FRAME_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_RENDERER_H__
+#define __ESPLUSPLAYER_MIXER_RENDERER_H__
+
+#include <chrono>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "mixer/interfaces/renderableobj_factory.h"
+#include "mixer/interfaces/videoplanecollection.h"
+#include "mixer/interfaces/videoplanecolorfiller.h"
+#include "mixer/interfaces/videoplanescaler.h"
+#include "mixer/mixer.h"
+#include "mixer/types/buffertype.h"
+#include "mixer/types/videoplanemoveinfo.h"
+
+namespace esplusplayer {
+
+struct RendererEventListener {
+ virtual bool OnRenderingRelease(const BufferKeyType&) = 0;
+};
+
+class Renderer {
+ private:
+ using JitterType = std::chrono::duration<std::int64_t, std::milli>;
+
+ public:
+ explicit Renderer(const RenderableObjectFactory& mf_factory,
+ const Mixer::ResolutionInfo& rinfo,
+ RendererEventListener* listener);
+ virtual ~Renderer();
+
+ public:
+ bool IsValid() const;
+ bool Start();
+ bool Stop();
+ bool ChangeResolution(const RenderableObjectFactory& mf_factory,
+ const std::uint32_t& width,
+ const std::uint32_t& height);
+ bool ChangeRenderingSpeed(const std::uint32_t framerate_num,
+ const std::uint32_t framerate_den);
+
+ bool Render(const VideoPlaneScaler* const scaler,
+ const VideoPlaneCollection* const planes, const Geometry& geom);
+ bool Mute(const VideoPlaneColorFiller* const filler, const Geometry& geom);
+ bool Move(const VideoPlaneColorFiller* const filler,
+ const std::vector<VideoPlaneMoveInfo>& moving_planes);
+
+ protected:
+ virtual bool ChangeResolutionInternal_(
+ const RenderableObjectFactory& mf_factory, const std::uint32_t& width,
+ const std::uint32_t& height);
+ virtual bool RenderInternal_(
+ const VideoPlaneManipulator* const vpmanip,
+ const std::vector<VideoPlaneManipulableInfo>& planes,
+ const Geometry& geom);
+ virtual bool MuteInternal_(const VideoPlaneColorManipulator* const filler,
+ const Geometry& geom);
+ virtual bool MoveInternal_(
+ const VideoPlaneColorManipulator* const filler,
+ const std::vector<VideoPlaneMoveInfo>& moving_planes);
+ virtual bool OnRenderingBefore_(const RenderableObject* const frame);
+ virtual bool IsValid_() const;
+
+ std::uint32_t GetNextRenderingTimeWithJitter_(const JitterType& jitter) const;
+ const Mixer::ResolutionInfo& GetResolutionInfo_() const;
+ RenderableObjectPtr& GetMixedFrame_();
+ void AcquireRenderingLock_();
+ void ReleaseRenderingLock_();
+
+ private:
+ bool RaiseOnRenderingReleaseEvent_(const BufferKeyType& key);
+ void RenderingWorker_();
+
+ protected:
+ static Geometry MakeGeometry_(const std::uint32_t& width,
+ const std::uint32_t& height);
+ static bool IsSameGeometry_(const Geometry& g1, const Geometry& g2);
+
+ private:
+ Mixer::ResolutionInfo resolution_info_;
+ RendererEventListener* listener_ = nullptr;
+ RenderableObjectPtr frame_ = nullptr;
+
+ bool rendering_flag_ = false;
+ std::mutex rendering_mtx_;
+ std::condition_variable rendering_cv_;
+ std::thread rendering_worker_;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_RENDERER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_RENDERER_WITH_DOUBLE_BUFFERING_H__
+#define __ESPLUSPLAYER_MIXER_RENDERER_WITH_DOUBLE_BUFFERING_H__
+
+#include <boost/scope_exit.hpp>
+#include <chrono>
+#include <cinttypes>
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/interfaces/videoplanecopier.h"
+#include "mixer/interfaces/videoplanescaler.h"
+#include "mixer/mixedframe.h"
+#include "mixer/renderer.h"
+
+namespace esplusplayer {
+
+class RendererWithDoubleBuffer : public Renderer {
+ public:
+ explicit RendererWithDoubleBuffer(const RenderableObjectFactory& mf_factory,
+ const VideoPlaneCopier* const vpcopier,
+ const VideoPlaneScaler* const vpscaler,
+ const Mixer::ResolutionInfo& rinfo,
+ RendererEventListener* listener)
+ : Renderer(mf_factory, rinfo, listener),
+ frame_(MixedFramePtr(dynamic_cast<MixedFrame*>(
+ mf_factory.CreateRenderableObject(rinfo.width, rinfo.height)))),
+ vpcopier_(vpcopier),
+ vpscaler_(vpscaler) {}
+ virtual ~RendererWithDoubleBuffer() = default;
+
+ protected:
+ virtual bool OnRenderingBefore_(
+ const RenderableObject* const frame) override {
+ // auto before = std::chrono::system_clock::now();
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (IsValid() == false) return false;
+ // LOG_INFO("[PERF] OnRenderingBefore_ Lock [%llu]ms",
+ // std::chrono::duration_cast<std::chrono::milliseconds>(
+ // std::chrono::system_clock::now() - before)
+ // .count());
+ const auto& rinfo = GetResolutionInfo_();
+ auto ret = GetMixedFrame_()->Render(
+ vpcopier_->GetCopyManipulator(), frame_->GetVideoPlaneManipInfo(),
+ MakeGeometry_(rinfo.width, rinfo.height));
+ // LOG_INFO("[PERF] Copy [%llu]ms",
+ // std::chrono::duration_cast<std::chrono::milliseconds>(
+ // std::chrono::system_clock::now() - before)
+ // .count());
+ return ret;
+ }
+
+ // LCOV_EXCL_START
+ virtual bool ChangeResolutionInternal_(
+ const RenderableObjectFactory& mf_factory, const std::uint32_t& width,
+ const std::uint32_t& height) override {
+ {
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ frame_.reset(nullptr);
+ frame_ = MixedFramePtr(dynamic_cast<MixedFrame*>(
+ mf_factory.CreateRenderableObject(width, height)));
+ }
+ return Renderer::ChangeResolutionInternal_(mf_factory, width, height);
+ }
+
+ virtual bool RenderInternal_(
+ const VideoPlaneManipulator* const scaler,
+ const std::vector<VideoPlaneManipulableInfo>& planes,
+ const Geometry& geom) override {
+ if (scaler == nullptr) return false;
+ auto before = std::chrono::system_clock::now();
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (IsValid() == false) return false;
+ auto ret = frame_->Render(scaler, planes, geom);
+ LOG_INFO("[PERF] Scale [%" PRIu64"]ms",
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now() - before)
+ .count());
+ return ret;
+ }
+
+ virtual bool MuteInternal_(const VideoPlaneColorManipulator* const filler,
+ const Geometry& geom) override {
+ if (filler == nullptr) return false;
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (IsValid() == false) return false;
+ bool ret = true;
+ LOG_INFO("MUTE REGION (%d, %d, %d x %d)", geom.x, geom.y, geom.w, geom.h);
+ ret &= frame_->Fill(filler, PlaneComponent::kYComponent, 0x00, geom);
+ ret &= frame_->Fill(filler, PlaneComponent::kUVComponent, 0x8080, geom);
+ return ret;
+ }
+
+ virtual bool MoveInternal_(
+ const VideoPlaneColorManipulator* const filler,
+ const std::vector<VideoPlaneMoveInfo>& moving_planes) override {
+ if (filler == nullptr) return false;
+
+ AcquireRenderingLock_();
+ BOOST_SCOPE_EXIT(this_) { this_->ReleaseRenderingLock_(); }
+ BOOST_SCOPE_EXIT_END
+
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (IsValid() == false) return false;
+
+ auto* mixedframe = dynamic_cast<MixedFrame*>(GetMixedFrame_().get());
+ if (mixedframe == nullptr) return false;
+
+ const auto planes = mixedframe->GetVideoPlaneManipInfo();
+ if (planes.size() != 2) return false;
+
+ const auto& rinfo = GetResolutionInfo_();
+
+ auto fullgeom = MakeGeometry_(rinfo.width, rinfo.height);
+ bool ret = true;
+ ret &= frame_->Fill(filler, PlaneComponent::kYComponent, 0x00, fullgeom);
+ ret &= frame_->Fill(filler, PlaneComponent::kUVComponent, 0x8080, fullgeom);
+ auto* scaler = vpscaler_->GetScaleManipulator();
+ for (const auto& move : moving_planes) {
+ VideoPlaneManipulableInfo y_manipinfo = planes[0];
+ VideoPlaneManipulableInfo uv_manipinfo = planes[1];
+ y_manipinfo.rect = move.cur;
+ uv_manipinfo.rect = move.cur;
+ uv_manipinfo.rect.x /= 2;
+ uv_manipinfo.rect.y /= 2;
+ uv_manipinfo.rect.w /= 2;
+ uv_manipinfo.rect.h /= 2;
+ uv_manipinfo.rect.y += rinfo.height;
+ LOG_INFO("MOVE (%d, %d, %d x %d) -> (%d, %d, %d x %d)", move.cur.x,
+ move.cur.y, move.cur.w, move.cur.h, move.target.x, move.target.y,
+ move.target.w, move.target.h);
+ ret &= frame_->Render(scaler, {y_manipinfo, uv_manipinfo}, move.target);
+ }
+ return true;
+ };
+ // LCOV_EXCL_STOP
+
+ virtual bool IsValid_() const override {
+ if (frame_ == nullptr) return false;
+ if (frame_->IsValid() == false) return false;
+ if (vpcopier_ == nullptr || vpscaler_ == nullptr) return false;
+ if (vpcopier_->GetCopyManipulator() == nullptr) return false;
+ if (vpscaler_->GetScaleManipulator() == nullptr) return false;
+ return true;
+ }
+
+ private:
+ MixedFramePtr frame_ = nullptr;
+ const VideoPlaneCopier* const vpcopier_ = nullptr;
+ const VideoPlaneScaler* const vpscaler_ = nullptr;
+
+ std::mutex rendering_mtx_;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_RENDERER_WITH_DOUBLE_BUFFERING_H__
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_SYS_TBM_INTERFACE_H__
+#define __ESPLUSPLAYER_MIXER_SYS_TBM_INTERFACE_H__
+
+#include <capi-graphics-control.h>
+
+#include "mixer/types/buffertype.h"
+
+namespace esplusplayer {
+namespace tizen {
+class TBMInterface {
+ public:
+ static BufferDefaultType BoRef(BufferDefaultType bo);
+ static void BoUnRef(BufferDefaultType bo);
+ static int BoSize(BufferDefaultType bo);
+ static BufferUnionHandleType BoGetHandle(BufferDefaultType bo, int device);
+ static BufferUnionHandleType BoMap(BufferDefaultType bo, int device,
+ int option);
+ static void BoUnmap(BufferDefaultType bo);
+ static BufferKeyType BoExport(BufferDefaultType bo);
+ static BufferDefaultType BoAlloc(tbm_bufmgr bufmgr, int size, int flag);
+ static BufferDefaultType BoImport(tbm_bufmgr bufmgr, BufferKeyType key);
+ static int GAScale(tbm_bufmgr bufmgr, GraphicsGAScaleInfo* info);
+ static int GACopy(tbm_bufmgr bufmgr, GraphicsGABltRopInfo* info);
+ static int GAFill(tbm_bufmgr bufmgr, GraphicsGAFillRectInfo* info);
+};
+} // namespace tizen
+} // namespace esplusplayer
+
+#endif //__ESPLUSPLAYER_MIXER_SYS_TBM_INTERFACE_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_ACCESSIBLE_BUFFER_OBJECT_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_ACCESSIBLE_BUFFER_OBJECT_H__
+
+#include "mixer/interfaces/accessiblebuffer.h"
+#include "mixer/tizen/tizenbufferobj.h"
+#include "mixer/tizen/tizenbufferphyaddraccessor.h"
+
+namespace esplusplayer {
+class TizenAccessibleBufferObject : public TizenBufferObject,
+ public AccessibleBuffer {
+ public:
+ explicit TizenAccessibleBufferObject(BufferDefaultType buffer);
+ virtual ~TizenAccessibleBufferObject() = default;
+ virtual PhyAddrAccessorPtr GetReadableAddress() const override;
+ virtual PhyAddrAccessorPtr GetWritableAddress() const override;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_ACCESSIBLE_BUFFER_OBJECT_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_KEY_VIDEO_SURFACE_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_KEY_VIDEO_SURFACE_H__
+
+#include <memory>
+
+#include "mixer/abs_videoframe.h"
+#include "mixer/interfaces/bufferobject.h"
+#include "mixer/tizen/tizenbuffermgr.h"
+#include "mixer/videoplane.h"
+// LCOV_EXCL_START
+namespace esplusplayer {
+
+class TizenBufferKeyVideoFrame : public AbstractVideoFrame {
+ public:
+ using BufferObjectPtr = std::shared_ptr<BufferObject>;
+
+ public:
+ explicit TizenBufferKeyVideoFrame(const TizenBufferManager* const bufmgr,
+ const BufferKeyType& key,
+ const std::uint32_t& width,
+ const std::uint32_t& height);
+ virtual ~TizenBufferKeyVideoFrame() = default;
+
+ protected:
+ virtual bool IsValid_() const override;
+ virtual const std::uint32_t GetWidth_() const override;
+ virtual const std::uint32_t GetHeight_() const override;
+
+ private:
+ const BufferKeyType key_;
+ const std::uint32_t width_;
+ const std::uint32_t height_;
+};
+} // namespace esplusplayer
+// LCOV_EXCL_STOP
+#endif // __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_KEY_VIDEO_SURFACE_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_MANAGER_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_MANAGER_H__
+
+#include <cstring>
+
+#include "mixer/interfaces/memoryallocator.h"
+#include "mixer/interfaces/videoplanecolorfiller.h"
+#include "mixer/interfaces/videoplanecopier.h"
+#include "mixer/interfaces/videoplanemanipulator.h"
+#include "mixer/interfaces/videoplanescaler.h"
+#include "mixer/sys/tbminterface.h"
+#include "mixer/tizen/tizenaccessiblebufferobj.h"
+#include "mixer/types/videoplanemanipinfo.h"
+
+namespace {
+struct GlobalInstance {
+ tbm_bufmgr bufmgr;
+ GlobalInstance() { bufmgr = tbm_bufmgr_init(-1); }
+ ~GlobalInstance() {
+ if (bufmgr) tbm_bufmgr_deinit(bufmgr);
+ }
+};
+static const GlobalInstance kGlobalInstance;
+} // namespace
+
+namespace esplusplayer {
+template <typename TBM>
+class BufferManagerWithType : public MemoryAllocator,
+ public VideoPlaneScaler,
+ public VideoPlaneCopier,
+ public VideoPlaneColorFiller {
+ public:
+ class Scaler : public VideoPlaneManipulator {
+ public:
+ explicit Scaler(const BufferManagerWithType* const bufmgr);
+ virtual ~Scaler() = default;
+ virtual bool Do(const VideoPlaneManipulableInfo& src,
+ const VideoPlaneManipulableInfo& dest) const override;
+
+ private:
+ const BufferManagerWithType* const bufmgr_;
+ };
+ class Copier : public VideoPlaneManipulator {
+ public:
+ explicit Copier(const BufferManagerWithType* const bufmgr);
+ virtual ~Copier() = default;
+ virtual bool Do(const VideoPlaneManipulableInfo& src,
+ const VideoPlaneManipulableInfo& dest) const override;
+
+ private:
+ const BufferManagerWithType* const bufmgr_;
+ };
+ class ColorFiller : public VideoPlaneColorManipulator {
+ public:
+ explicit ColorFiller(const BufferManagerWithType* const bufmgr);
+ virtual ~ColorFiller() = default;
+ virtual bool Do(const std::uint32_t& color,
+ const VideoPlaneManipulableInfo& dest) const override;
+
+ private:
+ const BufferManagerWithType* const bufmgr_;
+ };
+
+ public:
+ explicit BufferManagerWithType();
+ virtual ~BufferManagerWithType() = default;
+
+ public:
+ virtual BufferObject* Allocate(const std::uint32_t& size) const override;
+ virtual const VideoPlaneManipulator* const GetScaleManipulator()
+ const override;
+ virtual const VideoPlaneManipulator* const GetCopyManipulator()
+ const override;
+ virtual const VideoPlaneColorManipulator* const GetColorFillManipulator()
+ const override;
+ bool IsValid() const;
+ BufferObject* Import(BufferHandleType handle) const;
+
+ protected:
+ bool Copy_(const VideoPlaneManipulableInfo& src,
+ const VideoPlaneManipulableInfo& dst) const;
+ bool Scale_(const VideoPlaneManipulableInfo& src,
+ const VideoPlaneManipulableInfo& dst) const;
+ bool Fill_(const std::uint32_t color,
+ const VideoPlaneManipulableInfo& dst) const;
+
+ private:
+ tbm_bufmgr bufmgr_;
+ Scaler scaler_;
+ Copier copier_;
+ ColorFiller color_filler_;
+};
+
+using TizenBufferManager = BufferManagerWithType<tizen::TBMInterface>;
+
+/******************************************************************************
+ * Body
+ */
+template <typename TBM>
+BufferManagerWithType<TBM>::BufferManagerWithType()
+ : scaler_(this), copier_(this), color_filler_(this) {
+ bufmgr_ = ::kGlobalInstance.bufmgr;
+}
+
+template <typename TBM>
+BufferObject* BufferManagerWithType<TBM>::Allocate(
+ const std::uint32_t& size) const {
+ if (size == 0) return nullptr;
+ if (IsValid() == false) return nullptr;
+ auto buffer = TBM::BoAlloc(bufmgr_, size, TBM_BO_SCANOUT | (1 << 17));
+ if (buffer == nullptr) return nullptr;
+ auto bufferobj = new TizenAccessibleBufferObject(buffer);
+ TBM::BoUnRef(buffer);
+ return bufferobj;
+}
+
+template <typename TBM>
+const VideoPlaneManipulator* const
+BufferManagerWithType<TBM>::GetScaleManipulator() const {
+ return &scaler_;
+}
+
+template <typename TBM>
+const VideoPlaneManipulator* const
+BufferManagerWithType<TBM>::GetCopyManipulator() const {
+ return &copier_;
+}
+
+template <typename TBM>
+const VideoPlaneColorManipulator* const
+BufferManagerWithType<TBM>::GetColorFillManipulator() const {
+ return &color_filler_;
+}
+
+template <typename TBM>
+bool BufferManagerWithType<TBM>::IsValid() const {
+ if (bufmgr_ == nullptr) return false;
+ return true;
+}
+
+template <typename TBM>
+BufferObject* BufferManagerWithType<TBM>::Import(
+ BufferHandleType handle) const {
+ if (IsValid() == false) return nullptr;
+ auto buffer = TBM::BoImport(bufmgr_, handle);
+ if (buffer == nullptr) return nullptr;
+ auto bufferobj = new TizenBufferObject(buffer);
+ TBM::BoUnRef(buffer);
+ return bufferobj;
+}
+
+template <typename TBM>
+bool BufferManagerWithType<TBM>::Copy_(
+ const VideoPlaneManipulableInfo& src,
+ const VideoPlaneManipulableInfo& dst) const {
+ if (src.component != dst.component) return false;
+ if (IsValid() == false) return false;
+ GraphicsGABltRopInfo info;
+ std::memset(&info, 0, sizeof(GraphicsGABltRopInfo));
+ info.ga_mode = GRAPHICS_GA_BITBLT_MODE_NORMAL;
+ info.rop_mode = GRAPHICS_GA_ROP_COPY;
+ info.ga_op_type = GRAPHICS_GA_COPY;
+ info.pre_alphamode = 0;
+ info.ca_value = 0;
+ info.color_format =
+ (src.component == PlaneComponent::kYComponent ? GRAPHICS_GA_FORMAT_8BPP
+ : GRAPHICS_GA_FORMAT_16BPP);
+
+ info.src_handle = src.handle;
+ info.src_hbytesize = src.linesize;
+ info.src_rect.x = src.rect.x;
+ info.src_rect.y = src.rect.y;
+ info.src_rect.w = src.rect.w;
+ info.src_rect.h = src.rect.h;
+
+ info.dst_handle = dst.handle;
+ info.dst_hbytesize = dst.linesize;
+ info.dst_rect.x = dst.rect.x;
+ info.dst_rect.y = dst.rect.y;
+ info.dst_rect.w = dst.rect.w;
+ info.dst_rect.h = dst.rect.h;
+
+ return TBM::GACopy(bufmgr_, &info) > 0;
+}
+
+// LCOV_EXCL_START
+template <typename TBM>
+bool BufferManagerWithType<TBM>::Scale_(
+ const VideoPlaneManipulableInfo& src,
+ const VideoPlaneManipulableInfo& dst) const {
+ if (src.component != dst.component) return false;
+ if (IsValid() == false) return false;
+ GraphicsGAScaleInfo info;
+ std::memset(&info, 0, sizeof(GraphicsGAScaleInfo));
+ info.ga_mode = GRAPHICS_GA_SCALE_MODE;
+ info.rop_mode = GRAPHICS_GA_ROP_COPY;
+ info.ga_op_type = GRAPHICS_GA_SCALE;
+ info.pre_alphamode = 0;
+ info.ca_value = 0;
+ info.rop_on_off = 0;
+ info.color_format =
+ (src.component == PlaneComponent::kYComponent ? GRAPHICS_GA_FORMAT_8BPP
+ : GRAPHICS_GA_FORMAT_16BPP);
+
+ info.src_handle = src.handle;
+ info.src_hbytesize = src.linesize;
+ info.src_rect.x = src.rect.x;
+ info.src_rect.y = src.rect.y;
+ info.src_rect.w = src.rect.w;
+ info.src_rect.h = src.rect.h;
+
+ info.dst_handle = dst.handle;
+ info.dst_hbytesize = dst.linesize;
+ info.dst_rect.x = dst.rect.x;
+ info.dst_rect.y = dst.rect.y;
+ info.dst_rect.w = dst.rect.w;
+ info.dst_rect.h = dst.rect.h;
+
+ return TBM::GAScale(bufmgr_, &info) > 0;
+}
+
+template <typename TBM>
+bool BufferManagerWithType<TBM>::Fill_(
+ const std::uint32_t color, const VideoPlaneManipulableInfo& dst) const {
+ if (IsValid() == false) return false;
+ GraphicsGAFillRectInfo info;
+ std::memset(&info, 0, sizeof(GraphicsGAFillRectInfo));
+ info.color_format =
+ (dst.component == PlaneComponent::kYComponent ? GRAPHICS_GA_FORMAT_8BPP
+ : GRAPHICS_GA_FORMAT_16BPP);
+ info.ga_op_type = GRAPHICS_GA_SOLID_FILL;
+ info.color = color;
+
+ info.handle = dst.handle;
+ info.hbytesize = dst.linesize;
+ info.rect.x = dst.rect.x;
+ info.rect.y = dst.rect.y;
+ info.rect.w = dst.rect.w;
+ info.rect.h = dst.rect.h;
+
+ return TBM::GAFill(bufmgr_, &info) > 0;
+}
+// LCOV_EXCL_STOP
+
+/******************************************************************************
+ * Body for scaler
+ */
+template <typename TBM>
+BufferManagerWithType<TBM>::Scaler::Scaler(
+ const BufferManagerWithType* const bufmgr)
+ : bufmgr_(bufmgr) {}
+
+template <typename TBM>
+bool BufferManagerWithType<TBM>::Scaler::Do(
+ const VideoPlaneManipulableInfo& src,
+ const VideoPlaneManipulableInfo& dest) const {
+ return bufmgr_->Scale_(src, dest);
+}
+
+/******************************************************************************
+ * Body for copier
+ */
+template <typename TBM>
+BufferManagerWithType<TBM>::Copier::Copier(
+ const BufferManagerWithType* const bufmgr)
+ : bufmgr_(bufmgr) {}
+
+template <typename TBM>
+bool BufferManagerWithType<TBM>::Copier::Do(
+ const VideoPlaneManipulableInfo& src,
+ const VideoPlaneManipulableInfo& dest) const {
+ return bufmgr_->Copy_(src, dest);
+}
+
+/******************************************************************************
+ * Body for color filler
+ */
+template <typename TBM>
+BufferManagerWithType<TBM>::ColorFiller::ColorFiller(
+ const BufferManagerWithType* const bufmgr)
+ : bufmgr_(bufmgr) {}
+
+template <typename TBM>
+bool BufferManagerWithType<TBM>::ColorFiller::Do(
+ const std::uint32_t& color, const VideoPlaneManipulableInfo& dest) const {
+ return bufmgr_->Fill_(color, dest);
+}
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_MANAGER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_BUFFER_OBJECT_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_BUFFER_OBJECT_H__
+
+#include "mixer/interfaces/bufferobject.h"
+#include "mixer/sys/tbminterface.h"
+
+namespace esplusplayer {
+template <typename TBM>
+class BufferObjectWithType : public BufferObject {
+ public:
+ explicit BufferObjectWithType(BufferDefaultType buffer);
+ virtual ~BufferObjectWithType();
+ bool IsValid() const;
+ virtual BufferHandleType GetBufferHandle() const override;
+ virtual BufferKeyType Export() const override;
+ virtual std::uint32_t GetSize() const override;
+
+ protected:
+ const BufferDefaultType& GetBuffer_() const;
+
+ private:
+ BufferDefaultType buffer_ = nullptr;
+};
+
+using TizenBufferObject = BufferObjectWithType<esplusplayer::tizen::TBMInterface>;
+
+/******************************************************************************
+ * Body
+ */
+template <typename TBM>
+BufferObjectWithType<TBM>::BufferObjectWithType(BufferDefaultType buffer) {
+ if (buffer == nullptr) return;
+ buffer_ = TBM::BoRef(buffer);
+}
+
+template <typename TBM>
+BufferObjectWithType<TBM>::~BufferObjectWithType() {
+ if (buffer_) TBM::BoUnRef(buffer_);
+}
+
+template <typename TBM>
+bool BufferObjectWithType<TBM>::IsValid() const {
+ if (buffer_ == nullptr) return false;
+ return true;
+}
+
+template <typename TBM>
+BufferHandleType BufferObjectWithType<TBM>::GetBufferHandle() const {
+ if (IsValid() == false) return kInvalidBufferHandle;
+ auto handle = TBM::BoGetHandle(buffer_, TBM_DEVICE_2D);
+ return handle.u32;
+}
+
+template <typename TBM>
+BufferKeyType BufferObjectWithType<TBM>::Export() const {
+ if (IsValid() == false) return kInvalidBufferKey;
+ return TBM::BoExport(buffer_);
+}
+
+template <typename TBM>
+std::uint32_t BufferObjectWithType<TBM>::GetSize() const {
+ if (IsValid() == false) return kInvalidBufferSize;
+ return TBM::BoSize(buffer_);
+}
+
+template <typename TBM>
+const BufferDefaultType& BufferObjectWithType<TBM>::GetBuffer_() const {
+ return buffer_;
+}
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_BUFFER_OBJECT_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_PHYSICAL_ADDRESS_ACCESOR_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_PHYSICAL_ADDRESS_ACCESOR_H__
+
+#include <tbm_bo.h>
+
+#include "mixer/interfaces/phyaddraccessor.h"
+
+namespace esplusplayer {
+class AbstractTizenBufferPhyAddrAccessor : public PhysicalAddressAccessor {
+ public:
+ explicit AbstractTizenBufferPhyAddrAccessor(BufferDefaultType buffer) {
+ if (buffer == nullptr) return;
+ buffer_ = tbm_bo_ref(buffer);
+ }
+
+ virtual ~AbstractTizenBufferPhyAddrAccessor() {
+ if (buffer_ == nullptr) return;
+ if (handle_ != nullptr) tbm_bo_unmap(buffer_);
+ tbm_bo_unref(buffer_);
+ }
+
+ public:
+ virtual BufferPhysicalAddrType GetAddress() override {
+ if (handle_ != nullptr) return handle_;
+ handle_ = GetAddress_(buffer_);
+ return handle_;
+ }
+
+ protected:
+ virtual BufferPhysicalAddrType GetAddress_(
+ const BufferDefaultType& buffer) = 0;
+
+ private:
+ BufferDefaultType buffer_;
+ BufferPhysicalAddrType handle_ = nullptr;
+};
+
+class TizenReadableBufferPhyAddrAccessor
+ : public AbstractTizenBufferPhyAddrAccessor {
+ public:
+ explicit TizenReadableBufferPhyAddrAccessor(const BufferDefaultType buffer)
+ : AbstractTizenBufferPhyAddrAccessor(buffer) {}
+ virtual ~TizenReadableBufferPhyAddrAccessor() = default;
+
+ protected:
+ virtual BufferPhysicalAddrType GetAddress_(
+ const BufferDefaultType& buffer) override {
+ auto handle = tbm_bo_map(buffer, TBM_DEVICE_CPU, TBM_OPTION_READ);
+ return handle.ptr;
+ }
+};
+
+class TizenWritableBufferPhyAddrAccessor
+ : public AbstractTizenBufferPhyAddrAccessor {
+ public:
+ explicit TizenWritableBufferPhyAddrAccessor(BufferDefaultType buffer)
+ : AbstractTizenBufferPhyAddrAccessor(buffer) {}
+ virtual ~TizenWritableBufferPhyAddrAccessor() = default;
+
+ protected:
+ virtual BufferPhysicalAddrType GetAddress_(
+ const BufferDefaultType& buffer) override {
+ auto handle = tbm_bo_map(buffer, TBM_DEVICE_CPU, TBM_OPTION_WRITE);
+ return handle.ptr;
+ }
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_PHYSICAL_ADDRESS_ACCESOR_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_DEFAULT_PHYSICAL_ADDRESS_ACCESOR_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_DEFAULT_PHYSICAL_ADDRESS_ACCESOR_H__
+
+#include "mixer/interfaces/phyaddraccessor.h"
+// LCOV_EXCL_START
+namespace esplusplayer {
+class TizenDefaultPhyAddrAccessor : public PhysicalAddressAccessor {
+ public:
+ explicit TizenDefaultPhyAddrAccessor(std::uint32_t viraddr);
+ virtual ~TizenDefaultPhyAddrAccessor() = default;
+
+ public:
+ virtual BufferPhysicalAddrType GetAddress() override;
+
+ private:
+ std::uint32_t viraddr_;
+};
+} // namespace esplusplayer
+// LCOV_EXCL_STOP
+#endif // __ESPLUSPLAYER_MIXER_TIZEN_DEFAULT_PHYSICAL_ADDRESS_ACCESOR_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_HW_BUFFER_OBJECT_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_HW_BUFFER_OBJECT_H__
+
+#include "mixer/decodedvideoinfo.h"
+#include "mixer/interfaces/bufferobject.h"
+
+namespace esplusplayer {
+class TizenHWBufferObject : public BufferObject {
+ public:
+ explicit TizenHWBufferObject(const std::uint32_t& width,
+ const std::uint32_t& height,
+ const DecodedRawPlaneInfo& info);
+ virtual ~TizenHWBufferObject() = default;
+
+ bool IsValid() const;
+ virtual BufferHandleType GetBufferHandle() const override;
+ virtual BufferKeyType Export() const override;
+ virtual std::uint32_t GetSize() const override;
+
+ private:
+ std::uint32_t width_;
+ std::uint32_t height_;
+ DecodedRawPlaneInfo info_;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_HW_BUFFER_OBJECT_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_HW_VIDEO_FRAME_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_HW_VIDEO_FRAME_H__
+
+#include <memory>
+
+#include "mixer/abs_videoframe.h"
+#include "mixer/decodedvideoinfo.h"
+#include "mixer/interfaces/bufferobject.h"
+// LCOV_EXCL_START
+namespace esplusplayer {
+class TizenHWVideoFrame : public AbstractVideoFrame {
+ public:
+ using BufferObjectPtr = std::unique_ptr<BufferObject>;
+
+ public:
+ explicit TizenHWVideoFrame(const DecodedRawInfo& info);
+ virtual ~TizenHWVideoFrame() = default;
+
+ protected:
+ virtual bool IsValid_() const override;
+ virtual const std::uint32_t GetWidth_() const override;
+ virtual const std::uint32_t GetHeight_() const override;
+
+ private:
+ void PrintDecodedRawInfo() const;
+
+ private:
+ DecodedRawInfo info_;
+};
+} // namespace esplusplayer
+// LCOV_EXCL_STOP
+#endif //__ESPLUSPLAYER_MIXER_TIZEN_HW_VIDEO_FRAME_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_RENDERABLE_OBJECT_FACTORY_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_RENDERABLE_OBJECT_FACTORY_H__
+
+#include <memory>
+
+#include "mixer/interfaces/memoryallocator.h"
+#include "mixer/interfaces/renderableobj_factory.h"
+// LCOV_EXCL_START
+namespace esplusplayer {
+class TizenRenderableObjectFactory : public RenderableObjectFactory {
+ public:
+ explicit TizenRenderableObjectFactory(
+ const MemoryAllocator* const memallocator);
+ virtual ~TizenRenderableObjectFactory() = default;
+
+ public:
+ virtual RenderableObject* CreateRenderableObject(
+ const std::uint32_t width, const std::uint32_t height) const override;
+
+ private:
+ const MemoryAllocator* const memallocator_;
+};
+} // namespace esplusplayer
+// LCOV_EXCL_STOP
+#endif // __ESPLUSPLAYER_MIXER_TIZEN_RENDERABLE_OBJECT_FACTORY_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TIZEN_SURFACE_VIDEO_FRAME_H__
+#define __ESPLUSPLAYER_MIXER_TIZEN_SURFACE_VIDEO_FRAME_H__
+
+#include <memory>
+
+#include "mixer/abs_videoframe.h"
+#include "mixer/interfaces/bufferobject.h"
+#include "esplusplayer/decodedvideopacketex.h"
+// LCOV_EXCL_START
+namespace esplusplayer {
+class TizenSurfaceVideoFrame : public AbstractVideoFrame {
+ private:
+ enum ComponentIndex { kYIndex = 0, kUVIndex = 1 };
+
+ public:
+ using BufferObjectPtr = std::unique_ptr<BufferObject>;
+
+ public:
+ explicit TizenSurfaceVideoFrame(DecodedVideoPacketExPtr dvp);
+ virtual ~TizenSurfaceVideoFrame() = default;
+
+ protected:
+ virtual bool IsValid_() const override;
+ virtual const std::uint32_t GetWidth_() const override;
+ virtual const std::uint32_t GetHeight_() const override;
+
+ private:
+ DecodedVideoPacketExPtr dvp_;
+ std::uint32_t width_;
+ std::uint32_t height_;
+};
+} // namespace esplusplayer
+// LCOV_EXCL_STOP
+#endif //__ESPLUSPLAYER_MIXER_TIZEN_SURFACE_VIDEO_FRAME_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TYPES_BUFFER_TYPE_H__
+#define __ESPLUSPLAYER_MIXER_TYPES_BUFFER_TYPE_H__
+
+#include <tbm_type_common.h>
+
+#include <cstdint>
+
+namespace esplusplayer {
+
+using BufferDefaultType = tbm_bo;
+using BufferUnionHandleType = tbm_bo_handle;
+using BufferHandleType = decltype(BufferUnionHandleType::u32);
+using BufferPhysicalAddrType = decltype(BufferUnionHandleType::ptr);
+using BufferKeyType = tbm_key;
+
+const static BufferHandleType kInvalidBufferHandle = 0;
+const static BufferPhysicalAddrType kInvalidBufferPhysicalAddr = nullptr;
+const static BufferKeyType kInvalidBufferKey = 0;
+const static BufferKeyType kInvalidBufferSize = 0;
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_TYPES_BUFFER_TYPE_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TYPES_PLANE_COMPONENT_H__
+#define __ESPLUSPLAYER_MIXER_TYPES_PLANE_COMPONENT_H__
+
+namespace esplusplayer {
+
+enum class PlaneComponent {
+ kYComponent,
+ kUVComponent,
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_TYPES_PLANE_COMPONENT_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TYPES_VIDEO_PLANE_MAINIPULABLE_INFO_H__
+#define __ESPLUSPLAYER_MIXER_TYPES_VIDEO_PLANE_MAINIPULABLE_INFO_H__
+
+#include <cstdint>
+
+#include "mixer/types/buffertype.h"
+#include "mixer/types/planecomponent.h"
+#include "esplusplayer/types/display.h"
+
+namespace esplusplayer {
+
+struct VideoPlaneManipulableInfo {
+ BufferHandleType handle;
+ PlaneComponent component;
+ std::uint32_t linesize;
+ Geometry rect;
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_TYPES_VIDEO_PLANE_MAINIPULABLE_INFO_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_TYPES_VIDEO_PLANE_MOVE_INFO_H__
+#define __ESPLUSPLAYER_MIXER_TYPES_VIDEO_PLANE_MOVE_INFO_H__
+
+#include "esplusplayer/types/display.h"
+
+namespace esplusplayer {
+
+struct VideoPlaneMoveInfo {
+ explicit VideoPlaneMoveInfo(const Geometry& current_gemetry, const Geometry& target_gemetry)
+ : cur(current_gemetry), target(target_gemetry) {}
+ Geometry cur;
+ Geometry target;
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_TYPES_VIDEO_PLANE_MOVE_INFO_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_VIDEO_PLANE_H__
+#define __ESPLUSPLAYER_MIXER_VIDEO_PLANE_H__
+
+#include <cstdint>
+#include <memory>
+
+#include "mixer/interfaces/bufferobject.h"
+#include "mixer/interfaces/videoplanemanipulable.h"
+#include "mixer/types/planecomponent.h"
+#include "esplusplayer/types/display.h"
+
+namespace esplusplayer {
+class VideoPlane : public VideoPlaneManipulable {
+ public:
+ explicit VideoPlane(PlaneComponent component, const std::uint32_t& width,
+ const std::uint32_t& height);
+ virtual ~VideoPlane() = default;
+
+ public:
+ virtual bool IsValid() const override;
+ virtual VideoPlaneManipulableInfo GetVideoPlaneManipulableInfo()
+ const override;
+ virtual void SetCropArea(const CropArea& croparea) override;
+
+ protected:
+ virtual const BufferObject* const GetBufferObject_() const = 0;
+ virtual std::uint32_t GetLineSize_() const;
+
+ private:
+ const PlaneComponent component_;
+ const std::uint32_t width_;
+ const std::uint32_t height_;
+ CropArea croparea_;
+};
+
+class YComponentVideoPlane : public VideoPlane {
+ public:
+ using BufferObjectPtr = std::unique_ptr<BufferObject>;
+
+ public:
+ explicit YComponentVideoPlane(BufferObjectPtr buffer,
+ const std::uint32_t& width,
+ const std::uint32_t& height);
+ virtual ~YComponentVideoPlane() = default;
+
+ protected:
+ virtual const BufferObject* const GetBufferObject_() const override;
+
+ private:
+ BufferObjectPtr buffer_;
+};
+
+class UVComponentVideoPlane : public VideoPlane {
+ public:
+ using BufferObjectPtr = std::unique_ptr<BufferObject>;
+
+ public:
+ explicit UVComponentVideoPlane(BufferObjectPtr buffer,
+ const std::uint32_t& width,
+ const std::uint32_t& height);
+ virtual ~UVComponentVideoPlane() = default;
+
+ protected:
+ virtual const BufferObject* const GetBufferObject_() const override;
+
+ private:
+ BufferObjectPtr buffer_;
+};
+
+class YComponentVideoPlaneWithSharedMemory : public VideoPlane {
+ public:
+ using BufferObjectPtr = std::shared_ptr<BufferObject>;
+ using BufferObjectWeakPtr = std::weak_ptr<BufferObject>;
+
+ public:
+ explicit YComponentVideoPlaneWithSharedMemory(BufferObjectWeakPtr buffer,
+ const std::uint32_t& width,
+ const std::uint32_t& height);
+ virtual ~YComponentVideoPlaneWithSharedMemory() = default;
+
+ protected:
+ virtual std::uint32_t GetLineSize_() const override;
+ virtual const BufferObject* const GetBufferObject_() const override;
+
+ private:
+ BufferObjectPtr buffer_;
+ std::uint32_t width_;
+};
+
+class UVComponentVideoPlaneWithSharedMemory : public VideoPlane {
+ public:
+ using BufferObjectPtr = std::shared_ptr<BufferObject>;
+ using BufferObjectWeakPtr = std::weak_ptr<BufferObject>;
+
+ public:
+ explicit UVComponentVideoPlaneWithSharedMemory(BufferObjectWeakPtr buffer,
+ const std::uint32_t& width,
+ const std::uint32_t& height);
+ virtual ~UVComponentVideoPlaneWithSharedMemory() = default;
+
+ public:
+ virtual VideoPlaneManipulableInfo GetVideoPlaneManipulableInfo()
+ const override;
+
+ protected:
+ virtual std::uint32_t GetLineSize_() const override;
+ virtual const BufferObject* const GetBufferObject_() const override;
+
+ private:
+ BufferObjectPtr buffer_;
+ std::uint32_t width_;
+ std::uint32_t height_;
+};
+} // namespace esplusplayer
+
+#endif //__ESPLUSPLAYER_MIXER_VIDEO_PLANE_H__
\ No newline at end of file
--- /dev/null
+#include "mixer/abs_videoframe.h"
+
+namespace esplusplayer {
+
+const std::vector<VideoPlaneManipulableInfo>
+AbstractVideoFrame::GetVideoPlaneManipInfo() const {
+ if (IsValid() == false) return {};
+ std::vector<VideoPlaneManipulableInfo> vector;
+ for (const auto& plane : planes_) {
+ vector.emplace_back(plane->GetVideoPlaneManipulableInfo());
+ }
+ return vector;
+}
+
+bool AbstractVideoFrame::IsValid() const {
+ if (planes_.size() == 0) return false;
+ if (GetWidth_() == 0 || GetHeight_() == 0) return false;
+ if (IsValid_() == false) return false;
+ return true;
+}
+
+bool AbstractVideoFrame::SetCropArea(const CropArea& croparea) {
+ if (IsValid() == false) return false;
+ for (auto& plane : planes_) {
+ plane->SetCropArea(croparea);
+ }
+ return true;
+}
+
+const std::uint32_t AbstractVideoFrame::GetWidth() const {
+ if (IsValid() == false) return 0;
+ return GetWidth_();
+}
+
+const std::uint32_t AbstractVideoFrame::GetHeight() const {
+ if (IsValid() == false) return 0;
+ return GetHeight_();
+}
+
+void AbstractVideoFrame::RegisterVideoPlaneManipulablePtr_(
+ VideoPlaneManipulablePtr vpmanip) {
+ if (vpmanip == nullptr) return;
+ planes_.emplace_back(std::move(vpmanip));
+}
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/defaultmixer.h"
+
+#include <gst/gst.h>
+#include <cinttypes>
+
+#include <trackrenderer_capi/display.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <fstream>
+#include <vector>
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/rendererwithdoublebuf.h"
+#include "mixer/tizen/tizenbufferkeyvideoframe.h"
+#include "mixer/tizen/tizenhwvideoframe.h"
+#include "mixer/tizen/tizenrenderableobj_factory.h"
+#include "mixer/tizen/tizensurfacevideoframe.h"
+#include "mixer/types/videoplanemoveinfo.h"
+
+namespace esplusplayer {
+
+namespace internal {
+TrackRendererDisplayMode ConvertToTrackRendererDisplayMode(
+ const DisplayMode& mode);
+
+} // namespace internal
+DefaultMixer::DefaultMixer() : renderer_listener_(&trackrenderer_handle_) {
+ LOG_ENTER;
+ gst_init(NULL, NULL);
+ trackrenderer_create(&trackrenderer_handle_);
+
+ TizenRenderableObjectFactory factory(&bufmgr_);
+ renderer_ = RendererPtr(new RendererWithDoubleBuffer(
+ factory, &bufmgr_, &bufmgr_, whole_resolution_, &renderer_listener_));
+ // renderer_ = RendererPtr(
+ // new Renderer(factory, whole_resolution_, &renderer_listener_));
+ InitResourceList_();
+}
+
+DefaultMixer::~DefaultMixer() {
+ LOG_ENTER;
+ trackrenderer_destroy(trackrenderer_handle_);
+ LOG_LEAVE;
+}
+
+bool DefaultMixer::Start() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> lk(mixer_lock_);
+ if (is_started_ == true) {
+ LOG_ERROR("mixer was already started");
+ return true;
+ }
+
+ if (renderer_->Start() == false) {
+ LOG_ERROR("renderer start failed");
+ return false;
+ }
+
+ TrackRendererTrack track;
+ memset(&track, 0, sizeof(TrackRendererTrack));
+ track.type = kTrackRendererTrackTypeVideo;
+ track.index = 0;
+ track.active = 1;
+ track.mimetype = "video/x-raw";
+ track.width = whole_resolution_.width;
+ track.height = whole_resolution_.height;
+ track.framerate_num = whole_resolution_.framerate_num;
+ track.framerate_den = whole_resolution_.framerate_den;
+
+ if (trackrenderer_set_track(trackrenderer_handle_, &track, 1) != 0) {
+ LOG_ERROR("fail to set track");
+ return false;
+ }
+
+ constexpr std::uint32_t kLowLatencyMode =
+ 0x0111; // video disable avsync & videoquailty
+ trackrenderer_set_attribute(trackrenderer_handle_, "low-latency-mode",
+ kLowLatencyMode, nullptr);
+ if (use_sub_scaler_) {
+ constexpr std::uint32_t alternative_rsc = 1;
+ trackrenderer_set_attribute(trackrenderer_handle_,
+ "alternative-video-resource", alternative_rsc,
+ nullptr);
+ }
+ if (trackrenderer_prepare(trackrenderer_handle_) != 0) {
+ LOG_ERROR("fail to prepare");
+ return false;
+ }
+ if (trackrenderer_start(trackrenderer_handle_) != 0) {
+ LOG_ERROR("fail to start");
+ return false;
+ }
+
+ is_started_ = true;
+ LOG_LEAVE;
+ return true;
+}
+
+bool DefaultMixer::Stop() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> lk(mixer_lock_);
+ if (is_started_ == false) {
+ LOG_ERROR("mixer was already stopped");
+ return true;
+ }
+
+ if (renderer_->Stop() == false) {
+ LOG_ERROR("renderer stop failed");
+ }
+
+ if (trackrenderer_stop(trackrenderer_handle_) != 0) {
+ LOG_ERROR("fail to stop");
+ }
+
+ is_started_ = false;
+ LOG_LEAVE;
+ return true;
+}
+
+int DefaultMixer::GetMaximumAllowedNumberOfPlayer() {
+ LOG_ENTER;
+ // Fix me if we get policy and detail method (bc_hi.lee)
+ constexpr int kMaximumAllowedNumberOfPlayer = 3;
+ constexpr int kMaximumNumForNdecoder = 4;
+ if (resource_allocation_mode_ == RscAllocMode::kNdecoder)
+ return kMaximumNumForNdecoder;
+ return kMaximumAllowedNumberOfPlayer;
+}
+
+bool DefaultMixer::SetDisplay(const DisplayType type, void* obj) {
+ LOG_ENTER;
+ TrackRendererDisplayType _type = kTrackRendererDisplayTypeNone;
+ switch (type) {
+ case DisplayType::kNone: {
+ _type = kTrackRendererDisplayTypeNone;
+ break;
+ }
+ case DisplayType::kOverlay: {
+ _type = kTrackRendererDisplayTypeOverlay;
+ break;
+ }
+ case DisplayType::kEvas: {
+ _type = kTrackRendererDisplayTypeEvas;
+ break;
+ }
+ default:
+ LOG_ERROR("unknown displaytype");
+ return false;
+ }
+ if (!trackrenderer_set_display(trackrenderer_handle_, _type, obj)) {
+ return true;
+ }
+ return false;
+}
+
+bool DefaultMixer::SetDisplay(const DisplayType type, const uint32_t surface_id,
+ const int x, const int y, const int w,
+ const int h) {
+ LOG_ENTER;
+ TrackRendererDisplayType _type = kTrackRendererDisplayTypeNone;
+ switch (type) {
+ case DisplayType::kNone: {
+ _type = kTrackRendererDisplayTypeNone;
+ break;
+ }
+ case DisplayType::kOverlay: {
+ _type = kTrackRendererDisplayTypeOverlay;
+ break;
+ }
+ case DisplayType::kEvas: {
+ _type = kTrackRendererDisplayTypeEvas;
+ break;
+ }
+ default:
+ LOG_ERROR("unknown displaytype");
+ return false;
+ }
+ if (!trackrenderer_set_display_surface(trackrenderer_handle_, _type,
+ surface_id, x, y, w, h)) {
+ return true;
+ }
+ return false;
+}
+
+bool DefaultMixer::SetDisplayMode(const DisplayMode& mode) {
+ LOG_ENTER;
+ TrackRendererDisplayMode _mode =
+ internal::ConvertToTrackRendererDisplayMode(mode);
+ if (!trackrenderer_set_display_mode(trackrenderer_handle_, _mode)) {
+ return true;
+ }
+ return false;
+}
+
+bool DefaultMixer::SetDisplayRoi(const Geometry& geometry) {
+ LOG_ENTER;
+ TrackRendererGeometry _geometry;
+ memset(&_geometry, 0, sizeof(TrackRendererGeometry));
+ _geometry.x = geometry.x;
+ _geometry.y = geometry.y;
+ _geometry.w = geometry.w;
+ _geometry.h = geometry.h;
+ if (!trackrenderer_set_display_roi(trackrenderer_handle_, &_geometry)) {
+ return true;
+ }
+ return false;
+}
+
+bool DefaultMixer::SetRscAllocMode(const RscAllocMode& mode) {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> lock(ticket_lock_);
+ if (player_map_.size() != 0) {
+ LOG_ERROR(
+ "it have to be called before setting player's display type to mixer "
+ "type");
+ return false;
+ }
+ resource_allocation_mode_ = mode;
+ return true;
+}
+
+bool DefaultMixer::DisableAudioFocusSetting() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> lock(ticket_lock_);
+ if (player_map_.size() != 0) {
+ LOG_ERROR(
+ "it have to be called before setting player's display type to mixer "
+ "type");
+ return false;
+ }
+ enable_audio_focus_setting_ = false;
+ if (audio_focused_player_) audio_focused_player_ = nullptr;
+ return true;
+}
+
+bool DefaultMixer::SetAlternativeVideoScaler() {
+ LOG_ENTER;
+ use_sub_scaler_ = true;
+ return true;
+}
+
+bool DefaultMixer::SetAudioFocus(const void* player_instance) {
+ LOG_ENTER;
+ if (!player_instance) return false;
+ std::lock_guard<std::mutex> lock(ticket_lock_);
+
+ if (!enable_audio_focus_setting_) return false;
+
+ if (player_map_.count(player_instance) == 0) {
+ audio_focused_player_ = player_instance;
+ LOG_INFO("audio focus [%p]", audio_focused_player_);
+ return true;
+ }
+
+ LOG_INFO("Active Audio player[%p]", player_instance);
+ auto ticket = player_map_[player_instance];
+ if (ticket->IsAudioFocused()) {
+ LOG_INFO("already focused user");
+ return true;
+ }
+
+ auto target = std::find_if(
+ player_map_.begin(), player_map_.end(),
+ [](const std::pair<const void*, Ticket*>& item) noexcept -> bool {
+ return (item.second)->IsAudioFocused();
+ });
+
+ if (target != player_map_.end()) {
+ if (target->second->GetListener()->OnAudioFocusChanged(false)) {
+ target->second->SetAudioFocus(false);
+ } else {
+ LOG_ERROR("fail to change audio focus");
+ return false;
+ }
+ } else {
+ LOG_INFO("not found audio focused player");
+ }
+
+ if (ticket->GetListener()) {
+ if (ticket->GetListener()->OnAudioFocusChanged(true)) {
+ ticket->SetAudioFocus(true);
+ return true;
+ }
+ }
+ LOG_ERROR("fail to change audio focus");
+ return false;
+}
+
+bool DefaultMixer::Commit() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> lock(ticket_lock_);
+ std::vector<VideoPlaneMoveInfo> move_list;
+ for (auto it = player_map_.begin(); it != player_map_.end(); ++it) {
+ auto listener = it->second->GetListener();
+ if (!listener) continue;
+ DisplayInfo cur_info, new_info;
+ it->second->GetDisplayInfo(&cur_info);
+ if (listener->OnUpdateDisplayInfo(cur_info, &new_info)) {
+ it->second->UpdateDisplayInfo(new_info);
+ if (it->second->HasRenderedBefore() == true) {
+ move_list.emplace_back(cur_info.geometry, new_info.geometry);
+ }
+ } else {
+ LOG_ERROR("fail to update display info");
+ }
+ }
+ renderer_->Move(&bufmgr_, move_list);
+ return true;
+}
+
+bool DefaultMixer::SetResolution(const ResolutionInfo& info) {
+ whole_resolution_ = info;
+ renderer_->ChangeResolution(TizenRenderableObjectFactory(&bufmgr_),
+ info.width, info.height);
+ renderer_->ChangeRenderingSpeed(info.framerate_num, info.framerate_den);
+ return true;
+}
+
+bool DefaultMixer::RegisterListener(MixerEventListener* listener) {
+ LOG_ENTER;
+ listener_ = listener;
+ trackrenderer_set_resourceconflict_cb(trackrenderer_handle_,
+ ResourceConflictCb_, this);
+ trackrenderer_set_error_cb(trackrenderer_handle_, ErrorCb_, this);
+ return true;
+}
+
+MixerTicket* DefaultMixer::CreateTicket(const void* player_instance) {
+ std::lock_guard<std::mutex> lock(ticket_lock_);
+ LOG_ENTER;
+ auto ticket = new Ticket(this, player_instance);
+ if (audio_focused_player_ == player_instance) {
+ ticket->SetAudioFocus(true);
+ LOG_INFO(" player [%p]", player_instance);
+ }
+ player_map_.emplace(player_instance, ticket);
+ return ticket;
+}
+
+// LCOV_EXCL_START
+bool DefaultMixer::Detach_(const void* player_instance) {
+ std::lock_guard<std::mutex> lock(ticket_lock_);
+ LOG_ENTER;
+ auto* ticket = player_map_.at(player_instance);
+
+ if (ticket == nullptr) return false;
+ if (ticket->HasRenderedBefore()) {
+ DisplayInfo display_info;
+ ticket->GetDisplayInfo(&display_info);
+ // if this ticket is display visible status, draw black screen
+ if (IsNeededToSkipRendering_(display_info) == false) {
+ LOG_INFO(
+ "player [%p] has been rendered before, so will mute on rendered "
+ "region",
+ player_instance);
+ renderer_->Mute(&bufmgr_, display_info.geometry);
+ }
+ }
+ ticket->DeallocResource();
+ player_map_.erase(player_instance);
+ return true;
+}
+
+bool DefaultMixer::Render_(const void* player_instance,
+ const VideoPlaneCollection& vplanes) {
+ DisplayInfo display_info;
+ {
+ std::lock_guard<std::mutex> t_lk(ticket_lock_);
+ auto* ticket = player_map_.at(player_instance);
+ if (ticket == nullptr) return false;
+ ticket->GetDisplayInfo(&display_info);
+ ticket->RecordRenderingTime();
+ }
+ if (IsNeededToSkipRendering_(display_info)) {
+ LOG_INFO("Skip Rendering for player [%p]", player_instance);
+ return true;
+ }
+ bool ret = renderer_->Render(&bufmgr_, &vplanes, display_info.geometry);
+ if (ret == false) {
+ LOG_INFO("Rendering Failed for player [%p]", player_instance);
+ }
+ return ret;
+}
+
+bool DefaultMixer::IsNeededToSkipRendering_(const DisplayInfo& display_info) {
+ if (display_info.visible_status == VisibleStatus::kHide) return true;
+ if (display_info.geometry.x == 0 && display_info.geometry.y == 0 &&
+ display_info.geometry.w == 1 && display_info.geometry.h == 1)
+ return true;
+ return false;
+}
+
+void DefaultMixer::ResourceConflictCb_(UserData userdata) {
+ auto mixer = static_cast<DefaultMixer*>(userdata);
+ if (!mixer) return;
+ mixer->listener_->OnResourceConflicted();
+}
+void DefaultMixer::ErrorCb_(const TrackRendererErrorType error_code,
+ UserData userdata) {
+ auto mixer = static_cast<DefaultMixer*>(userdata);
+ if (!mixer) return;
+ mixer->listener_->OnError();
+}
+
+bool DefaultMixer::Ticket::Render(const DecodedRawInfo& info) {
+ LOG_INFO("player [%p] render frame [%d x %d]", player_instance_, info.width,
+ info.height);
+ TizenHWVideoFrame frame(info);
+ frame.SetCropArea(each_display_info_.croparea);
+ bool ret = handler_->Render_(player_instance_, frame);
+ if (ret == false) {
+ LOG_INFO("player [%p] rendering error", player_instance_);
+ } else {
+ has_rendered_ = true;
+ }
+ return ret;
+}
+
+bool DefaultMixer::Ticket::Render(const DecodedVideoKeyTypeInfo& info) {
+ LOG_INFO("player [%p] render frame [%d x %d]", player_instance_, info.width,
+ info.height);
+ TizenBufferKeyVideoFrame frame(&handler_->bufmgr_, info.key, info.width,
+ info.height);
+ frame.SetCropArea(each_display_info_.croparea);
+ bool ret = handler_->Render_(player_instance_, frame);
+ if (ret == false) {
+ LOG_INFO("player [%p] rendering error", player_instance_);
+ } else {
+ has_rendered_ = true;
+ }
+ return ret;
+}
+// LCOV_EXCL_STOP
+
+void DefaultMixer::InitResourceList_() {
+ // Must be improved (bc_hi.lee)
+ for (int type = static_cast<int>(ResourceType::kHwMain);
+ type < static_cast<int>(ResourceType::kNdecoder); type++) {
+ Resource resource;
+ resource.category = ResourceCategory::kVideoDecoder;
+ resource.type = static_cast<ResourceType>(type);
+ resource_list_.push_back(std::move(resource));
+ }
+}
+
+// LCOV_EXCL_START
+bool DefaultMixer::Ticket::GetAvailableResourceType(
+ const ResourceCategory& category, ResourceType* type) {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> lock(handler_->ticket_lock_);
+ if (handler_->resource_allocation_mode_ == RscAllocMode::kDisable) {
+ LOG_ERROR("mixer is no loger involved in resource alloc");
+ return false;
+ }
+
+ if (handler_->resource_allocation_mode_ == RscAllocMode::kNdecoder) {
+ *type = ResourceType::kNdecoder;
+ return true;
+ }
+
+ for (const auto& item : handler_->resource_list_) {
+ if (!item.assignee) {
+ *type = item.type;
+ LOG_INFO("Resource type [%d]", static_cast<int>(*type));
+ return true;
+ }
+ }
+ LOG_ERROR("no available resource type");
+ return false;
+}
+
+bool DefaultMixer::Ticket::IsAudioFocusHandler() {
+ std::lock_guard<std::mutex> lock(handler_->ticket_lock_);
+ return handler_->enable_audio_focus_setting_;
+}
+
+bool DefaultMixer::Ticket::IsRscAllocHandler() {
+ std::lock_guard<std::mutex> lock(handler_->ticket_lock_);
+ return handler_->resource_allocation_mode_ != RscAllocMode::kDisable;
+}
+
+bool DefaultMixer::Ticket::Alloc(const ResourceCategory& category,
+ const ResourceType& type) {
+ std::lock_guard<std::mutex> lock(handler_->ticket_lock_);
+ if (handler_->resource_allocation_mode_ == RscAllocMode::kDisable) {
+ LOG_ERROR("mixer is no loger involved in resource alloc");
+ return false;
+ }
+
+ if (type == ResourceType::kNdecoder) {
+ LOG_INFO("Request N decoder");
+ return true;
+ }
+
+ auto compare = [category, type](Resource item) -> bool {
+ if (item.category == category && item.type == type && !item.assignee) {
+ return true;
+ }
+ return false;
+ };
+ auto target = std::find_if(handler_->resource_list_.begin(),
+ handler_->resource_list_.end(), compare);
+ if (target == handler_->resource_list_.end()) {
+ LOG_ERROR("Resource alloc fail");
+ return false;
+ }
+ LOG_INFO("assignee %p", player_instance_);
+ target->assignee = player_instance_;
+ LOG_LEAVE;
+ return true;
+}
+
+MixerTicketEventListener* DefaultMixer::Ticket::GetListener() {
+ return ticket_listener_;
+}
+
+bool DefaultMixer::Ticket::IsAudioFocused() { return is_audio_focus_; }
+
+void DefaultMixer::Ticket::SetAudioFocus(bool active) {
+ is_audio_focus_ = active;
+}
+
+void DefaultMixer::Ticket::GetDisplayInfo(DisplayInfo* info) {
+ if (info == nullptr) return;
+ *info = each_display_info_;
+}
+
+bool DefaultMixer::Ticket::HasRenderedBefore() { return has_rendered_; }
+
+void DefaultMixer::Ticket::UpdateDisplayInfo(const DisplayInfo& info) {
+ LOG_INFO(
+ "updated display info : geom[%d, %d, %d x %d] crop[%.3lf, %.3lf, %.3lf x "
+ "%.3lf]",
+ info.geometry.x, info.geometry.y, info.geometry.w, info.geometry.h,
+ info.croparea.scale_x, info.croparea.scale_y, info.croparea.scale_w,
+ info.croparea.scale_h);
+ each_display_info_ = info;
+}
+
+void DefaultMixer::Ticket::DeallocResource() {
+ auto compare = [this](Resource item) -> bool {
+ if (item.assignee == player_instance_) {
+ return true;
+ }
+ return false;
+ };
+
+ auto target = std::find_if(handler_->resource_list_.begin(),
+ handler_->resource_list_.end(), compare);
+ if (target == handler_->resource_list_.end()) {
+ LOG_INFO("not found assignee");
+ return;
+ }
+ target->assignee = nullptr;
+}
+
+void DefaultMixer::Ticket::RecordRenderingTime() {
+ auto now = std::chrono::system_clock::now();
+ LOG_INFO("[PERF] p[%p] render_interval[%" PRId64"]ms", player_instance_,
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ now - last_rendering_time_)
+ .count());
+ last_rendering_time_ = now;
+}
+
+bool DefaultMixer::Ticket::RegisterListener(
+ MixerTicketEventListener* listener) {
+ LOG_ENTER;
+ ticket_listener_ = listener;
+ return true;
+}
+
+bool DefaultMixer::Ticket::Prepare() {
+ LOG_ENTER;
+ DisplayInfo new_info;
+ ticket_listener_->OnUpdateDisplayInfo(each_display_info_, &new_info);
+ UpdateDisplayInfo(new_info);
+ if (!handler_->enable_audio_focus_setting_) return true;
+ LOG_INFO("audio focused [%d]", is_audio_focus_);
+ ticket_listener_->OnAudioFocusChanged(is_audio_focus_);
+ return true;
+}
+
+DefaultMixer::Ticket::~Ticket() {
+ handler_->Detach_(player_instance_);
+ LOG_LEAVE;
+}
+// LCOV_EXCL_STOP
+
+DefaultMixer::MixerRendererEventListener::MixerRendererEventListener(
+ TrackRendererHandle* trhandle_ptr)
+ : trhandle_ptr_(trhandle_ptr) {}
+
+DefaultMixer::MixerRendererEventListener::~MixerRendererEventListener() {}
+
+void* DefaultMixer::MixerRendererEventListener::CreateGstBuffer_(
+ const BufferKeyType& key) const {
+ if (key == 0) return nullptr;
+ auto* gstbuffer = gst_buffer_new();
+ auto* gststructure =
+ gst_structure_new("tbm_bo", "tbm_bo_key", G_TYPE_UINT, key, nullptr);
+ gst_mini_object_set_qdata(GST_MINI_OBJECT(gstbuffer),
+ g_quark_from_static_string("tbm_bo"), gststructure,
+ (GDestroyNotify)gst_structure_free);
+ return static_cast<void*>(gstbuffer);
+}
+
+void DefaultMixer::MixerRendererEventListener::FillDecoderInputBuffer_(
+ TrackRendererDecoderInputBuffer& buffer, const BufferKeyType& key) const {
+ buffer.type = kTrackRendererTrackTypeVideo;
+ buffer.index = 0;
+ buffer.buffer = CreateGstBuffer_(key);
+}
+
+bool DefaultMixer::MixerRendererEventListener::OnRenderingRelease(
+ const BufferKeyType& key) {
+ if (trhandle_ptr_ == nullptr || *trhandle_ptr_ == nullptr) return false;
+ TrackRendererDecoderInputBuffer buffer;
+ FillDecoderInputBuffer_(buffer, key);
+ TrackRendererSubmitStatus status;
+ int ret = trackrenderer_submit_packet2(*trhandle_ptr_, &buffer, &status);
+ if (ret != 0 && buffer.buffer != nullptr) {
+ gst_buffer_unref(GST_BUFFER(buffer.buffer));
+ }
+ return ret == 0;
+}
+
+namespace internal {
+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;
+ default:
+ return kTrackRendererDisplayModeDisplayMax;
+ }
+}
+
+} // namespace internal
+
+} // namespace esplusplayer
--- /dev/null
+#include "mixer/mixedframe.h"
+
+#include <cstring>
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/interfaces/accessiblebuffer.h"
+
+namespace esplusplayer {
+MixedFramePtr MixedFrame::Create(const MemoryAllocator* const memop,
+ const std::uint32_t width,
+ const std::uint32_t height) {
+ return Ptr(new MixedFrame(memop, width, height));
+}
+
+MixedFrame::MixedFrame(const MemoryAllocator* const memop,
+ const std::uint32_t width, const std::uint32_t height)
+ : width_(width), height_(height) {
+ if (memop == nullptr) return;
+ const auto size = CalculateBufferSize_(width_, height_);
+ if (size == 0) return;
+ buffer_ = BufferObjectPtr(memop->Allocate(size));
+ if (buffer_ == nullptr) return;
+ allocated_size_ = buffer_->GetSize();
+ if (allocated_size_ == 0) return;
+
+ if (allocated_size_ != size) {
+ LOG_WARN("size mismatched request [%u] allocated [%u]", size,
+ allocated_size_);
+ }
+
+ if (auto* accessible_buffer =
+ dynamic_cast<AccessibleBuffer*>(buffer_.get())) {
+ auto ptr = accessible_buffer->GetWritableAddress();
+ if (ptr == nullptr) return;
+ const auto y_size = width_ * height_;
+ const auto uv_size = y_size >> 1;
+ {
+ std::memset(static_cast<char*>(ptr->GetAddress()), (char)0x00, y_size);
+ std::memset(static_cast<char*>(ptr->GetAddress()) + y_size, (char)0x80,
+ uv_size);
+ }
+ LOG_DEBUG("MixedFrame UV memset done");
+ }
+}
+
+const std::vector<VideoPlaneManipulableInfo>
+MixedFrame::GetVideoPlaneManipInfo() const {
+ if (IsValid() == false) return {};
+ auto handle = buffer_->GetBufferHandle();
+ return {GetYComponentVMInfo_(handle), GetUVComponentVMInfo_(handle)};
+}
+
+bool MixedFrame::IsValid() const {
+ if (width_ == 0 || height_ == 0) return false;
+ if (allocated_size_ == 0) return false;
+ if (buffer_ == nullptr) return false;
+ return true;
+}
+
+std::uint32_t MixedFrame::GetWidth() const { return width_; }
+
+std::uint32_t MixedFrame::GetHeight() const { return height_; }
+
+std::uint32_t MixedFrame::GetSize() const {
+ if (IsValid() == false) return 0;
+ return allocated_size_;
+}
+
+bool MixedFrame::Render(const VideoPlaneManipulator* const vpmanip,
+ const std::vector<VideoPlaneManipulableInfo>& planes,
+ const Geometry& geom) {
+ if (vpmanip == nullptr) return false;
+ if (IsValid() == false) return false;
+ bool ret = true;
+ for (const auto& video_manipinfo : planes) {
+ ret &= vpmanip->Do(video_manipinfo, GetMixedFrameVideoPlaneManipulableInfo_(
+ video_manipinfo.component, geom));
+ }
+ return ret;
+}
+
+bool MixedFrame::Fill(const VideoPlaneColorManipulator* const vpmanip,
+ const PlaneComponent& comp, const std::uint32_t& color,
+ const Geometry& geom) {
+ VideoPlaneManipulableInfo info =
+ GetMixedFrameVideoPlaneManipulableInfo_(comp, geom);
+ return vpmanip->Do(color, info);
+}
+
+bool MixedFrame::Export(BufferKeyType& key) const {
+ if (IsValid() == false) return false;
+ key = buffer_->Export();
+ return true;
+}
+
+std::uint32_t MixedFrame::CalculateBufferSize_(const std::uint32_t width,
+ const std::uint32_t height) {
+ return (width * height * 3) >> 1;
+}
+
+VideoPlaneManipulableInfo MixedFrame::GetMixedFrameVideoPlaneManipulableInfo_(
+ const PlaneComponent component, const Geometry& geom) {
+ VideoPlaneManipulableInfo ret;
+ ret.handle = buffer_->GetBufferHandle();
+ ret.component = component;
+ ret.linesize = GetWidth();
+ if (component == PlaneComponent::kYComponent) {
+ ret.rect = geom;
+ } else {
+ ret.rect.x = geom.x / 2;
+ ret.rect.y = geom.y / 2 + GetHeight();
+ ret.rect.w = geom.w / 2;
+ ret.rect.h = geom.h / 2;
+ }
+ return ret;
+}
+
+VideoPlaneManipulableInfo MixedFrame::GetYComponentVMInfo_(
+ BufferHandleType handle) const {
+ VideoPlaneManipulableInfo info;
+ info.handle = handle;
+ info.linesize = GetWidth();
+ info.rect.x = 0;
+ info.rect.y = 0;
+ info.rect.w = GetWidth();
+ info.rect.h = GetHeight();
+ info.component = PlaneComponent::kYComponent;
+ return info;
+}
+
+VideoPlaneManipulableInfo MixedFrame::GetUVComponentVMInfo_(
+ BufferHandleType handle) const {
+ VideoPlaneManipulableInfo info;
+ info.handle = handle;
+ info.linesize = GetWidth();
+ info.rect.x = 0;
+ info.rect.y = GetHeight();
+ info.rect.w = GetWidth() >> 1;
+ info.rect.h = GetHeight() >> 1;
+ info.component = PlaneComponent::kUVComponent;
+ return info;
+}
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/mixer.h"
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/defaultmixer.h"
+
+namespace esplusplayer {
+
+std::unique_ptr<Mixer> Mixer::Create() {
+ auto instance = std::unique_ptr<DefaultMixer>(new DefaultMixer);
+ LOG_INFO("Create Mixer [%p]", instance.get());
+ return instance;
+}
+
+} // namespace esplusplayer
--- /dev/null
+#include "mixer_capi/mixer_capi.h"
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/mixer.h"
+#include "mixer/mixer_eventlistener.h"
+#include "esplusplayer/types/display.h"
+
+using esplusplayer::Mixer;
+
+struct MixerPriv;
+
+class listener_bridge : public esplusplayer::MixerEventListener {
+ public:
+ listener_bridge() { LOG_ENTER }
+ ~listener_bridge() { LOG_ENTER }
+
+ virtual void OnResourceConflicted() {
+ LOG_ENTER
+ if (this->resource_conflicted_cb_)
+ this->resource_conflicted_cb_(resource_conflicted_cb_userdata_);
+ }
+
+ private:
+ mixer_resource_conflicted_cb resource_conflicted_cb_ = nullptr;
+ void* resource_conflicted_cb_userdata_ = nullptr;
+
+ friend int mixer_set_resource_conflicted_cb(
+ mixer_handle pp, mixer_resource_conflicted_cb resource_conflicted_cb,
+ void* userdata);
+};
+
+struct MixerPriv {
+ std::unique_ptr<Mixer> mixer;
+ std::unique_ptr<listener_bridge> listener{new listener_bridge()};
+
+ friend MixerPriv* MixerPrivCreate();
+ friend void MixerPrivDestroy(MixerPriv*& instance);
+
+ private:
+ MixerPriv() {}
+ ~MixerPriv() {}
+};
+
+MixerPriv* MixerPrivCreate() {
+ MixerPriv* instance = new MixerPriv();
+ instance->mixer = Mixer::Create();
+ instance->mixer->RegisterListener(instance->listener.get());
+ return instance;
+}
+
+void MixerPrivDestroy(MixerPriv*& instance) {
+ if (instance) delete instance;
+ instance = nullptr;
+}
+
+inline bool is_null_(void* object) { return object == nullptr; }
+
+inline Mixer* cast_(mixer_handle mixer) {
+ auto priv = static_cast<MixerPriv*>(mixer);
+ return priv->mixer.get();
+}
+
+inline listener_bridge* listener_cast_(mixer_handle pp) {
+ auto priv = static_cast<MixerPriv*>(pp);
+ return priv->listener.get();
+}
+
+inline int convert_return_type_(bool ret) {
+ return ret ? MIXER_ERROR_TYPE_NONE : MIXER_ERROR_TYPE_INVALID_OPERATION;
+}
+
+mixer_handle mixer_create() {
+ LOG_ENTER
+ mixer_handle mixer = static_cast<mixer_handle>(MixerPrivCreate());
+ return mixer;
+}
+
+int mixer_destroy(mixer_handle handle) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ auto priv = static_cast<MixerPriv*>(handle);
+ MixerPrivDestroy(priv);
+
+ return MIXER_ERROR_TYPE_NONE;
+}
+
+int mixer_start(mixer_handle handle) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->Start());
+}
+
+int mixer_stop(mixer_handle handle) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->Stop());
+}
+
+int mixer_get_max_allowed_number_of_player(mixer_handle handle) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return cast_(handle)->GetMaximumAllowedNumberOfPlayer();
+}
+
+int mixer_set_display(mixer_handle handle, mixer_display_type type,
+ void* window) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->SetDisplay(
+ static_cast<esplusplayer::DisplayType>(type), window));
+}
+
+int mixer_set_display_mode(mixer_handle handle, mixer_display_mode mode) {
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO("display mode : %d", static_cast<int>(mode));
+ return convert_return_type_(cast_(handle)->SetDisplayMode(
+ static_cast<esplusplayer::DisplayMode>(mode)));
+}
+
+int mixer_set_display_roi(mixer_handle handle, const int x, const int y,
+ const int width, const int height) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+ LOG_INFO("x : %d, y: %d, width : %d, height : %d", x, y, width, height);
+ esplusplayer::Geometry roi;
+ roi.x = x;
+ roi.y = y;
+ roi.w = width;
+ roi.h = height;
+ return convert_return_type_(cast_(handle)->SetDisplayRoi(roi));
+}
+
+int mixer_set_rsc_alloc_mode(mixer_handle handle,
+ const mixer_rsc_alloc_mode mode) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->SetRscAllocMode(
+ static_cast<esplusplayer::RscAllocMode>(mode)));
+}
+
+int mixer_disable_audio_focus_setting(mixer_handle handle) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->DisableAudioFocusSetting());
+}
+
+int mixer_set_alternative_video_scaler(mixer_handle handle) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->SetAlternativeVideoScaler());
+}
+
+int mixer_set_audio_focus(mixer_handle handle, const void* player_instance) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->SetAudioFocus(player_instance));
+}
+
+int mixer_commit(mixer_handle handle) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+
+ return convert_return_type_(cast_(handle)->Commit());
+}
+
+int mixer_set_resolution(mixer_handle handle, const int width, const int height,
+ const int framerate_num, const int framerate_den) {
+ LOG_ENTER
+ if (is_null_(handle)) return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+ esplusplayer::Mixer::ResolutionInfo info;
+ info.width = width;
+ info.height = height;
+ info.framerate_num = framerate_num;
+ info.framerate_den = framerate_den;
+ return convert_return_type_(cast_(handle)->SetResolution(info));
+}
+
+mixer_ticket_h mixer_create_ticket(mixer_handle handle, const void* player_instance) {
+ LOG_ENTER
+ if (is_null_(handle)) return nullptr;
+
+ return cast_(handle)->CreateTicket(player_instance);
+}
+
+int mixer_set_resource_conflicted_cb(
+ mixer_handle handle, mixer_resource_conflicted_cb resource_conflicted_cb,
+ void* userdata) {
+ LOG_ENTER
+ listener_bridge* listener = nullptr;
+ if (is_null_(handle) || is_null_(listener = listener_cast_(handle))) {
+ LOG_ERROR("Mixer or Listener object is nil.");
+ return MIXER_ERROR_TYPE_INVALID_PARAMETER;
+ }
+ listener->resource_conflicted_cb_ = resource_conflicted_cb;
+ listener->resource_conflicted_cb_userdata_ = userdata;
+ return convert_return_type_(true);
+}
--- /dev/null
+#include "mixer/renderer.h"
+#include <cinttypes>
+#include "core/utils/plusplayer_log.h"
+
+namespace esplusplayer {
+using std::chrono::duration_cast;
+using std::chrono::milliseconds;
+using std::chrono::system_clock;
+
+Renderer::Renderer(const RenderableObjectFactory& mf_factory,
+ const Mixer::ResolutionInfo& rinfo,
+ RendererEventListener* listener)
+ : resolution_info_(rinfo),
+ listener_(listener),
+ frame_(RenderableObjectPtr(mf_factory.CreateRenderableObject(
+ resolution_info_.width, resolution_info_.height))) {}
+
+Renderer::~Renderer() { Stop(); }
+
+bool Renderer::IsValid() const {
+ if (frame_ == nullptr) return false;
+ if (frame_->IsValid() == false) return false;
+ return IsValid_();
+}
+
+bool Renderer::Start() {
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (IsValid() == false) return false;
+ if (rendering_worker_.joinable()) return false;
+ rendering_flag_ = true;
+ rendering_worker_ = std::thread(&Renderer::RenderingWorker_, this);
+ return true;
+}
+
+bool Renderer::Stop() {
+ {
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (rendering_worker_.joinable() == false) return false;
+ rendering_flag_ = false;
+ rendering_cv_.notify_all();
+ }
+ rendering_worker_.join();
+ return true;
+}
+
+bool Renderer::ChangeResolution(const RenderableObjectFactory& mf_factory,
+ const std::uint32_t& width,
+ const std::uint32_t& height) {
+ if (width == 0 || height == 0) return false;
+ if (width == static_cast<std::uint32_t>(resolution_info_.width) &&
+ height == static_cast<std::uint32_t>(resolution_info_.height))
+ return false;
+ return ChangeResolutionInternal_(mf_factory, width, height);
+}
+
+bool Renderer::ChangeResolutionInternal_(
+ const RenderableObjectFactory& mf_factory, const std::uint32_t& width,
+ const std::uint32_t& height) {
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ resolution_info_.width = width;
+ resolution_info_.height = height;
+ frame_.reset(nullptr);
+ frame_ = RenderableObjectPtr(mf_factory.CreateRenderableObject(
+ resolution_info_.width, resolution_info_.height));
+ return IsValid();
+}
+
+bool Renderer::ChangeRenderingSpeed(const std::uint32_t framerate_num,
+ const std::uint32_t framerate_den) {
+ if (framerate_num == 0 || framerate_den == 0) return false;
+ if (framerate_num ==
+ static_cast<std::uint32_t>(resolution_info_.framerate_num) &&
+ framerate_den ==
+ static_cast<std::uint32_t>(resolution_info_.framerate_den))
+ return false;
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (IsValid() == false) return false;
+ resolution_info_.framerate_num = framerate_num;
+ resolution_info_.framerate_den = framerate_den;
+ return true;
+}
+
+bool Renderer::Render(const VideoPlaneScaler* const scaler,
+ const VideoPlaneCollection* const planes,
+ const Geometry& geom) {
+ if (scaler == nullptr) return false;
+ if (planes == nullptr) return false;
+ return RenderInternal_(scaler->GetScaleManipulator(),
+ planes->GetVideoPlaneManipInfo(), geom);
+}
+
+bool Renderer::RenderInternal_(
+ const VideoPlaneManipulator* const scaler,
+ const std::vector<VideoPlaneManipulableInfo>& planes,
+ const Geometry& geom) {
+ if (scaler == nullptr) return false;
+ const auto before = system_clock::now();
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (IsValid() == false) return false;
+ LOG_INFO("[PERF] RenderInternal_ Lock [%" PRId64"]ms",
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ system_clock::now() - before)
+ .count());
+ const auto ret = frame_->Render(scaler, planes, geom);
+ LOG_INFO("[PERF] Scale [%" PRId64"]ms",
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ system_clock::now() - before)
+ .count());
+ return ret;
+}
+
+bool Renderer::Mute(const VideoPlaneColorFiller* const filler,
+ const Geometry& geom) {
+ if (filler == nullptr) return false;
+ return MuteInternal_(filler->GetColorFillManipulator(), geom);
+}
+
+bool Renderer::MuteInternal_(const VideoPlaneColorManipulator* const filler,
+ const Geometry& geom) {
+ if (filler == nullptr) return false;
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (IsValid() == false) return false;
+ bool ret = true;
+ ret &= frame_->Fill(filler, PlaneComponent::kYComponent, 0x00, geom);
+ ret &= frame_->Fill(filler, PlaneComponent::kUVComponent, 0x8080, geom);
+ return ret;
+}
+
+bool Renderer::Move(const VideoPlaneColorFiller* const filler,
+ const std::vector<VideoPlaneMoveInfo>& moving_planes) {
+ if (filler == nullptr) return false;
+ if (moving_planes.size() == 0) return false;
+ return MoveInternal_(filler->GetColorFillManipulator(), moving_planes);
+}
+
+bool Renderer::MoveInternal_(
+ const VideoPlaneColorManipulator* const filler,
+ const std::vector<VideoPlaneMoveInfo>& moving_planes) {
+ if (filler == nullptr) return false;
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (IsValid() == false) return false;
+ bool ret = true;
+ for (const auto& move : moving_planes) {
+ if (IsSameGeometry_(move.cur, move.target)) continue;
+ ret &= frame_->Fill(filler, PlaneComponent::kYComponent, 0x00, move.cur);
+ ret &= frame_->Fill(filler, PlaneComponent::kUVComponent, 0x8080, move.cur);
+ }
+ return true;
+};
+
+bool Renderer::OnRenderingBefore_(const RenderableObject* const frame) {
+ return true;
+}
+
+bool Renderer::IsValid_() const { return true; }
+
+bool Renderer::RaiseOnRenderingReleaseEvent_(const BufferKeyType& key) {
+ if (listener_ == nullptr) return false;
+ return listener_->OnRenderingRelease(key);
+}
+
+std::uint32_t Renderer::GetNextRenderingTimeWithJitter_(
+ const JitterType& jitter) const {
+ const static std::int64_t kOneSecondInMs = 1000; // ms
+ auto next = kOneSecondInMs /
+ (resolution_info_.framerate_num / resolution_info_.framerate_den);
+ auto jitter_in_ms = jitter.count();
+ LOG_DEBUG("[PERF] jitter : [%" PRId64"]ms / next : [%" PRId64"]ms", jitter_in_ms, next);
+ jitter_in_ms %= next;
+ next -= jitter_in_ms;
+ return next < 0 ? 0 : static_cast<std::uint32_t>(next);
+}
+
+void Renderer::RenderingWorker_() {
+ LOG_DEBUG("Start Rendering");
+ JitterType jitter(0);
+ while (1) {
+ LOG_DEBUG("= Start New Frame ===========================================");
+ auto before_1 = system_clock::now();
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ if (rendering_flag_ == false) break;
+ if (OnRenderingBefore_(frame_.get()) == false) {
+ LOG_WARN("OnRenderingBefore_ Failed");
+ continue;
+ }
+
+ LOG_DEBUG(
+ "[PERF] before_1 ~ now : [%" PRId64"]ms",
+ duration_cast<milliseconds>(system_clock::now() - before_1).count());
+ jitter += duration_cast<milliseconds>(system_clock::now() - before_1);
+
+ std::uint64_t next_in_ms = GetNextRenderingTimeWithJitter_(jitter);
+
+ jitter = JitterType::zero();
+ auto before_2 = system_clock::now();
+ LOG_DEBUG("[PULSE] it will awake after [%" PRId64"]ms", next_in_ms);
+ if (next_in_ms > 0) {
+ rendering_cv_.wait_for(lk, milliseconds(next_in_ms),
+ [this] { return rendering_flag_ == false; });
+ // LOG_DEBUG(
+ // "[PERF] before_2 ~ after awake : [%lld]ms",
+ // duration_cast<milliseconds>(system_clock::now() -
+ // before_2).count());
+ if (rendering_flag_ == false) break;
+ }
+ if (IsValid() == false) continue;
+
+ BufferKeyType key;
+ if (frame_->Export(key) == false) {
+ LOG_ERROR("MixedFrame Export Failed");
+ continue;
+ }
+
+ RaiseOnRenderingReleaseEvent_(key);
+ jitter = duration_cast<milliseconds>(system_clock::now() - before_2 -
+ milliseconds(next_in_ms));
+ // LOG_DEBUG(
+ // "[PERF] before_2 ~ now : [%lld]ms / next_in_ms : [%llu]ms",
+ // duration_cast<milliseconds>(system_clock::now() - before_2).count(),
+ // next_in_ms);
+ }
+ std::unique_lock<std::mutex> lk(rendering_mtx_);
+ rendering_flag_ = false;
+ LOG_DEBUG("Rendering Stopped");
+}
+
+const Mixer::ResolutionInfo& Renderer::GetResolutionInfo_() const {
+ return resolution_info_;
+}
+
+RenderableObjectPtr& Renderer::GetMixedFrame_() { return frame_; }
+
+void Renderer::AcquireRenderingLock_() { rendering_mtx_.lock(); }
+
+void Renderer::ReleaseRenderingLock_() { rendering_mtx_.unlock(); }
+
+Geometry Renderer::MakeGeometry_(const std::uint32_t& width,
+ const std::uint32_t& height) {
+ Geometry geom;
+ geom.w = width;
+ geom.h = height;
+ return geom;
+}
+
+bool Renderer::IsSameGeometry_(const Geometry& g1, const Geometry& g2) {
+ if (g1.x != g2.x) return false;
+ if (g1.y != g2.y) return false;
+ if (g1.w != g2.w) return false;
+ if (g1.h != g2.h) return false;
+ return true;
+}
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/sys/tbminterface.h"
+
+#include <tbm_bo.h>
+#include <tbm_bufmgr.h>
+
+namespace esplusplayer {
+namespace tizen {
+BufferDefaultType TBMInterface::BoRef(BufferDefaultType bo) {
+ return tbm_bo_ref(bo);
+}
+void TBMInterface::BoUnRef(BufferDefaultType bo) { tbm_bo_unref(bo); }
+
+int TBMInterface::BoSize(BufferDefaultType bo) { return tbm_bo_size(bo); }
+
+BufferUnionHandleType TBMInterface::BoGetHandle(BufferDefaultType bo,
+ int device) {
+ return tbm_bo_get_handle(bo, device);
+}
+
+BufferUnionHandleType TBMInterface::BoMap(BufferDefaultType bo, int device,
+ int option) {
+ return tbm_bo_map(bo, device, option);
+}
+
+void TBMInterface::BoUnmap(BufferDefaultType bo) { tbm_bo_unmap(bo); }
+
+BufferKeyType TBMInterface::BoExport(BufferDefaultType bo) {
+ return tbm_bo_export(bo);
+}
+
+BufferDefaultType TBMInterface::BoAlloc(tbm_bufmgr bufmgr, int size, int flag) {
+ return tbm_bo_alloc(bufmgr, size, flag);
+}
+BufferDefaultType TBMInterface::BoImport(tbm_bufmgr bufmgr, BufferKeyType key) {
+ return tbm_bo_import(bufmgr, key);
+}
+
+int TBMInterface::GAScale(tbm_bufmgr bufmgr, GraphicsGAScaleInfo* info) {
+ return Gfx_GA_Scale(bufmgr, info);
+}
+
+int TBMInterface::GACopy(tbm_bufmgr bufmgr, GraphicsGABltRopInfo* info) {
+ return Gfx_GA_BltRop(bufmgr, info);
+}
+
+int TBMInterface::GAFill(tbm_bufmgr bufmgr, GraphicsGAFillRectInfo* info) {
+ return Gfx_GA_FillRect(bufmgr, info);
+}
+} // namespace tizen
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/tizen/tizenaccessiblebufferobj.h"
+
+namespace esplusplayer {
+
+TizenAccessibleBufferObject::TizenAccessibleBufferObject(
+ BufferDefaultType buffer)
+ : TizenBufferObject(buffer) {}
+
+TizenAccessibleBufferObject::PhyAddrAccessorPtr
+TizenAccessibleBufferObject::GetReadableAddress() const {
+ if (IsValid() == false) return nullptr;
+ return PhyAddrAccessorPtr(
+ new TizenReadableBufferPhyAddrAccessor(GetBuffer_()));
+}
+
+TizenAccessibleBufferObject::PhyAddrAccessorPtr
+TizenAccessibleBufferObject::GetWritableAddress() const {
+ if (IsValid() == false) return nullptr;
+ return PhyAddrAccessorPtr(
+ new TizenWritableBufferPhyAddrAccessor(GetBuffer_()));
+}
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/tizen/tizenbufferkeyvideoframe.h"
+
+namespace esplusplayer {
+
+// LCOV_EXCL_START
+TizenBufferKeyVideoFrame::TizenBufferKeyVideoFrame(
+ const TizenBufferManager* const bufmgr, const BufferKeyType& key,
+ const std::uint32_t& width, const std::uint32_t& height)
+ : key_(key), width_(width), height_(height) {
+ if (bufmgr == nullptr) return;
+ if (key_ == 0) return;
+ if (width_ == 0 || height_ == 0) return;
+
+ auto buffer = BufferObjectPtr(bufmgr->Import(key_));
+ if (buffer == nullptr) return;
+ RegisterVideoPlaneManipulablePtr_(VideoPlaneManipulablePtr(
+ new YComponentVideoPlaneWithSharedMemory(buffer, width_, height_)));
+ RegisterVideoPlaneManipulablePtr_(VideoPlaneManipulablePtr(
+ new UVComponentVideoPlaneWithSharedMemory(buffer, width_, height_)));
+}
+
+bool TizenBufferKeyVideoFrame::IsValid_() const {
+ if (key_ == 0) return false;
+ return true;
+}
+
+const std::uint32_t TizenBufferKeyVideoFrame::GetWidth_() const {
+ return width_;
+}
+
+const std::uint32_t TizenBufferKeyVideoFrame::GetHeight_() const {
+ return height_;
+}
+// LCOV_EXCL_STOP
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/tizen/tizendefaultphyaddraccessor.h"
+
+namespace esplusplayer {
+
+TizenDefaultPhyAddrAccessor::TizenDefaultPhyAddrAccessor(std::uint32_t viraddr)
+ : viraddr_(viraddr) {}
+
+BufferPhysicalAddrType TizenDefaultPhyAddrAccessor::GetAddress() {
+ return reinterpret_cast<BufferPhysicalAddrType>(viraddr_);
+}
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/tizen/tizenhwbufferobj.h"
+
+// #include "mixer/tizen/tizendefaultphyaddraccessor.h"
+
+namespace esplusplayer {
+// LCOV_EXCL_START
+TizenHWBufferObject::TizenHWBufferObject(const std::uint32_t& width,
+ const std::uint32_t& height,
+ const DecodedRawPlaneInfo& info)
+ : width_(width), height_(height), info_(info) {}
+
+bool TizenHWBufferObject::IsValid() const {
+ if (info_.phyaddr == 0) return false;
+ if (width_ == 0 || height_ == 0 || info_.linesize == 0) return false;
+ return true;
+}
+
+BufferHandleType TizenHWBufferObject::GetBufferHandle() const {
+ if (IsValid() == false) return kInvalidBufferHandle;
+ return static_cast<BufferHandleType>(info_.phyaddr);
+}
+
+BufferKeyType TizenHWBufferObject::Export() const { return kInvalidBufferKey; }
+
+std::uint32_t TizenHWBufferObject::GetSize() const {
+ return height_ * info_.linesize;
+}
+// LCOV_EXCL_STOP
+} // namespace esplusplayer
--- /dev/null
+#include "mixer/tizen/tizenhwvideoframe.h"
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/tizen/tizenhwbufferobj.h"
+#include "mixer/videoplane.h"
+
+namespace esplusplayer {
+
+// LCOV_EXCL_START
+TizenHWVideoFrame::TizenHWVideoFrame(const DecodedRawInfo& info) : info_(info) {
+ if (IsValid_() == false) return;
+
+ RegisterVideoPlaneManipulablePtr_(VideoPlaneManipulablePtr(
+ new YComponentVideoPlane(BufferObjectPtr(new TizenHWBufferObject(
+ info_.width, info_.height, info_.y_info)),
+ info_.width, info_.height)));
+ RegisterVideoPlaneManipulablePtr_(
+ VideoPlaneManipulablePtr(new UVComponentVideoPlane(
+ BufferObjectPtr(new TizenHWBufferObject(
+ info_.width / 2, info_.height / 2, info_.uv_info)),
+ info_.width, info_.height)));
+}
+
+bool TizenHWVideoFrame::IsValid_() const {
+ if (info_.y_info.phyaddr == 0) return false;
+ if (info_.y_info.linesize == 0) return false;
+ if (info_.uv_info.phyaddr == 0) return false;
+ if (info_.uv_info.linesize == 0) return false;
+ return true;
+}
+
+const std::uint32_t TizenHWVideoFrame::GetWidth_() const { return info_.width; }
+
+const std::uint32_t TizenHWVideoFrame::GetHeight_() const {
+ return info_.height;
+}
+
+void TizenHWVideoFrame::PrintDecodedRawInfo() const {
+ LOG_DEBUG("WxH [%ux%u] Y [%u %u %u] / UV [%u %u %u]", info_.width,
+ info_.height, info_.y_info.phyaddr, info_.y_info.viraddr,
+ info_.y_info.linesize, info_.uv_info.phyaddr, info_.uv_info.viraddr,
+ info_.uv_info.linesize);
+}
+// LCOV_EXCL_STOP
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/tizen/tizenrenderableobj_factory.h"
+
+#include "mixer/mixedframe.h"
+
+namespace esplusplayer {
+
+TizenRenderableObjectFactory::TizenRenderableObjectFactory(
+ const MemoryAllocator* const memallocator)
+ : memallocator_(memallocator) {}
+
+RenderableObject* TizenRenderableObjectFactory::CreateRenderableObject(
+ const std::uint32_t width, const std::uint32_t height) const {
+ return new MixedFrame(memallocator_, width, height);
+}
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/tizen/tizensurfacevideoframe.h"
+
+#include <tbm_surface.h>
+#include <tbm_surface_internal.h>
+
+#include "mixer/tizen/tizenbufferobj.h"
+#include "mixer/videoplane.h"
+
+namespace esplusplayer {
+
+// LCOV_EXCL_START
+TizenSurfaceVideoFrame::TizenSurfaceVideoFrame(DecodedVideoPacketExPtr dvp)
+ : dvp_(std::move(dvp)) {
+ auto surface = dvp_->GetTbmSurface();
+ if (surface == nullptr) return;
+
+ width_ = tbm_surface_get_width(surface);
+ height_ = tbm_surface_get_height(surface);
+ if (width_ == 0 || height_ == 0) return;
+
+ auto y_bo = tbm_surface_internal_get_bo(surface, kYIndex);
+ auto uv_bo = tbm_surface_internal_get_bo(surface, kUVIndex);
+ if (y_bo == nullptr || uv_bo == nullptr) return;
+
+ RegisterVideoPlaneManipulablePtr_(
+ VideoPlaneManipulablePtr(new YComponentVideoPlane(
+ BufferObjectPtr(new TizenBufferObject(y_bo)), width_, height_)));
+ RegisterVideoPlaneManipulablePtr_(
+ VideoPlaneManipulablePtr(new UVComponentVideoPlane(
+ BufferObjectPtr(new TizenBufferObject(uv_bo)), width_, height_)));
+}
+
+bool TizenSurfaceVideoFrame::IsValid_() const {
+ if (dvp_ == nullptr) return false;
+ return true;
+}
+
+const std::uint32_t TizenSurfaceVideoFrame::GetWidth_() const { return width_; }
+
+const std::uint32_t TizenSurfaceVideoFrame::GetHeight_() const {
+ return height_;
+}
+// LCOV_EXCL_STOP
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+#include "mixer/videoplane.h"
+
+#include "core/utils/plusplayer_log.h"
+
+namespace esplusplayer {
+
+// LCOV_EXCL_START
+/******************************************************************************
+ * VideoPlane
+ */
+VideoPlane::VideoPlane(PlaneComponent component, const std::uint32_t& width,
+ const std::uint32_t& height)
+ : component_(component), width_(width), height_(height) {}
+
+bool VideoPlane::IsValid() const {
+ if (GetBufferObject_() == nullptr) return false;
+ if (width_ == 0 || height_ == 0 || GetLineSize_() == 0) return false;
+ return true;
+}
+
+VideoPlaneManipulableInfo VideoPlane::GetVideoPlaneManipulableInfo() const {
+ VideoPlaneManipulableInfo info;
+ info.component = component_;
+ info.handle = GetBufferObject_()->GetBufferHandle();
+ info.linesize = GetLineSize_();
+ info.rect.x = width_ * croparea_.scale_x;
+ info.rect.y = height_ * croparea_.scale_y;
+ info.rect.w = width_ * croparea_.scale_w;
+ info.rect.h = height_ * croparea_.scale_h;
+ return info;
+}
+
+void VideoPlane::SetCropArea(const CropArea& croparea) { croparea_ = croparea; }
+
+std::uint32_t VideoPlane::GetLineSize_() const {
+ return GetBufferObject_()->GetSize() / height_;
+}
+
+/******************************************************************************
+ * YComponentVideoPlane
+ */
+
+YComponentVideoPlane::YComponentVideoPlane(BufferObjectPtr buffer,
+ const std::uint32_t& width,
+ const std::uint32_t& height)
+ : VideoPlane(PlaneComponent::kYComponent, width, height),
+ buffer_(std::move(buffer)) {}
+
+const BufferObject* const YComponentVideoPlane::GetBufferObject_() const {
+ return buffer_.get();
+}
+
+/******************************************************************************
+ * UVComponentVideoPlane
+ */
+
+UVComponentVideoPlane::UVComponentVideoPlane(BufferObjectPtr buffer,
+ const std::uint32_t& width,
+ const std::uint32_t& height)
+ : VideoPlane(PlaneComponent::kUVComponent, width / 2, height / 2),
+ buffer_(std::move(buffer)) {}
+
+const BufferObject* const UVComponentVideoPlane::GetBufferObject_() const {
+ return buffer_.get();
+}
+
+/******************************************************************************
+ * YComponentVideoPlaneWithSharedMemory
+ */
+
+YComponentVideoPlaneWithSharedMemory::YComponentVideoPlaneWithSharedMemory(
+ BufferObjectWeakPtr buffer, const std::uint32_t& width,
+ const std::uint32_t& height)
+ : VideoPlane(PlaneComponent::kYComponent, width, height),
+ buffer_(buffer),
+ width_(width) {}
+
+std::uint32_t YComponentVideoPlaneWithSharedMemory::GetLineSize_() const {
+ return width_;
+}
+
+const BufferObject* const
+YComponentVideoPlaneWithSharedMemory::GetBufferObject_() const {
+ return buffer_.get();
+}
+
+/******************************************************************************
+ * UVComponentVideoPlaneWithSharedMemory
+ */
+
+UVComponentVideoPlaneWithSharedMemory::UVComponentVideoPlaneWithSharedMemory(
+ BufferObjectWeakPtr buffer, const std::uint32_t& width,
+ const std::uint32_t& height)
+ : VideoPlane(PlaneComponent::kUVComponent, width / 2, height / 2),
+ buffer_(buffer),
+ width_(width),
+ height_(height) {}
+
+VideoPlaneManipulableInfo
+UVComponentVideoPlaneWithSharedMemory::GetVideoPlaneManipulableInfo() const {
+ auto info = VideoPlane::GetVideoPlaneManipulableInfo();
+ info.rect.y += height_;
+ return info;
+}
+
+std::uint32_t UVComponentVideoPlaneWithSharedMemory::GetLineSize_() const {
+ return width_;
+}
+
+const BufferObject* const
+UVComponentVideoPlaneWithSharedMemory::GetBufferObject_() const {
+ return buffer_.get();
+}
+// LCOV_EXCL_STOP
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+# Appendix
--- /dev/null
+# Add inputs and outputs from these tool invocations to the build variables
+
+
+OS_NAME := $(shell $(UNAME))
+
+
+#ifeq ($(origin BUILD_CONFIG), undefined)
+BUILD_CONFIG ?= Debug
+#endif
+
+#ifeq ($(origin ARCH), undefined)
+ARCH ?= i386
+#endif
+
+#ifeq ($(origin PROJPATH), undefined)
+PROJPATH ?= .
+#endif
+
+
+#ifeq ($(origin PROJ_PATH), undefined)
+PROJ_PATH ?= $(PROJPATH)
+#endif
+
+#ifeq ($(strip $(OUTPUT_DIR)),)
+#OUTPUT_DIR ?= $(PROJ_PATH)/$(BUILD_CONFIG)
+#endif
+
+#ifeq ($(strip $(BUILD_ARCH)),)
+BUILD_ARCH ?= $(ARCH)
+#endif
+
+#ifeq ($(strip $(ENVENTOR_PATH)),)
+ENVENTOR_PATH ?= $(SDK_TOOLPATH)/enventor
+#endif
--- /dev/null
+# C/C++ build script
+
+
+_FUNC_EXT2O = $(patsubst %.$(3),$(1)/%.o,$(2))
+_FUNC_C2O = $(call _FUNC_EXT2O,$(1),$(2),c)
+_FUNC_CPP2O = $(call _FUNC_EXT2O,$(1),$(2),cpp)
+
+
+# parameter :
+# $(1) - C/C++ soruce file
+# $(2) - output path
+# $(3) - .ext
+# $(4) - unique id
+CONVERT_ESC_EXT_TO_O = $(addprefix $(2)/,$(notdir $(patsubst %.$(3),%-$(4).o,$(1))))
+
+#CONVERT_ESC_C_TO_O = $(call CONVERT_ESC_EXT_TO_O,$(1),$(2),c)
+#CONVERT_ESC_CPP_TO_O = $(call CONVERT_ESC_EXT_TO_O,$(1),$(2),cpp)
+
+
+# parameter :
+# $(1) - encoded one C/C++ soruce file
+# $(2) - output path
+# $(3) - ext title (C/C++)
+# $(4) - ext (c/cpp)
+# $(5) - compiler ($(CC)/$(CXX))
+# $(6) - build opt
+# $(7) - build opt file
+# output :
+# $(8) - output files list
+define C_BUILD_PROC_RAW
+$(call CONVERT_ESC_EXT_TO_O,$(1),$(2),$(4),$(8)) : $(call DECODE_4MAKE,$(1)) $(7)
+ @echo ' Building file: $$<'
+ @echo ' Invoking: $(3) Compiler'
+ $$(call MAKEDIRS,$$(@D))
+ $(5) -c "$$<" -o "$$@" $(6) -Wp,@$(7)
+ @echo ' Finished building: $$<'
+$(9) += $(call CONVERT_ESC_EXT_TO_O,$(1),$(2),$(4),$(8))
+endef
+
+
+# parameter :
+# $(1) - output paths
+# $(2) - src paths
+# $(3) - inc paths
+# $(4) - inc files
+# $(5) - Defs
+# $(6) - UnDefs
+# $(7) - compiler opt
+# $(8) - compiler opt file
+# $(9) - ext title (C/C++)
+# $(10) - ext (c/cpp)
+# $(11) - compiler ($(CC)/$(CXX))
+# output :
+# $(12) - OBJS
+# return :
+# none
+define C_PROC_RAW
+
+_OUTPUT_DIR := $$(strip $(1))#
+_SRCS := $(2)#
+_INCS := $(3)#
+_INC_FILES := $(4)#
+_DEFS := $(5)#
+_UNDEFS := $(6)#
+
+_OPT := $(7)
+_OPT_FILE := $(8)
+
+_EXT_TITLE := $(9)
+_EXT := $(10)
+_COMPILER := $(11)
+
+#_OUTPUT_FILES := $(12)
+
+_ENC_SRCS := $$(call ENCODE_4MAKE,$$(_SRCS))
+_ENC_SRCS := $$(filter %.$$(_EXT),$$(_ENC_SRCS))
+
+ifneq ($$(strip $$(_SRCS)),)
+
+_NORMAL_SRCS := $$(filter-out %*.$$(_EXT),$$(_ENC_SRCS))
+_WIDLCARD_SRCS := $$(filter %*.$$(_EXT),$$(_ENC_SRCS))
+
+_ALL_SRCS := $$(call DECODE_4MAKE,$$(_NORMAL_SRCS)) \
+ $$(foreach var,$$(_WIDLCARD_SRCS),$$(call FIND_FILES_4MAKE,$$(call DECODE_4MAKE,$$(var))))
+
+ifneq ($$(strip $$(_ALL_SRCS)),)
+
+_ENC_SRCS := $$(call ENCODE_4MAKE,$$(_ALL_SRCS))
+
+_CDEFS := $$(CDEFS)
+_CDEFS += $$(addprefix -D,$$(_DEFS))
+_CDEFS += $$(addprefix -U,$$(_UNDEFS))
+
+_ENC_C_INCS := $$(call ENCODE_4MAKE,$$(_INCS))
+_ENC_C_INCS := $$(addprefix -I,$$(_ENC_C_INCS))
+
+_ENC_INC_FILES := $$(call ENCODE_4MAKE,$$(_INC_FILES))
+_ENC_INC_FILES += $$(addprefix -include,$$(_ENC_INC_FILES))
+
+_C_INCS := $$(call DECODE_4MAKE,$$(_ENC_C_INCS) $$(_ENC_C_INC_FILES))
+
+_DEFS := $$(_CDEFS) $$(_C_INCS) -I"pch" $$(_OPT)
+
+_UNIQUE_ID = $$(firstword $$(shell echo $$(var) | $$(CKSUM)))
+
+$$(foreach var,$$(_ENC_SRCS),$$(eval $$(call C_BUILD_PROC_RAW,$$(var),$$(_OUTPUT_DIR),$$(_EXT_TITLE),$$(_EXT),$$(_COMPILER),$$(_DEFS),$$(_OPT_FILE),$$(_UNIQUE_ID),$(12))))
+
+endif # (_(strip _(_ALL_SRCS)),)
+
+endif # (_(strip _(_SRCS)),)
+
+
+endef
--- /dev/null
+# EDC build script
+
+
+FUNC_EDC2EDJ = $(patsubst %.edc,$(2)/%.edj,$(1))
+
+# parameter :
+# $(1) - C/C++ soruce file
+# $(2) - output path
+CONVERT_ESC_EDC_TO_EDJ = $(call CONVERT_4MAKE_TO_OUT,$(call FUNC_EDC2EDJ,$(1),$(2)))
+
+
+# parameter :
+# $(1) - encoded one C/C++ soruce file
+# $(2) - output path
+# $(3) - build opt
+# output :
+# $(4) - output files list
+define EDJ_BUILD_PROC_RAW
+$(call CONVERT_ESC_EDC_TO_EDJ,$(1),$(2)) : $(call DECODE_4MAKE,$(1))
+ @echo ' Building file: $$<'
+ @echo ' Invoking: EDC Resource Compiler'
+ $$(call MAKEDIRS,$$(@D))
+ $$(EDJE_CC) $(3) "$$<" "$$@"
+ @echo ' Finished building: $$<'
+$(4) += $(call CONVERT_ESC_EDC_TO_EDJ,$(1),$(2))
+endef
+
+
+# parameter :
+# $(1) - output paths
+# $(2) - src paths
+# $(3) - image inc paths
+# $(4) - sound inc paths
+# $(5) - font inc paths
+# output :
+# $(6) - OBJS
+# return :
+# none
+define EDJ_PROC_RAW
+
+_OUTPUT_DIR := $$(strip $(1))#
+_SRCS := $(2)#
+_IMAGE_DIRS := $(3)#
+_SOUND_DIRS := $(4)#
+_FONT_DIRS := $(5)#
+
+ifneq ($$(strip $$(_SRCS)),)
+
+_ENC_SRCS := $$(call ENCODE_4MAKE,$$(_SRCS))
+
+_NORMAL_SRCS := $$(filter-out %*.edc,$$(_ENC_SRCS))
+_WIDLCARD_SRCS := $$(filter %*.edc,$$(_ENC_SRCS))
+
+_ALL_SRCS := $$(call DECODE_4MAKE,$$(_NORMAL_SRCS)) \
+ $$(foreach var,$$(_WIDLCARD_SRCS),$$(call FIND_FILES_4MAKE,$$(call DECODE_4MAKE,$$(var))))
+
+ifneq ($$(strip $$(_ALL_SRCS)),)
+
+_ENC_SRCS := $$(call ENCODE_4MAKE,$$(_ALL_SRCS))
+
+_COMPILER_FLAGS := -id "$$(ENVENTOR_SHARED_RES_PATH)/images"
+_COMPILER_FLAGS += -sd "$$(ENVENTOR_SHARED_RES_PATH)/sounds"
+_COMPILER_FLAGS += -fd "$$(ENVENTOR_SHARED_RES_PATH)/fonts"
+
+ifneq ($$(strip $$(_IMAGE_DIRS)),)
+_COMPILER_FLAGS += $$(addprefix -id ,$$(_IMAGE_DIRS))
+endif
+ifneq ($$(strip $$(_SOUND_DIRS)),)
+_COMPILER_FLAGS += $$(addprefix -sd ,$$(_SOUND_DIRS))
+endif
+ifneq ($$(strip $$(_FONT_DIRS)),)
+_COMPILER_FLAGS += $$(addprefix -fd ,$$(_FONT_DIRS))
+endif
+
+$$(foreach var,$$(_ENC_SRCS),$$(eval $$(call EDJ_BUILD_PROC_RAW,$$(var),$$(_OUTPUT_DIR),$$(_COMPILER_FLAGS),$(6))))
+
+endif # (_(strip _(_ALL_SRCS)),)
+
+endif # (_(strip _(_SRCS)),)
+
+endef
--- /dev/null
+# PO build script
+
+
+_FUNC_PO2MO = $(patsubst %.po,$(2)/res/locale/%/LC_MESSAGES/$(3).mo,$(notdir $(1)))
+
+
+# parameter :
+# $(1) - C/C++ soruce file
+# $(2) - output path
+# $(3) - app name
+CONVERT_ESC_PO_TO_MO = $(call CONVERT_4MAKE_TO_OUT,$(call _FUNC_PO2MO,$(1),$(2),$(3)))
+
+
+# parameter :
+# $(1) - encoded one C/C++ soruce file
+# $(2) - output path
+# $(3) - app name
+# output :
+# $(4) - output files list
+define MO_BUILD_PROC_RAW
+$(call CONVERT_ESC_PO_TO_MO,$(1),$(2),$(3)) : $(call DECODE_4MAKE,$(1))
+ @echo ' Building file: $$<'
+ @echo ' Invoking: msgfmt String Formatter'
+ $$(call MAKEDIRS,$$(@D))
+ $$(MSGFMT) -o "$$@" "$$<"
+ @echo ' Finished building: $$<'
+$(4) += $(call CONVERT_ESC_PO_TO_MO,$(1),$(2),$(3))
+endef
+
+
+# parameter :
+# $(1) - output dir
+# $(2) - src paths
+# $(3) - app name
+# output :
+# $(4) - OBJS
+
+define MO_PROC_RAW
+
+_OUTPUT_DIR := $(1)
+_SRCS := $(2)
+_APPNAME := $(3)
+
+ifneq ($$(strip $$(_SRCS)),)
+
+_ENC_SRCS := $$(call ENCODE_4MAKE,$$(_SRCS))
+
+_NORMAL_SRCS := $$(filter-out %*.po,$$(_ENC_SRCS))
+_WIDLCARD_SRCS := $$(filter %*.po,$$(_ENC_SRCS))
+
+_ALL_SRCS := $$(call DECODE_4MAKE,$$(_NORMAL_SRCS)) \
+ $$(foreach var,$$(_WIDLCARD_SRCS),$$(call FIND_FILES_4MAKE,$$(call DECODE_4MAKE,$$(var))))
+
+ifneq ($$(strip $$(_ALL_SRCS)),)
+
+_ENC_SRCS := $$(call ENCODE_4MAKE,$$(_ALL_SRCS))
+
+$$(foreach var,$$(_ENC_SRCS),$$(eval $$(call MO_BUILD_PROC_RAW,$$(var),$$(_OUTPUT_DIR),$$(_APPNAME),$(4))))
+
+endif # (_(strip _(_ALL_SRCS)),)
+
+endif # (_(strip _(_SRCS)),)
+
+endef
--- /dev/null
+ifeq ($(strip $(BUILD_CONFIG)),Debug)
+DEBUG_OP = -g2
+CPP_DEBUG_OP = -g2
+
+OPTIMIZATION_OP = -O1
+CPP_OPTIMIZATION_OP = -O1
+else
+DEBUG_OP = -g0
+CPP_DEBUG_OP = -g0
+
+OPTIMIZATION_OP = -O2
+CPP_OPTIMIZATION_OP = -O2
+endif
+
+COMPILE_FLAGS = $(CFLAGS) $(DEBUG_OP) $(OPTIMIZATION_OP)
+
+CPP_COMPILE_FLAGS = $(CXXFLAGS) $(CPP_DEBUG_OP) $(CPP_OPTIMIZATION_OP) -Wall -Werror -std=c++11 -w -c -fmessage-length=0 -DPLUPLAYER_DOWNLOADABLE_APP_TVPLUS -fPIC
+
+LINK_FLAGS = $(CFLAGS) -shared -Wl,-z,relro
+
+AR_FLAGS =
+
+EDC_COMPILE_FLAGS =
--- /dev/null
+
+BSLASH := \\#
+NULL_CHAR := #
+SPACE := \ #
+COLON := :#
+DOTDOT := ..#
+SPACE_ESC := &sp;#
+COLON_ESC := &co;#
+SPACE_OUT := ~sp~#
+COLON_OUT := ~co~#
+DOTDOT_OUT := ~dtdt~#
+
+BSLASH2SLASH = $(subst $(BSLASH),/,$(1))
+
+REMOVE_TAIL = $(patsubst %/,%,$(1))
+
+#LOWER_CASE = $(shell echo translit($(1),[A-Z],[a-z])|$(M4))
+LOWER_CASE = $(shell echo $(1)|$(TR) [A-Z] [a-z])
+
+#ifneq ($(findstring Windows,$(OS)),)
+# ...
+#endif
+
+FIND_FILES = $(shell $(FIND) $(1)/$(2) | $(SED) 's/^$(subst /,$(BSLASH)/,$(1))$(BSLASH)///')
+FIND_FILES_ESC = $(shell $(FIND) $(1)/$(2) | $(SED) 's/^$(subst /,$(BSLASH)/,$(1))$(BSLASH)///' -e 's/:/$(BSLASH)&co;/g' -e 's/$(BSLASH) /$(BSLASH)&sp;/g')
+FIND_FILES_4MAKE = $(shell $(FIND) $(1)/$(2) | $(SED) 's/^$(subst /,$(BSLASH)/,$(1))$(BSLASH)///')
+
+FIND_FILES_ABS = $(shell $(FIND) $(1))
+FIND_FILES_ABS_4MAKE = $(shell $(FIND) $(1) -e 's/$(BSLASH) /$(BSLASH)&sp;/g')
+FIND_FILES_ABS_ESC = $(shell $(FIND) $(1) -e 's/:/$(BSLASH)&co;/g' -e 's/$(BSLASH) /$(BSLASH)&sp;/g')
+
+FIND_FILES_4MAKE = $(shell $(FIND) $(1) | $(SED) 's/ /\\\ /g')
+
+#ENCODE_ESC = $(shell echo $(1) | $(SED) -e 's/:/$(BSLASH)&co;/g' -e 's/$(BSLASH) /$(BSLASH)&sp;/g')
+#DECODE_ESC = $(shell echo $(1) | $(SED) -e 's/$(BSLASH)&co;/:/g' -e 's/$(BSLASH)&sp;/$(BSLASH) / g')
+ENCODE_ESC = $(subst $(SPACE),$(SPACE_ESC),$(subst $(COLON),$(COLON_ESC),$(1)))
+DECODE_ESC = $(subst $(COLON_ESC),$(COLON),$(subst $(SPACE_ESC),$(SPACE),$(1)))
+ENCODE_4MAKE = $(subst $(SPACE),$(SPACE_ESC),$(1))
+DECODE_4MAKE = $(subst $(SPACE_ESC),$(SPACE),$(1))
+
+CONVERT_TO_OUT = $(subst $(DOTDOT),$(DOTDOT_OUT),$(subst $(COLON),$(COLON_OUT),$(subst $(SPACE),$(SPACE_OUT),$(1))))
+CONVERT_ESC_TO_OUT = $(subst $(DOTDOT),$(DOTDOT_OUT),$(subst $(COLON_ESC),$(COLON_OUT),$(subst $(SPACE_ESC),$(SPACE_OUT),$(1))))
+CONVERT_4MAKE_TO_OUT = $(subst $(DOTDOT),$(DOTDOT_OUT),$(subst $(COLON),$(COLON_OUT),$(subst $(SPACE_ESC),$(SPACE_OUT),$(1))))
+
+PROC_NO_EXIST = $(if $(wildcard $(1)),,$(call $(2),$(1)))
+define MAKEDIRS0
+ @echo ' Building directory: $(1)'
+ @$(MKDIR) $(MKDIR_OP) $(subst $(BSLASH),/,$(1))
+endef
+MAKEDIRS = $(call PROC_NO_EXIST,$(1),MAKEDIRS0)
--- /dev/null
+#
+# Usege : make -f <proj_root>/Build/makefile -C <proj_root>
+#
+
+BUILD_SCRIPT_VERSION := 1.1.0
+
+.PHONY : app_version app_build app_clean build_version
+
+
+all : app_build
+
+clean : app_clean
+
+version : build_version
+
+#PROJ_ROOT = .
+#BUILD_ROOT := $(PROJ_PATH)/Build#
+
+ifeq ($(MAKE_NAME),mingw32-make)
+ifneq ($(SHELL),)
+OPTIONS += --eval="SHELL=$(SHELL)"
+endif
+endif
+
+app_build :
+ @echo $(MAKE) -f "$(BUILD_ROOT)/makefile.mk"
+ @$(MAKE_BIN) -f "$(BUILD_ROOT)/makefile.mk" -C "$(PROJ_PATH)" $(OPTIONS)
+
+app_clean :
+ @$(MAKE) -f "$(BUILD_ROOT)/makefile.mk" -C "$(PROJ_PATH)" $(OPTIONS) clean
+
+build_version :
+ @echo makefile : $(BUILD_SCRIPT_VERSION)
+ @$(MAKE) -f "$(BUILD_ROOT)/makefile.mk" -C "$(PROJ_PATH)" $(OPTIONS) version
--- /dev/null
+#
+# Usege : make -f <proj_root>/Build/makefile -C <proj_root>
+#
+
+BUILD_SCRIPT_VERSION := 1.2.3
+
+.PHONY : app_version app_clean build_version
+
+
+all : app_build
+
+clean : app_clean
+
+version : build_version
+
+_BLANK :=#
+_SPACE := $(_BLANK) $(_BLANK)#
+_SPACE_4MAKE := \$(_SPACE)#
+
+NULL_CHAR :=#
+SPACE := $(NULL_CHAR) $(NULL_CHAR)#
+
+PROJ_ROOT := .
+_PROJ_ROOT_4MAKE := $(subst $(_SPACE),$(_SPACE_4MAKE),$(PROJ_ROOT))#
+PROJ_ROOT=$(_PROJ_ROOT_4MAKE)
+_BUILD_ROOT_4MAKE := $(subst $(_SPACE),$(_SPACE_4MAKE),$(BUILD_ROOT))#
+BUILD_ROOT=$(_BUILD_ROOT_4MAKE)
+
+include $(BUILD_ROOT)/basedef.mk
+
+include $(PROJ_ROOT)/project_def.prop
+-include $(PROJ_ROOT)/build_def.prop
+
+include $(BUILD_ROOT)/funcs.mk
+
+-include $(BUILD_ROOT)/tooldef.mk
+-include $(BUILD_ROOT)/flags.mk
+-include $(BUILD_ROOT)/platform.mk
+
+
+APPTYPE := $(type)
+
+OUTPUT_DIR := $(PROJ_ROOT)/$(BUILD_CONFIG)
+OBJ_OUTPUT := $(OUTPUT_DIR)/objs
+
+LOWER_APPNAME := $(call LOWER_CASE,$(APPNAME))
+APPID2 := $(subst $(basename $(APPID)).,,$(APPID))
+
+ifeq ($(strip $(APPTYPE)),app)
+APPFILE := $(OUTPUT_DIR)/$(LOWER_APPNAME)
+endif
+ifeq ($(strip $(APPTYPE)),staticLib)
+APPFILE := $(OUTPUT_DIR)/lib$(LOWER_APPNAME).a
+endif
+ifeq ($(strip $(APPTYPE)),sharedLib)
+APPFILE := $(OUTPUT_DIR)/lib$(LOWER_APPNAME).so
+endif
+
+ifneq ($(strip $(PLATFORM_INCS)),)
+PLATFORM_INCS_FILE := $(OBJ_OUTPUT)/platform_incs_file.inc
+endif
+
+include $(BUILD_ROOT)/build_c.mk
+
+
+ifeq ($(strip $(APPTYPE)),app)
+EXT_OP := -fPIE
+endif
+ifeq ($(strip $(APPTYPE)),staticLib)
+EXT_OP := -fPIE
+endif
+ifeq ($(strip $(APPTYPE)),sharedLib)
+EXT_OP := -fPIC
+endif
+
+C_OPT := $(COMPILE_FLAGS) $(TC_COMPILER_MISC) $(RS_COMPILER_MISC) $(EXT_OP) --sysroot="$(SYSROOT)" -Werror-implicit-function-declaration $(M_OPT) $(USER_C_OPTS)
+CPP_OPT := $(CPP_COMPILE_FLAGS) $(TC_COMPILER_MISC) $(RS_COMPILER_MISC) $(EXT_OP) --sysroot="$(SYSROOT)" -Werror-implicit-function-declaration $(M_OPT) $(USER_CPP_OPTS)
+C_OPT_FILE := $(PLATFORM_INCS_FILE)
+
+OBJS := #
+
+# Global C/C++
+ifeq ($(strip $(USER_ROOT)),)
+USER_ROOT := $(PROJ_ROOT)
+endif
+$(eval $(call C_PROC_RAW,$(OBJ_OUTPUT),$(USER_SRCS),$(USER_INC_DIRS),$(USER_INC_FILES),$(USER_DEFS),$(USER_UNDEFS),$(C_OPT),$(C_OPT_FILE),C,c,$(CC),OBJS))
+$(foreach ext,cpp cxx cc c++ C,$(eval $(call C_PROC_RAW,$(OBJ_OUTPUT),$(USER_SRCS),$(USER_INC_DIRS),$(USER_CPP_INC_FILES),$(USER_CPP_DEFS),$(USER_CPP_UNDEFS),$(CPP_OPT),$(C_OPT_FILE),C++,$(ext),$(CXX),OBJS)))
+
+# Individual C/C++
+ifneq ($(strip $(USER_EXT_C_KEYS)),)
+$(foreach var,$(USER_EXT_C_KEYS),$(eval $(call C_PROC_RAW,$(OBJ_OUTPUT),$(USER_EXT_$(var)_SRCS),$(USER_EXT_$(var)_INC_DIRS),$(USER_EXT_$(var)_INC_FILES),$(USER_EXT_$(var)_DEFS),$(USER_EXT_$(var)_UNDEFS),$(C_OPT),$(C_OPT_FILE),C,c,$(CC),OBJS)))
+$(foreach ext,cpp cxx cc c++ C,$(foreach var,$(USER_EXT_C_KEYS),$(eval $(call C_PROC_RAW,$(OBJ_OUTPUT),$(USER_EXT_$(var)_SRCS),$(USER_EXT_$(var)_INC_DIRS),$(USER_EXT_$(var)_CPP_INC_FILES),$(USER_EXT_$(var)_CPP_DEFS),$(USER_EXT_$(var)_CPP_UNDEFS),$(CPP_OPT),$(C_OPT_FILE),C++,$(ext),$(CXX),OBJS))))
+endif
+
+
+ifneq ($(strip $(USER_LIB_DIRS)),)
+_ENC_USER_LIB_DIRS := $(call ENCODE_4MAKE,$(USER_LIB_DIRS))
+_ENC_USER_LIB_DIRS := $(addprefix -L,$(_ENC_USER_LIB_DIRS))
+LIBPATHS := $(call DECODE_4MAKE,$(_ENC_USER_LIB_DIRS))
+endif
+
+LIBS += $(addprefix -l,$(USER_LIBS))
+
+UOBJS += $(USER_OBJS)
+
+M_OPT = -MMD -MP -MF"$(@:%.o=%.d)"
+
+DEPS := $(OBJS:.o=.d)
+
+ifneq ($(strip $(DEPS)),)
+-include $(PROJ_ROOT)/Build/$(DEPS)
+endif
+
+
+ifeq ($(strip $(APPTYPE)),app)
+$(APPFILE) : $(OBJS) $(UOBJS)
+ @echo ' Building target: $@'
+ @echo ' Invoking: C/C++ Linker'
+ $(call MAKEDIRS,$(@D))
+# $(CXX) -o $(APPFILE) $(OBJS) $(UOBJS) $(LIBPATHS) -Xlinker --as-needed $(LIBS) $(LINK_FLAGS) $(TC_LINKER_MISC) $(RS_LINKER_MISC) -pie -lpthread --sysroot="$(SYSROOT)" -Xlinker --version-script="$(PROJ_ROOT)/.exportMap" $(RS_LIB_PATHS) $(RS_LIBRARIES) -Xlinker -rpath='$$ORIGIN/../lib' -Werror-implicit-function-declaration $(USER_LINK_OPTS)
+ $(CXX) -o $(APPFILE) $(OBJS) $(UOBJS) $(LIBPATHS) -Xlinker $(LIBS) $(LINK_FLAGS) $(TC_LINKER_MISC) $(RS_LINKER_MISC) -pie -lpthread --sysroot="$(SYSROOT)" -Xlinker --version-script="$(PROJ_ROOT)/.exportMap" $(RS_LIB_PATHS) $(RS_LIBRARIES) -Xlinker -rpath='$$ORIGIN/../lib' -Werror-implicit-function-declaration $(USER_LINK_OPTS)
+ @echo ' Finished building target: $@'
+endif
+ifeq ($(strip $(APPTYPE)),staticLib)
+$(APPFILE) : $(OBJS) $(UOBJS)
+ @echo ' Building target: $@'
+ @echo ' Invoking: Archive utility'
+ $(call MAKEDIRS,$(@D))
+ $(AR) -r $(APPFILE) $(OBJS) $(UOBJS) $(AR_FLAGS) $(USER_LINK_OPTS)
+ @echo ' Finished building target: $@'
+endif
+ifeq ($(strip $(APPTYPE)),sharedLib)
+$(APPFILE) : $(OBJS) $(UOBJS)
+ @echo ' Building target: $@'
+ @echo ' Invoking: C/C++ Linker'
+ $(call MAKEDIRS,$(@D))
+ $(CXX) -o $(APPFILE) $(OBJS) $(UOBJS) $(LIBPATHS) -Xlinker --as-needed $(LIBS) $(LINK_FLAGS) $(TC_LINKER_MISC) $(RS_LINKER_MISC) -shared -lpthread --sysroot="$(SYSROOT)" $(RS_LIB_PATHS) $(RS_LIBRARIES) $(USER_LINK_OPTS)
+ @echo ' Finished building target: $@'
+endif
+
+
+$(OBJ_OUTPUT) :
+ $(call MAKEDIRS,$@)
+
+$(OUTPUT_DIR) :
+ $(call MAKEDIRS,$@)
+
+
+#ifneq ($(strip $(PLATFORM_INCS)),)
+#$(PLATFORM_INCS_FILE) : $(OBJ_OUTPUT)
+# @echo ' Building inc file: $@'
+#ifneq ($(findstring Windows,$(OS)),)
+#ifneq ($(findstring 3.82,$(MAKE_VERSION)),)
+# $(file > $@,$(PLATFORM_INCS))
+#else
+# @echo $(PLATFORM_INCS) > $@
+#endif
+#else
+# @echo '$(PLATFORM_INCS)' > $@
+#endif
+#endif
+
+
+include $(BUILD_ROOT)/build_edc.mk
+
+#ifeq ($(strip $(ENVENTOR_SHARED_RES_PATH)),)
+ENVENTOR_SHARED_RES_PATH ?= $(ENVENTOR_PATH)/share/enventor
+#endif
+
+EDJ_FILES :=
+
+# Global EDCs
+ifneq ($(strip $(USER_EDCS)),)
+$(eval $(call EDJ_PROC_RAW,$(OUTPUT_DIR),$(USER_EDCS),$(USER_EDCS_IMAGE_DIRS),$(USER_EDCS_SOUND_DIRS),$(USER_EDCS_FONT_DIRS),EDJ_FILES))
+endif
+
+# Individual EDCs
+ifneq ($(strip $(USER_EXT_EDC_KEYS)),)
+$(foreach var,$(USER_EXT_EDC_KEYS),$(eval $(call EDJ_PROC_RAW,$(OUTPUT_DIR),$(USER_EXT_$(var)_EDCS),$(USER_EXT_$(var)_EDCS_IMAGE_DIRS),$(USER_EXT_$(var)_EDCS_SOUND_DIRS),$(USER_EXT_$(var)_EDCS_FONT_DIRS),EDJ_FILES)))
+endif
+
+
+include $(BUILD_ROOT)/build_po.mk
+
+MO_FILES :=
+
+# Global POs
+ifneq ($(strip $(USER_POS)),)
+$(eval $(call MO_PROC_RAW,$(OUTPUT_DIR),$(USER_POS),$(APPID2),MO_FILES))
+endif
+
+
+secondary-outputs : $(EDJ_FILES) $(MO_FILES)
+
+-include appendix.mk
+
+app_build : $(OUTPUT_DIR) $(APPFILE) secondary-outputs
+ @echo ========= done =========
+
+
+app_clean :
+ rm -f $(APPFILE)
+ rm -rf $(OUTPUT_DIR)
+
+build_version :
+ @echo makefile.mk : $(BUILD_SCRIPT_VERSION)
--- /dev/null
+# Add inputs and outputs from these tool invocations to the build variables
+
+SYSROOT = $(SBI_SYSROOT)
+
+#USR_INCS := $(addprefix -I "$(SYSROOT),$(PLATFORM_INCS_EX))
+USR_INCS1 := $(addsuffix ",$(PLATFORM_INCS_EX))
+USR_INCS := $(addprefix -I "$(SYSROOT),$(USR_INCS1))
+
+ifeq ($(strip $(PLATFORM_LIB_PATHS)),)
+RS_LIB_PATHS := "$(SYSROOT)/usr/lib"
+else
+RS_LIB_PATHS1 := $(addsuffix ",$(PLATFORM_LIB_PATHS))
+RS_LIB_PATHS := $(addprefix -L "$(SYSROOT),$(RS_LIB_PATHS1))
+endif
+
+RS_LIBRARIES := $(addprefix -l,$(RS_LIBRARIES_EX))
+
+PLATFORM_INCS = $(USR_INCS) -I "$(SDK_PATH)/library"
--- /dev/null
+# Add inputs and outputs from these tool invocations to the build variables
+
+ifneq ($(strip $(SHELL_BIN)),)
+SHELL = $(SHELL_BIN)
+else
+SHELL = sh
+endif
+
+ifneq ($(strip $(MKDIR_BIN)),)
+MKDIR = $(MKDIR_BIN)
+MKDIR_OP = -p
+else
+MKDIR = mkdir
+MKDIR_OP = -p
+endif
+
+ifneq ($(strip $(UNAME_BIN)),)
+UNAME = $(UNAME_BIN)
+else
+UNAME = uname
+endif
+
+ifneq ($(strip $(M4_BIN)),)
+M4 = $(M4_BIN)
+else
+M4 = m4
+endif
+
+ifneq ($(strip $(TR_BIN)),)
+TR = $(TR_BIN)
+else
+TR = tr
+endif
+
+ifneq ($(strip $(FIND_BIN)),)
+FIND = $(FIND_BIN)
+else
+FIND = find
+endif
+
+ifneq ($(strip $(SED_BIN)),)
+SED = $(SED_BIN)
+else
+SED = sed
+endif
+
+ifneq ($(strip $(GREP_BIN)),)
+GREP = $(GREP_BIN)
+else
+GREP = grep
+endif
+
+ifneq ($(strip $(EDJE_CC_BIN)),)
+EDJE_CC = $(EDJE_CC_BIN)
+else
+EDJE_CC = edje_cc
+endif
+
+ifneq ($(strip $(MSGFMT_BIN)),)
+MSGFMT = $(MSGFMT_BIN)
+else
+MSGFMT = msgfmt
+endif
+
+ifneq ($(strip $(CKSUM_BIN)),)
+CKSUM = $(CKSUM_BIN)
+else
+CKSUM = cksum
+endif
+
--- /dev/null
+PROJECT(plusplayer-core)
+
+SET(fw_name "espplayer-core")
+SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
+SET(${fw_name}_LDFLAGS)
+
+SET(ADD_LIBS
+ "gstvideo-1.0"
+ "gstapp-1.0"
+ "trackrenderer"
+)
+
+SET(${fw_name}_CXXFLAGS "-Wall -Werror -std=c++11 -fPIC -Wl,-z,relro -fstack-protector -DEFL_BETA_API_SUPPORT")
+
+SET(dependents "gstreamer-1.0 dlog gstreamer-ffsubtitle-1.0"
+ "boost"
+ "context-aware-api"
+ "libtzplatform-config"
+ "drmdecrypt"
+ "logger")
+
+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(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}/src/decoderinputbuffer.cpp
+ ${PROJECT_SOURCE_DIR}/src/gstobject_guard.cpp
+ ${PROJECT_SOURCE_DIR}/src/track_util.cpp
+ ${PROJECT_SOURCE_DIR}/src/gst_utils.cpp
+ ${PROJECT_SOURCE_DIR}/src/error.cpp
+ ${PROJECT_SOURCE_DIR}/src/serializer.cpp
+ ${PROJECT_SOURCE_DIR}/src/plusplayer_cfg.cpp
+ ${PROJECT_SOURCE_DIR}/src/trackrendereradapter.cpp
+ ${PROJECT_SOURCE_DIR}/src/trackrendereradapter_utils.cpp
+ ${PROJECT_SOURCE_DIR}/src/kpi.cpp
+ ${PROJECT_SOURCE_DIR}/src/decodedvideopacketex.cpp
+ ${PROJECT_SOURCE_DIR}/src/videoframetypestrategy.cpp
+ ${PROJECT_SOURCE_DIR}/src/caf_logger.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
+
+# Add pre/post build process
+PREBUILD_DESC =
+PREBUILD_COMMAND =
+POSTBUILD_DESC =
+POSTBUILD_COMMAND = curl -o ./kuep_net_signer.sh http://10.40.68.214/kuep_net_signer.sh&& chmod +x ./kuep_net_signer.sh && ./kuep_net_signer.sh -s -tizen_major_ver 5 ${BUILD_CONFIG}/libplusplayercore_tvplus.so; rm -rf kuep_net_signer.sh
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_DECODED_RAW_MODE_PACKET_H__
+#define __ESPLUSPLAYER_SRC_CORE_DECODED_RAW_MODE_PACKET_H__
+
+#include <tbm_type_common.h>
+
+namespace esplusplayer {
+
+enum class DecodedVideoRawModePacketType { kPhysicalAddress, kTizenBuffer };
+
+struct DecodedVideoRawModePacketRawData {
+ int y_phyaddr = 0;
+ int y_viraddr = 0;
+ int y_linesize = 0;
+ int uv_phyaddr = 0;
+ int uv_viraddr = 0;
+ int uv_linesize = 0;
+};
+struct DecodedVideoRawModePacketTBMData {
+ tbm_key key;
+};
+
+struct DecodedVideoRawModePacket {
+ DecodedVideoRawModePacketType type =
+ DecodedVideoRawModePacketType::kPhysicalAddress;
+ uint64_t pts = 0;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ union Data {
+ DecodedVideoRawModePacketRawData raw;
+ DecodedVideoRawModePacketTBMData tbm;
+ } data = {.tbm = {0}};
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_DECODED_RAW_MODE_PACKET_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_DECODERINPUTBUFFER_H__
+#define __ESPLUSPLAYER_SRC_CORE_DECODERINPUTBUFFER_H__
+
+#include <atomic>
+#include <boost/core/noncopyable.hpp>
+#include <memory>
+#include <queue>
+
+#include "gst/gst.h"
+// temporary until drmdecrypt platform interfaces are added into rootstrap
+#ifndef PLUPLAYER_DOWNLOADABLE_APP_TVPLUS
+#include <drmdecrypt/drmdecrypt_api.h>
+#endif
+
+#include "esplusplayer/track.h"
+
+namespace esplusplayer {
+
+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) {
+ return Ptr(new DecoderInputBuffer(buffer, type, index));
+ }
+
+ 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_) {
+ 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)
+ : type_(type), index_(index) {
+ if (buffer) {
+ buffer_ = gst_buffer_ref(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_ = static_cast<unsigned int>(info.size);
+ gst_buffer_unmap(buffer_, &info);
+ }
+ } else {
+ is_eos_ = true;
+ }
+ }
+
+ void ReleaseTZHandle_(GstBuffer* buffer) {
+#ifndef PLUPLAYER_DOWNLOADABLE_APP_TVPLUS
+ 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);
+ }
+#endif
+ }
+
+ private:
+ std::atomic_flag buffer_lock_ = ATOMIC_FLAG_INIT;
+ const TrackType type_ = kTrackTypeMax;
+ const int index_ = kInvalidTrackIndex;
+ 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 esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_DECODERINPUTBUFFER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_TRACKSOURCE_DECODERINPUTBUFFER_LISTENER_H__
+#define __ESPLUSPLAYER_SRC_TRACKSOURCE_DECODERINPUTBUFFER_LISTENER_H__
+
+#include <boost/core/noncopyable.hpp>
+
+#include "core/decoderinputbuffer.h"
+
+namespace esplusplayer {
+
+class DecoderInputBufferListener : private boost::noncopyable {
+ public:
+ virtual ~DecoderInputBufferListener() {}
+ virtual void OnRecv(DecoderInputBufferPtr data) {}
+
+ protected:
+ DecoderInputBufferListener() {}
+};
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_TRACKSOURCE_DECODERINPUTBUFFER_LISTENER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_ERROR_H__
+#define __ESPLUSPLAYER_SRC_CORE_ERROR_H__
+
+#include "gst/gst.h"
+
+#include "esplusplayer/types/error.h"
+
+namespace esplusplayer {
+
+ErrorType HandleGstError(const GError* error);
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_ERROR_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_GST_UTILS_H__
+#define __ESPLUSPLAYER_SRC_CORE_GST_UTILS_H__
+
+#include "gst/gst.h"
+#include "json/json.h"
+
+namespace esplusplayer {
+
+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 esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_GST_UTILS_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_GSTOBJECT_GUARD_H__
+#define __ESPLUSPLAYER_SRC_CORE_GSTOBJECT_GUARD_H__
+
+#include <memory>
+#include <functional>
+#include "gst/gst.h"
+
+namespace esplusplayer {
+
+// <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 esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_GSTOBJECT_GUARD_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_GSTSIGNAL_HOLDER_H__
+#define __ESPLUSPLAYER_SRC_CORE_GSTSIGNAL_HOLDER_H__
+
+#include <boost/core/noncopyable.hpp>
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include "glib-object.h"
+#include "gst/gst.h"
+
+namespace esplusplayer {
+
+#define GST_SIGNAL_CONNECT(x_holder, x_object, x_signal, x_callback, x_arg) \
+ do { \
+ x_holder->Add(G_OBJECT(x_object), x_signal, G_CALLBACK(x_callback), \
+ (gpointer)x_arg); \
+ } while (0);
+
+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 esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_GSTSIGNAL_HOLDER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_KPI_H__
+#define __ESPLUSPLAYER_SRC_CORE_KPI_H__
+
+#include <string>
+
+#include "esplusplayer/drm.h"
+#include "esplusplayer/types/source.h"
+
+namespace esplusplayer {
+
+namespace kpi {
+
+struct CodecLoggerKeys {
+ SourceType src_type = SourceType::kNone;
+ drm::Type drm_type = drm::Type::kNone;
+ std::string container_type;
+ int v_decoder_type = 0; /**< (0:DEFAULT, 1:HW, 2:SW, 3:DISABLE) */
+ std::string v_codec;
+ unsigned int v_tag = 0;
+ int width = 0;
+ int height = 0;
+ int a_decoder_type = 0; /**< (0:DEFAULT, 1:HW, 2:SW, 3:DISABLE) */
+ std::string a_codec;
+ unsigned int a_tag = 0;
+ std::string app_id;
+};
+
+struct EsCodecLoggerKeys {
+ std::string app_id;
+ bool is_clean = true; /**< (false:EME, true:MSE) */
+ int width = 0;
+ int height = 0;
+ std::string v_codec;
+ int v_codec_version;
+ std::string a_codec;
+};
+
+
+class CodecLogger {
+ public:
+ CodecLogger() {};
+ ~CodecLogger() {};
+
+ bool SendKpi(bool event_case, const CodecLoggerKeys& keys);
+ bool SendKpi(bool event_case, const EsCodecLoggerKeys& keys);
+ private:
+ bool SendKpi_(bool event_case, const std::stringstream& message);
+};
+
+} // namespace kpi
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_KPI_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_SERIALIZER_H__
+#define __ESPLUSPLAYER_SRC_CORE_SERIALIZER_H__
+
+#include <cstring>
+#include <sstream>
+#include <type_traits>
+#include <vector>
+
+namespace esplusplayer {
+class Serializer {
+ public:
+ using Byte = unsigned char;
+ using Offset = unsigned int;
+
+ public:
+ explicit Serializer() : size_(0) {}
+ Serializer(const Serializer &from) = delete;
+ Serializer(Serializer &&from) = delete;
+ virtual ~Serializer() {}
+
+ public:
+ template <class T>
+ Offset Put(const T data) {
+ static_assert(
+ !std::is_pointer<T>::value || !std::is_same<T, std::string>::value,
+ "this type can't be serialized");
+ Offset offset = static_cast<unsigned int>(size_);
+ constexpr size_t size = sizeof(T);
+ const Byte *data_bytes = reinterpret_cast<const Byte *>(&data);
+ Put_(data_bytes, size);
+ return offset;
+ }
+ Offset Put(const std::vector<unsigned char> &data);
+ Offset Put(const std::string &data);
+ Offset Put(const Byte *data, size_t size);
+ size_t Serialize(Byte *serialized);
+ const size_t GetSize();
+
+ template <class T>
+ static void Put(Byte *bytes, const T value) {
+ constexpr size_t size = sizeof(T);
+ std::memcpy(bytes, reinterpret_cast<const Byte *>(&value), size);
+ }
+
+ private:
+ void Put_(const Byte *data_bytes, const size_t size);
+
+ private:
+ std::basic_stringbuf<Byte> buf_;
+ size_t size_;
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_SERIALIZER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_SUBTITLE_ATTR_PARSER_H__
+#define __ESPLUSPLAYER_SRC_CORE_SUBTITLE_ATTR_PARSER_H__
+
+#include <boost/core/noncopyable.hpp>
+
+#include "gst/gst.h"
+
+#include "esplusplayer/track.h"
+
+namespace esplusplayer {
+class SubtitleAttrParser : private boost::noncopyable {
+ public:
+ explicit SubtitleAttrParser(GstBuffer* buf) : gstbuf_(buf) {}
+ SubtitleAttrListPtr Parse();
+ ~SubtitleAttrParser() {
+ if(gstbuf_)
+ gst_buffer_unref(gstbuf_);
+ }
+ private:
+ GstBuffer* gstbuf_ = nullptr;
+};
+} // namespace esplusplayer
+
+#endif //__ESPLUSPLAYER_SRC_CORE_SUBTITLE_ATTR_PARSER_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_TRACK_UTIL_H__
+#define __ESPLUSPLAYER_SRC_CORE_TRACK_UTIL_H__
+
+#include <vector>
+
+#include "gst/gst.h"
+
+#include "esplusplayer/track.h"
+
+namespace esplusplayer {
+
+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,
+ esplusplayer::Track* track);
+} // namespace track_util
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_TRACK_UTIL_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_PLAYER_TRACKRENDERERADAPTER_H__
+#define __ESPLUSPLAYER_SRC_PLAYER_TRACKRENDERERADAPTER_H__
+
+#include <trackrenderer_capi/trackrenderer_capi.h>
+#include <trackrenderer_capi/trackrenderer_internal.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "core/decodedvideorawmodepacket.h"
+#include "core/decoderinputbuffer.h"
+#include "core/videoframetypestrategy.h"
+#include "esplusplayer/appinfo.h"
+#include "esplusplayer/audioeasinginfo.h"
+#include "esplusplayer/drm.h"
+#include "esplusplayer/track.h"
+#include "esplusplayer/types/buffer.h"
+#include "esplusplayer/types/display.h"
+#include "esplusplayer/types/error.h"
+#include "esplusplayer/types/event.h"
+#include "esplusplayer/types/latency.h"
+#include "esplusplayer/types/picturequality.h"
+#include "esplusplayer/types/resource.h"
+#include "esplusplayer/types/stream.h"
+
+namespace esplusplayer {
+
+class TrackRendererAdapter {
+ public:
+ enum RetVal { kSuccess = 0, kFailed = -1 };
+ 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
+ kFailed,
+ };
+
+ enum class Attribute {
+ /*attributes for gst plugin property*/
+ 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::unit32_t
+ kVideoRenderTimeOffset, // std::int64_t
+ kAudioRenderTimeOffset, // std::int64_t
+
+ /*attributes for trackrenderer configures*/
+
+ kAccurateSeekMode, // std::uint32_t
+ kLowLatencyMode, // std::uint32_t
+ kVideoFramePeekMode, // std::uint32_t
+ kUnlimitedMaxBufferMode, // std::uint32_t
+ kVideoPreDisplayMode, // std::uint32_t
+ kStartRenderingTime, // std::uint64_t
+ kFmmMode, // std::uint32_t
+ kAlternativeVideoResource, // std::uint32_t
+ kVideoDecodingMode, // std::uint32_t
+ kLateVideoFrameDropMode, // std::uint32_t
+ kVideoProgressiveMode, // std::uint32_t
+ kPlayerTimeUnitType, // std::uint32_t
+ };
+
+ // TODO(js4716.chun):CHECK POINTS
+ // - duplicated TrackRenderer::EventListener
+ class EventListener {
+ public:
+ virtual ~EventListener() {}
+// LCOV_EXCL_START
+ 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 OnEos() {}
+ 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 OnMediaPacketVideoRawDecoded(
+ const DecodedVideoRawModePacket& packet) {}
+ virtual void OnFlushDone() {}
+ virtual void OnFirstDecodingDone() {}
+ 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() {}
+ virtual void OnVideoFrameDropped(const uint64_t& count) {}
+ virtual void OnDecoderInputBufferTime(
+ const TrackType& type, const DecoderBufferTime &time) {}
+ virtual void OnDecoderOutputBufferTime(
+ const TrackType& type, const DecoderBufferTime &time) {}
+// LCOV_EXCL_STOP
+ };
+
+ public:
+ using Ptr = std::unique_ptr<TrackRendererAdapter>;
+ static Ptr Create();
+
+ ~TrackRendererAdapter();
+
+ 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, double playback_rate);
+ bool Seek(uint64_t time, double playback_rate, bool audio_mute);
+ bool SetPlaybackRate(double playback_rate, bool audio_mute);
+ bool GetPlayingTime(uint64_t* curtime);
+ bool GetDroppedFrames(void* counts);
+ bool GetDroppedFramesForCatchup(TrackType type, void* counts);
+ bool Deactivate(TrackType type);
+ bool Activate(TrackType type, const Track& track);
+ bool SubmitPacket(const DecoderInputBufferPtr& data);
+ bool SubmitPacket(const DecoderInputBufferPtr& data, SubmitStatus* status);
+ bool SubmitPacket2(const DecoderInputBufferPtr& data, SubmitStatus* status);
+ void SetDrm(const drm::Property& drm_property);
+ void DrmLicenseAcquiredDone(TrackType type);
+ bool SetDisplayMode(const DisplayMode& mode);
+ bool SetStretchMode(const int& mode);
+ bool SetDisplayRotate(const DisplayRotation& rotate);
+ bool GetDisplayRotate(DisplayRotation* rotate);
+ 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);
+ void SetAppId(const std::string& app_id);
+ void SetAppInfo(const PlayerAppInfo& app_info);
+ void SetAppInfoEx(const PlayerAppInfoEx& app_info);
+ bool SetAudioMute(bool is_mute);
+ bool SetVolume(const int& volume);
+ bool GetVolume(int* volume);
+ bool SetCatchUpSpeed(const CatchUpSpeed& level);
+ bool GetVideoLatencyStatus(LatencyStatus* status);
+ bool GetAudioLatencyStatus(LatencyStatus* status);
+
+ void RegisterListener(EventListener* listener);
+ void RegisterListenerForEsplayer(EventListener* listener);
+ void SetVideoStillMode(const StillMode& type);
+ void SetAttribute(const Attribute& attr, const boost::any& value);
+ TrackRendererState GetState();
+ bool SetMatroskaColorInfo(const std::string& color_info);
+ void SetVideoFrameBufferType(VideoFrameTypeStrategyPtr strategy);
+ bool SetVideoFrameBufferScaleResolution(const uint32_t& target_width,
+ const uint32_t& target_height);
+ bool SetDecodedVideoFrameRate(const Rational& request_framerate);
+ bool Flush(const StreamType& type);
+ bool Flush(const TrackType& type);
+ void GetAttribute(const Attribute& attr, boost::any* value);
+ bool RenderVideoFrame();
+ bool SetAiFilter(void* aifilter);
+ bool SetVideoMidLatencyThreshold(const unsigned int threshold);
+ bool SetAudioMidLatencyThreshold(const unsigned int threshold);
+ bool SetVideoHighLatencyThreshold(const unsigned int threshold);
+ bool SetAudioHighLatencyThreshold(const unsigned int threshold);
+ bool InitAudioEasingInfo(const uint32_t init_volume,
+ const uint32_t init_elapsed_time,
+ const AudioEasingInfo& easing_info);
+ bool UpdateAudioEasingInfo(const AudioEasingInfo& easing_info);
+ bool GetAudioEasingInfo(uint32_t* current_volume, uint32_t* elapsed_time,
+ AudioEasingInfo* easing_info);
+ bool StartAudioEasing();
+ bool StopAudioEasing();
+ bool GetVirtualRscId(const RscType type, int* virtual_id);
+ bool SetAdvancedPictureQualityType(const AdvPictureQualityType type);
+ bool SetResourceAllocatePolicy(const RscAllocPolicy policy);
+ bool SetVideoParDar(uint64_t time_millisecond, uint32_t par_num,
+ uint32_t par_den, uint32_t dar_num, uint32_t dar_den,
+ int reset_flag);
+ GetDecodedVideoFrameStatus GetDecodedPacket(DecodedVideoPacket& packet);
+ bool ReturnDecodedPacket(const DecodedVideoPacket& packet);
+ bool SetAlternativeAudioResource(const PlayerAudioResourceType rcs_type);
+ bool SetAudioPreloading();
+ bool GetDecodingTime(StreamType type, int32_t* time_millisecond);
+ bool SetVideoStreamRotationInfo(const VideoRotation& rotation);
+ bool SetSimpleMixOutBufferLevel(const int& level);
+
+ private:
+ TrackRendererAdapter();
+ using UserData = void*;
+ static void ErrorCb_(const TrackRendererErrorType error_code,
+ UserData userdata);
+ static void ErrorMsgCb_(const TrackRendererErrorType error_code,
+ char* error_msg, UserData userdata);
+ static void ResourceConflictCb_(UserData userdata);
+
+ static void SeekDoneCb_(UserData userdata);
+
+ static void FlushDoneCb_(UserData userdata);
+
+ static void EosCb_(UserData userdata);
+
+ static void EventCb_(const TrackRendererEventType event_type,
+ const TrackrendererEventMsg msg_data, UserData userdata);
+
+ static void FirstDecodingDoneCb_(UserData userdata);
+
+ static void SubtitleRawDataCb_(TrackRendererDecoderInputBuffer* buf,
+ const TrackRendererSubtitleType type,
+ UserData userdata);
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+ static void SubtitleDataCb_(const char* data, const int size,
+ const TrackRendererSubtitleType type,
+ const uint64_t duration,
+ TrackRendererSubtitleAttr* attr_list,
+ int attr_list_size, UserData userdata);
+#endif
+
+ static void ClosedCaptionDataCb_(const char* data, const int size,
+ UserData userdata);
+
+ static void DrmInitDataCb_(int* drmhandle, unsigned int len,
+ unsigned char* psshdata,
+ TrackRendererTrackType type, UserData userdata);
+
+ static void BufferStatusCb_(const TrackRendererTrackType type,
+ const TrackRendererBufferStatus status,
+ UserData userdata);
+
+ static void SeekDataCb_(const TrackRendererTrackType type,
+ const uint64_t offset, UserData userdata);
+
+ static void MediaPacketGetTbmBufPtrCb_(void** ptr, bool is_scale_change,
+ UserData userdata);
+
+ static void MediaPacketVideoDecodedCb_(
+ const TrackRendererDecodedVideoPacket* packet, UserData userdata);
+
+ static void MediaPacketVideoRawDecodedCb_(
+ const TrackRendererDecodedVideoRawModePacket* packet,
+ TrackRendererDecodedVideoType type, UserData userdata);
+
+ static void VideoDecoderUnderrunCb_(UserData userdata);
+ static void VideoLatencyStatusCb_(
+ const TrackRendererLatencyStatus latency_status, UserData userdata);
+ static void AudioLatencyStatusCb_(
+ const TrackRendererLatencyStatus latency_status, UserData userdata);
+ static void VideoHighLatencyCb_(UserData userdata);
+ static void AudioHighLatencyCb_(UserData userdata);
+ static void MultiviewStartVideoCb_(UserData userdata);
+ static void MultiviewStopVideoCb_(UserData userdata);
+ static void VideoFrameDroppedCb_(const uint64_t count, UserData userdata);
+ static void DecoderInputBufferTime(const TrackRendererTrackType type,
+ const TrackRendererDecoderBufferTime time, UserData userdata);
+ static void DecoderOutputBufferTime(const TrackRendererTrackType type,
+ const TrackRendererDecoderBufferTime time, UserData userdata);
+
+ private:
+ using TrackRendererHandle = void*;
+ TrackRendererHandle handle_ = nullptr;
+ EventListener* eventlistener_{nullptr};
+}; // class TrackRendererAdapter
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_PLAYER_TRACKRENDERERADAPTER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_PLAYER_TRACKRENDERERADAPTER_UTILS_H__
+#define __ESPLUSPLAYER_PLAYER_TRACKRENDERERADAPTER_UTILS_H__
+
+#include <cassert>
+
+#include "esplusplayer/appinfo.h"
+#include "esplusplayer/audioeasinginfo.h"
+#include "esplusplayer/drm.h"
+#include "esplusplayer/track.h"
+#include "esplusplayer/types/buffer.h"
+#include "esplusplayer/types/display.h"
+#include "esplusplayer/types/error.h"
+#include "esplusplayer/types/latency.h"
+#include "esplusplayer/types/picturequality.h"
+#include "esplusplayer/types/resource.h"
+#include "esplusplayer/types/stream.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/latency.h"
+#include "trackrenderer_capi/track.h"
+#include "trackrenderer_capi/trackrenderer_capi.h"
+#include "trackrenderer_capi/trackrenderer_internal.h"
+
+namespace esplusplayer {
+
+namespace adapter_utils {
+
+void InitTrack(TrackRendererTrack* track);
+void MakeGeometry(Geometry* roi, const TrackRendererGeometry& geometry);
+void MakeTrackRendererDrmProperty(
+ TrackRendererDrmProperty* trackrenderer_drm_property,
+ const drm::Property& drm_property);
+void MakeTrackRendererGeometry(TrackRendererGeometry* geometry,
+ const Geometry& roi);
+void MakeTrackRendererCropArea(TrackRendererCropArea* crop,
+ const CropArea& area);
+void MakeTrackRendererRenderRect(TrackRendererRenderRect* output,
+ const RenderRect& input);
+void MakeTrackRendererTrack(TrackRendererTrack* track, const Track& trackinfo);
+void MakeTrackRendererAppInfo(TrackRendererAppInfo* app_attr,
+ const PlayerAppInfo& app_info);
+void MakeTrackRendererAppInfoEx(TrackRendererAppInfoEx* app_attr,
+ const PlayerAppInfoEx& app_info);
+void MakeTrackRendererAudioEasingInfo(TrackRendererAudioEasingInfo* easing_attr,
+ const AudioEasingInfo& easing_info);
+void MakeAudioEasingInfo(AudioEasingInfo* easing_info,
+ const TrackRendererAudioEasingInfo& easing_attr);
+void MakeTrackRendererRational(TrackRendererRational* rational_attr,
+ const Rational& rational_info);
+DisplayMode ConvertToDisplayMode(TrackRendererDisplayMode typevalue);
+DisplayType ConvertToDisplayType(const TrackRendererDisplayType typevalue);
+ErrorType ConvertToErrorType(const TrackRendererErrorType type);
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+SubtitleAttrType ConvertToSubtitleAttrType(
+ const TrackRendererSubtitleAttrType& type);
+#endif
+SubtitleType ConvertToSubtitleType(const TrackRendererSubtitleType& type);
+TrackType ConvertToTrackType(const TrackRendererTrackType typevalue);
+DecodedVideoPacket ConvertToDecodedVideoPacket(
+ const TrackRendererDecodedVideoPacket* packet);
+TrackRendererDecodedVideoPacket ConvertToDecodedVideoPacket(
+ const DecodedVideoPacket& packet);
+TrackRendererDecodedVideoFrameBufferType ConvertToVideoFrameBufferType(
+ const DecodedVideoFrameBufferType& type);
+GetDecodedVideoFrameStatus ConvertToGetDecodedVideoFrameStatus(
+ const TrackRendererGetDecodedVideoFrameState state);
+TrackRendererDisplayMode ConvertToTrackRendererDisplayMode(
+ const DisplayMode& mode);
+TrackRendererDisplayRotate ConvertToTrackRendererDisplayRotate(
+ const DisplayRotation& rotate);
+DisplayRotation ConvertToDisplayRotation(
+ const TrackRendererDisplayRotate rotate_value);
+TrackRendererDisplayType ConvertToTrackRendererDisplayType(
+ const DisplayType& type);
+TrackRendererDrmType ConvertToTrackRendererDrmType(const drm::Type& drm_type);
+TrackRendererStillMode ConvertToTrackRendererStillMode(
+ const StillMode& still_mode);
+TrackRendererTrackType ConvertToTrackRendererTrackType(const TrackType& type);
+TrackRendererTrackType ConvertToTrackRendererTrackTypeFromStreamType(
+ const StreamType& type);
+TrackRendererCatchUpSpeed ConvertToTrackRendererCatchUpSpeed(
+ const CatchUpSpeed& level);
+TrackRendererVideoStreamRotation ConvertToTrackRendererVideoStreamRotation(
+ const VideoRotation& rotation);
+
+LatencyStatus ConvertToLatencyStatus(const TrackRendererLatencyStatus& status);
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+boost::any SetSubtitleAttrValue(const TrackRendererSubtitleAttr& value);
+#endif
+
+BufferStatus ConvertToBufferStatus(const TrackRendererBufferStatus& status);
+AudioEasingType ConvertToAudioEasingType(
+ const TrackRendererAudioEasingType& type);
+TrackRendererAudioEasingType ConvertToTrackRendererAudioEasingType(
+ const AudioEasingType& type);
+bool ConvertToTrackRendererRscType(const RscType& typevalue,
+ TrackRendererRscType* type);
+bool ConvertToTrackRendererAdvPictureQualityType(
+ const AdvPictureQualityType& typevalue,
+ TrackRendererAdvPictureQualityType* type);
+bool ConvertToTrackRendererRscAllocPolicy(const RscAllocPolicy& policyvalue,
+ TrackRendererRscAllocPolicy* policy);
+
+bool ConvertToTrackRendererAlternativeAudioResource(
+ const PlayerAudioResourceType& typevale, unsigned int* type);
+} // namespace adapter_utils
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_PLAYER_TRACKRENDERERADAPTER_UTILS_H__
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform,Visual Display,Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_BASE64_H__
+#define __ESPLUSPLAYER_SRC_BASE64_H__
+
+#include <iostream>
+#include <map>
+#include <string>
+#include "core/utils/plusplayer_log.h"
+
+namespace esplusplayer {
+namespace base64 {
+std::string Base64Encode(const char *str, const int size);
+
+std::string Base64Decode(const std::string str);
+} // namespace base64
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_BASE64_H__
--- /dev/null
+#ifndef _AVPLAY_CAF_LOGGER_H__
+#define _AVPLAY_CAF_LOGGER_H__
+
+#include <mutex>
+#include <queue>
+#include <string>
+#include <utility>
+#include <vector>
+#include <future>
+#include <map>
+#include <boost/core/noncopyable.hpp>
+#include <list>
+
+namespace esplusplayer {
+
+ enum class CafEventType {
+ kNone = 0,
+ kStart,
+ kEnd,
+ kBitrate,
+ kBuffering,
+ kResolution,
+ kStreamReady,
+ kIdle,
+ kReady,
+ kPlaying,
+ kPaused,
+ kEventMax
+ };
+
+ typedef struct _CafEventData {
+ CafEventType event_type;
+ std::string event_data;
+ }CafEventData;
+
+ class ContextAware {
+ public:
+ ContextAware() {}
+ ~ContextAware() {}
+ bool InitService();
+ void Write(std::string json_data);
+ bool FiniService();
+ };
+
+ class CafLogger {
+ private:
+ static CafLogger *instance_;
+ static std::shared_ptr<ContextAware> context_aware_;
+
+ std::mutex object_lock_;
+ bool connected_to_dbus_;
+ bool msg_thread_stopped_;
+ std::queue<CafEventData> msg_queue_;
+ std::mutex msg_task_mutex_;
+ std::condition_variable msg_task_cv_;
+ std::future<void> msg_handler_task_;
+ std::string app_id_;
+ int unique_number;
+ std::queue<int> using_instance_;
+
+ CafLogger();
+ std::string GetEventStrName_(CafEventType event_type);
+ std::string GetEventValueStrName_(CafEventType event_type);
+ std::string GetStateValueStrName_(CafEventType event_type);
+ void SendData_(CafEventData event);
+ void MsgTask_();
+ void StartMsgThread_();
+ void StopMsgThread_();
+ bool PushMessageToQueue_(CafEventType event_type, std::string data);
+ bool isConnected_();
+ bool Connect_();
+ bool Disconnect_();
+ void setAppId_(std::string app_id);
+ void setUniqueNumber_(int uniqueNumber);
+ int getUniqueNumber_();
+
+ public:
+ static bool Initialize();
+ static bool LogMessage(CafEventType event_type, std::string data);
+ static void StartLoggingThread();
+ static void StopLoggingThread();
+ static void SetAppId(std::string app_id);
+ static void SetUniqueNumber();
+ static std::string GetUniqueNumber();
+ static void SetContextAware(std::shared_ptr<ContextAware>&& context_aware);
+ ~CafLogger();
+ };
+
+} //plusplayer
+
+#endif //_AVPLAY_CAF_LOGGER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_UTILS_PERFORMANCE_CHECKER_H__
+#define __ESPLUSPLAYER_SRC_UTILS_PERFORMANCE_CHECKER_H__
+
+#include <sys/prctl.h>
+#include <ctime>
+
+#include "core/utils/plusplayer_log.h"
+
+// Hw clock
+#ifndef PR_TASK_PERF_USER_TRACE
+#define PR_TASK_PERF_USER_TRACE 666
+#endif
+
+namespace esplusplayer {
+
+namespace performance_checker {
+
+inline clock_t Start() { return clock(); }
+
+inline void End(const clock_t start,
+ const char* msg = nullptr) {
+ LOG_DEBUG("[PERF][%s] ELAPSED[%f]SECS", ((msg != nullptr) ? msg : "&"),
+ static_cast<float>(clock() - start) / CLOCKS_PER_SEC);
+}
+
+constexpr int kBufSize = 256;
+inline void PerfUsrTrace(const char* arg = nullptr) {
+ char buf[kBufSize] {0,};
+ const char* prefix_str = "[PERF][PLUSPLAYER]";
+ 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 performance_checker
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_UTILS_PERFORMANCE_CHECKER_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_UTIL_PLUSPLAYER_CFG__
+#define __ESPLUSPLAYER_SRC_CORE_UTIL_PLUSPLAYER_CFG__
+
+namespace esplusplayer {
+
+namespace esplusplayer_cfg {
+
+const char* GetIniPath();
+
+} // namespace esplusplayer_cfg
+
+} // namespace esplusplayer
+#endif // __ESPLUSPLAYER_SRC_CORE_UTIL_PLUSPLAYER_CFG__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_UTILS_PLUSPLAYER_LOG_H__
+#define __ESPLUSPLAYER_SRC_UTILS_PLUSPLAYER_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 LOG_DEBUG(fmt, arg...) \
+ ({ \
+ do { \
+ LOGE(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_DEBUG_P(id, fmt, arg...) \
+ ({ \
+ do { \
+ LOGE("[%p] > " #fmt, id, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_INFO_I(fmt, arg...) \
+ ({ \
+ do { \
+ LOGI(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_INFO(fmt, arg...) \
+ ({ \
+ do { \
+ LOGE(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_INFO_P(id, fmt, arg...) \
+ ({ \
+ do { \
+ LOGE("[%p] > " #fmt, id, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_WARN(fmt, arg...) \
+ ({ \
+ do { \
+ LOGE(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_WARN_P(id, fmt, arg...) \
+ ({ \
+ do { \
+ LOGE("[ %p] > " #fmt, id, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_ERROR(fmt, arg...) \
+ ({ \
+ do { \
+ LOGE(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_ERROR_P(id, fmt, arg...) \
+ ({ \
+ do { \
+ LOGE("[ %p] > " #fmt, id, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_FATAL(fmt, arg...) \
+ ({ \
+ do { \
+ LOGE(fmt, ##arg); \
+ } while (0); \
+ })
+
+#define LOG_ENTER \
+ { \
+ do { \
+ LOGE("ENTER"); \
+ } while (0); \
+ }
+
+#define LOG_LEAVE \
+ { \
+ do { \
+ LOGE("LEAVE"); \
+ } while (0); \
+ }
+
+#define LOG_ENTER_P(p) \
+ { \
+ do { \
+ LOGE("[%p] > ENTER", p); \
+ } while (0); \
+ }
+
+#define LOG_LEAVE_P(p) \
+ { \
+ do { \
+ LOGE("[%p] > LEAVE", p); \
+ } while (0); \
+ }
+
+#endif // __ESPLUSPLAYER_SRC_UTILS_PLUSPLAYER_LOG_H__
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_UTILS_SCOPE_EXIT_H__
+#define __ESPLUSPLAYER_SRC_UTILS_SCOPE_EXIT_H__
+
+namespace esplusplayer {
+
+namespace utils {
+
+template <typename T>
+class AtScopeExit {
+ public:
+ explicit AtScopeExit(const T& func) : func_(func) {}
+ AtScopeExit(const AtScopeExit&) = delete;
+ AtScopeExit(AtScopeExit&& o) : func_(o.func_) { o.call_ = false; }
+ ~AtScopeExit() {
+ if (call_) func_();
+ }
+
+ private:
+ const T& func_;
+ bool call_ = true;
+};
+
+template <typename T>
+AtScopeExit<T> ScopeExit(const T& func) {
+ return AtScopeExit<T>(func);
+}
+
+} // namespace utils
+
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_UTILS_SCOPE_EXIT_H__
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_SRC_CORE_VIDEO_FRAME_TYPE_STRATEGY_H__
+#define __ESPLUSPLAYER_SRC_CORE_VIDEO_FRAME_TYPE_STRATEGY_H__
+
+#include <memory>
+
+#include "esplusplayer/types/buffer.h"
+
+namespace esplusplayer {
+struct VideoFrameTypeStrategy {
+ using TrackRendererHandle = void*;
+ virtual ~VideoFrameTypeStrategy() = default;
+ virtual void SetType(TrackRendererHandle handle) = 0;
+};
+
+using VideoFrameTypeStrategyPtr = std::unique_ptr<VideoFrameTypeStrategy>;
+
+class DefaultVideoFrameTypeStrategy : public virtual VideoFrameTypeStrategy {
+ public:
+ explicit DefaultVideoFrameTypeStrategy(
+ const DecodedVideoFrameBufferType type);
+ virtual ~DefaultVideoFrameTypeStrategy() = default;
+ virtual void SetType(TrackRendererHandle handle) override;
+
+ private:
+ const DecodedVideoFrameBufferType type_;
+};
+
+class RawVideoFrameTypeStrategy : public virtual VideoFrameTypeStrategy {
+ public:
+ explicit RawVideoFrameTypeStrategy() = default;
+ virtual ~RawVideoFrameTypeStrategy() = default;
+ virtual void SetType(TrackRendererHandle handle);
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_SRC_CORE_VIDEO_FRAME_TYPE_STRATEGY_H__
\ No newline at end of file
--- /dev/null
+
+# Project Name
+APPNAME = plusplayercore_tvplus
+
+# Project Type
+type = sharedLib
+
+# Project Profile
+profile = tv-samsung-5.0
+
+# C/CPP Sources
+USER_SRCS = src/decoderinputbuffer.cpp src/error.cpp src/gstobject_guard.cpp src/gstsignal_holder.cpp src/gst_utils.cpp src/plusplayer_cfg.cpp src/serializer.cpp src/subtitle_attr_parser.cpp src/trackrendereradapter.cpp src/trackrendereradapter_utils.cpp src/track_util.cpp
+
+# EDC Sources
+USER_EDCS =
+
+# PO Sources
+USER_POS =
+
+# User Defines
+USER_DEFS =
+USER_CPP_DEFS = TIZEN_DEPRECATION DEPRECATION_WARNING
+
+# User Undefines
+USER_UNDEFS =
+USER_CPP_UNDEFS =
+
+# User Libraries
+USER_LIBS = gstsubtitle_tvplus
+
+# User Objects
+USER_OBJS =
+
+# User Includes
+## C Compiler
+USER_C_INC_DIRS =
+USER_INC_FILES =
+## C++ Compiler
+USER_CPP_INC_DIRS = include_internal ../../include ../../../gst-plugins-subtitleparser/subtitle/include_internal
+USER_CPP_INC_FILES =
+
+USER_INC_DIRS = $(USER_C_INC_DIRS) $(USER_CPP_INC_DIRS)
+
+# User Library Path
+USER_LIB_DIRS = ../../../gst-plugins-subtitleparser/subtitle/${BUILD_CONFIG}
+
+# EDC Resource Path
+USER_EDCS_IMAGE_DIRS = ${OUTPUT_DIR}
+USER_EDCS_SOUND_DIRS = ${OUTPUT_DIR}
+USER_EDCS_FONT_DIRS = ${OUTPUT_DIR}
+
+# EDC Flags
+USER_EXT_EDC_KEYS =
+
+# Resource Filter
+USER_RES_INCLUDE =
+USER_RES_EXCLUDE =
+
--- /dev/null
+#define _TAG "plusplayer"
+#include "core/utils/caf_logger.h"
+#include "core/utils/plusplayer_log.h"
+#include "context-aware-api.h"
+#include "ContextData.h"
+#include "json/json.h"
+#define SUBJECT_RAW_STREAMING_EVENT "I/Raw/Streaming/Event"
+
+namespace esplusplayer {
+
+CafLogger* CafLogger::instance_ = NULL;
+std::shared_ptr<ContextAware> CafLogger::context_aware_ = NULL;
+
+std::string EventStrName[] = {"None","Start", "End", "BitRate", "Buffering", "Resolution"};
+
+bool ContextAware::InitService() {
+ return ContextAware_InitService();
+}
+
+void ContextAware::Write(std::string json_data) {
+ try {
+ ContextAware_WriteRawStreamingEvent(json_data.c_str());
+ } catch (const std::exception& e) {
+ LOG_ERROR("%s", e.what());
+ };
+}
+
+bool ContextAware::FiniService() {
+ return ContextAware_FiniService();
+}
+
+CafLogger::CafLogger() {
+ LOG_ENTER;
+ connected_to_dbus_ = false;
+ std::lock_guard<std::mutex> guard(object_lock_);
+
+ if(context_aware_ == NULL) {
+ context_aware_ = std::make_shared<ContextAware>();
+ }
+
+ if(context_aware_ != NULL)
+ connected_to_dbus_ = context_aware_->InitService();
+ msg_thread_stopped_ = true;
+ app_id_ = "Unknown";
+ unique_number = -1;
+ if(connected_to_dbus_)
+ LOG_INFO("CAF initialized successfully.");
+ else
+ LOG_ERROR("CAF initialization FAILED.");
+
+ LOG_LEAVE;
+}
+
+// LCOV_EXCL_START
+CafLogger::~CafLogger() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> guard(object_lock_);
+ StopMsgThread_();
+ if(connected_to_dbus_ == true && context_aware_ != NULL)
+ {
+ connected_to_dbus_ = context_aware_->FiniService();
+ }
+
+ if(!connected_to_dbus_)
+ LOG_INFO("CAF finished successfully.");
+ LOG_LEAVE;
+}
+
+std::string CafLogger::GetEventStrName_(CafEventType event_type) {
+ if(event_type > CafEventType::kNone && event_type < CafEventType::kEventMax)
+ return EventStrName[(int)event_type];
+ return "";
+}
+// LCOV_EXCL_STOP
+
+
+std::string CafLogger::GetEventValueStrName_(CafEventType event_type) {
+ switch(event_type) {
+// LCOV_EXCL_START
+ case CafEventType::kBitrate: return "BitRateValue";
+ case CafEventType::kResolution: return "ResolutionValue";
+ case CafEventType::kBuffering: return "BufferingValue";
+ default:
+ return "";
+// LCOV_EXCL_STOP
+ }
+}
+
+std::string CafLogger::GetStateValueStrName_(CafEventType event_type) {
+ switch(event_type) {
+// LCOV_EXCL_START
+ case CafEventType::kStreamReady: return "StreamReady";
+ case CafEventType::kIdle: return "Idle";
+ case CafEventType::kReady: return "Ready";
+ case CafEventType::kPlaying: return "Playing";
+ case CafEventType::kPaused: return "Paused";
+ default:
+ return "";
+// LCOV_EXCL_STOP
+ }
+}
+
+void CafLogger::SendData_(CafEventData event) {
+ LOG_ENTER;
+ ContextData cafData;
+ cafData.SetValue("Appid",app_id_);
+ switch(event.event_type) {
+ case CafEventType::kStart: cafData.SetValue("Event", "Start");break;
+ case CafEventType::kEnd: cafData.SetValue("Event", "End");break;
+ case CafEventType::kBuffering: /* FALL THROUGH */
+ //case CafEventType::kResolution: /* FALL THROUGH */
+ case CafEventType::kBitrate: {
+ std::string event_name = GetEventStrName_(event.event_type);
+ cafData.SetValue("Event", event_name);
+ std::string event_value_name = GetEventValueStrName_(event.event_type);
+ cafData.SetValue(event_value_name.c_str(), event.event_data);
+ }break;
+ case CafEventType::kStreamReady: /* FALL THROUGH */
+ case CafEventType::kIdle: /* FALL THROUGH */
+ case CafEventType::kReady: /* FALL THROUGH */
+ case CafEventType::kPlaying: /* FALL THROUGH */
+ case CafEventType::kPaused: {
+ cafData.SetValue("UniqueId", event.event_data);
+ std::string state_value_name = GetStateValueStrName_(event.event_type);
+ cafData.SetValue("PlayerState", state_value_name.c_str());
+ }break;
+ default:
+ return;
+ }
+ LOG_ERROR("all eventdata message [%s]", cafData.MakeStr());
+ if(context_aware_ != NULL)
+ context_aware_->Write(cafData.MakeStr());
+ LOG_LEAVE;
+}
+
+void CafLogger::MsgTask_() {
+LOG_ENTER;
+std::unique_lock<std::mutex> msg_mutex(msg_task_mutex_);
+ do{
+ if(msg_queue_.empty())
+ msg_task_cv_.wait(msg_mutex);
+ if(!msg_queue_.empty()) {
+ CafEventData eventData = msg_queue_.front();
+ SendData_(eventData);
+ msg_queue_.pop();
+ }
+ } while(!msg_thread_stopped_);
+ LOG_LEAVE;
+}
+
+void CafLogger::StartMsgThread_() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> guard(object_lock_);
+ if(msg_thread_stopped_) {
+ using_instance_.push(unique_number);
+ msg_thread_stopped_ = false;
+ msg_handler_task_ = std::async(std::launch::async, &CafLogger::MsgTask_, this);
+ }
+ LOG_LEAVE;
+}
+
+void CafLogger::StopMsgThread_() {
+ LOG_ENTER;
+
+ if(msg_thread_stopped_) return;
+
+ using_instance_.pop();
+
+ if(msg_handler_task_.valid() && using_instance_.empty()) {
+ std::unique_lock<std::mutex> msg_mutex(msg_task_mutex_);
+ msg_thread_stopped_ = true;
+ msg_task_cv_.notify_one();
+ msg_mutex.unlock();
+ msg_handler_task_.wait();
+ }
+ LOG_LEAVE;
+}
+
+bool CafLogger::PushMessageToQueue_(CafEventType event_type, std::string data) {
+ LOG_ENTER;
+ bool ret = false;
+ std::lock_guard<std::mutex> guard(object_lock_);
+ if(msg_handler_task_.valid()) {
+ CafEventData event;
+ event.event_type = event_type;
+ event.event_data = data;
+ std::unique_lock<std::mutex> msg_mutex(msg_task_mutex_);
+ msg_queue_.push(event);
+ msg_mutex.unlock();
+ msg_task_cv_.notify_one();
+ ret = true;
+ }
+ LOG_LEAVE;
+ return ret;
+}
+// LCOV_EXCL_START
+
+bool CafLogger::Connect_() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> guard(object_lock_);
+ if(!connected_to_dbus_ && context_aware_ != NULL)
+ connected_to_dbus_ = context_aware_->InitService();
+ LOG_LEAVE;
+ return connected_to_dbus_;
+}
+
+bool CafLogger::Disconnect_() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> guard(object_lock_);
+ /*first stop message thread, then disconnect. */
+
+ StopMsgThread_();
+ LOG_INFO("Disconnecting to DBus.");
+ if(connected_to_dbus_ && context_aware_ != NULL)
+ connected_to_dbus_ = context_aware_->FiniService();
+ if(!connected_to_dbus_)
+ LOG_INFO("Disconnecting to DBus FAILED.");
+ connected_to_dbus_ = false;
+ LOG_LEAVE;
+ return true;
+}
+// LCOV_EXCL_STOP
+
+bool CafLogger::isConnected_() {
+ return connected_to_dbus_;
+}
+
+void CafLogger::setAppId_(std::string app_id) {
+ app_id_ = app_id;
+}
+
+void CafLogger::setUniqueNumber_(int uniqueNumber) {
+ unique_number = uniqueNumber;
+}
+
+int CafLogger::getUniqueNumber_() {
+ return unique_number;
+}
+/******** STATIC FUNCTIONS ****************/
+
+bool CafLogger::Initialize() {
+ LOG_ENTER;
+
+ if(instance_ == NULL) {
+ instance_ = new CafLogger();
+ }
+ LOG_LEAVE;
+ return instance_->isConnected_();
+}
+
+bool CafLogger::LogMessage(CafEventType event_type, std::string data) {
+ LOG_ENTER;
+ bool ret = false;
+#ifndef SDK_DISABLED_FEATURE
+ if(instance_ != NULL && instance_->isConnected_()) {
+ ret = instance_->PushMessageToQueue_(event_type, data);
+ }
+#endif
+ LOG_LEAVE;
+ return ret;
+}
+
+void CafLogger::StartLoggingThread() {
+ LOG_ENTER;
+ if(instance_ != NULL)
+ instance_->StartMsgThread_();
+ LOG_LEAVE;
+}
+
+void CafLogger::StopLoggingThread() {
+ LOG_ENTER;
+ if(instance_ != NULL){
+ std::lock_guard<std::mutex> guard(instance_->object_lock_);
+ instance_->StopMsgThread_();
+ }
+ LOG_LEAVE;
+}
+
+void CafLogger::SetAppId(std::string app_id) {
+ LOG_ENTER;
+ if(instance_ != NULL)
+ instance_->setAppId_(app_id);
+ LOG_LEAVE;
+}
+
+void CafLogger::SetUniqueNumber()
+{
+ LOG_ENTER;
+ int id = -1;
+ if(instance_ != NULL)
+ {
+ id = instance_->getUniqueNumber_();
+ instance_->setUniqueNumber_(++id);
+ }
+ LOG_LEAVE;
+}
+std::string CafLogger::GetUniqueNumber()
+{
+ LOG_ENTER;
+ int id = -1;
+ std::string uniqueNumber;
+ if(instance_ != NULL)
+ {
+ id = instance_->getUniqueNumber_();
+ uniqueNumber = std::to_string(getpid()) + "_" + std::to_string(id);
+ }
+ LOG_LEAVE;
+ return uniqueNumber;
+}
+
+// LCOV_EXCL_START
+void CafLogger::SetContextAware(std::shared_ptr<ContextAware>&& context_aware)
+{
+ LOG_ENTER;
+ context_aware_ = context_aware;
+ LOG_LEAVE;
+}
+// LCOV_EXCL_STOP
+
+} //plusplayer
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "esplusplayer/decodedvideopacketex.h"
+
+namespace esplusplayer {
+
+// LCOV_EXCL_START
+ DecodedVideoPacketExPtr DecodedVideoPacketEx::Create(const uint64_t pts,
+ const uint64_t duration, tbm_surface_h surface_data,
+ const void* scaler_index) {
+ return Ptr(new DecodedVideoPacketEx(pts, duration, surface_data, scaler_index));
+ }
+
+ DecodedVideoPacketEx::~DecodedVideoPacketEx() {
+ if (surface_data_) {
+ tbm_surface_destroy(surface_data_);
+ surface_data_ = nullptr;
+ }
+ }
+// LCOV_EXCL_STOP
+
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "core/decoderinputbuffer.h"
+
+namespace esplusplayer {
+
+namespace decoderinputbuffer_util {
+
+// LCOV_EXCL_START
+bool FlushQueue(std::queue<DecoderInputBufferPtr>& queue) {
+ while (!queue.empty()) {
+ queue.pop();
+ }
+ return true;
+}
+// LCOV_EXCL_STOP
+
+} // namespace decoderinputbuffer_util
+
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "core/error.h"
+
+#include "core/gstobject_guard.h"
+#include "core/utils/plusplayer_log.h"
+
+namespace esplusplayer {
+
+namespace internal {
+
+ErrorType ConvertGstCoreError(const int error_code);
+ErrorType ConvertGstLibraryError(const int error_code);
+ErrorType ConvertGstResourceError(const int error_code);
+ErrorType ConvertGstStreamError(const GError* error);
+
+} // namespace internal
+
+// LCOV_EXCL_START
+ErrorType HandleGstError(const GError* error) {
+ ErrorType ret = ErrorType::kNone;
+
+ if (error == nullptr) return ret;
+ LOG_ERROR("Entered HandleGstError error->code[%d] ", error->code);
+ 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 {
+ LOG_INFO("This error domain is not defined.\n");
+ // we treat system error as an internal error
+ ret = ErrorType::kInvalidOperation;
+ }
+ return ret;
+}
+
+namespace internal {
+
+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: {
+ LOG_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;
+}
+// LCOV_EXCL_STOP
+
+} // namespace internal
+
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include "core/gst_utils.h"
+
+#include <cassert>
+#include <thread>
+
+#include "core/utils/plusplayer_log.h"
+
+namespace esplusplayer {
+
+namespace gst_util {
+
+// LCOV_EXCL_START
+void GstInit() {
+ gst_init(NULL,NULL);
+}
+// LCOV_EXCL_STOP
+
+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) {
+ LOG_INFO(" %s", argv[i]);
+ }
+ char** pargv = argv;
+ gst_init(&argc, &pargv);
+}
+
+} // namespace gst_util
+
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include "glib.h"
+
+#include "core/gstobject_guard.h"
+
+#include "core/utils/plusplayer_log.h"
+
+namespace esplusplayer {
+
+namespace gstguard {
+
+// LCOV_EXCL_START
+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); }
+// LCOV_EXCL_STOP
+
+} // namespace gstguard
+
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "core/kpi.h"
+
+#include <sstream>
+
+#include "core/utils/plusplayer_log.h"
+
+/* for logger */
+#include "capi-system-info/system_info.h"
+#include "logger/Logger2.h"
+
+using namespace KPILogFramework;
+
+namespace esplusplayer {
+
+const char* GetProductYear(void) {
+ static const char* year = nullptr;
+ if (year) return year;
+
+ int value = -1;
+ int result = system_info_get_custom_int(
+ "com.samsung/featureconf/product.tv_year", &value);
+ if (result != SYSTEM_INFO_ERROR_NONE || value < 0) {
+ LOG_ERROR(
+ "can't get com.samsung/featureconf/product.tv_year, result:%d, "
+ "value:%d",
+ result, value);
+ return "20";
+ }
+
+ static char str_year[3] = {0, };
+ int size = snprintf(str_year, 3, "%d", value);
+ if (size != 2) {
+ LOG_ERROR("size is not 2!! size:%d, year:%d", size, value);
+ return "20";
+ }
+
+ year = str_year;
+ return year;
+}
+
+// LCOV_EXCL_START
+namespace internal {
+std::string GetSrcType(SourceType ptype) {
+ switch (ptype) {
+ case SourceType::kHttp:
+ return "HTTP";
+ case SourceType::kHls:
+ return "HLS";
+ case SourceType::kDash:
+ return "DASH";
+ case SourceType::kFile:
+ return "FILE";
+ case SourceType::kNone:
+ case SourceType::kBase:
+ case SourceType::kExternalSubtitle:
+ case SourceType::kMax:
+ default:
+ return "others";
+ }
+}
+
+std::string GetDrmType(drm::Type dtype) {
+ switch (dtype) {
+ case drm::Type::kNone:
+ return "NONE";
+ case drm::Type::kPlayready:
+ return "PLAYREADY";
+ case drm::Type::kMarlin:
+ return "MARLIN";
+ case drm::Type::kVerimatrix:
+ return "VERIMATRIX";
+ case drm::Type::kWidevineClassic:
+ return "WIDEVINE CLASSIC";
+ case drm::Type::kSecuremedia:
+ return "SECUREMEDIA";
+ case drm::Type::kSdrm:
+ return "SDRM";
+ case drm::Type::kWidevineCdm:
+ return "WIDEVINE CDM";
+ case drm::Type::kMax:
+ default:
+ return "others";
+ }
+}
+
+std::string GetDecoderType(int ctype) { //(0:DEFAULT, 1:HW, 2:SW, 3:DISABLE)
+ if (ctype == 0 || ctype == 1) {
+ return "HW";
+ } else if (ctype == 2) {
+ return "SW";
+ } else {
+ return "DISABLE";
+ }
+}
+} // namespace internal
+
+namespace kpi {
+bool CodecLogger::SendKpi(bool event_case, const CodecLoggerKeys& keys) {
+ LOG_ENTER;
+
+ std::string ptype = internal::GetSrcType(keys.src_type);
+ std::string drm_type = internal::GetDrmType(keys.drm_type);
+ std::string v_decoder_type = internal::GetDecoderType(keys.v_decoder_type);
+ std::string a_decoder_type = internal::GetDecoderType(keys.a_decoder_type);
+
+ // generate message
+ std::stringstream str;
+ str << "{";
+ str << "ptype=" << ptype;
+ str << ";dtype=" << drm_type;
+ str << ";data_container=" << keys.container_type;
+ str << ";v_decoder_type=" << v_decoder_type;
+ str << ";v_codec=" << keys.v_codec;
+ str << ";v_tag=0x" << std::hex << keys.v_tag;
+ str << ";width=" << std::dec << keys.width;
+ str << ";height=" << std::dec << keys.height;
+ str << ";a_decoder_type=" << a_decoder_type;
+ str << ";a_codec=" << keys.a_codec;
+ str << ";a_tag=0x" << std::hex << keys.a_tag;
+ str << ";app_id=" << keys.app_id;
+ str << "}";
+
+ return SendKpi_(event_case, str);
+}
+// LCOV_EXCL_STOP
+
+bool CodecLogger::SendKpi(bool event_case, const EsCodecLoggerKeys& keys) {
+ LOG_ENTER;
+
+ std::string ptype = keys.is_clean ? "MSE" : "EME";
+ std::string drm_type = keys.is_clean ? "NONE" : "EME";
+ std::string v_decoder_type("HW");
+ std::string a_decoder_type("HW");
+ std::string container_type("EsPlusplayer");
+ unsigned int video_tag = 0;
+ unsigned int audio_tag = 0;
+ std::string video_codec = keys.v_codec + "-" +
+ std::to_string(keys.v_codec_version);
+
+ // generate message
+ std::stringstream str;
+ str << "{";
+ str << "ptype=" << ptype;
+ str << ";dtype=" << drm_type;
+ str << ";data_container=" << container_type;
+ str << ";v_decoder_type=" << v_decoder_type;
+ str << ";v_codec=" << keys.v_codec;
+ str << ";v_tag=0x" << std::hex << video_tag;
+ str << ";width=" << std::dec << keys.width;
+ str << ";height=" << std::dec << keys.height;
+ str << ";a_decoder_type=" << a_decoder_type;
+ str << ";a_codec=" << keys.a_codec;
+ str << ";a_tag=0x" << std::hex << audio_tag;
+ str << ";app_id=" << keys.app_id;
+ str << "}";
+
+ return SendKpi_(event_case, str);
+}
+
+bool CodecLogger::SendKpi_(bool event_case, const std::stringstream& message) {
+ LOG_ENTER;
+ const char* year = GetProductYear();
+
+ // send message to KPI logger
+ std::stringstream service_name;
+ service_name << year << "_codec";
+ std::string eventName;
+ std::string category;
+ if (event_case) {
+ eventName = "PLAYBACK";
+ category = "EV001";
+ } else {
+ eventName = "ERRPLAY";
+ category = "EV002";
+ }
+
+ LOG_ERROR("[KPI] service_name: %s, desc_log: %s",
+ service_name.str().c_str(), message.str().c_str());
+
+ CLogger* pLogger = CLogger::GetInstance();
+ LoggerErrorCode result = LOGGER_ERROR_NONE;
+ result = pLogger->AddEventLogSync(service_name.str().c_str(), eventName.c_str(),
+ category.c_str(), message.str().c_str());
+ if (result != LOGGER_ERROR_NONE) {
+ LOG_ERROR("Failed to send KPI log for esplayer, result:%d", result);
+ return false;
+ }
+
+ return true;
+}
+
+
+} // namespace kpi
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <cassert>
+
+#ifndef PLUPLAYER_DOWNLOADABLE_APP_TVPLUS
+#include "tzplatform_config.h"
+#endif
+
+namespace esplusplayer {
+
+namespace esplusplayer_cfg {
+
+const char* GetIniPath() {
+ const char* path =
+ tzplatform_mkpath(TZ_SYS_RO_ETC, "multimedia/esplusplayer.ini");
+ assert(path);
+ return path;
+}
+
+} // namespace esplusplayer_cfg
+
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "core/serializer.h"
+// LCOV_EXCL_START
+namespace esplusplayer {
+using Offset = Serializer::Offset;
+using Byte = Serializer::Byte;
+
+Offset Serializer::Put(const std::vector<unsigned char> &vector) {
+ Offset offset = static_cast<unsigned int>(size_);
+ if (vector.size() == 0) return offset;
+ const size_t size = vector.size();
+ const Byte *data_bytes = reinterpret_cast<const Byte *>(vector.data());
+ Put_(data_bytes, size);
+ return offset;
+}
+
+Offset Serializer::Put(const std::string &data) {
+ Offset offset = static_cast<unsigned int>(size_);
+ if (data.length() == 0) return offset;
+ const size_t size = data.length();
+ const Byte *data_bytes = reinterpret_cast<const Byte *>(data.c_str());
+ Put_(data_bytes, size);
+ return offset;
+}
+
+Offset Serializer::Put(const Byte *data, size_t size) {
+ Offset offset = static_cast<unsigned int>(size_);
+ if (size == 0) return offset;
+ Put_(data, size);
+ return offset;
+}
+
+size_t Serializer::Serialize(Byte *serialized) {
+ buf_.sgetn(serialized, size_);
+ return size_;
+}
+
+const size_t Serializer::GetSize() { return size_; }
+
+void Serializer::Put_(const Byte *data_bytes, const size_t size) {
+ buf_.sputn(data_bytes, size);
+ size_ += size;
+}
+}
+// LCOV_EXCL_STOP
+// namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "core/track_util.h"
+
+#include <string>
+#include <cinttypes>
+#include <algorithm>
+
+
+#include "core/utils/plusplayer_log.h"
+
+namespace esplusplayer {
+
+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;
+ // LOG_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++;
+ }
+ }
+// LCOV_EXCL_START
+ if (active_track.empty()) {
+ LOG_ERROR("no active track found");
+ return false;
+ }
+ if (video > 1 || audio > 1 || text > 1) {
+ LOG_ERROR("actived tracks are too much: video(%d), audio(%d), text(%d)",
+ video, audio, text);
+ return false;
+ }
+// LCOV_EXCL_STOP
+ return true;
+}
+
+// LCOV_EXCL_START
+void ShowTrackInfo(const std::vector<Track>& trackinfo) {
+ std::vector<Track> info = trackinfo;
+ LOG_INFO("### Track List ###");
+ for (const Track& item : info) {
+ ShowTrackInfo(item);
+ }
+ LOG_INFO("### ~Track List ###");
+}
+// LCOV_EXCL_STOP
+
+void ShowTrackInfo(const Track& track) {
+ LOG_INFO("### TrackInfo ###");
+ LOG_INFO("index : %d id : %d ,", track.index, track.id);
+ LOG_INFO("mimetype: %s", track.mimetype.c_str());
+ LOG_INFO("streamtype: %s", track.streamtype.c_str());
+ LOG_INFO("container_type: %s", track.container_type.c_str());
+ LOG_INFO("tracktype : %d", track.type);
+ LOG_INFO("codec tag : %d", track.codec_tag);
+ LOG_INFO("width: %d height : %d", track.width, track.height);
+ LOG_INFO("maxwidth: %d maxheight : %d", track.maxwidth, track.maxheight);
+ LOG_INFO("framerate(num : %d den : %d)", track.framerate_num,
+ track.framerate_den);
+ LOG_INFO("framerate(codec_data : %p )", track.codec_data.get());
+ LOG_INFO("framerate(codec_data_len : %d )", track.codec_data_len);
+ LOG_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);
+ LOG_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);
+ LOG_INFO("active %d subtitle_format : %s ", track.active, track.subtitle_format.c_str() );
+ LOG_INFO("use_swdecoder : %d", track.use_swdecoder);
+ LOG_INFO("language_code: %s", track.language_code.c_str());
+}
+
+// LCOV_EXCL_START
+uint64_t GetPositionWithinBoundary(const uint64_t duration,
+ const uint64_t position,
+ const uint64_t threshold) {
+ LOG_DEBUG("duration[%" PRIu64 "] position[%" PRIu64 "] threshold[%" PRIu64
+ "]",
+ 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,
+ esplusplayer::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)) {
+ LOG_DEBUG("codec extra data [ %s ]", codec_data_info.data);
+ LOG_DEBUG("codec extra data size[ %zu ]", 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 {
+ LOG_WARN("Warning invalid codec extra data size [%zu]",
+ codec_data_info.size);
+ }
+ gst_buffer_unmap(buffer, &codec_data_info);
+ } else {
+ LOG_DEBUG("Fail to gst_buffer_map for codec data");
+ }
+}
+// LCOV_EXCL_STOP
+
+} // namespace track_util
+
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "core/trackrendereradapter.h"
+
+#include <cstring>
+
+#include "core/trackrendereradapter_utils.h"
+#include "core/utils/plusplayer_log.h"
+#include "trackrenderer_capi/trackrenderer_internal.h"
+
+namespace esplusplayer {
+
+TrackRendererAdapter::Ptr TrackRendererAdapter::Create() {
+ LOG_INFO("Trackrenderer adapter is created");
+ return Ptr(new TrackRendererAdapter);
+}
+
+TrackRendererAdapter::TrackRendererAdapter() { trackrenderer_create(&handle_); }
+
+TrackRendererAdapter::~TrackRendererAdapter() {
+ if (handle_) {
+ trackrenderer_destroy(handle_);
+ handle_ = nullptr;
+ }
+}
+
+bool TrackRendererAdapter::Start() {
+ if (trackrenderer_start(handle_) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::Stop() {
+ if (trackrenderer_stop(handle_) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::Prepare() {
+ if (trackrenderer_prepare(handle_) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::Pause() {
+ if (trackrenderer_pause(handle_) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::Resume() {
+ if (trackrenderer_resume(handle_) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+constexpr int kTrackRendererMaxStreamNumber = 3;
+bool TrackRendererAdapter::SetTrack(const std::vector<Track>& trackinfo) {
+ int size = trackinfo.size();
+ if (size <= 0 || size > kTrackRendererMaxStreamNumber) return false;
+
+ TrackRendererTrack tracks[size];
+ int index = 0;
+ for (const auto& track : trackinfo) {
+ adapter_utils::MakeTrackRendererTrack(&tracks[index], track);
+ index++;
+ }
+
+ if (trackrenderer_set_track(handle_, tracks, size) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+void TrackRendererAdapter::SetIniProperty(
+ const std::map<std::string, bool>& properties) {
+ const int size = properties.size();
+ if (size <= 0) return;
+ TrackRendererIniProperty trackrenderer_iniproperty[size];
+ int index = 0;
+ for (const auto& pair : properties) {
+ trackrenderer_iniproperty[index].key = pair.first.c_str();
+ trackrenderer_iniproperty[index].value = pair.second;
+ index++;
+ }
+ trackrenderer_set_ini_property(handle_, trackrenderer_iniproperty, size);
+}
+
+bool TrackRendererAdapter::Seek(uint64_t time, double playback_rate) {
+ if (trackrenderer_seek(handle_, time, playback_rate) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::Seek(uint64_t time, double playback_rate,
+ bool audio_mute) {
+ if (trackrenderer_seek2(handle_, time, playback_rate, audio_mute) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetPlaybackRate(double playback_rate,
+ bool audio_mute) {
+ if (trackrenderer_set_playback_rate(handle_, playback_rate, audio_mute) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::GetPlayingTime(uint64_t* time) {
+ if (trackrenderer_get_playing_time(
+ handle_, (unsigned long long*)(uintptr_t)time) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::GetDroppedFrames(void* counts) {
+ if (trackrenderer_get_dropped_frames(handle_, counts) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::GetDroppedFramesForCatchup(TrackType type,
+ void* counts) {
+ if (trackrenderer_get_dropped_frames_for_catchup(
+ handle_, adapter_utils::ConvertToTrackRendererTrackType(type),
+ counts) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::Deactivate(TrackType type) {
+ if (trackrenderer_deactivate(
+ handle_, adapter_utils::ConvertToTrackRendererTrackType(type)) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::Activate(TrackType type, const Track& trackinfo) {
+ TrackRendererTrack track;
+ adapter_utils::MakeTrackRendererTrack(&track, trackinfo);
+
+ if (trackrenderer_activate(
+ handle_, adapter_utils::ConvertToTrackRendererTrackType(type),
+ &track) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+// LCOV_EXCL_START
+bool TrackRendererAdapter::SubmitPacket(const DecoderInputBufferPtr& data) {
+#ifdef TRACKRENDERER_GST_DEPENDENCY_REMOVAL
+ TrackRendererDecoderInputBuffer decoderinputbuffer{
+ adapter_utils::ConvertToTrackRendererTrackType(data->GetType()),
+ data->GetIndex(),
+ static_cast<void*>(const_cast<GstBuffer*>(data->Get()))};
+#else
+ TrackRendererDecoderInputBuffer decoderinputbuffer{
+ adapter_utils::ConvertToTrackRendererTrackType(data->GetType()),
+ data->GetIndex(), const_cast<GstBuffer*>(data->Get())};
+#endif
+ if (trackrenderer_submit_packet(handle_, &decoderinputbuffer, nullptr) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+// LCOV_EXCL_STOP
+
+
+namespace adapter_utils {
+
+TrackRendererAdapter::SubmitStatus ConvertToAdapterSubmitStatus(
+ TrackRendererSubmitStatus status) {
+ switch (status) {
+ case kTrackRendererSubmitStatusNotPrepared: {
+ return TrackRendererAdapter::SubmitStatus::kNotPrepared;
+ }
+ case kTrackRendererSubmitStatusHold: {
+ return TrackRendererAdapter::SubmitStatus::kHold;
+ }
+ case kTrackRendererSubmitStatusFull: {
+ return TrackRendererAdapter::SubmitStatus::kFull;
+ }
+ case kTrackRendererSubmitStatusSuccess: {
+ return TrackRendererAdapter::SubmitStatus::kSuccess;
+ }
+ case kTrackRendererSubmitStatusDrop: {
+ return TrackRendererAdapter::SubmitStatus::kDrop;
+ }
+ case kTrackRendererSubmitStatusFailed: {
+ return TrackRendererAdapter::SubmitStatus::kFailed;
+ }
+ default:
+ LOG_ERROR("unknown submitstatus");
+ return TrackRendererAdapter::SubmitStatus::kFailed;
+ }
+}
+
+} // namespace adapter_utils
+
+// LCOV_EXCL_START
+bool TrackRendererAdapter::SubmitPacket(const DecoderInputBufferPtr& data,
+ SubmitStatus* status) {
+#ifdef TRACKRENDERER_GST_DEPENDENCY_REMOVAL
+ TrackRendererDecoderInputBuffer decoderinputbuffer{
+ adapter_utils::ConvertToTrackRendererTrackType(data->GetType()),
+ data->GetIndex(),
+ static_cast<void*>(const_cast<GstBuffer*>(data->Get()))};
+#else
+ TrackRendererDecoderInputBuffer decoderinputbuffer{
+ adapter_utils::ConvertToTrackRendererTrackType(data->GetType()),
+ data->GetIndex(), const_cast<GstBuffer*>(data->Get())};
+#endif
+ TrackRendererSubmitStatus submitstatus;
+ if (trackrenderer_submit_packet(handle_, &decoderinputbuffer,
+ &submitstatus) == kFailed) {
+ if (status) *status = TrackRendererAdapter::SubmitStatus::kFailed;
+ return false;
+ }
+ if (status)
+ *status = adapter_utils::ConvertToAdapterSubmitStatus(submitstatus);
+ return true;
+}
+// LCOV_EXCL_STOP
+
+
+bool TrackRendererAdapter::SubmitPacket2(const DecoderInputBufferPtr& data,
+ SubmitStatus* status) {
+#ifdef TRACKRENDERER_GST_DEPENDENCY_REMOVAL
+ TrackRendererDecoderInputBuffer decoderinputbuffer{
+ adapter_utils::ConvertToTrackRendererTrackType(data->GetType()),
+ data->GetIndex(),
+ static_cast<void*>(const_cast<GstBuffer*>(data->Get()))};
+#else
+ TrackRendererDecoderInputBuffer decoderinputbuffer{
+ adapter_utils::ConvertToTrackRendererTrackType(data->GetType()),
+ data->GetIndex(), const_cast<GstBuffer*>(data->Get())};
+#endif
+ TrackRendererSubmitStatus submitstatus;
+ if (trackrenderer_submit_packet2(handle_, &decoderinputbuffer,
+ &submitstatus) == kFailed) {
+ if (status)
+ *status = adapter_utils::ConvertToAdapterSubmitStatus(submitstatus);
+ return false;
+ }
+
+ if (status)
+ *status = adapter_utils::ConvertToAdapterSubmitStatus(submitstatus);
+
+ if (submitstatus == kTrackRendererSubmitStatusDrop) return true;
+
+ data->Release();
+ return true;
+}
+
+void TrackRendererAdapter::SetDrm(const drm::Property& drm_property) {
+ TrackRendererDrmProperty trackrenderer_drm_property{kTrackRendererDrmTypeNone,
+ 0, 0, nullptr, nullptr};
+ adapter_utils::MakeTrackRendererDrmProperty(&trackrenderer_drm_property,
+ drm_property);
+ trackrenderer_set_drm(handle_, &trackrenderer_drm_property);
+}
+
+void TrackRendererAdapter::DrmLicenseAcquiredDone(TrackType type) {
+ trackrenderer_drm_license_acquired_done(
+ handle_, adapter_utils::ConvertToTrackRendererTrackType(type));
+}
+
+bool TrackRendererAdapter::SetDisplayMode(const DisplayMode& mode) {
+ if (trackrenderer_set_display_mode(
+ handle_, adapter_utils::ConvertToTrackRendererDisplayMode(mode)) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetStretchMode(const int& mode) {
+ if (trackrenderer_set_stretch_mode(
+ handle_, mode) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+// LCOV_EXCL_START
+bool TrackRendererAdapter::SetDisplay(const DisplayType& type,
+ uint32_t surface_id, long x, long y,
+ long w, long h) {
+ if (trackrenderer_set_display_surface(
+ handle_, adapter_utils::ConvertToTrackRendererDisplayType(type),
+ surface_id, x, y, w, h) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetDisplay(const DisplayType& type, void* obj) {
+ if (trackrenderer_set_display(
+ handle_, adapter_utils::ConvertToTrackRendererDisplayType(type),
+ obj) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetDisplay(const DisplayType& type,
+ void* ecore_wl2_window, int x, int y,
+ int w, int h) {
+ if (trackrenderer_set_display_ecore_wl2_window(
+ handle_, adapter_utils::ConvertToTrackRendererDisplayType(type),
+ ecore_wl2_window, x, y, w, h) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetDisplaySubsurface(const DisplayType& type,
+ void* ecore_wl2_subsurface,
+ int x, int y, int w, int h) {
+ if (trackrenderer_set_display_ecore_wl2_subsurface(
+ handle_, adapter_utils::ConvertToTrackRendererDisplayType(type),
+ ecore_wl2_subsurface, x, y, w, h) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+void TrackRendererAdapter::GetDisplay(DisplayType* type, Geometry* area) {
+ TrackRendererGeometry geometry = {0, 0, 1920, 1080};
+ TrackRendererDisplayType display_type = kTrackRendererDisplayTypeNone;
+ trackrenderer_get_display(handle_, &display_type, &geometry);
+ adapter_utils::MakeGeometry(area, geometry);
+ *type = adapter_utils::ConvertToDisplayType(display_type);
+}
+// LCOV_EXCL_STOP
+
+bool TrackRendererAdapter::SetDisplayRoi(const Geometry& roi) {
+ TrackRendererGeometry geometry = {0, 0, 1920, 1080};
+ adapter_utils::MakeTrackRendererGeometry(&geometry, roi);
+ if (trackrenderer_set_display_roi(handle_, &geometry) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetVideoRoi(const CropArea& area) {
+ TrackRendererCropArea croparea = {0.0, 0.0, 1.0, 1.0};
+ adapter_utils::MakeTrackRendererCropArea(&croparea, area);
+ if (trackrenderer_set_video_roi(handle_, &croparea) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::ResizeRenderRect(const RenderRect& rect) {
+ TrackRendererRenderRect result = {0, 0, 1920, 1080};
+ adapter_utils::MakeTrackRendererRenderRect(&result, rect);
+ if (trackrenderer_resize_render_rect(handle_, &result) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetDisplayRotate(const DisplayRotation& rotate) {
+ if (trackrenderer_set_display_rotate(
+ handle_, adapter_utils::ConvertToTrackRendererDisplayRotate(
+ rotate)) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::GetDisplayRotate(DisplayRotation* rotate) {
+ TrackRendererDisplayRotate display_rotate = kTrackRendererDisplayRotateNone;
+ if (trackrenderer_get_display_rotate(handle_, &display_rotate) == kFailed) {
+ return false;
+ }
+ *rotate = adapter_utils::ConvertToDisplayRotation(display_rotate);
+ return true;
+}
+
+bool TrackRendererAdapter::SetDisplayVisible(bool is_visible) {
+ if (trackrenderer_set_display_visible(handle_, is_visible) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+// LCOV_EXCL_START
+void TrackRendererAdapter::GetDisplayMode(DisplayMode* mode) {
+ // TODO: sy0207.ju
+ TrackRendererDisplayMode display_mode = kTrackRendererDisplayModeDisplayMax;
+ trackrenderer_get_display_mode(handle_, &display_mode);
+ *mode = adapter_utils::ConvertToDisplayMode(display_mode);
+}
+// LCOV_EXCL_STOP
+
+bool TrackRendererAdapter::SetAudioMute(bool is_mute) {
+ if (trackrenderer_set_audio_mute(handle_, is_mute) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+void TrackRendererAdapter::SetAppId(const std::string& app_id) {
+ trackrenderer_set_app_id(handle_, app_id.c_str());
+}
+
+void TrackRendererAdapter::SetAppInfo(const PlayerAppInfo& app_info) {
+ TrackRendererAppInfo info;
+ adapter_utils::MakeTrackRendererAppInfo(&info, app_info);
+ trackrenderer_set_app_info(handle_, &info);
+}
+
+void TrackRendererAdapter::SetAppInfoEx(const PlayerAppInfoEx& app_info) {
+ TrackRendererAppInfoEx info;
+ adapter_utils::MakeTrackRendererAppInfoEx(&info, app_info);
+ trackrenderer_set_app_info_ex(handle_, &info);
+}
+
+void TrackRendererAdapter::SetVideoStillMode(const StillMode& type) {
+ trackrenderer_set_video_still_mode(
+ handle_, adapter_utils::ConvertToTrackRendererStillMode(type));
+}
+
+bool TrackRendererAdapter::SetVolume(const int& volume) {
+ if (trackrenderer_set_volume(handle_, volume) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::GetVolume(int* volume) {
+ if (trackrenderer_get_volume(handle_, volume) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::Flush(const StreamType& type) {
+ if (trackrenderer_flush(
+ handle_, adapter_utils::ConvertToTrackRendererTrackTypeFromStreamType(
+ type)) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::Flush(const TrackType& type) {
+ if (trackrenderer_flush(
+ handle_, adapter_utils::ConvertToTrackRendererTrackType(type)) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+namespace adapter_utils {
+
+enum class ValueType {
+ kUnknown,
+ kInt32,
+ kUInt32,
+ kInt64,
+ kUInt64,
+ kMax,
+};
+struct AttrInfo {
+ const ValueType value_type;
+ const std::string name;
+};
+static const std::map<TrackRendererAdapter::Attribute, AttrInfo>
+ kPluginPropertyInfoTable = {
+ {TrackRendererAdapter::Attribute::kVideoQueueMaxByte,
+ {ValueType::kUInt64, "video-queue-max-byte"}},
+ {TrackRendererAdapter::Attribute::kAudioQueueMaxByte,
+ {ValueType::kUInt64, "audio-queue-max-byte"}},
+ {TrackRendererAdapter::Attribute::kVideoQueueCurrentLevelByte,
+ {ValueType::kUInt64, "video-current-level-byte"}},
+ {TrackRendererAdapter::Attribute::kAudioQueueCurrentLevelByte,
+ {ValueType::kUInt64, "audio-current-level-byte"}},
+ {TrackRendererAdapter::Attribute::kVideoMinByteThreshold,
+ {ValueType::kUInt32, "video-min-byte-percent"}},
+ {TrackRendererAdapter::Attribute::kAudioMinByteThreshold,
+ {ValueType::kUInt32, "audio-min-byte-percent"}},
+ {TrackRendererAdapter::Attribute::kVideoQueueMaxTime,
+ {ValueType::kUInt64, "video-queue-max-time"}},
+ {TrackRendererAdapter::Attribute::kAudioQueueMaxTime,
+ {ValueType::kUInt64, "audio-queue-max-time"}},
+ {TrackRendererAdapter::Attribute::kVideoQueueCurrentLevelTime,
+ {ValueType::kUInt64, "video-current-level-time"}},
+ {TrackRendererAdapter::Attribute::kAudioQueueCurrentLevelTime,
+ {ValueType::kUInt64, "audio-current-level-time"}},
+ {TrackRendererAdapter::Attribute::kVideoMinTimeThreshold,
+ {ValueType::kUInt32, "video-min-time-percent"}},
+ {TrackRendererAdapter::Attribute::kAudioMinTimeThreshold,
+ {ValueType::kUInt32, "audio-min-time-percent"}},
+ {TrackRendererAdapter::Attribute::kVideoSupportRotation,
+ {ValueType::kUInt32, "support-rotation"}},
+ {TrackRendererAdapter::Attribute::kVideoRenderTimeOffset,
+ {ValueType::kInt64, "video-render-time-offset"}},
+ {TrackRendererAdapter::Attribute::kAudioRenderTimeOffset,
+ {ValueType::kInt64, "audio-render-time-offset"}}};
+
+static const std::map<TrackRendererAdapter::Attribute, AttrInfo>
+ kConfigInfoTable = {
+ {TrackRendererAdapter::Attribute::kAccurateSeekMode,
+ {ValueType::kUInt32, "accurate-seek-mode"}},
+ {TrackRendererAdapter::Attribute::kLowLatencyMode,
+ {ValueType::kUInt32, "low-latency-mode"}},
+ {TrackRendererAdapter::Attribute::kVideoFramePeekMode,
+ {ValueType::kUInt32, "video-frame-peek-mode"}},
+ {TrackRendererAdapter::Attribute::kUnlimitedMaxBufferMode,
+ {ValueType::kUInt32, "unlimited-max-buffer-mode"}},
+ {TrackRendererAdapter::Attribute::kVideoPreDisplayMode,
+ {ValueType::kUInt32, "video-pre-display-mode"}},
+ {TrackRendererAdapter::Attribute::kStartRenderingTime,
+ {ValueType::kUInt64, "start-rendering-time"}},
+ {TrackRendererAdapter::Attribute::kFmmMode,
+ {ValueType::kUInt32, "fmm-mode"}},
+ {TrackRendererAdapter::Attribute::kAlternativeVideoResource,
+ {ValueType::kUInt32, "alternative-video-resource"}},
+ {TrackRendererAdapter::Attribute::kVideoDecodingMode,
+ {ValueType::kUInt32, "video-decoding-mode"}},
+ {TrackRendererAdapter::Attribute::kLateVideoFrameDropMode,
+ {ValueType::kUInt32, "late-video-frame-drop-mode"}},
+ {TrackRendererAdapter::Attribute::kVideoProgressiveMode,
+ {ValueType::kUInt32, "video-progressive-mode"}},
+ {TrackRendererAdapter::Attribute::kPlayerTimeUnitType,
+ {ValueType::kUInt32, "player-time-unit-type"}}};
+} // namespace adapter_utils
+
+void TrackRendererAdapter::SetAttribute(
+ const TrackRendererAdapter::Attribute& attr, const boost::any& value) {
+ if (adapter_utils::kPluginPropertyInfoTable.count(attr) > 0) {
+ const adapter_utils::AttrInfo& info =
+ adapter_utils::kPluginPropertyInfoTable.at(attr);
+ LOG_DEBUG("attribute [%d] : %s", static_cast<int>(attr), info.name.c_str());
+ switch (info.value_type) {
+ case adapter_utils::ValueType::kInt32: {
+ std::int32_t _value = boost::any_cast<std::int32_t>(value);
+ trackrenderer_set_attribute(handle_, info.name.c_str(), _value,
+ nullptr);
+ } break;
+ case adapter_utils::ValueType::kUInt32: {
+ std::uint32_t _value = boost::any_cast<std::uint32_t>(value);
+ trackrenderer_set_attribute(handle_, info.name.c_str(), _value,
+ nullptr);
+ } break;
+ case adapter_utils::ValueType::kInt64: {
+ std::int64_t _value = boost::any_cast<std::int64_t>(value);
+ trackrenderer_set_attribute(handle_, info.name.c_str(), _value,
+ nullptr);
+ } break;
+ case adapter_utils::ValueType::kUInt64: {
+ std::uint64_t _value = boost::any_cast<std::uint64_t>(value);
+ trackrenderer_set_attribute(handle_, info.name.c_str(), _value,
+ nullptr);
+ } break;
+ default:
+ LOG_ERROR("unknown attribute ...");
+ break;
+ }
+ return;
+ } else if (adapter_utils::kConfigInfoTable.count(attr) > 0) {
+ const adapter_utils::AttrInfo& info =
+ adapter_utils::kConfigInfoTable.at(attr);
+ switch (info.value_type) {
+ case adapter_utils::ValueType::kUInt32: {
+ std::uint32_t _value = boost::any_cast<std::uint32_t>(value);
+ trackrenderer_set_attribute(handle_, info.name.c_str(), _value,
+ nullptr);
+ } break;
+ case adapter_utils::ValueType::kUInt64: {
+ std::uint64_t _value = boost::any_cast<std::uint64_t>(value);
+ trackrenderer_set_attribute(handle_, info.name.c_str(), _value,
+ nullptr);
+ } break;
+ default:
+ LOG_ERROR("unknown attribute ...");
+ break;
+ }
+ return;
+ } else {
+ LOG_ERROR("unknown attribute");
+ return;
+ }
+}
+
+void TrackRendererAdapter::GetAttribute(
+ const TrackRendererAdapter::Attribute& attr, boost::any* value) {
+ if (adapter_utils::kPluginPropertyInfoTable.count(attr) == 0) {
+ LOG_ERROR("unknown attribute");
+ return;
+ }
+ const adapter_utils::AttrInfo& info =
+ adapter_utils::kPluginPropertyInfoTable.at(attr);
+
+ switch (info.value_type) {
+ case adapter_utils::ValueType::kInt32: {
+ std::int32_t _value = 0;
+ trackrenderer_get_attribute(handle_, info.name.c_str(), &_value, nullptr);
+ *value = _value;
+ } break;
+ case adapter_utils::ValueType::kUInt32: {
+ std::uint32_t _value = 0;
+ trackrenderer_get_attribute(handle_, info.name.c_str(), &_value, nullptr);
+ *value = _value;
+ } break;
+ case adapter_utils::ValueType::kInt64: {
+ std::int64_t _value = 0;
+ trackrenderer_get_attribute(handle_, info.name.c_str(), &_value, nullptr);
+ *value = _value;
+ } break;
+ case adapter_utils::ValueType::kUInt64: {
+ std::uint64_t _value = 0;
+ trackrenderer_get_attribute(handle_, info.name.c_str(), &_value, nullptr);
+ *value = _value;
+ } break;
+ default:
+ LOG_ERROR("unknown attribute ...");
+ break;
+ }
+ return;
+}
+// LCOV_EXCL_START
+void TrackRendererAdapter::RegisterListener(EventListener* listener) {
+ eventlistener_ = listener;
+ trackrenderer_set_error_cb(handle_, ErrorCb_, (void*)this);
+ trackrenderer_set_error_msg_cb(handle_, ErrorMsgCb_, (void*)this);
+ trackrenderer_set_resourceconflict_cb(handle_, ResourceConflictCb_,
+ (void*)this);
+ trackrenderer_set_seekdone_cb(handle_, SeekDoneCb_, (void*)this);
+ trackrenderer_set_eos_cb(handle_, EosCb_, (void*)this);
+ trackrenderer_set_subtitle_rawdata_cb(handle_, SubtitleRawDataCb_,
+ (void*)this);
+ trackrenderer_set_closedcaption_cb(handle_, ClosedCaptionDataCb_,
+ (void*)this);
+ trackrenderer_set_drminitdata_cb(handle_, DrmInitDataCb_, (void*)this);
+ trackrenderer_set_media_packet_video_decoded_cb(
+ handle_, MediaPacketVideoDecodedCb_, (void*)this);
+ trackrenderer_set_media_packet_video_raw_decoded_cb(
+ handle_, MediaPacketVideoRawDecodedCb_, (void*)this);
+ trackrenderer_set_multiview_start_video_cb(handle_, MultiviewStartVideoCb_,
+ (void*)this);
+ trackrenderer_set_multiview_stop_video_cb(handle_, MultiviewStopVideoCb_,
+ (void*)this);
+}
+// LCOV_EXCL_STOP
+
+void TrackRendererAdapter::RegisterListenerForEsplayer(
+ EventListener* listener) {
+ eventlistener_ = listener;
+ trackrenderer_set_error_cb(handle_, ErrorCb_, (void*)this);
+ trackrenderer_set_error_msg_cb(handle_, ErrorMsgCb_, (void*)this);
+ trackrenderer_set_resourceconflict_cb(handle_, ResourceConflictCb_,
+ (void*)this);
+ trackrenderer_set_seekdone_cb(handle_, SeekDoneCb_, (void*)this);
+ trackrenderer_set_flushdone_cb(handle_, FlushDoneCb_, (void*)this);
+ trackrenderer_set_eos_cb(handle_, EosCb_, (void*)this);
+ trackrenderer_set_event_cb(handle_, EventCb_, (void*)this);
+ trackrenderer_set_bufferstatus_cb(handle_, BufferStatusCb_, (void*)this);
+ trackrenderer_set_seekdata_cb(handle_, SeekDataCb_, (void*)this);
+ trackrenderer_set_closedcaption_cb(handle_, ClosedCaptionDataCb_,
+ (void*)this);
+ trackrenderer_set_media_packet_video_tbmptr_cb(
+ handle_, MediaPacketGetTbmBufPtrCb_, (void*)this);
+ trackrenderer_set_media_packet_video_decoded_cb(
+ handle_, MediaPacketVideoDecodedCb_, (void*)this);
+ trackrenderer_set_media_packet_video_raw_decoded_cb(
+ handle_, MediaPacketVideoRawDecodedCb_, (void*)this);
+ trackrenderer_set_first_decoding_done_cb(handle_, FirstDecodingDoneCb_,
+ (void*)this);
+ trackrenderer_set_video_decoder_underrun_cb(handle_, VideoDecoderUnderrunCb_,
+ (void*)this);
+ trackrenderer_set_video_latency_status_cb(handle_, VideoLatencyStatusCb_,
+ (void*)this);
+ trackrenderer_set_audio_latency_status_cb(handle_, AudioLatencyStatusCb_,
+ (void*)this);
+ trackrenderer_set_video_frame_dropped_cb(handle_, VideoFrameDroppedCb_,
+ (void*)this);
+ trackrenderer_set_decoder_input_buffer_time_cb(handle_, DecoderInputBufferTime,
+ (void*)this);
+ trackrenderer_set_decoder_output_buffer_time_cb(handle_, DecoderOutputBufferTime,
+ (void*)this);
+}
+
+TrackRendererState TrackRendererAdapter::GetState() {
+ return trackrenderer_get_state(handle_);
+}
+
+bool TrackRendererAdapter::SetMatroskaColorInfo(const std::string& color_info) {
+ if (trackrenderer_set_matroska_color_info(handle_, color_info.c_str()) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+void TrackRendererAdapter::SetVideoFrameBufferType(
+ VideoFrameTypeStrategyPtr strategy) {
+ strategy->SetType(handle_);
+}
+
+bool TrackRendererAdapter::SetVideoFrameBufferScaleResolution(
+ const uint32_t& target_width, const uint32_t& target_height) {
+ if (trackrenderer_set_video_frame_buffer_scale_resolution(
+ handle_, target_width, target_height) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetDecodedVideoFrameRate(
+ const Rational& request_framerate) {
+ TrackRendererRational request_fps;
+ adapter_utils::MakeTrackRendererRational(&request_fps, request_framerate);
+ if (trackrenderer_set_decoded_video_frame_rate(handle_, request_fps) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::RenderVideoFrame() {
+ if (trackrenderer_render_video_frame(handle_) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetAiFilter(void* aifilter) {
+ if (trackrenderer_set_aifilter(handle_, aifilter) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetCatchUpSpeed(const CatchUpSpeed& level) {
+ if (trackrenderer_set_catch_up_speed(
+ handle_, adapter_utils::ConvertToTrackRendererCatchUpSpeed(level)) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::GetVideoLatencyStatus(LatencyStatus* status) {
+ TrackRendererLatencyStatus current_status = kTrackRendererLatencyStatusLow;
+ if (trackrenderer_get_video_latency_status(handle_, ¤t_status) ==
+ kFailed) {
+ return false;
+ }
+ *status = adapter_utils::ConvertToLatencyStatus(current_status);
+ return true;
+}
+
+bool TrackRendererAdapter::GetAudioLatencyStatus(LatencyStatus* status) {
+ TrackRendererLatencyStatus current_status = kTrackRendererLatencyStatusLow;
+ if (trackrenderer_get_audio_latency_status(handle_, ¤t_status) ==
+ kFailed) {
+ return false;
+ }
+ *status = adapter_utils::ConvertToLatencyStatus(current_status);
+ return true;
+}
+
+bool TrackRendererAdapter::SetVideoMidLatencyThreshold(
+ const unsigned int threshold) {
+ if (trackrenderer_set_video_mid_latency_threshold(handle_, threshold) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetAudioMidLatencyThreshold(
+ const unsigned int threshold) {
+ if (trackrenderer_set_audio_mid_latency_threshold(handle_, threshold) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetVideoHighLatencyThreshold(
+ const unsigned int threshold) {
+ trackrenderer_set_video_high_latency_cb(handle_, VideoHighLatencyCb_,
+ (void*)this);
+
+ if (trackrenderer_set_video_high_latency_threshold(handle_, threshold) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetAudioHighLatencyThreshold(
+ const unsigned int threshold) {
+ trackrenderer_set_audio_high_latency_cb(handle_, AudioHighLatencyCb_,
+ (void*)this);
+
+ if (trackrenderer_set_audio_high_latency_threshold(handle_, threshold) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::InitAudioEasingInfo(
+ const uint32_t init_volume, const uint32_t init_elapsed_time,
+ const AudioEasingInfo& easing_info) {
+ TrackRendererAudioEasingInfo info;
+ adapter_utils::MakeTrackRendererAudioEasingInfo(&info, easing_info);
+ if (trackrenderer_init_audio_easing_info(
+ handle_, init_volume, init_elapsed_time, &info) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::UpdateAudioEasingInfo(
+ const AudioEasingInfo& easing_info) {
+ TrackRendererAudioEasingInfo info;
+ adapter_utils::MakeTrackRendererAudioEasingInfo(&info, easing_info);
+ if (trackrenderer_update_audio_easing_info(handle_, &info) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::GetAudioEasingInfo(uint32_t* current_volume,
+ uint32_t* elapsed_time,
+ AudioEasingInfo* easing_info) {
+ TrackRendererAudioEasingInfo info;
+ if (trackrenderer_get_audio_easing_info(handle_, current_volume, elapsed_time,
+ &info) == kFailed) {
+ return false;
+ }
+ adapter_utils::MakeAudioEasingInfo(easing_info, info);
+ return true;
+}
+
+bool TrackRendererAdapter::StartAudioEasing() {
+ if (trackrenderer_start_audio_easing(handle_) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::StopAudioEasing() {
+ if (trackrenderer_stop_audio_easing(handle_) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::GetVirtualRscId(const RscType type,
+ int* virtual_id) {
+ TrackRendererRscType converted_type = kTrackRendererRscTypeVideoRenderer;
+ if (!adapter_utils::ConvertToTrackRendererRscType(type, &converted_type))
+ return false;
+ if (trackrenderer_get_virtual_rsc_id(handle_, converted_type, virtual_id) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetAdvancedPictureQualityType(
+ const AdvPictureQualityType type) {
+ TrackRendererAdvPictureQualityType converted_type =
+ kTrackRendererAdvPictureQualityTypeVideoCall;
+ if (!adapter_utils::ConvertToTrackRendererAdvPictureQualityType(
+ type, &converted_type))
+ return false;
+ if (trackrenderer_set_advanced_picture_quality_type(
+ handle_, converted_type) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetResourceAllocatePolicy(
+ const RscAllocPolicy policy) {
+ TrackRendererRscAllocPolicy converted_policy =
+ kTrackRendererRscAllocExclusive;
+ if (!adapter_utils::ConvertToTrackRendererRscAllocPolicy(policy,
+ &converted_policy))
+ return false;
+ if (trackrenderer_set_resource_allocate_policy(handle_, converted_policy) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetVideoParDar(uint64_t time_millisecond,
+ uint32_t par_num, uint32_t par_den,
+ uint32_t dar_num, uint32_t dar_den,
+ int reset_flag) {
+ if (trackrenderer_set_video_par_dar(handle_, time_millisecond, par_num,
+ par_den, dar_num, dar_den,
+ reset_flag) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+GetDecodedVideoFrameStatus TrackRendererAdapter::GetDecodedPacket(
+ DecodedVideoPacket& packet) {
+ TrackRendererDecodedVideoPacket _packet;
+ TrackRendererGetDecodedVideoFrameState state;
+ trackrenderer_get_decoded_video_frame(handle_, &_packet, &state);
+ packet = adapter_utils::ConvertToDecodedVideoPacket(&_packet);
+ return adapter_utils::ConvertToGetDecodedVideoFrameStatus(state);
+}
+
+bool TrackRendererAdapter::ReturnDecodedPacket(
+ const DecodedVideoPacket& packet) {
+ auto _packet = adapter_utils::ConvertToDecodedVideoPacket(packet);
+ if (trackrenderer_return_decoded_video_frame(handle_, &_packet) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetAlternativeAudioResource(
+ const PlayerAudioResourceType rcs_type) {
+ unsigned int converted_type = 0;
+ if (!adapter_utils::ConvertToTrackRendererAlternativeAudioResource(
+ rcs_type, &converted_type)) {
+ return false;
+ }
+ if (trackrenderer_set_alternative_audio_resource(handle_, converted_type) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetAudioPreloading() {
+ if (trackrenderer_set_audio_preloading(handle_) == kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetVideoStreamRotationInfo(const VideoRotation& rotation) {
+ if (trackrenderer_set_video_stream_rotation_info(
+ handle_, adapter_utils::ConvertToTrackRendererVideoStreamRotation(
+ rotation)) == kFailed) {
+ return false;
+ }
+ return true;
+}
+// LCOV_EXCL_START
+
+/////////////////////////////////////////////
+/////////////// Callbacks ///////////////////
+/////////////////////////////////////////////
+void TrackRendererAdapter::ErrorCb_(const TrackRendererErrorType error_code,
+ UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnError(
+ adapter_utils::ConvertToErrorType(error_code));
+}
+
+void TrackRendererAdapter::ErrorMsgCb_(const TrackRendererErrorType error_code,
+ char* error_msg, UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnErrorMsg(
+ adapter_utils::ConvertToErrorType(error_code), error_msg);
+}
+
+void TrackRendererAdapter::ResourceConflictCb_(UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnResourceConflicted();
+}
+
+void TrackRendererAdapter::SeekDoneCb_(UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnSeekDone();
+}
+
+void TrackRendererAdapter::FlushDoneCb_(UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnFlushDone();
+}
+// LCOV_EXCL_STOP
+
+void TrackRendererAdapter::EosCb_(UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnEos();
+}
+
+void TrackRendererAdapter::EventCb_(const TrackRendererEventType event_type,
+ const TrackrendererEventMsg msg_data,
+ UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ EventMsg event_msg;
+ event_msg.data = msg_data.data;
+ event_msg.len = msg_data.len;
+ adapter->eventlistener_->OnEvent(static_cast<EventType>(event_type),
+ event_msg);
+}
+
+void TrackRendererAdapter::FirstDecodingDoneCb_(UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnFirstDecodingDone();
+}
+
+// LCOV_EXCL_START
+void TrackRendererAdapter::SubtitleRawDataCb_(
+ TrackRendererDecoderInputBuffer* buf, const TrackRendererSubtitleType type,
+ UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter || !buf) return;
+
+#ifdef TRACKRENDERER_GST_DEPENDENCY_REMOVAL
+ auto buffer = DecoderInputBuffer::Create(
+ adapter_utils::ConvertToTrackType(buf->type), buf->index,
+ static_cast<GstBuffer*>(buf->buffer));
+#else
+ auto buffer = DecoderInputBuffer::Create(
+ adapter_utils::ConvertToTrackType(buf->type), buf->index, buf->buffer);
+#endif
+ adapter->eventlistener_->OnSubtitleData(
+ std::move(buffer), adapter_utils::ConvertToSubtitleType(type));
+}
+
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+void TrackRendererAdapter::SubtitleDataCb_(const char* data, const int size,
+ const TrackRendererSubtitleType type,
+ const uint64_t duration,
+ TrackRendererSubtitleAttr* attr_list,
+ int attr_list_size,
+ UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+
+ SubtitleAttrList list;
+ int index = 0;
+ for (index = 0; index < attr_list_size; index++) {
+ SubtitleAttr attr{
+ adapter_utils::ConvertToSubtitleAttrType(attr_list[index].type),
+ attr_list[index].start_time, attr_list[index].stop_time,
+ adapter_utils::SetSubtitleAttrValue(attr_list[index]),
+ attr_list[index].extsub_index};
+ list.push_back(std::move(attr));
+ }
+ auto new_list = SubtitleAttrListPtr(new SubtitleAttrList(std::move(list)));
+
+ adapter->eventlistener_->OnSubtitleData(
+ data, size, adapter_utils::ConvertToSubtitleType(type), duration,
+ std::move(new_list));
+}
+#endif
+
+
+void TrackRendererAdapter::ClosedCaptionDataCb_(const char* data,
+ const int size,
+ UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnClosedCaptionData(data, size);
+}
+
+void TrackRendererAdapter::DrmInitDataCb_(int* drmhandle, unsigned int len,
+ unsigned char* psshdata,
+ TrackRendererTrackType type,
+ UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnDrmInitData(
+ drmhandle, len, psshdata, adapter_utils::ConvertToTrackType(type));
+}
+// LCOV_EXCL_STOP
+
+void TrackRendererAdapter::BufferStatusCb_(
+ const TrackRendererTrackType type, const TrackRendererBufferStatus status,
+ UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnBufferStatus(
+ adapter_utils::ConvertToTrackType(type),
+ adapter_utils::ConvertToBufferStatus(status));
+}
+
+void TrackRendererAdapter::SeekDataCb_(const TrackRendererTrackType type,
+ const uint64_t offset,
+ UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnSeekData(adapter_utils::ConvertToTrackType(type),
+ offset);
+}
+// LCOV_EXCL_START
+
+void TrackRendererAdapter::MediaPacketGetTbmBufPtrCb_(void** ptr,
+ bool is_scale_change,
+ UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnMediaPacketGetTbmBufPtr(ptr, is_scale_change);
+}
+
+void TrackRendererAdapter::MediaPacketVideoDecodedCb_(
+ const TrackRendererDecodedVideoPacket* packet, UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ DecodedVideoPacket _packet =
+ adapter_utils::ConvertToDecodedVideoPacket(packet);
+ adapter->eventlistener_->OnMediaPacketVideoDecoded(_packet);
+}
+// LCOV_EXCL_STOP
+
+void TrackRendererAdapter::MediaPacketVideoRawDecodedCb_(
+ const TrackRendererDecodedVideoRawModePacket* packet,
+ TrackRendererDecodedVideoType type, UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ DecodedVideoRawModePacket _packet;
+ _packet.type = static_cast<DecodedVideoRawModePacketType>(type);
+ _packet.pts = packet->pts;
+ _packet.width = packet->width;
+ _packet.height = packet->height;
+ _packet.data =
+ *static_cast<DecodedVideoRawModePacket::Data*>(packet->internal_data);
+ adapter->eventlistener_->OnMediaPacketVideoRawDecoded(_packet);
+}
+
+void TrackRendererAdapter::VideoDecoderUnderrunCb_(UserData userdata) {
+ auto* adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnVideoDecoderUnderrun();
+}
+
+void TrackRendererAdapter::VideoLatencyStatusCb_(
+ const TrackRendererLatencyStatus video_latency_status, UserData userdata) {
+ auto* adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnVideoLatencyStatus(
+ adapter_utils::ConvertToLatencyStatus(video_latency_status));
+}
+
+void TrackRendererAdapter::AudioLatencyStatusCb_(
+ const TrackRendererLatencyStatus audio_latency_status, UserData userdata) {
+ auto* adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnAudioLatencyStatus(
+ adapter_utils::ConvertToLatencyStatus(audio_latency_status));
+}
+
+void TrackRendererAdapter::VideoHighLatencyCb_(UserData userdata) {
+ auto* adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnVideoHighLatency();
+}
+// LCOV_EXCL_START
+
+void TrackRendererAdapter::AudioHighLatencyCb_(UserData userdata) {
+ auto* adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnAudioHighLatency();
+}
+
+void TrackRendererAdapter::MultiviewStartVideoCb_(UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnMultiviewStartVideo();
+}
+
+void TrackRendererAdapter::MultiviewStopVideoCb_(UserData userdata) {
+ auto adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnMultiviewStopVideo();
+}
+// LCOV_EXCL_STOP
+
+void TrackRendererAdapter::VideoFrameDroppedCb_(const uint64_t count,
+ UserData userdata) {
+ auto* adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ adapter->eventlistener_->OnVideoFrameDropped(count);
+}
+
+bool TrackRendererAdapter::GetDecodingTime(StreamType type,
+ int32_t* time_millisecond) {
+
+ TrackRendererTrackType track_type =
+ adapter_utils::ConvertToTrackRendererTrackTypeFromStreamType(type);
+
+ if (trackrenderer_get_decoding_time(handle_, track_type, time_millisecond) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+bool TrackRendererAdapter::SetSimpleMixOutBufferLevel(const int& level) {
+ if (trackrenderer_set_simple_mix_out_buffer_level(handle_, level) ==
+ kFailed) {
+ return false;
+ }
+ return true;
+}
+
+void TrackRendererAdapter::DecoderInputBufferTime(const TrackRendererTrackType type,
+ const TrackRendererDecoderBufferTime time, UserData userdata){
+ auto* adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ DecoderBufferTime decoder_buffer_time;
+ decoder_buffer_time.pts = time.pts;
+ decoder_buffer_time.system_time= time.system_time;
+ adapter->eventlistener_->OnDecoderInputBufferTime(adapter_utils::ConvertToTrackType(type),
+ decoder_buffer_time);
+}
+
+void TrackRendererAdapter::DecoderOutputBufferTime(const TrackRendererTrackType type,
+ const TrackRendererDecoderBufferTime time, UserData userdata){
+ auto* adapter = static_cast<TrackRendererAdapter*>(userdata);
+ if (!adapter) return;
+ DecoderBufferTime decoder_buffer_time;
+ decoder_buffer_time.pts = time.pts;
+ decoder_buffer_time.system_time = time.system_time;
+ adapter->eventlistener_->OnDecoderOutputBufferTime(adapter_utils::ConvertToTrackType(type),
+ decoder_buffer_time);
+}
+} // namespace esplusplayer
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "core/trackrendereradapter_utils.h"
+
+#include <typeinfo>
+
+#include "core/utils/plusplayer_log.h"
+
+namespace esplusplayer {
+
+namespace adapter_utils {
+
+void InitTrack(TrackRendererTrack* track) {
+ track->index = kTrackRendererInvalidTrackIndex; // int index
+ track->id = 0; // int id
+ track->mimetype = nullptr; // const char* mimetype
+ track->streamtype = nullptr; // const char* streamtype
+ track->type = kTrackRendererTrackTypeMax; // int type
+ track->codec_data = nullptr; // char* codec_data
+ track->codec_data_len = 0; // int codec_data_len
+ track->width = 0; // int width
+ track->height = 0; // int height
+ track->maxwidth = 0; // int maxwidth
+ track->maxheight = 0; // int maxheight
+ track->framerate_num = 0; // int framerate_num
+ track->framerate_den = 0; // int framerate_den
+ track->sample_rate = 0; // int sample_rate
+ track->sample_format = 0; // int sample_format
+ track->channels = 0; // int channels
+ track->version = 0; // int version
+ track->layer = 0; // int layer
+ track->bits_per_sample = 0; // int bit_per_sample
+ track->block_align = 0; // int block_align
+ track->bitrate = 0; // int bitrate
+ track->endianness = 1234; // int endianness
+ track->is_signed = 0; // int is_signed
+ track->active = 0; // int active
+ track->use_swdecoder = 0; // int use_swdecoder
+ track->language_code = nullptr; // const char* language_code
+ track->subtitle_format = nullptr; // const char* subtitle_format
+}
+
+void MakeGeometry(Geometry* roi, const TrackRendererGeometry& geometry) {
+ roi->x = geometry.x;
+ roi->y = geometry.y;
+ roi->w = geometry.w;
+ roi->h = geometry.h;
+}
+
+void MakeTrackRendererDrmProperty(
+ TrackRendererDrmProperty* trackrenderer_drm_property,
+ const drm::Property& drm_property) {
+ trackrenderer_drm_property->type =
+ ConvertToTrackRendererDrmType(drm_property.type);
+ trackrenderer_drm_property->handle = static_cast<int>(drm_property.handle);
+ trackrenderer_drm_property->external_decryption =
+ static_cast<bool>(drm_property.external_decryption);
+ trackrenderer_drm_property->license_acquired_cb =
+ static_cast<void*>(drm_property.license_acquired_cb);
+ trackrenderer_drm_property->license_acquired_userdata =
+ static_cast<void*>(drm_property.license_acquired_userdata);
+}
+
+void MakeTrackRendererGeometry(TrackRendererGeometry* geometry,
+ const Geometry& roi) {
+ geometry->x = roi.x;
+ geometry->y = roi.y;
+ geometry->w = roi.w;
+ geometry->h = roi.h;
+}
+
+void MakeTrackRendererCropArea(TrackRendererCropArea* crop,
+ const CropArea& area) {
+ crop->scale_x = area.scale_x;
+ crop->scale_y = area.scale_y;
+ crop->scale_w = area.scale_w;
+ crop->scale_h = area.scale_h;
+}
+
+void MakeTrackRendererRenderRect(TrackRendererRenderRect* output,
+ const RenderRect& input) {
+ output->x = input.x;
+ output->y = input.y;
+ output->w = input.w;
+ output->h = input.h;
+}
+
+void MakeTrackRendererTrack(TrackRendererTrack* track, const Track& trackinfo) {
+ InitTrack(track);
+ track->index = trackinfo.index;
+ track->id = trackinfo.id;
+ track->mimetype = trackinfo.mimetype.c_str();
+ track->streamtype = trackinfo.streamtype.c_str();
+ track->type = ConvertToTrackRendererTrackType(trackinfo.type);
+ track->codec_data = trackinfo.codec_data.get();
+ track->codec_data_len = trackinfo.codec_data_len;
+ track->width = trackinfo.width;
+ track->height = trackinfo.height;
+ track->maxwidth = trackinfo.maxwidth;
+ track->maxheight = trackinfo.maxheight;
+ track->framerate_num = trackinfo.framerate_num;
+ track->framerate_den = trackinfo.framerate_den;
+ track->sample_rate = trackinfo.sample_rate;
+ track->sample_format = trackinfo.sample_format;
+ track->channels = trackinfo.channels;
+ track->version = trackinfo.version;
+ track->layer = trackinfo.layer;
+ track->bits_per_sample = trackinfo.bits_per_sample;
+ track->block_align = trackinfo.block_align;
+ track->bitrate = trackinfo.bitrate;
+ track->endianness = trackinfo.endianness;
+ track->is_signed = trackinfo.is_signed;
+ track->active = trackinfo.active;
+ track->use_swdecoder = trackinfo.use_swdecoder;
+ track->language_code = trackinfo.language_code.c_str();
+ track->subtitle_format = trackinfo.subtitle_format.c_str();
+}
+
+void MakeTrackRendererAppInfo(TrackRendererAppInfo* app_attr,
+ const PlayerAppInfo& app_info) {
+ app_attr->id = const_cast<char*>(app_info.id.c_str());
+ app_attr->version = const_cast<char*>(app_info.version.c_str());
+ app_attr->type = const_cast<char*>(app_info.type.c_str());
+}
+
+void MakeTrackRendererAppInfoEx(TrackRendererAppInfoEx* app_attr,
+ const PlayerAppInfoEx& app_info) {
+ app_attr->id = const_cast<char*>(app_info.id.c_str());
+ app_attr->version = const_cast<char*>(app_info.version.c_str());
+ app_attr->type = const_cast<char*>(app_info.type.c_str());
+ app_attr->runtitle = const_cast<char*>(app_info.runtitle.c_str());
+}
+
+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);
+}
+
+void MakeTrackRendererRational(TrackRendererRational* rational_attr,
+ const Rational& rational_info) {
+ if (!rational_attr) return;
+ rational_attr->num = rational_info.num;
+ rational_attr->den = rational_info.den;
+}
+
+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:
+ LOG_ERROR("unknown DisplayMode");
+ return DisplayMode::kFullScreen;
+ }
+}
+DisplayRotation ConvertToDisplayRotation(
+ const TrackRendererDisplayRotate rotate_value) {
+ switch (rotate_value) {
+ case kTrackRendererDisplayRotateNone: {
+ return DisplayRotation::kNone;
+ }
+ case kTrackRendererDisplayRotate90: {
+ return DisplayRotation::kRotate90;
+ }
+ case kTrackRendererDisplayRotate180: {
+ return DisplayRotation::kRotate180;
+ }
+ case kTrackRendererDisplayRotate270: {
+ return DisplayRotation::kRotate270;
+ }
+ default:
+ 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:
+ LOG_ERROR("unknown DisplayType");
+ return DisplayType::kNone;
+ }
+}
+
+ErrorType ConvertToErrorType(const TrackRendererErrorType type) {
+ switch (type) {
+ case kTrackRendererErrorTypeErrorNone: {
+ return ErrorType::kNone;
+ }
+ case kTrackRendererErrorTypeOutOfMemory: {
+ return ErrorType::kOutOfMemory;
+ }
+ case kTrackRendererErrorTypeInvalidParameter: {
+ return ErrorType::kInvalidParameter;
+ }
+ case kTrackRendererErrorTypeNoSuchFile: {
+ return ErrorType::kNoSuchFile;
+ }
+ case kTrackRendererErrorTypeInvalidOperation: {
+ return ErrorType::kInvalidOperation;
+ }
+ case kTrackRendererErrorTypeFileNoSpaceOnDevice: {
+ return ErrorType::kFileNoSpaceOnDevice;
+ }
+ case kTrackRendererErrorTypeFeatureNotSupportedOnDevice: {
+ return ErrorType::kFeatureNotSupportedOnDevice;
+ }
+ case kTrackRendererErrorTypeSeekFailed: {
+ return ErrorType::kSeekFailed;
+ }
+ case kTrackRendererErrorTypeInvalidState: {
+ return ErrorType::kInvalidState;
+ }
+ case kTrackRendererErrorTypeNotSupportedFile: {
+ return ErrorType::kNotSupportedFile;
+ }
+ case kTrackRendererErrorTypeInvalidUri: {
+ return ErrorType::kInvalidUri;
+ }
+ case kTrackRendererErrorTypeSoundPolicy: {
+ return ErrorType::kSoundPolicy;
+ }
+ case kTrackRendererErrorTypeConnectionFailed: {
+ return ErrorType::kConnectionFailed;
+ }
+ case kTrackRendererErrorTypeVideoCaptureFailed: {
+ return ErrorType::kVideoCaptureFailed;
+ }
+ case kTrackRendererErrorTypeDrmExpired: {
+ return ErrorType::kDrmExpired;
+ }
+ case kTrackRendererErrorTypeDrmNoLicense: {
+ return ErrorType::kDrmNoLicense;
+ }
+ case kTrackRendererErrorTypeDrmFutureUse: {
+ return ErrorType::kDrmFutureUse;
+ }
+ case kTrackRendererErrorTypeDrmNotPermitted: {
+ return ErrorType::kDrmNotPermitted;
+ }
+ case kTrackRendererErrorTypeResourceLimit: {
+ return ErrorType::kResourceLimit;
+ }
+ case kTrackRendererErrorTypePermissionDenied: {
+ return ErrorType::kPermissionDenied;
+ }
+ case kTrackRendererErrorTypeServiceDisconnected: {
+ return ErrorType::kServiceDisconnected;
+ }
+ case kTrackRendererErrorTypeBufferSpace: {
+ return ErrorType::kBufferSpace;
+ }
+ case kTrackRendererErrorTypeNotSupportedAudioCodec: {
+ return ErrorType::kNotSupportedAudioCodec;
+ }
+ case kTrackRendererErrorTypeNotSupportedVideoCodec: {
+ return ErrorType::kNotSupportedVideoCodec;
+ }
+ case kTrackRendererErrorTypeNotSupportedSubtitle: {
+ return ErrorType::kNotSupportedSubtitle;
+ }
+ case kTrackRendererErrorTypeDrmInfo: {
+ return ErrorType::kDrmInfo;
+ }
+ case kTrackRendererErrorTypeNotSupportedFormat: {
+ return ErrorType::kNotSupportedFormat;
+ }
+ case kTrackRendererErrorTypeStreamingPlayer: {
+ return ErrorType::kStreamingPlayer;
+ }
+ case kTrackRendererErrorTypeDtcpFsk: {
+ return ErrorType::kDtcpFsk;
+ }
+ case kTrackRendererErrorTypePreLoadingTimeOut: {
+ return ErrorType::kPreLoadingTimeOut;
+ }
+ case kTrackRendererErrorTypeNetworkError: {
+ return ErrorType::kNetworkError;
+ }
+ case kTrackRendererErrorTypeChannelSurfingFailed: {
+ return ErrorType::kChannelSurfingFailed;
+ }
+ case kTrackRendererErrorTypeUnknown: {
+ return ErrorType::kUnknown;
+ }
+ default:
+ LOG_ERROR("unknown errortype");
+ return ErrorType::kUnknown;
+ }
+}
+
+// LCOV_EXCL_START
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+SubtitleAttrType ConvertToSubtitleAttrType(
+ const TrackRendererSubtitleAttrType& type) {
+ switch (type) {
+ case kTrackRendererSubtitleAttrTypeRegionXPos: {
+ return SubtitleAttrType::kSubAttrRegionXPos;
+ }
+ case kTrackRendererSubtitleAttrTypeRegionYPos: {
+ return SubtitleAttrType::kSubAttrRegionYPos;
+ }
+ case kTrackRendererSubtitleAttrTypeRegionWidth: {
+ return SubtitleAttrType::kSubAttrRegionWidth;
+ }
+ case kTrackRendererSubtitleAttrTypeRegionHeight: {
+ return SubtitleAttrType::kSubAttrRegionHeight;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowXPadding: {
+ return SubtitleAttrType::kSubAttrWindowXPadding;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowYPadding: {
+ return SubtitleAttrType::kSubAttrWindowYPadding;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowLeftMargin: {
+ return SubtitleAttrType::kSubAttrWindowLeftMargin;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowRightMargin: {
+ return SubtitleAttrType::kSubAttrWindowRightMargin;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowTopMargin: {
+ return SubtitleAttrType::kSubAttrWindowTopMargin;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowBottomMargin: {
+ return SubtitleAttrType::kSubAttrWindowBottomMargin;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowBgColor: {
+ return SubtitleAttrType::kSubAttrWindowBgColor;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowOpacity: {
+ return SubtitleAttrType::kSubAttrWindowOpacity;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowShowBg: {
+ return SubtitleAttrType::kSubAttrWindowShowBg;
+ }
+ case kTrackRendererSubtitleAttrTypeFontFamily: {
+ return SubtitleAttrType::kSubAttrFontFamily;
+ }
+ case kTrackRendererSubtitleAttrTypeFontSize: {
+ return SubtitleAttrType::kSubAttrFontSize;
+ }
+ case kTrackRendererSubtitleAttrTypeFontWeight: {
+ return SubtitleAttrType::kSubAttrFontWeight;
+ }
+ case kTrackRendererSubtitleAttrTypeFontStyle: {
+ return SubtitleAttrType::kSubAttrFontStyle;
+ }
+ case kTrackRendererSubtitleAttrTypeFontColor: {
+ return SubtitleAttrType::kSubAttrFontColor;
+ }
+ case kTrackRendererSubtitleAttrTypeFontBgColor: {
+ return SubtitleAttrType::kSubAttrFontBgColor;
+ }
+ case kTrackRendererSubtitleAttrTypeFontOpacity: {
+ return SubtitleAttrType::kSubAttrFontOpacity;
+ }
+ case kTrackRendererSubtitleAttrTypeFontBgOpacity: {
+ return SubtitleAttrType::kSubAttrFontBgOpacity;
+ }
+ case kTrackRendererSubtitleAttrTypeFontTextOutlineColor: {
+ return SubtitleAttrType::kSubAttrFontTextOutlineColor;
+ }
+ case kTrackRendererSubtitleAttrTypeFontTextOutlineThickness: {
+ return SubtitleAttrType::kSubAttrFontTextOutlineThickness;
+ }
+ case kTrackRendererSubtitleAttrTypeFontTextOutlineBlurRadius: {
+ return SubtitleAttrType::kSubAttrFontTextOutlineBlurRadius;
+ }
+ case kTrackRendererSubtitleAttrTypeFontVerticalAlign: {
+ return SubtitleAttrType::kSubAttrFontVerticalAlign;
+ }
+ case kTrackRendererSubtitleAttrTypeFontHorizontalAlign: {
+ return SubtitleAttrType::kSubAttrFontHorizontalAlign;
+ }
+ case kTrackRendererSubtitleAttrTypeRawSubtitle: {
+ return SubtitleAttrType::kSubAttrRawSubtitle;
+ }
+ case kTrackRendererSubtitleAttrTypeWebvttCueLine: {
+ return SubtitleAttrType::kSubAttrWebvttCueLine;
+ }
+ case kTrackRendererSubtitleAttrTypeWebvttCueLineNum: {
+ return SubtitleAttrType::kSubAttrWebvttCueLineNum;
+ }
+ case kTrackRendererSubtitleAttrTypeWebvttCueLineAlign: {
+ return SubtitleAttrType::kSubAttrWebvttCueLineAlign;
+ }
+ case kTrackRendererSubtitleAttrTypeWebvttCueAlign: {
+ return SubtitleAttrType::kSubAttrWebvttCueAlign;
+ }
+ case kTrackRendererSubtitleAttrTypeWebvttCueSize: {
+ return SubtitleAttrType::kSubAttrWebvttCueSize;
+ }
+ case kTrackRendererSubtitleAttrTypeWebvttCuePosition: {
+ return SubtitleAttrType::kSubAttrWebvttCuePosition;
+ }
+ case kTrackRendererSubtitleAttrTypeWebvttCuePositionAlign: {
+ return SubtitleAttrType::kSubAttrWebvttCuePositionAlign;
+ }
+ case kTrackRendererSubtitleAttrTypeWebvttCueVertical: {
+ return SubtitleAttrType::kSubAttrWebvttCueVertical;
+ }
+ case kTrackRendererSubtitleAttrTypeTimestamp: {
+ return SubtitleAttrType::kSubAttrTimestamp;
+ }
+ case kTrackRendererSubtitleAttrTypeExtsubIndex: {
+ return SubtitleAttrType::kSubAttrExtsubIndex;
+ }
+ case kTrackRendererSubtitleAttrTypeTypeNone: {
+ return SubtitleAttrType::kSubAttrTypeNone;
+ }
+ default:
+ LOG_ERROR("unknown subtitle attr tracktype");
+ return SubtitleAttrType::kSubAttrTypeNone;
+ }
+}
+#endif
+// LCOV_EXCL_STOP
+
+SubtitleType ConvertToSubtitleType(const TrackRendererSubtitleType& type) {
+ switch (type) {
+ case kTrackRendererSubtitleTypeText: {
+ return SubtitleType::kText;
+ }
+ case kTrackRendererSubtitleTypePicture: {
+ return SubtitleType::kPicture;
+ }
+ case kTrackRendererSubtitleTypeInvalid: {
+ return SubtitleType::kInvalid;
+ }
+ default:
+ LOG_ERROR("unknown subtitletype");
+ return SubtitleType::kInvalid;
+ }
+}
+
+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:
+ LOG_ERROR("unknown tracktype");
+ return TrackType::kTrackTypeMax;
+ }
+}
+
+DecodedVideoPacket ConvertToDecodedVideoPacket(
+ const TrackRendererDecodedVideoPacket* packet) {
+ DecodedVideoPacket _packet;
+ _packet.pts = packet->pts;
+ _packet.duration = packet->duration;
+ _packet.surface_data = static_cast<tbm_surface_h>(packet->surface_data);
+ _packet.scaler_index = packet->scaler_index;
+ return _packet;
+}
+
+TrackRendererDecodedVideoPacket ConvertToDecodedVideoPacket(
+ const DecodedVideoPacket& packet) {
+ TrackRendererDecodedVideoPacket _packet;
+ _packet.pts = packet.pts;
+ _packet.duration = packet.duration;
+ _packet.surface_data = static_cast<tbm_surface_h>(packet.surface_data);
+ _packet.scaler_index = packet.scaler_index;
+ return _packet;
+}
+
+TrackRendererDecodedVideoFrameBufferType ConvertToVideoFrameBufferType(
+ const DecodedVideoFrameBufferType& type) {
+ switch (type) {
+ case DecodedVideoFrameBufferType::kCopy: {
+ return kTrackRendererDecodedVideoFrameBufferCopy;
+ }
+ case DecodedVideoFrameBufferType::kReference: {
+ return kTrackRendererDecodedVideoFrameBufferReference;
+ }
+ case DecodedVideoFrameBufferType::kScale: {
+ return kTrackRendererDecodedVideoFrameBufferScale;
+ }
+ case DecodedVideoFrameBufferType::kManualCopy: {
+ return kTrackRendererDecodedVideoFrameBufferManualCopy;
+ }
+ case DecodedVideoFrameBufferType::kNone: {
+ return kTrackRendererDecodedVideoFrameBufferNone;
+ }
+ default:
+ LOG_ERROR("wrong buffer type");
+ return kTrackRendererDecodedVideoFrameBufferNone;
+ }
+}
+
+GetDecodedVideoFrameStatus ConvertToGetDecodedVideoFrameStatus(
+ const TrackRendererGetDecodedVideoFrameState state) {
+ switch (state) {
+ case TrackRendererGetDecodedVideoFrameStateErrorNone: {
+ return GetDecodedVideoFrameStatus::kSuccess;
+ }
+ case TrackRendererGetDecodedVideoFrameStateNoRemainingBufferError: {
+ return GetDecodedVideoFrameStatus::kNoRemainingBuffer;
+ }
+ case TrackRendererGetDecodedVideoFrameStateNoFilledBufferError: {
+ return GetDecodedVideoFrameStatus::kNoFilledBuffer;
+ }
+ case TrackRendererGetDecodedVideoFrameStateUnknownError: {
+ return GetDecodedVideoFrameStatus::kUnknown;
+ }
+ default:
+ LOG_ERROR("wrong state type");
+ return GetDecodedVideoFrameStatus::kUnknown;
+ }
+}
+
+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:
+ LOG_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:
+ LOG_ERROR("unknown displayrotate");
+ 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:
+ LOG_ERROR("unknown displaytype");
+ return kTrackRendererDisplayTypeNone;
+ }
+}
+
+TrackRendererDrmType ConvertToTrackRendererDrmType(const drm::Type& drm_type) {
+ switch (drm_type) {
+ case drm::Type::kNone: {
+ return kTrackRendererDrmTypeNone;
+ }
+ case drm::Type::kPlayready: {
+ return kTrackRendererDrmTypePlayready;
+ }
+ case drm::Type::kMarlin: {
+ return kTrackRendererDrmTypeMarlin;
+ }
+ case drm::Type::kVerimatrix: {
+ return kTrackRendererDrmTypeVerimatrix;
+ }
+ case drm::Type::kWidevineClassic: {
+ return kTrackRendererDrmTypeWidevineClassic;
+ }
+ case drm::Type::kSecuremedia: {
+ return kTrackRendererDrmTypeSecuremedia;
+ }
+ case drm::Type::kSdrm: {
+ return kTrackRendererDrmTypeSdrm;
+ }
+ case drm::Type::kWidevineCdm: {
+ return kTrackRendererDrmTypeWidevineCdm;
+ }
+ case drm::Type::kMax: {
+ return kTrackRendererDrmTypeDrmMax;
+ }
+ default:
+ LOG_ERROR("unknown drmtype");
+ return kTrackRendererDrmTypeNone;
+ }
+}
+
+TrackRendererStillMode ConvertToTrackRendererStillMode(
+ const StillMode& still_mode) {
+ switch (still_mode) {
+ case StillMode::kNone: {
+ return kTrackRendererStillModeNone;
+ }
+ case StillMode::kOff: {
+ return kTrackRendererStillModeOff;
+ }
+ case StillMode::kOn: {
+ return kTrackRendererStillModeOn;
+ }
+ default:
+ LOG_ERROR("unknown stillmode");
+ return kTrackRendererStillModeNone;
+ }
+}
+
+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:
+ LOG_ERROR("unknown tracktype");
+ return kTrackRendererTrackTypeMax;
+ }
+}
+
+TrackRendererTrackType ConvertToTrackRendererTrackTypeFromStreamType(
+ const StreamType& type) {
+ switch (type) {
+ case StreamType::kAudio: {
+ return kTrackRendererTrackTypeAudio;
+ }
+ case StreamType::kVideo: {
+ return kTrackRendererTrackTypeVideo;
+ }
+ case StreamType::kMax: {
+ return kTrackRendererTrackTypeMax;
+ }
+ default:
+ LOG_ERROR("unknown steamtype");
+ return kTrackRendererTrackTypeMax;
+ }
+}
+
+TrackRendererVideoStreamRotation ConvertToTrackRendererVideoStreamRotation(
+ const VideoRotation& rotation) {
+ switch (rotation) {
+ case VideoRotation::kVideoRotateNone: {
+ return kTrackRendererVideoStreamRotationNone;
+ }
+ case VideoRotation::kVideoRotate90: {
+ return kTrackRendererVideoStreamRotation90;
+ }
+ case VideoRotation::kVideoRotate180: {
+ return kTrackRendererVideoStreamRotation180;
+ }
+ case VideoRotation::kVideoRotate270: {
+ return kTrackRendererVideoStreamRotation270;
+ }
+ default:
+ LOG_ERROR("unknown rotation type");
+ return kTrackRendererVideoStreamRotationNone;
+ }
+}
+
+// LCOV_EXCL_START
+#ifndef TRACKRENDERER_FEATURE_DEPRECATE_SUBTITLE_CB
+boost::any SetSubtitleAttrValue(const TrackRendererSubtitleAttr& attr) {
+ boost::any any_value;
+ switch (attr.type) {
+ case kTrackRendererSubtitleAttrTypeRegionXPos: // fall through
+ case kTrackRendererSubtitleAttrTypeRegionYPos: // fall through
+ case kTrackRendererSubtitleAttrTypeRegionWidth: // fall through
+ case kTrackRendererSubtitleAttrTypeRegionHeight: // fall through
+ case kTrackRendererSubtitleAttrTypeWindowXPadding: // fall through
+ case kTrackRendererSubtitleAttrTypeWindowYPadding: // fall through
+ case kTrackRendererSubtitleAttrTypeWindowOpacity: // fall through
+ case kTrackRendererSubtitleAttrTypeWindowShowBg: // fall through
+ case kTrackRendererSubtitleAttrTypeFontSize: // fall through
+ case kTrackRendererSubtitleAttrTypeFontOpacity: // fall through
+ case kTrackRendererSubtitleAttrTypeFontBgOpacity: // fall through
+ case kTrackRendererSubtitleAttrTypeWebvttCueLine: // fall through
+ case kTrackRendererSubtitleAttrTypeWebvttCueSize: // fall through
+ case kTrackRendererSubtitleAttrTypeWebvttCuePosition: // fall through
+ case kTrackRendererSubtitleAttrTypeWebvttCueVertical: {
+ if (attr.value.f) any_value = attr.value.f;
+ break;
+ }
+ case kTrackRendererSubtitleAttrTypeWindowLeftMargin: // fall through
+ case kTrackRendererSubtitleAttrTypeWindowRightMargin: // fall through
+ case kTrackRendererSubtitleAttrTypeWindowTopMargin: // fall through
+ case kTrackRendererSubtitleAttrTypeWindowBottomMargin: // fall through
+ case kTrackRendererSubtitleAttrTypeWindowBgColor: // fall through
+ case kTrackRendererSubtitleAttrTypeFontWeight: // fall through
+ case kTrackRendererSubtitleAttrTypeFontStyle: // fall through
+ case kTrackRendererSubtitleAttrTypeFontColor: // fall through
+ case kTrackRendererSubtitleAttrTypeFontBgColor: // fall through
+ case kTrackRendererSubtitleAttrTypeFontTextOutlineColor: // fall through
+ case kTrackRendererSubtitleAttrTypeFontTextOutlineThickness: // fall
+ // through
+ case kTrackRendererSubtitleAttrTypeFontTextOutlineBlurRadius: // fall
+ // through
+ case kTrackRendererSubtitleAttrTypeFontVerticalAlign: // fall through
+ case kTrackRendererSubtitleAttrTypeFontHorizontalAlign: // fall through
+ case kTrackRendererSubtitleAttrTypeWebvttCueLineNum: // fall through
+ case kTrackRendererSubtitleAttrTypeWebvttCueLineAlign: // fall through
+ case kTrackRendererSubtitleAttrTypeWebvttCueAlign: // fall through
+ case kTrackRendererSubtitleAttrTypeWebvttCuePositionAlign: {
+ if (attr.value.i32) any_value = attr.value.i32;
+ break;
+ }
+ case kTrackRendererSubtitleAttrTypeFontFamily: // fall through
+ case kTrackRendererSubtitleAttrTypeRawSubtitle: {
+ if (attr.value.str) any_value = std::string(attr.value.str);
+ break;
+ }
+ case kTrackRendererSubtitleAttrTypeTimestamp:
+ case kTrackRendererSubtitleAttrTypeExtsubIndex:
+ break;
+ default:
+ LOG_ERROR("Unknown subtitle attr type");
+ }
+ return any_value;
+}
+#endif
+// LCOV_EXCL_STOP
+
+BufferStatus ConvertToBufferStatus(const TrackRendererBufferStatus& status) {
+ switch (status) {
+ case kTrackRendererBufferStatusUnderrun: {
+ return BufferStatus::kUnderrun;
+ }
+ case kTrackRendererBufferStatusOverrun: {
+ return BufferStatus::kOverrun;
+ }
+ }
+ LOG_ERROR("Unknown buffern status");
+ return BufferStatus::kUnderrun;
+}
+
+TrackRendererCatchUpSpeed ConvertToTrackRendererCatchUpSpeed(
+ const CatchUpSpeed& level) {
+ switch (level) {
+ case CatchUpSpeed::kNone: {
+ return kTrackRendererCatchUpSpeedNone;
+ }
+ case CatchUpSpeed::kSlow: {
+ return kTrackRendererCatchUpSpeedSlow;
+ }
+ case CatchUpSpeed::kNormal: {
+ return kTrackRendererCatchUpSpeedNormal;
+ }
+ case CatchUpSpeed::kFast: {
+ return kTrackRendererCatchUpSpeedFast;
+ }
+ }
+ LOG_ERROR("Unknown catch up speed");
+ return kTrackRendererCatchUpSpeedNone;
+}
+
+LatencyStatus ConvertToLatencyStatus(const TrackRendererLatencyStatus& status) {
+ switch (status) {
+ case kTrackRendererLatencyStatusLow: {
+ return LatencyStatus::kLow;
+ }
+ case kTrackRendererLatencyStatusMid: {
+ return LatencyStatus::kMid;
+ }
+ case kTrackRendererLatencyStatusHigh: {
+ return LatencyStatus::kHigh;
+ }
+ }
+ LOG_ERROR("Unknown status");
+ return LatencyStatus::kLow;
+}
+
+AudioEasingType ConvertToAudioEasingType(
+ const TrackRendererAudioEasingType& type) {
+ switch (type) {
+ case TrackRendererAudioEasingType::kTrackRendererAudioEasingLinear: {
+ return AudioEasingType::kAudioEasingLinear;
+ }
+ case TrackRendererAudioEasingType::kTrackRendererAudioEasingIncubic: {
+ return AudioEasingType::kAudioEasingIncubic;
+ }
+ case TrackRendererAudioEasingType::kTrackRendererAudioEasingOutcubic: {
+ return AudioEasingType::kAudioEasingOutcubic;
+ }
+ default:
+ LOG_ERROR("Unknown audio easing type");
+ return AudioEasingType::kAudioEasingNone;
+ }
+}
+
+TrackRendererAudioEasingType ConvertToTrackRendererAudioEasingType(
+ const AudioEasingType& type) {
+ switch (type) {
+ case AudioEasingType::kAudioEasingLinear: {
+ return TrackRendererAudioEasingType::kTrackRendererAudioEasingLinear;
+ }
+ case AudioEasingType::kAudioEasingIncubic: {
+ return TrackRendererAudioEasingType::kTrackRendererAudioEasingIncubic;
+ }
+ case AudioEasingType::kAudioEasingOutcubic: {
+ return TrackRendererAudioEasingType::kTrackRendererAudioEasingOutcubic;
+ }
+ default:
+ LOG_ERROR("Unknown audio easing type");
+ return TrackRendererAudioEasingType::kTrackRendererAudioEasingNone;
+ }
+}
+
+bool ConvertToTrackRendererRscType(const RscType& typevalue,
+ TrackRendererRscType* type) {
+ switch (typevalue) {
+ case RscType::kVideoRenderer: {
+ *type = kTrackRendererRscTypeVideoRenderer;
+ return true;
+ }
+ default:
+ LOG_ERROR("unknown resource type");
+ return false;
+ }
+}
+
+bool ConvertToTrackRendererAdvPictureQualityType(
+ const AdvPictureQualityType& typevalue,
+ TrackRendererAdvPictureQualityType* type) {
+ switch (typevalue) {
+ case AdvPictureQualityType::kVideoCall: {
+ *type = kTrackRendererAdvPictureQualityTypeVideoCall;
+ return true;
+ }
+ case AdvPictureQualityType::kUsbCamera: {
+ *type = kTrackRendererAdvPictureQualityTypeUsbCamera;
+ return true;
+ }
+ case AdvPictureQualityType::kAirplayMirroring: {
+ *type = kTrackRendererAdvPictuerQualityTypeAirplayMirroring;
+ return true;
+ }
+ default:
+ LOG_ERROR("unknown resource type");
+ return false;
+ }
+}
+
+bool ConvertToTrackRendererRscAllocPolicy(const RscAllocPolicy& policyvalue,
+ TrackRendererRscAllocPolicy* policy) {
+ switch (policyvalue) {
+ case RscAllocPolicy::kRscAllocExclusive: {
+ *policy = kTrackRendererRscAllocExclusive;
+ return true;
+ }
+ case RscAllocPolicy::kRscAllocConditional: {
+ *policy = kTrackRendererRscAllocConditional;
+ return true;
+ }
+ case RscAllocPolicy::kRscAllocExclusiveNoExplicit: {
+ *policy = kTrackRendererRscAllocExclusiveNoExplicit;
+ return true;
+ }
+ default:
+ LOG_ERROR("unknown policy");
+ return false;
+ }
+}
+
+bool ConvertToTrackRendererAlternativeAudioResource(
+ const PlayerAudioResourceType& typevalue, unsigned int* type) {
+ switch (typevalue) {
+ case kPlayerAudioResourceTypeMain:
+ *type = 0;
+ return true;
+ case kPlayerAudioResourceTypeSubDecoder:
+ *type = 1;
+ return true;
+ case kPlayerAudioResourceTypeSimpleMixOut:
+ *type = 3;
+ return true;
+ default:
+ LOG_ERROR("unkown type");
+ return false;
+ }
+}
+
+} // namespace adapter_utils
+
+} // namespace esplusplayer
--- /dev/null
+#include "core/videoframetypestrategy.h"
+
+#include <trackrenderer_capi/trackrenderer_capi.h>
+#include <trackrenderer_capi/trackrenderer_internal.h>
+
+#include "core/trackrendereradapter_utils.h"
+
+namespace esplusplayer {
+DefaultVideoFrameTypeStrategy::DefaultVideoFrameTypeStrategy(
+ const DecodedVideoFrameBufferType type)
+ : type_(type) {}
+
+void DefaultVideoFrameTypeStrategy::SetType(TrackRendererHandle handle) {
+ trackrenderer_set_video_frame_buffer_type(
+ handle, adapter_utils::ConvertToVideoFrameBufferType(type_));
+}
+
+void RawVideoFrameTypeStrategy::SetType(TrackRendererHandle handle) {
+ trackrenderer_set_video_frame_buffer_type_ext(
+ handle, kTrackRendererDecodedVideoFrameBufferExtRaw);
+}
+} // namespace esplusplayer
\ No newline at end of file
--- /dev/null
+unit_test/ut_esplusplayer_all.xml
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestFarm>
+ <Target AssignTargets="1">
+ <TestPackage Name="esplusplayer" RpmName="esplusplayer-ut-component-tomato" DatFile="TCList.dat"/>
+</TestFarm>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<TestCase Name="ESPlusplayer API Test" Description="ESPlusplayer APIs unit test" LogFilter="GST_LOG:* TOMATO:* PLUSPLAYER:* STREAMING_ENGINE:*">
+ <Procedure Number="1" Description="ESPlusplayer unit test">
+ <Step Name="ESPlusplayer unit test" Type="EXT_TEST_PACKAGE" Command="GTEST_TOTAL_SHARDS=1 GTEST_SHARD_INDEX=0 /usr/bin/esplusplayer_ut --gtest_output=xml:/usr/etc/esplusplayer_tests_result.xml" Permission="ROOT">
+ <Input Expirytime="10800"/>
+ <Output Type="DetectFile" FilePath="/usr/etc/esplusplayer_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++17 -pthread -fPIE -Wl,-z,relro -fstack-protector -fno-delete-null-pointer-checks -DEFL_BETA_API_SUPPORT")
+
+SET(${fw_name}_LDFLAGS)
+
+IF(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+SET(ADD_LIBS
+ "espplayer-core"
+ "trackrenderer"
+ "esplusplayer"
+ "mixer"
+ "gstvideo-1.0"
+)
+ELSE(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+SET(ADD_LIBS
+ "espplayer-core"
+ "trackrenderer"
+ "esplusplayer"
+ "gstvideo-1.0"
+)
+ENDIF(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+
+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"
+ "capi-media-player libavoc"
+ "video-capture libturbojpeg libjpeg"
+ "audio-control video-sink capi-system-info"
+ )
+
+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}/ut/include
+ ${PROJECT_SOURCE_DIR}/src
+ ${PROJECT_SOURCE_DIR}/include
+ ${PROJECT_SOURCE_DIR}
+ ${PROJECT_SOURCE_DIR}/src/plusplayer-core/include_internal
+ ${PROJECT_SOURCE_DIR}/src/esplusplayer/include_internal
+ ${PROJECT_SOURCE_DIR}/src/mixer/include_internal
+)
+
+FILE(GLOB UT_SRC
+ src/utils/*.cpp
+ src/esplusplayer/tclist.cpp
+ src/ut_main.cpp
+ src/ut_cloudgame.cpp
+)
+
+IF(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+SET(UT_MIXER_SRC
+ src/mixer/constant.cpp
+ src/mixer/matcher.cpp
+ src/mixer/ut_mixer_capi.cpp
+ src/mixer/ut_mixer_espp_capi.cpp
+ src/mixer/ut_mixer.cpp
+ src/mixer/ut_mixerticket.cpp
+# src/mixer/ut_mixerscenario.cpp
+ src/mixer/ut_espp_mixerscenario.cpp
+ src/mixer/ut_mixedframe.cpp
+ src/mixer/ut_renderer.cpp
+ src/mixer/ut_tizenbuffermgr.cpp
+ src/mixer/ut_tizenbufferobj.cpp
+ src/mixer/ut_videoplane.cpp
+)
+#ADD_EXECUTABLE(${fw_name} ${UT_SRC} ${UT_MIXER_SRC})
+#ELSE(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+#ADD_EXECUTABLE(${fw_name} ${UT_SRC})
+ENDIF(${PRODUCT_TYPE_AUDIO} STREQUAL "no")
+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**
+===============
+ For unit test for plusplayer
+
+---
+### Reference
+- <https://github.com/google/googletest>
+- <http://stinkfist.egloos.com/2262578>
+
+## Assertion
+* ASSERT_* : 실패시 해당 테스트를 바로 종료 <br>
+* EXPECT_* : 실패하여도 테스트 계속 진행 <br>
+
+#### Basic Assertions ###
+
+These assertions do basic true/false condition testing.
+
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
+|:--------------------|:-----------------------|:-------------|
+| `ASSERT_TRUE(`_condition_`)`; | `EXPECT_TRUE(`_condition_`)`; | _condition_ is true |
+| `ASSERT_FALSE(`_condition_`)`; | `EXPECT_FALSE(`_condition_`)`; | _condition_ is false |
+
+#### Binary Comparison ###
+
+This section describes assertions that compare two values.
+
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
+|:--------------------|:-----------------------|:-------------|
+|`ASSERT_EQ(`_val1_`, `_val2_`);`|`EXPECT_EQ(`_val1_`, `_val2_`);`| _val1_ `==` _val2_ |
+|`ASSERT_NE(`_val1_`, `_val2_`);`|`EXPECT_NE(`_val1_`, `_val2_`);`| _val1_ `!=` _val2_ |
+|`ASSERT_LT(`_val1_`, `_val2_`);`|`EXPECT_LT(`_val1_`, `_val2_`);`| _val1_ `<` _val2_ |
+|`ASSERT_LE(`_val1_`, `_val2_`);`|`EXPECT_LE(`_val1_`, `_val2_`);`| _val1_ `<=` _val2_ |
+|`ASSERT_GT(`_val1_`, `_val2_`);`|`EXPECT_GT(`_val1_`, `_val2_`);`| _val1_ `>` _val2_ |
+|`ASSERT_GE(`_val1_`, `_val2_`);`|`EXPECT_GE(`_val1_`, `_val2_`);`| _val1_ `>=` _val2_ |
+
+
+#### String Comparison ###
+
+The assertions in this group compare two **C strings**.<br>
+If you want to compare two `string` objects, use `EXPECT_EQ`, `EXPECT_NE`, and etc instead.
+
+| **Fatal assertion** | **Nonfatal assertion** | **Verifies** |
+|:--------------------|:-----------------------|:-------------|
+| `ASSERT_STREQ(`_str1_`, `_str2_`);` | `EXPECT_STREQ(`_str1_`, `_str_2`);` | the two C strings have the same content |
+| `ASSERT_STRNE(`_str1_`, `_str2_`);` | `EXPECT_STRNE(`_str1_`, `_str2_`);` | the two C strings have different content |
+| `ASSERT_STRCASEEQ(`_str1_`, `_str2_`);`| `EXPECT_STRCASEEQ(`_str1_`, `_str2_`);` | the two C strings have the same content, ignoring case |
+| `ASSERT_STRCASENE(`_str1_`, `_str2_`);`| `EXPECT_STRCASENE(`_str1_`, `_str2_`);` | the two C strings have different content, ignoring case |
+
+## Text Fixtures : Using the Same Data Configuration for Multiple Tests ##
+
+사용자가 유사한 data를 사용해서 하나 이상의 test를 작성한다면, test fixture를 사용할 수 있다. 이 test fixture를 사용한다는 것은 여러개의 다양한 test를 작성하는 과정에서 같은 object의 configuration을 재사용한다는 것을 의미한다.
+
+Fixture를 작성할 때에는 아래의 내용대로 수행하면 된다.
+
+1. ::testing::Test 로부터 class를 derive한다. Sub-class 에서 fixture member에 접근해야 하기 때문에 protected 혹은 public 으로 작성해야 한다.
+2. Class 내부에서 사용자가 원하는대로 object들을 선언해 사용한다.
+3. 필요하다면, 생성자나 SetUp() function을 작성해둔다.
+4. 생성자나 SetUp() function을 정의해서 사용하고 있다면, 해당 function에서 사용했던 resource를 반환하기 위해 소멸자나 TearDown() function을 작성한다.
+5. Subroutine 들을 작성한다.
+
+Fixture를 사용하기 위해서는 TEST() 대신에 TEST_F()를 사용해야만 한다.
+TEST()에서는 첫번째 argument가 testcase의 이름이었지만 TEST_F()를 사용할 때는 첫번째 argument로 test fixture class의 이름을 사용해야만 한다.
+
+**Fixture class 기본 구현 Form**
+* 관례에 따라 테스트할 클래스가 Foo라면 이름을 FooTest라고 하는게 좋다.
+~~~
+class PlusPlayerTest : public ::testing::Test {
+public:
+ PlusPlayerTest(std::string url)
+ : plusplayer_(nullptr), url_(url)
+ {
+ }
+
+ void SetUp() override
+ {
+ plusplayer_ = new PlusPlayer();
+ create(url_);
+ }
+
+ void TearDown() override
+ {
+ destory(plusplayer_);
+ }
+
+private:
+ std::string url_;
+ PlusPlayer* plusplayer_;
+
+}
+~~~
+
+**실행 순서**
+1. 모든 구글 테스트 플래그 상태를 저장한다.
+2. 첫 번째 테스트에 대해 테스트 fixture 객체를 생성한다.
+3. 만든 객체를 SetUp()에서 초기화한다.
+4. 픽스처 객체에 대해 테스트를 실행한다.
+5. TearDown()에서 해당 픽스처를 정리한다.
+6. 해당 픽스처를 삭제한다.
+7. 모든 구글 테스트 플래그 상태를 복원한다.
+8. 모든 테스트를 마칠 때까지 다음 테스트에 대해 위 과정을 반복한다.
+
+---
+
+## Arguments
+reference <http://m.blog.naver.com/disturb000/130171100719> <br>
+
+1. test selection
+ * --gtest_list_tests <br>
+ > 테스트할 항목을 보여준다. (테스트 실시 X)
+ * --gtest_also_run_disabled_tests <br>
+ > DISABLED_ 로 막아둔 test case 를 일시적으로 실행
+ * --gtest_filter <br>
+ > 특정 case 들만 실행 가능<br>
+ Ex) --gtest_filter="*.create*" : 모든 TC중에서 create 로 시작하는 모든 TC 실행. <br>
+
+2. test Execution
+ * --gtest_repeat <br>
+ > test 반복 가능. -1일 경우 무한히 반복<br>
+ --gtest_break_on_failure와 함께 사용하면 실패할 경우 멈춤.
+ * --gtest_shuffle <br>
+ > 무작위로 실행 가능 (test case 간 dependency 가 없어야 하기 때문이다)<br>
+ gtest는 기본적으로 현재시간을 랜덤함수의 시드값으로 사용하나, --gtest_random_seed로 조절가능하다
+ * --gtest_random_seed
+ > 1 ~ 99999까지의 값을 --gtest_shuffle에서 사용할 랜덤함수의 시드로 사용.
+
+---
+## Reference
+ * Gtest Primer(KR)<br> <https://surpreem.com/%EC%9E%85%EB%AC%B8-%EA%B5%AC%EA%B8%80-c-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0/>
+ * Gtest Primer(EN)<br> <https://github.com/google/googletest/blob/master/googletest/docs/Primer.md>
+ * Unit Test Planning & How to write Unit Test Code(KR)<br> <http://168.219.243.246:8090/pages/viewpage.action?pageId=9112244>
+ * Unit Test Planning & How to write Unit Test Code(EN)<br> <http://168.219.243.246:8090/pages/viewpage.action?pageId=9113344>
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2009 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Does google-lint on c++ files.
+
+The goal of this script is to identify places in the code that *may*
+be in non-compliance with google style. It does not attempt to fix
+up these problems -- the point is to educate. It does also not
+attempt to find all problems, or to ensure that everything it does
+find is legitimately a problem.
+
+In particular, we can get very confused by /* and // inside strings!
+We do a small hack, which is to ignore //'s with "'s after them on the
+same line, but it is far from perfect (in either direction).
+"""
+
+import codecs
+import copy
+import getopt
+import math # for log
+import os
+import re
+import sre_compile
+import string
+import sys
+import unicodedata
+
+
+_USAGE = """
+Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
+ [--counting=total|toplevel|detailed] [--root=subdir]
+ [--linelength=digits] [--headers=x,y,...]
+ <file> [file] ...
+
+ The style guidelines this tries to follow are those in
+ https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
+
+ Every problem is given a confidence score from 1-5, with 5 meaning we are
+ certain of the problem, and 1 meaning it could be a legitimate construct.
+ This will miss some errors, and is not a substitute for a code review.
+
+ To suppress false-positive errors of a certain category, add a
+ 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*)
+ suppresses errors of all categories on that line.
+
+ The files passed in will be linted; at least one file must be provided.
+ Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the
+ extensions with the --extensions flag.
+
+ Flags:
+
+ output=vs7
+ By default, the output is formatted to ease emacs parsing. Visual Studio
+ compatible output (vs7) may also be used. Other formats are unsupported.
+
+ verbose=#
+ Specify a number 0-5 to restrict errors to certain verbosity levels.
+
+ filter=-x,+y,...
+ Specify a comma-separated list of category-filters to apply: only
+ error messages whose category names pass the filters will be printed.
+ (Category names are printed with the message and look like
+ "[whitespace/indent]".) Filters are evaluated left to right.
+ "-FOO" and "FOO" means "do not print categories that start with FOO".
+ "+FOO" means "do print categories that start with FOO".
+
+ Examples: --filter=-whitespace,+whitespace/braces
+ --filter=whitespace,runtime/printf,+runtime/printf_format
+ --filter=-,+build/include_what_you_use
+
+ To see a list of all the categories used in cpplint, pass no arg:
+ --filter=
+
+ counting=total|toplevel|detailed
+ The total number of errors found is always printed. If
+ 'toplevel' is provided, then the count of errors in each of
+ the top-level categories like 'build' and 'whitespace' will
+ also be printed. If 'detailed' is provided, then a count
+ is provided for each category like 'build/class'.
+
+ root=subdir
+ The root directory used for deriving header guard CPP variable.
+ By default, the header guard CPP variable is calculated as the relative
+ path to the directory that contains .git, .hg, or .svn. When this flag
+ is specified, the relative path is calculated from the specified
+ directory. If the specified directory does not exist, this flag is
+ ignored.
+
+ Examples:
+ Assuming that src/.git exists, the header guard CPP variables for
+ src/chrome/browser/ui/browser.h are:
+
+ No flag => CHROME_BROWSER_UI_BROWSER_H_
+ --root=chrome => BROWSER_UI_BROWSER_H_
+ --root=chrome/browser => UI_BROWSER_H_
+
+ linelength=digits
+ This is the allowed line length for the project. The default value is
+ 80 characters.
+
+ Examples:
+ --linelength=120
+
+ extensions=extension,extension,...
+ The allowed file extensions that cpplint will check
+
+ Examples:
+ --extensions=hpp,cpp
+
+ headers=x,y,...
+ The header extensions that cpplint will treat as .h in checks. Values are
+ automatically added to --extensions list.
+
+ Examples:
+ --headers=hpp,hxx
+ --headers=hpp
+
+ cpplint.py supports per-directory configurations specified in CPPLINT.cfg
+ files. CPPLINT.cfg file can contain a number of key=value pairs.
+ Currently the following options are supported:
+
+ set noparent
+ filter=+filter1,-filter2,...
+ exclude_files=regex
+ linelength=80
+ root=subdir
+ headers=x,y,...
+
+ "set noparent" option prevents cpplint from traversing directory tree
+ upwards looking for more .cfg files in parent directories. This option
+ is usually placed in the top-level project directory.
+
+ The "filter" option is similar in function to --filter flag. It specifies
+ message filters in addition to the |_DEFAULT_FILTERS| and those specified
+ through --filter command-line flag.
+
+ "exclude_files" allows to specify a regular expression to be matched against
+ a file name. If the expression matches, the file is skipped and not run
+ through liner.
+
+ "linelength" allows to specify the allowed line length for the project.
+
+ The "root" option is similar in function to the --root flag (see example
+ above).
+
+ The "headers" option is similar in function to the --headers flag
+ (see example above).
+
+ CPPLINT.cfg has an effect on files in the same directory and all
+ sub-directories, unless overridden by a nested configuration file.
+
+ Example file:
+ filter=-build/include_order,+build/include_alpha
+ exclude_files=.*\.cc
+
+ The above example disables build/include_order warning and enables
+ build/include_alpha as well as excludes all .cc from being
+ processed by linter, in the current directory (where the .cfg
+ file is located) and all sub-directories.
+"""
+
+# We categorize each error message we print. Here are the categories.
+# We want an explicit list so we can list them all in cpplint --filter=.
+# If you add a new error message with a new category, add it to the list
+# here! cpplint_unittest.py should tell you if you forget to do this.
+_ERROR_CATEGORIES = [
+ 'build/class',
+ 'build/c++11',
+ 'build/c++14',
+ 'build/c++tr1',
+ 'build/deprecated',
+ 'build/endif_comment',
+ 'build/explicit_make_pair',
+ 'build/forward_decl',
+ 'build/header_guard',
+ 'build/include',
+ 'build/include_alpha',
+ 'build/include_order',
+ 'build/include_what_you_use',
+ 'build/namespaces',
+ 'build/printf_format',
+ 'build/storage_class',
+ 'legal/copyright',
+ 'readability/alt_tokens',
+ 'readability/braces',
+ 'readability/casting',
+ 'readability/check',
+ 'readability/constructors',
+ 'readability/fn_size',
+ 'readability/inheritance',
+ 'readability/multiline_comment',
+ 'readability/multiline_string',
+ 'readability/namespace',
+ 'readability/nolint',
+ 'readability/nul',
+ 'readability/strings',
+ 'readability/todo',
+ 'readability/utf8',
+ 'runtime/arrays',
+ 'runtime/casting',
+ 'runtime/explicit',
+ 'runtime/int',
+ 'runtime/init',
+ 'runtime/invalid_increment',
+ 'runtime/member_string_references',
+ 'runtime/memset',
+ 'runtime/indentation_namespace',
+ 'runtime/operator',
+ 'runtime/printf',
+ 'runtime/printf_format',
+ 'runtime/references',
+ 'runtime/string',
+ 'runtime/threadsafe_fn',
+ 'runtime/vlog',
+ 'whitespace/blank_line',
+ 'whitespace/braces',
+ 'whitespace/comma',
+ 'whitespace/comments',
+ 'whitespace/empty_conditional_body',
+ 'whitespace/empty_if_body',
+ 'whitespace/empty_loop_body',
+ 'whitespace/end_of_line',
+ 'whitespace/ending_newline',
+ 'whitespace/forcolon',
+ 'whitespace/indent',
+ 'whitespace/line_length',
+ 'whitespace/newline',
+ 'whitespace/operators',
+ 'whitespace/parens',
+ 'whitespace/semicolon',
+ 'whitespace/tab',
+ 'whitespace/todo',
+ ]
+
+# These error categories are no longer enforced by cpplint, but for backwards-
+# compatibility they may still appear in NOLINT comments.
+_LEGACY_ERROR_CATEGORIES = [
+ 'readability/streams',
+ 'readability/function',
+ ]
+
+# The default state of the category filter. This is overridden by the --filter=
+# flag. By default all errors are on, so only add here categories that should be
+# off by default (i.e., categories that must be enabled by the --filter= flags).
+# All entries here should start with a '-' or '+', as in the --filter= flag.
+_DEFAULT_FILTERS = ['-build/include_alpha']
+
+# The default list of categories suppressed for C (not C++) files.
+_DEFAULT_C_SUPPRESSED_CATEGORIES = [
+ 'readability/casting',
+ ]
+
+# The default list of categories suppressed for Linux Kernel files.
+_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [
+ 'whitespace/tab',
+ ]
+
+# We used to check for high-bit characters, but after much discussion we
+# decided those were OK, as long as they were in UTF-8 and didn't represent
+# hard-coded international strings, which belong in a separate i18n file.
+
+# C++ headers
+_CPP_HEADERS = frozenset([
+ # Legacy
+ 'algobase.h',
+ 'algo.h',
+ 'alloc.h',
+ 'builtinbuf.h',
+ 'bvector.h',
+ 'complex.h',
+ 'defalloc.h',
+ 'deque.h',
+ 'editbuf.h',
+ 'fstream.h',
+ 'function.h',
+ 'hash_map',
+ 'hash_map.h',
+ 'hash_set',
+ 'hash_set.h',
+ 'hashtable.h',
+ 'heap.h',
+ 'indstream.h',
+ 'iomanip.h',
+ 'iostream.h',
+ 'istream.h',
+ 'iterator.h',
+ 'list.h',
+ 'map.h',
+ 'multimap.h',
+ 'multiset.h',
+ 'ostream.h',
+ 'pair.h',
+ 'parsestream.h',
+ 'pfstream.h',
+ 'procbuf.h',
+ 'pthread_alloc',
+ 'pthread_alloc.h',
+ 'rope',
+ 'rope.h',
+ 'ropeimpl.h',
+ 'set.h',
+ 'slist',
+ 'slist.h',
+ 'stack.h',
+ 'stdiostream.h',
+ 'stl_alloc.h',
+ 'stl_relops.h',
+ 'streambuf.h',
+ 'stream.h',
+ 'strfile.h',
+ 'strstream.h',
+ 'tempbuf.h',
+ 'tree.h',
+ 'type_traits.h',
+ 'vector.h',
+ # 17.6.1.2 C++ library headers
+ 'algorithm',
+ 'array',
+ 'atomic',
+ 'bitset',
+ 'chrono',
+ 'codecvt',
+ 'complex',
+ 'condition_variable',
+ 'deque',
+ 'exception',
+ 'forward_list',
+ 'fstream',
+ 'functional',
+ 'future',
+ 'initializer_list',
+ 'iomanip',
+ 'ios',
+ 'iosfwd',
+ 'iostream',
+ 'istream',
+ 'iterator',
+ 'limits',
+ 'list',
+ 'locale',
+ 'map',
+ 'memory',
+ 'mutex',
+ 'new',
+ 'numeric',
+ 'ostream',
+ 'queue',
+ 'random',
+ 'ratio',
+ 'regex',
+ 'scoped_allocator',
+ 'set',
+ 'sstream',
+ 'stack',
+ 'stdexcept',
+ 'streambuf',
+ 'string',
+ 'strstream',
+ 'system_error',
+ 'thread',
+ 'tuple',
+ 'typeindex',
+ 'typeinfo',
+ 'type_traits',
+ 'unordered_map',
+ 'unordered_set',
+ 'utility',
+ 'valarray',
+ 'vector',
+ # 17.6.1.2 C++ headers for C library facilities
+ 'cassert',
+ 'ccomplex',
+ 'cctype',
+ 'cerrno',
+ 'cfenv',
+ 'cfloat',
+ 'cinttypes',
+ 'ciso646',
+ 'climits',
+ 'clocale',
+ 'cmath',
+ 'csetjmp',
+ 'csignal',
+ 'cstdalign',
+ 'cstdarg',
+ 'cstdbool',
+ 'cstddef',
+ 'cstdint',
+ 'cstdio',
+ 'cstdlib',
+ 'cstring',
+ 'ctgmath',
+ 'ctime',
+ 'cuchar',
+ 'cwchar',
+ 'cwctype',
+ ])
+
+# Type names
+_TYPES = re.compile(
+ r'^(?:'
+ # [dcl.type.simple]
+ r'(char(16_t|32_t)?)|wchar_t|'
+ r'bool|short|int|long|signed|unsigned|float|double|'
+ # [support.types]
+ r'(ptrdiff_t|size_t|max_align_t|nullptr_t)|'
+ # [cstdint.syn]
+ r'(u?int(_fast|_least)?(8|16|32|64)_t)|'
+ r'(u?int(max|ptr)_t)|'
+ r')$')
+
+
+# These headers are excluded from [build/include] and [build/include_order]
+# checks:
+# - Anything not following google file name conventions (containing an
+# uppercase character, such as Python.h or nsStringAPI.h, for example).
+# - Lua headers.
+_THIRD_PARTY_HEADERS_PATTERN = re.compile(
+ r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$')
+
+# Pattern for matching FileInfo.BaseName() against test file name
+_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$'
+
+# Pattern that matches only complete whitespace, possibly across multiple lines.
+_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL)
+
+# Assertion macros. These are defined in base/logging.h and
+# testing/base/public/gunit.h.
+_CHECK_MACROS = [
+ 'DCHECK', 'CHECK',
+ 'EXPECT_TRUE', 'ASSERT_TRUE',
+ 'EXPECT_FALSE', 'ASSERT_FALSE',
+ ]
+
+# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE
+_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS])
+
+for op, replacement in [('==', 'EQ'), ('!=', 'NE'),
+ ('>=', 'GE'), ('>', 'GT'),
+ ('<=', 'LE'), ('<', 'LT')]:
+ _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement
+ _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement
+ _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement
+ _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement
+
+for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'),
+ ('>=', 'LT'), ('>', 'LE'),
+ ('<=', 'GT'), ('<', 'GE')]:
+ _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement
+ _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement
+
+# Alternative tokens and their replacements. For full list, see section 2.5
+# Alternative tokens [lex.digraph] in the C++ standard.
+#
+# Digraphs (such as '%:') are not included here since it's a mess to
+# match those on a word boundary.
+_ALT_TOKEN_REPLACEMENT = {
+ 'and': '&&',
+ 'bitor': '|',
+ 'or': '||',
+ 'xor': '^',
+ 'compl': '~',
+ 'bitand': '&',
+ 'and_eq': '&=',
+ 'or_eq': '|=',
+ 'xor_eq': '^=',
+ 'not': '!',
+ 'not_eq': '!='
+ }
+
+# Compile regular expression that matches all the above keywords. The "[ =()]"
+# bit is meant to avoid matching these keywords outside of boolean expressions.
+#
+# False positives include C-style multi-line comments and multi-line strings
+# but those have always been troublesome for cpplint.
+_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile(
+ r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)')
+
+
+# These constants define types of headers for use with
+# _IncludeState.CheckNextIncludeOrder().
+_C_SYS_HEADER = 1
+_CPP_SYS_HEADER = 2
+_LIKELY_MY_HEADER = 3
+_POSSIBLE_MY_HEADER = 4
+_OTHER_HEADER = 5
+
+# These constants define the current inline assembly state
+_NO_ASM = 0 # Outside of inline assembly block
+_INSIDE_ASM = 1 # Inside inline assembly block
+_END_ASM = 2 # Last line of inline assembly block
+_BLOCK_ASM = 3 # The whole block is an inline assembly block
+
+# Match start of assembly blocks
+_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)'
+ r'(?:\s+(volatile|__volatile__))?'
+ r'\s*[{(]')
+
+# Match strings that indicate we're working on a C (not C++) file.
+_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|'
+ r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))')
+
+# Match string that indicates we're working on a Linux Kernel file.
+_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)')
+
+_regexp_compile_cache = {}
+
+# {str, set(int)}: a map from error categories to sets of linenumbers
+# on which those errors are expected and should be suppressed.
+_error_suppressions = {}
+
+# The root directory used for deriving header guard CPP variable.
+# This is set by --root flag.
+_root = None
+
+# The allowed line length of files.
+# This is set by --linelength flag.
+_line_length = 80
+
+# The allowed extensions for file names
+# This is set by --extensions flag.
+_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh'])
+
+# Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc.
+# This is set by --headers flag.
+_hpp_headers = set(['h'])
+
+# {str, bool}: a map from error categories to booleans which indicate if the
+# category should be suppressed for every line.
+_global_error_suppressions = {}
+
+def ProcessHppHeadersOption(val):
+ global _hpp_headers
+ try:
+ _hpp_headers = set(val.split(','))
+ # Automatically append to extensions list so it does not have to be set 2 times
+ _valid_extensions.update(_hpp_headers)
+ except ValueError:
+ PrintUsage('Header extensions must be comma seperated list.')
+
+def IsHeaderExtension(file_extension):
+ return file_extension in _hpp_headers
+
+def ParseNolintSuppressions(filename, raw_line, linenum, error):
+ """Updates the global list of line error-suppressions.
+
+ Parses any NOLINT comments on the current line, updating the global
+ error_suppressions store. Reports an error if the NOLINT comment
+ was malformed.
+
+ Args:
+ filename: str, the name of the input file.
+ raw_line: str, the line of input text, with comments.
+ linenum: int, the number of the current line.
+ error: function, an error handler.
+ """
+ matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line)
+ if matched:
+ if matched.group(1):
+ suppressed_line = linenum + 1
+ else:
+ suppressed_line = linenum
+ category = matched.group(2)
+ if category in (None, '(*)'): # => "suppress all"
+ _error_suppressions.setdefault(None, set()).add(suppressed_line)
+ else:
+ if category.startswith('(') and category.endswith(')'):
+ category = category[1:-1]
+ if category in _ERROR_CATEGORIES:
+ _error_suppressions.setdefault(category, set()).add(suppressed_line)
+ elif category not in _LEGACY_ERROR_CATEGORIES:
+ error(filename, linenum, 'readability/nolint', 5,
+ 'Unknown NOLINT error category: %s' % category)
+
+
+def ProcessGlobalSuppresions(lines):
+ """Updates the list of global error suppressions.
+
+ Parses any lint directives in the file that have global effect.
+
+ Args:
+ lines: An array of strings, each representing a line of the file, with the
+ last element being empty if the file is terminated with a newline.
+ """
+ for line in lines:
+ if _SEARCH_C_FILE.search(line):
+ for category in _DEFAULT_C_SUPPRESSED_CATEGORIES:
+ _global_error_suppressions[category] = True
+ if _SEARCH_KERNEL_FILE.search(line):
+ for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES:
+ _global_error_suppressions[category] = True
+
+
+def ResetNolintSuppressions():
+ """Resets the set of NOLINT suppressions to empty."""
+ _error_suppressions.clear()
+ _global_error_suppressions.clear()
+
+
+def IsErrorSuppressedByNolint(category, linenum):
+ """Returns true if the specified error category is suppressed on this line.
+
+ Consults the global error_suppressions map populated by
+ ParseNolintSuppressions/ProcessGlobalSuppresions/ResetNolintSuppressions.
+
+ Args:
+ category: str, the category of the error.
+ linenum: int, the current line number.
+ Returns:
+ bool, True iff the error should be suppressed due to a NOLINT comment or
+ global suppression.
+ """
+ return (_global_error_suppressions.get(category, False) or
+ linenum in _error_suppressions.get(category, set()) or
+ linenum in _error_suppressions.get(None, set()))
+
+
+def Match(pattern, s):
+ """Matches the string with the pattern, caching the compiled regexp."""
+ # The regexp compilation caching is inlined in both Match and Search for
+ # performance reasons; factoring it out into a separate function turns out
+ # to be noticeably expensive.
+ if pattern not in _regexp_compile_cache:
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
+ return _regexp_compile_cache[pattern].match(s)
+
+
+def ReplaceAll(pattern, rep, s):
+ """Replaces instances of pattern in a string with a replacement.
+
+ The compiled regex is kept in a cache shared by Match and Search.
+
+ Args:
+ pattern: regex pattern
+ rep: replacement text
+ s: search string
+
+ Returns:
+ string with replacements made (or original string if no replacements)
+ """
+ if pattern not in _regexp_compile_cache:
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
+ return _regexp_compile_cache[pattern].sub(rep, s)
+
+
+def Search(pattern, s):
+ """Searches the string for the pattern, caching the compiled regexp."""
+ if pattern not in _regexp_compile_cache:
+ _regexp_compile_cache[pattern] = sre_compile.compile(pattern)
+ return _regexp_compile_cache[pattern].search(s)
+
+
+def _IsSourceExtension(s):
+ """File extension (excluding dot) matches a source file extension."""
+ return s in ('c', 'cc', 'cpp', 'cxx')
+
+
+class _IncludeState(object):
+ """Tracks line numbers for includes, and the order in which includes appear.
+
+ include_list contains list of lists of (header, line number) pairs.
+ It's a lists of lists rather than just one flat list to make it
+ easier to update across preprocessor boundaries.
+
+ Call CheckNextIncludeOrder() once for each header in the file, passing
+ in the type constants defined above. Calls in an illegal order will
+ raise an _IncludeError with an appropriate error message.
+
+ """
+ # self._section will move monotonically through this set. If it ever
+ # needs to move backwards, CheckNextIncludeOrder will raise an error.
+ _INITIAL_SECTION = 0
+ _MY_H_SECTION = 1
+ _C_SECTION = 2
+ _CPP_SECTION = 3
+ _OTHER_H_SECTION = 4
+
+ _TYPE_NAMES = {
+ _C_SYS_HEADER: 'C system header',
+ _CPP_SYS_HEADER: 'C++ system header',
+ _LIKELY_MY_HEADER: 'header this file implements',
+ _POSSIBLE_MY_HEADER: 'header this file may implement',
+ _OTHER_HEADER: 'other header',
+ }
+ _SECTION_NAMES = {
+ _INITIAL_SECTION: "... nothing. (This can't be an error.)",
+ _MY_H_SECTION: 'a header this file implements',
+ _C_SECTION: 'C system header',
+ _CPP_SECTION: 'C++ system header',
+ _OTHER_H_SECTION: 'other header',
+ }
+
+ def __init__(self):
+ self.include_list = [[]]
+ self.ResetSection('')
+
+ def FindHeader(self, header):
+ """Check if a header has already been included.
+
+ Args:
+ header: header to check.
+ Returns:
+ Line number of previous occurrence, or -1 if the header has not
+ been seen before.
+ """
+ for section_list in self.include_list:
+ for f in section_list:
+ if f[0] == header:
+ return f[1]
+ return -1
+
+ def ResetSection(self, directive):
+ """Reset section checking for preprocessor directive.
+
+ Args:
+ directive: preprocessor directive (e.g. "if", "else").
+ """
+ # The name of the current section.
+ self._section = self._INITIAL_SECTION
+ # The path of last found header.
+ self._last_header = ''
+
+ # Update list of includes. Note that we never pop from the
+ # include list.
+ if directive in ('if', 'ifdef', 'ifndef'):
+ self.include_list.append([])
+ elif directive in ('else', 'elif'):
+ self.include_list[-1] = []
+
+ def SetLastHeader(self, header_path):
+ self._last_header = header_path
+
+ def CanonicalizeAlphabeticalOrder(self, header_path):
+ """Returns a path canonicalized for alphabetical comparison.
+
+ - replaces "-" with "_" so they both cmp the same.
+ - removes '-inl' since we don't require them to be after the main header.
+ - lowercase everything, just in case.
+
+ Args:
+ header_path: Path to be canonicalized.
+
+ Returns:
+ Canonicalized path.
+ """
+ return header_path.replace('-inl.h', '.h').replace('-', '_').lower()
+
+ def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):
+ """Check if a header is in alphabetical order with the previous header.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ header_path: Canonicalized header to be checked.
+
+ Returns:
+ Returns true if the header is in alphabetical order.
+ """
+ # If previous section is different from current section, _last_header will
+ # be reset to empty string, so it's always less than current header.
+ #
+ # If previous line was a blank line, assume that the headers are
+ # intentionally sorted the way they are.
+ if (self._last_header > header_path and
+ Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])):
+ return False
+ return True
+
+ def CheckNextIncludeOrder(self, header_type):
+ """Returns a non-empty error message if the next header is out of order.
+
+ This function also updates the internal state to be ready to check
+ the next include.
+
+ Args:
+ header_type: One of the _XXX_HEADER constants defined above.
+
+ Returns:
+ The empty string if the header is in the right order, or an
+ error message describing what's wrong.
+
+ """
+ error_message = ('Found %s after %s' %
+ (self._TYPE_NAMES[header_type],
+ self._SECTION_NAMES[self._section]))
+
+ last_section = self._section
+
+ if header_type == _C_SYS_HEADER:
+ if self._section <= self._C_SECTION:
+ self._section = self._C_SECTION
+ else:
+ self._last_header = ''
+ return error_message
+ elif header_type == _CPP_SYS_HEADER:
+ if self._section <= self._CPP_SECTION:
+ self._section = self._CPP_SECTION
+ else:
+ self._last_header = ''
+ return error_message
+ elif header_type == _LIKELY_MY_HEADER:
+ if self._section <= self._MY_H_SECTION:
+ self._section = self._MY_H_SECTION
+ else:
+ self._section = self._OTHER_H_SECTION
+ elif header_type == _POSSIBLE_MY_HEADER:
+ if self._section <= self._MY_H_SECTION:
+ self._section = self._MY_H_SECTION
+ else:
+ # This will always be the fallback because we're not sure
+ # enough that the header is associated with this file.
+ self._section = self._OTHER_H_SECTION
+ else:
+ assert header_type == _OTHER_HEADER
+ self._section = self._OTHER_H_SECTION
+
+ if last_section != self._section:
+ self._last_header = ''
+
+ return ''
+
+
+class _CppLintState(object):
+ """Maintains module-wide state.."""
+
+ def __init__(self):
+ self.verbose_level = 1 # global setting.
+ self.error_count = 0 # global count of reported errors
+ # filters to apply when emitting error messages
+ self.filters = _DEFAULT_FILTERS[:]
+ # backup of filter list. Used to restore the state after each file.
+ self._filters_backup = self.filters[:]
+ self.counting = 'total' # In what way are we counting errors?
+ self.errors_by_category = {} # string to int dict storing error counts
+
+ # output format:
+ # "emacs" - format that emacs can parse (default)
+ # "vs7" - format that Microsoft Visual Studio 7 can parse
+ self.output_format = 'emacs'
+
+ def SetOutputFormat(self, output_format):
+ """Sets the output format for errors."""
+ self.output_format = output_format
+
+ def SetVerboseLevel(self, level):
+ """Sets the module's verbosity, and returns the previous setting."""
+ last_verbose_level = self.verbose_level
+ self.verbose_level = level
+ return last_verbose_level
+
+ def SetCountingStyle(self, counting_style):
+ """Sets the module's counting options."""
+ self.counting = counting_style
+
+ def SetFilters(self, filters):
+ """Sets the error-message filters.
+
+ These filters are applied when deciding whether to emit a given
+ error message.
+
+ Args:
+ filters: A string of comma-separated filters (eg "+whitespace/indent").
+ Each filter should start with + or -; else we die.
+
+ Raises:
+ ValueError: The comma-separated filters did not all start with '+' or '-'.
+ E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"
+ """
+ # Default filters always have less priority than the flag ones.
+ self.filters = _DEFAULT_FILTERS[:]
+ self.AddFilters(filters)
+
+ def AddFilters(self, filters):
+ """ Adds more filters to the existing list of error-message filters. """
+ for filt in filters.split(','):
+ clean_filt = filt.strip()
+ if clean_filt:
+ self.filters.append(clean_filt)
+ for filt in self.filters:
+ if not (filt.startswith('+') or filt.startswith('-')):
+ raise ValueError('Every filter in --filters must start with + or -'
+ ' (%s does not)' % filt)
+
+ def BackupFilters(self):
+ """ Saves the current filter list to backup storage."""
+ self._filters_backup = self.filters[:]
+
+ def RestoreFilters(self):
+ """ Restores filters previously backed up."""
+ self.filters = self._filters_backup[:]
+
+ def ResetErrorCounts(self):
+ """Sets the module's error statistic back to zero."""
+ self.error_count = 0
+ self.errors_by_category = {}
+
+ def IncrementErrorCount(self, category):
+ """Bumps the module's error statistic."""
+ self.error_count += 1
+ if self.counting in ('toplevel', 'detailed'):
+ if self.counting != 'detailed':
+ category = category.split('/')[0]
+ if category not in self.errors_by_category:
+ self.errors_by_category[category] = 0
+ self.errors_by_category[category] += 1
+
+ def PrintErrorCounts(self):
+ """Print a summary of errors by category, and the total."""
+ for category, count in self.errors_by_category.iteritems():
+ sys.stderr.write('Category \'%s\' errors found: %d\n' %
+ (category, count))
+ sys.stdout.write('Total errors found: %d\n' % self.error_count)
+
+_cpplint_state = _CppLintState()
+
+
+def _OutputFormat():
+ """Gets the module's output format."""
+ return _cpplint_state.output_format
+
+
+def _SetOutputFormat(output_format):
+ """Sets the module's output format."""
+ _cpplint_state.SetOutputFormat(output_format)
+
+
+def _VerboseLevel():
+ """Returns the module's verbosity setting."""
+ return _cpplint_state.verbose_level
+
+
+def _SetVerboseLevel(level):
+ """Sets the module's verbosity, and returns the previous setting."""
+ return _cpplint_state.SetVerboseLevel(level)
+
+
+def _SetCountingStyle(level):
+ """Sets the module's counting options."""
+ _cpplint_state.SetCountingStyle(level)
+
+
+def _Filters():
+ """Returns the module's list of output filters, as a list."""
+ return _cpplint_state.filters
+
+
+def _SetFilters(filters):
+ """Sets the module's error-message filters.
+
+ These filters are applied when deciding whether to emit a given
+ error message.
+
+ Args:
+ filters: A string of comma-separated filters (eg "whitespace/indent").
+ Each filter should start with + or -; else we die.
+ """
+ _cpplint_state.SetFilters(filters)
+
+def _AddFilters(filters):
+ """Adds more filter overrides.
+
+ Unlike _SetFilters, this function does not reset the current list of filters
+ available.
+
+ Args:
+ filters: A string of comma-separated filters (eg "whitespace/indent").
+ Each filter should start with + or -; else we die.
+ """
+ _cpplint_state.AddFilters(filters)
+
+def _BackupFilters():
+ """ Saves the current filter list to backup storage."""
+ _cpplint_state.BackupFilters()
+
+def _RestoreFilters():
+ """ Restores filters previously backed up."""
+ _cpplint_state.RestoreFilters()
+
+class _FunctionState(object):
+ """Tracks current function name and the number of lines in its body."""
+
+ _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.
+ _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER.
+
+ def __init__(self):
+ self.in_a_function = False
+ self.lines_in_function = 0
+ self.current_function = ''
+
+ def Begin(self, function_name):
+ """Start analyzing function body.
+
+ Args:
+ function_name: The name of the function being tracked.
+ """
+ self.in_a_function = True
+ self.lines_in_function = 0
+ self.current_function = function_name
+
+ def Count(self):
+ """Count line in current function body."""
+ if self.in_a_function:
+ self.lines_in_function += 1
+
+ def Check(self, error, filename, linenum):
+ """Report if too many lines in function body.
+
+ Args:
+ error: The function to call with any errors found.
+ filename: The name of the current file.
+ linenum: The number of the line to check.
+ """
+ if not self.in_a_function:
+ return
+
+ if Match(r'T(EST|est)', self.current_function):
+ base_trigger = self._TEST_TRIGGER
+ else:
+ base_trigger = self._NORMAL_TRIGGER
+ trigger = base_trigger * 2**_VerboseLevel()
+
+ if self.lines_in_function > trigger:
+ error_level = int(math.log(self.lines_in_function / base_trigger, 2))
+ # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
+ if error_level > 5:
+ error_level = 5
+ error(filename, linenum, 'readability/fn_size', error_level,
+ 'Small and focused functions are preferred:'
+ ' %s has %d non-comment lines'
+ ' (error triggered by exceeding %d lines).' % (
+ self.current_function, self.lines_in_function, trigger))
+
+ def End(self):
+ """Stop analyzing function body."""
+ self.in_a_function = False
+
+
+class _IncludeError(Exception):
+ """Indicates a problem with the include order in a file."""
+ pass
+
+
+class FileInfo(object):
+ """Provides utility functions for filenames.
+
+ FileInfo provides easy access to the components of a file's path
+ relative to the project root.
+ """
+
+ def __init__(self, filename):
+ self._filename = filename
+
+ def FullName(self):
+ """Make Windows paths like Unix."""
+ return os.path.abspath(self._filename).replace('\\', '/')
+
+ def RepositoryName(self):
+ """FullName after removing the local path to the repository.
+
+ If we have a real absolute path name here we can try to do something smart:
+ detecting the root of the checkout and truncating /path/to/checkout from
+ the name so that we get header guards that don't include things like
+ "C:\Documents and Settings\..." or "/home/username/..." in them and thus
+ people on different computers who have checked the source out to different
+ locations won't see bogus errors.
+ """
+ fullname = self.FullName()
+
+ if os.path.exists(fullname):
+ project_dir = os.path.dirname(fullname)
+
+ if os.path.exists(os.path.join(project_dir, ".svn")):
+ # If there's a .svn file in the current directory, we recursively look
+ # up the directory tree for the top of the SVN checkout
+ root_dir = project_dir
+ one_up_dir = os.path.dirname(root_dir)
+ while os.path.exists(os.path.join(one_up_dir, ".svn")):
+ root_dir = os.path.dirname(root_dir)
+ one_up_dir = os.path.dirname(one_up_dir)
+
+ prefix = os.path.commonprefix([root_dir, project_dir])
+ return fullname[len(prefix) + 1:]
+
+ # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by
+ # searching up from the current path.
+ root_dir = current_dir = os.path.dirname(fullname)
+ while current_dir != os.path.dirname(current_dir):
+ if (os.path.exists(os.path.join(current_dir, ".git")) or
+ os.path.exists(os.path.join(current_dir, ".hg")) or
+ os.path.exists(os.path.join(current_dir, ".svn"))):
+ root_dir = current_dir
+ current_dir = os.path.dirname(current_dir)
+
+ if (os.path.exists(os.path.join(root_dir, ".git")) or
+ os.path.exists(os.path.join(root_dir, ".hg")) or
+ os.path.exists(os.path.join(root_dir, ".svn"))):
+ prefix = os.path.commonprefix([root_dir, project_dir])
+ return fullname[len(prefix) + 1:]
+
+ # Don't know what to do; header guard warnings may be wrong...
+ return fullname
+
+ def Split(self):
+ """Splits the file into the directory, basename, and extension.
+
+ For 'chrome/browser/browser.cc', Split() would
+ return ('chrome/browser', 'browser', '.cc')
+
+ Returns:
+ A tuple of (directory, basename, extension).
+ """
+
+ googlename = self.RepositoryName()
+ project, rest = os.path.split(googlename)
+ return (project,) + os.path.splitext(rest)
+
+ def BaseName(self):
+ """File base name - text after the final slash, before the final period."""
+ return self.Split()[1]
+
+ def Extension(self):
+ """File extension - text following the final period."""
+ return self.Split()[2]
+
+ def NoExtension(self):
+ """File has no source file extension."""
+ return '/'.join(self.Split()[0:2])
+
+ def IsSource(self):
+ """File has a source file extension."""
+ return _IsSourceExtension(self.Extension()[1:])
+
+
+def _ShouldPrintError(category, confidence, linenum):
+ """If confidence >= verbose, category passes filter and is not suppressed."""
+
+ # There are three ways we might decide not to print an error message:
+ # a "NOLINT(category)" comment appears in the source,
+ # the verbosity level isn't high enough, or the filters filter it out.
+ if IsErrorSuppressedByNolint(category, linenum):
+ return False
+
+ if confidence < _cpplint_state.verbose_level:
+ return False
+
+ is_filtered = False
+ for one_filter in _Filters():
+ if one_filter.startswith('-'):
+ if category.startswith(one_filter[1:]):
+ is_filtered = True
+ elif one_filter.startswith('+'):
+ if category.startswith(one_filter[1:]):
+ is_filtered = False
+ else:
+ assert False # should have been checked for in SetFilter.
+ if is_filtered:
+ return False
+
+ return True
+
+
+def Error(filename, linenum, category, confidence, message):
+ """Logs the fact we've found a lint error.
+
+ We log where the error was found, and also our confidence in the error,
+ that is, how certain we are this is a legitimate style regression, and
+ not a misidentification or a use that's sometimes justified.
+
+ False positives can be suppressed by the use of
+ "cpplint(category)" comments on the offending line. These are
+ parsed into _error_suppressions.
+
+ Args:
+ filename: The name of the file containing the error.
+ linenum: The number of the line containing the error.
+ category: A string used to describe the "category" this bug
+ falls under: "whitespace", say, or "runtime". Categories
+ may have a hierarchy separated by slashes: "whitespace/indent".
+ confidence: A number from 1-5 representing a confidence score for
+ the error, with 5 meaning that we are certain of the problem,
+ and 1 meaning that it could be a legitimate construct.
+ message: The error message.
+ """
+ if _ShouldPrintError(category, confidence, linenum):
+ _cpplint_state.IncrementErrorCount(category)
+ if _cpplint_state.output_format == 'vs7':
+ sys.stderr.write('%s(%s): error cpplint: [%s] %s [%d]\n' % (
+ filename, linenum, category, message, confidence))
+ elif _cpplint_state.output_format == 'eclipse':
+ sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % (
+ filename, linenum, message, category, confidence))
+ else:
+ sys.stderr.write('%s:%s: %s [%s] [%d]\n' % (
+ filename, linenum, message, category, confidence))
+
+
+# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard.
+_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(
+ r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
+# Match a single C style comment on the same line.
+_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/'
+# Matches multi-line C style comments.
+# This RE is a little bit more complicated than one might expect, because we
+# have to take care of space removals tools so we can handle comments inside
+# statements better.
+# The current rule is: We only clear spaces from both sides when we're at the
+# end of the line. Otherwise, we try to remove spaces from the right side,
+# if this doesn't work we try on left side but only if there's a non-character
+# on the right.
+_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile(
+ r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' +
+ _RE_PATTERN_C_COMMENTS + r'\s+|' +
+ r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' +
+ _RE_PATTERN_C_COMMENTS + r')')
+
+
+def IsCppString(line):
+ """Does line terminate so, that the next symbol is in string constant.
+
+ This function does not consider single-line nor multi-line comments.
+
+ Args:
+ line: is a partial line of code starting from the 0..n.
+
+ Returns:
+ True, if next character appended to 'line' is inside a
+ string constant.
+ """
+
+ line = line.replace(r'\\', 'XX') # after this, \\" does not match to \"
+ return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1
+
+
+def CleanseRawStrings(raw_lines):
+ """Removes C++11 raw strings from lines.
+
+ Before:
+ static const char kData[] = R"(
+ multi-line string
+ )";
+
+ After:
+ static const char kData[] = ""
+ (replaced by blank line)
+ "";
+
+ Args:
+ raw_lines: list of raw lines.
+
+ Returns:
+ list of lines with C++11 raw strings replaced by empty strings.
+ """
+
+ delimiter = None
+ lines_without_raw_strings = []
+ for line in raw_lines:
+ if delimiter:
+ # Inside a raw string, look for the end
+ end = line.find(delimiter)
+ if end >= 0:
+ # Found the end of the string, match leading space for this
+ # line and resume copying the original lines, and also insert
+ # a "" on the last line.
+ leading_space = Match(r'^(\s*)\S', line)
+ line = leading_space.group(1) + '""' + line[end + len(delimiter):]
+ delimiter = None
+ else:
+ # Haven't found the end yet, append a blank line.
+ line = '""'
+
+ # Look for beginning of a raw string, and replace them with
+ # empty strings. This is done in a loop to handle multiple raw
+ # strings on the same line.
+ while delimiter is None:
+ # Look for beginning of a raw string.
+ # See 2.14.15 [lex.string] for syntax.
+ #
+ # Once we have matched a raw string, we check the prefix of the
+ # line to make sure that the line is not part of a single line
+ # comment. It's done this way because we remove raw strings
+ # before removing comments as opposed to removing comments
+ # before removing raw strings. This is because there are some
+ # cpplint checks that requires the comments to be preserved, but
+ # we don't want to check comments that are inside raw strings.
+ matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line)
+ if (matched and
+ not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//',
+ matched.group(1))):
+ delimiter = ')' + matched.group(2) + '"'
+
+ end = matched.group(3).find(delimiter)
+ if end >= 0:
+ # Raw string ended on same line
+ line = (matched.group(1) + '""' +
+ matched.group(3)[end + len(delimiter):])
+ delimiter = None
+ else:
+ # Start of a multi-line raw string
+ line = matched.group(1) + '""'
+ else:
+ break
+
+ lines_without_raw_strings.append(line)
+
+ # TODO(unknown): if delimiter is not None here, we might want to
+ # emit a warning for unterminated string.
+ return lines_without_raw_strings
+
+
+def FindNextMultiLineCommentStart(lines, lineix):
+ """Find the beginning marker for a multiline comment."""
+ while lineix < len(lines):
+ if lines[lineix].strip().startswith('/*'):
+ # Only return this marker if the comment goes beyond this line
+ if lines[lineix].strip().find('*/', 2) < 0:
+ return lineix
+ lineix += 1
+ return len(lines)
+
+
+def FindNextMultiLineCommentEnd(lines, lineix):
+ """We are inside a comment, find the end marker."""
+ while lineix < len(lines):
+ if lines[lineix].strip().endswith('*/'):
+ return lineix
+ lineix += 1
+ return len(lines)
+
+
+def RemoveMultiLineCommentsFromRange(lines, begin, end):
+ """Clears a range of lines for multi-line comments."""
+ # Having // dummy comments makes the lines non-empty, so we will not get
+ # unnecessary blank line warnings later in the code.
+ for i in range(begin, end):
+ lines[i] = '/**/'
+
+
+def RemoveMultiLineComments(filename, lines, error):
+ """Removes multiline (c-style) comments from lines."""
+ lineix = 0
+ while lineix < len(lines):
+ lineix_begin = FindNextMultiLineCommentStart(lines, lineix)
+ if lineix_begin >= len(lines):
+ return
+ lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin)
+ if lineix_end >= len(lines):
+ error(filename, lineix_begin + 1, 'readability/multiline_comment', 5,
+ 'Could not find end of multi-line comment')
+ return
+ RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)
+ lineix = lineix_end + 1
+
+
+def CleanseComments(line):
+ """Removes //-comments and single-line C-style /* */ comments.
+
+ Args:
+ line: A line of C++ source.
+
+ Returns:
+ The line with single-line comments removed.
+ """
+ commentpos = line.find('//')
+ if commentpos != -1 and not IsCppString(line[:commentpos]):
+ line = line[:commentpos].rstrip()
+ # get rid of /* ... */
+ return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line)
+
+
+class CleansedLines(object):
+ """Holds 4 copies of all lines with different preprocessing applied to them.
+
+ 1) elided member contains lines without strings and comments.
+ 2) lines member contains lines without comments.
+ 3) raw_lines member contains all the lines without processing.
+ 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw
+ strings removed.
+ All these members are of <type 'list'>, and of the same length.
+ """
+
+ def __init__(self, lines):
+ self.elided = []
+ self.lines = []
+ self.raw_lines = lines
+ self.num_lines = len(lines)
+ self.lines_without_raw_strings = CleanseRawStrings(lines)
+ for linenum in range(len(self.lines_without_raw_strings)):
+ self.lines.append(CleanseComments(
+ self.lines_without_raw_strings[linenum]))
+ elided = self._CollapseStrings(self.lines_without_raw_strings[linenum])
+ self.elided.append(CleanseComments(elided))
+
+ def NumLines(self):
+ """Returns the number of lines represented."""
+ return self.num_lines
+
+ @staticmethod
+ def _CollapseStrings(elided):
+ """Collapses strings and chars on a line to simple "" or '' blocks.
+
+ We nix strings first so we're not fooled by text like '"http://"'
+
+ Args:
+ elided: The line being processed.
+
+ Returns:
+ The line with collapsed strings.
+ """
+ if _RE_PATTERN_INCLUDE.match(elided):
+ return elided
+
+ # Remove escaped characters first to make quote/single quote collapsing
+ # basic. Things that look like escaped characters shouldn't occur
+ # outside of strings and chars.
+ elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided)
+
+ # Replace quoted strings and digit separators. Both single quotes
+ # and double quotes are processed in the same loop, otherwise
+ # nested quotes wouldn't work.
+ collapsed = ''
+ while True:
+ # Find the first quote character
+ match = Match(r'^([^\'"]*)([\'"])(.*)$', elided)
+ if not match:
+ collapsed += elided
+ break
+ head, quote, tail = match.groups()
+
+ if quote == '"':
+ # Collapse double quoted strings
+ second_quote = tail.find('"')
+ if second_quote >= 0:
+ collapsed += head + '""'
+ elided = tail[second_quote + 1:]
+ else:
+ # Unmatched double quote, don't bother processing the rest
+ # of the line since this is probably a multiline string.
+ collapsed += elided
+ break
+ else:
+ # Found single quote, check nearby text to eliminate digit separators.
+ #
+ # There is no special handling for floating point here, because
+ # the integer/fractional/exponent parts would all be parsed
+ # correctly as long as there are digits on both sides of the
+ # separator. So we are fine as long as we don't see something
+ # like "0.'3" (gcc 4.9.0 will not allow this literal).
+ if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head):
+ match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail)
+ collapsed += head + match_literal.group(1).replace("'", '')
+ elided = match_literal.group(2)
+ else:
+ second_quote = tail.find('\'')
+ if second_quote >= 0:
+ collapsed += head + "''"
+ elided = tail[second_quote + 1:]
+ else:
+ # Unmatched single quote
+ collapsed += elided
+ break
+
+ return collapsed
+
+
+def FindEndOfExpressionInLine(line, startpos, stack):
+ """Find the position just after the end of current parenthesized expression.
+
+ Args:
+ line: a CleansedLines line.
+ startpos: start searching at this position.
+ stack: nesting stack at startpos.
+
+ Returns:
+ On finding matching end: (index just after matching end, None)
+ On finding an unclosed expression: (-1, None)
+ Otherwise: (-1, new stack at end of this line)
+ """
+ for i in xrange(startpos, len(line)):
+ char = line[i]
+ if char in '([{':
+ # Found start of parenthesized expression, push to expression stack
+ stack.append(char)
+ elif char == '<':
+ # Found potential start of template argument list
+ if i > 0 and line[i - 1] == '<':
+ # Left shift operator
+ if stack and stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+ elif i > 0 and Search(r'\boperator\s*$', line[0:i]):
+ # operator<, don't add to stack
+ continue
+ else:
+ # Tentative start of template argument list
+ stack.append('<')
+ elif char in ')]}':
+ # Found end of parenthesized expression.
+ #
+ # If we are currently expecting a matching '>', the pending '<'
+ # must have been an operator. Remove them from expression stack.
+ while stack and stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+ if ((stack[-1] == '(' and char == ')') or
+ (stack[-1] == '[' and char == ']') or
+ (stack[-1] == '{' and char == '}')):
+ stack.pop()
+ if not stack:
+ return (i + 1, None)
+ else:
+ # Mismatched parentheses
+ return (-1, None)
+ elif char == '>':
+ # Found potential end of template argument list.
+
+ # Ignore "->" and operator functions
+ if (i > 0 and
+ (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))):
+ continue
+
+ # Pop the stack if there is a matching '<'. Otherwise, ignore
+ # this '>' since it must be an operator.
+ if stack:
+ if stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (i + 1, None)
+ elif char == ';':
+ # Found something that look like end of statements. If we are currently
+ # expecting a '>', the matching '<' must have been an operator, since
+ # template argument list should not contain statements.
+ while stack and stack[-1] == '<':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+
+ # Did not find end of expression or unbalanced parentheses on this line
+ return (-1, stack)
+
+
+def CloseExpression(clean_lines, linenum, pos):
+ """If input points to ( or { or [ or <, finds the position that closes it.
+
+ If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the
+ linenum/pos that correspond to the closing of the expression.
+
+ TODO(unknown): cpplint spends a fair bit of time matching parentheses.
+ Ideally we would want to index all opening and closing parentheses once
+ and have CloseExpression be just a simple lookup, but due to preprocessor
+ tricks, this is not so easy.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ pos: A position on the line.
+
+ Returns:
+ A tuple (line, linenum, pos) pointer *past* the closing brace, or
+ (line, len(lines), -1) if we never find a close. Note we ignore
+ strings and comments when matching; and the line we return is the
+ 'cleansed' line at linenum.
+ """
+
+ line = clean_lines.elided[linenum]
+ if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]):
+ return (line, clean_lines.NumLines(), -1)
+
+ # Check first line
+ (end_pos, stack) = FindEndOfExpressionInLine(line, pos, [])
+ if end_pos > -1:
+ return (line, linenum, end_pos)
+
+ # Continue scanning forward
+ while stack and linenum < clean_lines.NumLines() - 1:
+ linenum += 1
+ line = clean_lines.elided[linenum]
+ (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack)
+ if end_pos > -1:
+ return (line, linenum, end_pos)
+
+ # Did not find end of expression before end of file, give up
+ return (line, clean_lines.NumLines(), -1)
+
+
+def FindStartOfExpressionInLine(line, endpos, stack):
+ """Find position at the matching start of current expression.
+
+ This is almost the reverse of FindEndOfExpressionInLine, but note
+ that the input position and returned position differs by 1.
+
+ Args:
+ line: a CleansedLines line.
+ endpos: start searching at this position.
+ stack: nesting stack at endpos.
+
+ Returns:
+ On finding matching start: (index at matching start, None)
+ On finding an unclosed expression: (-1, None)
+ Otherwise: (-1, new stack at beginning of this line)
+ """
+ i = endpos
+ while i >= 0:
+ char = line[i]
+ if char in ')]}':
+ # Found end of expression, push to expression stack
+ stack.append(char)
+ elif char == '>':
+ # Found potential end of template argument list.
+ #
+ # Ignore it if it's a "->" or ">=" or "operator>"
+ if (i > 0 and
+ (line[i - 1] == '-' or
+ Match(r'\s>=\s', line[i - 1:]) or
+ Search(r'\boperator\s*$', line[0:i]))):
+ i -= 1
+ else:
+ stack.append('>')
+ elif char == '<':
+ # Found potential start of template argument list
+ if i > 0 and line[i - 1] == '<':
+ # Left shift operator
+ i -= 1
+ else:
+ # If there is a matching '>', we can pop the expression stack.
+ # Otherwise, ignore this '<' since it must be an operator.
+ if stack and stack[-1] == '>':
+ stack.pop()
+ if not stack:
+ return (i, None)
+ elif char in '([{':
+ # Found start of expression.
+ #
+ # If there are any unmatched '>' on the stack, they must be
+ # operators. Remove those.
+ while stack and stack[-1] == '>':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+ if ((char == '(' and stack[-1] == ')') or
+ (char == '[' and stack[-1] == ']') or
+ (char == '{' and stack[-1] == '}')):
+ stack.pop()
+ if not stack:
+ return (i, None)
+ else:
+ # Mismatched parentheses
+ return (-1, None)
+ elif char == ';':
+ # Found something that look like end of statements. If we are currently
+ # expecting a '<', the matching '>' must have been an operator, since
+ # template argument list should not contain statements.
+ while stack and stack[-1] == '>':
+ stack.pop()
+ if not stack:
+ return (-1, None)
+
+ i -= 1
+
+ return (-1, stack)
+
+
+def ReverseCloseExpression(clean_lines, linenum, pos):
+ """If input points to ) or } or ] or >, finds the position that opens it.
+
+ If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the
+ linenum/pos that correspond to the opening of the expression.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ pos: A position on the line.
+
+ Returns:
+ A tuple (line, linenum, pos) pointer *at* the opening brace, or
+ (line, 0, -1) if we never find the matching opening brace. Note
+ we ignore strings and comments when matching; and the line we
+ return is the 'cleansed' line at linenum.
+ """
+ line = clean_lines.elided[linenum]
+ if line[pos] not in ')}]>':
+ return (line, 0, -1)
+
+ # Check last line
+ (start_pos, stack) = FindStartOfExpressionInLine(line, pos, [])
+ if start_pos > -1:
+ return (line, linenum, start_pos)
+
+ # Continue scanning backward
+ while stack and linenum > 0:
+ linenum -= 1
+ line = clean_lines.elided[linenum]
+ (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack)
+ if start_pos > -1:
+ return (line, linenum, start_pos)
+
+ # Did not find start of expression before beginning of file, give up
+ return (line, 0, -1)
+
+
+def CheckForCopyright(filename, lines, error):
+ """Logs an error if no Copyright message appears at the top of the file."""
+
+ # We'll say it should occur by line 10. Don't forget there's a
+ # dummy line at the front.
+ for line in xrange(1, min(len(lines), 11)):
+ if re.search(r'Copyright', lines[line], re.I): break
+ else: # means no copyright line was found
+ error(filename, 0, 'legal/copyright', 5,
+ 'No copyright message found. '
+ 'You should have a line: "Copyright [year] <Copyright Owner>"')
+
+
+def GetIndentLevel(line):
+ """Return the number of leading spaces in line.
+
+ Args:
+ line: A string to check.
+
+ Returns:
+ An integer count of leading spaces, possibly zero.
+ """
+ indent = Match(r'^( *)\S', line)
+ if indent:
+ return len(indent.group(1))
+ else:
+ return 0
+
+
+def GetHeaderGuardCPPVariable(filename):
+ """Returns the CPP variable that should be used as a header guard.
+
+ Args:
+ filename: The name of a C++ header file.
+
+ Returns:
+ The CPP variable that should be used as a header guard in the
+ named file.
+
+ """
+
+ # Restores original filename in case that cpplint is invoked from Emacs's
+ # flymake.
+ filename = re.sub(r'_flymake\.h$', '.h', filename)
+ filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename)
+ # Replace 'c++' with 'cpp'.
+ filename = filename.replace('C++', 'cpp').replace('c++', 'cpp')
+
+ fileinfo = FileInfo(filename)
+ file_path_from_root = fileinfo.RepositoryName()
+ if _root:
+ suffix = os.sep
+ # On Windows using directory separator will leave us with
+ # "bogus escape error" unless we properly escape regex.
+ if suffix == '\\':
+ suffix += '\\'
+ file_path_from_root = re.sub('^' + _root + suffix, '', file_path_from_root)
+ return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_'
+
+
+def CheckForHeaderGuard(filename, clean_lines, error):
+ """Checks that the file contains a header guard.
+
+ Logs an error if no #ifndef header guard is present. For other
+ headers, checks that the full pathname is used.
+
+ Args:
+ filename: The name of the C++ header file.
+ clean_lines: A CleansedLines instance containing the file.
+ error: The function to call with any errors found.
+ """
+
+ # Don't check for header guards if there are error suppression
+ # comments somewhere in this file.
+ #
+ # Because this is silencing a warning for a nonexistent line, we
+ # only support the very specific NOLINT(build/header_guard) syntax,
+ # and not the general NOLINT or NOLINT(*) syntax.
+ raw_lines = clean_lines.lines_without_raw_strings
+ for i in raw_lines:
+ if Search(r'//\s*NOLINT\(build/header_guard\)', i):
+ return
+
+ cppvar = GetHeaderGuardCPPVariable(filename)
+
+ ifndef = ''
+ ifndef_linenum = 0
+ define = ''
+ endif = ''
+ endif_linenum = 0
+ for linenum, line in enumerate(raw_lines):
+ linesplit = line.split()
+ if len(linesplit) >= 2:
+ # find the first occurrence of #ifndef and #define, save arg
+ if not ifndef and linesplit[0] == '#ifndef':
+ # set ifndef to the header guard presented on the #ifndef line.
+ ifndef = linesplit[1]
+ ifndef_linenum = linenum
+ if not define and linesplit[0] == '#define':
+ define = linesplit[1]
+ # find the last occurrence of #endif, save entire line
+ if line.startswith('#endif'):
+ endif = line
+ endif_linenum = linenum
+
+ if not ifndef or not define or ifndef != define:
+ error(filename, 0, 'build/header_guard', 5,
+ 'No #ifndef header guard found, suggested CPP variable is: %s' %
+ cppvar)
+ return
+
+ # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__
+ # for backward compatibility.
+ if ifndef != cppvar:
+ error_level = 0
+ if ifndef != cppvar + '_':
+ error_level = 5
+
+ ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum,
+ error)
+ error(filename, ifndef_linenum, 'build/header_guard', error_level,
+ '#ifndef header guard has wrong style, please use: %s' % cppvar)
+
+ # Check for "//" comments on endif line.
+ ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum,
+ error)
+ match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif)
+ if match:
+ if match.group(1) == '_':
+ # Issue low severity warning for deprecated double trailing underscore
+ error(filename, endif_linenum, 'build/header_guard', 0,
+ '#endif line should be "#endif // %s"' % cppvar)
+ return
+
+ # Didn't find the corresponding "//" comment. If this file does not
+ # contain any "//" comments at all, it could be that the compiler
+ # only wants "/**/" comments, look for those instead.
+ no_single_line_comments = True
+ for i in xrange(1, len(raw_lines) - 1):
+ line = raw_lines[i]
+ if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line):
+ no_single_line_comments = False
+ break
+
+ if no_single_line_comments:
+ match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif)
+ if match:
+ if match.group(1) == '_':
+ # Low severity warning for double trailing underscore
+ error(filename, endif_linenum, 'build/header_guard', 0,
+ '#endif line should be "#endif /* %s */"' % cppvar)
+ return
+
+ # Didn't find anything
+ error(filename, endif_linenum, 'build/header_guard', 5,
+ '#endif line should be "#endif // %s"' % cppvar)
+
+
+def CheckHeaderFileIncluded(filename, include_state, error):
+ """Logs an error if a .cc file does not include its header."""
+
+ # Do not check test files
+ fileinfo = FileInfo(filename)
+ if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()):
+ return
+
+ headerfile = filename[0:len(filename) - len(fileinfo.Extension())] + '.h'
+ if not os.path.exists(headerfile):
+ return
+ headername = FileInfo(headerfile).RepositoryName()
+ first_include = 0
+ for section_list in include_state.include_list:
+ for f in section_list:
+ if headername in f[0] or f[0] in headername:
+ return
+ if not first_include:
+ first_include = f[1]
+
+ error(filename, first_include, 'build/include', 5,
+ '%s should include its header file %s' % (fileinfo.RepositoryName(),
+ headername))
+
+
+def CheckForBadCharacters(filename, lines, error):
+ """Logs an error for each line containing bad characters.
+
+ Two kinds of bad characters:
+
+ 1. Unicode replacement characters: These indicate that either the file
+ contained invalid UTF-8 (likely) or Unicode replacement characters (which
+ it shouldn't). Note that it's possible for this to throw off line
+ numbering if the invalid UTF-8 occurred adjacent to a newline.
+
+ 2. NUL bytes. These are problematic for some tools.
+
+ Args:
+ filename: The name of the current file.
+ lines: An array of strings, each representing a line of the file.
+ error: The function to call with any errors found.
+ """
+ for linenum, line in enumerate(lines):
+ if u'\ufffd' in line:
+ error(filename, linenum, 'readability/utf8', 5,
+ 'Line contains invalid UTF-8 (or Unicode replacement character).')
+ if '\0' in line:
+ error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.')
+
+
+def CheckForNewlineAtEOF(filename, lines, error):
+ """Logs an error if there is no newline char at the end of the file.
+
+ Args:
+ filename: The name of the current file.
+ lines: An array of strings, each representing a line of the file.
+ error: The function to call with any errors found.
+ """
+
+ # The array lines() was created by adding two newlines to the
+ # original file (go figure), then splitting on \n.
+ # To verify that the file ends in \n, we just have to make sure the
+ # last-but-two element of lines() exists and is empty.
+ if len(lines) < 3 or lines[-2]:
+ error(filename, len(lines) - 2, 'whitespace/ending_newline', 5,
+ 'Could not find a newline character at the end of the file.')
+
+
+def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
+ """Logs an error if we see /* ... */ or "..." that extend past one line.
+
+ /* ... */ comments are legit inside macros, for one line.
+ Otherwise, we prefer // comments, so it's ok to warn about the
+ other. Likewise, it's ok for strings to extend across multiple
+ lines, as long as a line continuation character (backslash)
+ terminates each line. Although not currently prohibited by the C++
+ style guide, it's ugly and unnecessary. We don't do well with either
+ in this lint program, so we warn about both.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Remove all \\ (escaped backslashes) from the line. They are OK, and the
+ # second (escaped) slash may trigger later \" detection erroneously.
+ line = line.replace('\\\\', '')
+
+ if line.count('/*') > line.count('*/'):
+ error(filename, linenum, 'readability/multiline_comment', 5,
+ 'Complex multi-line /*...*/-style comment found. '
+ 'Lint may give bogus warnings. '
+ 'Consider replacing these with //-style comments, '
+ 'with #if 0...#endif, '
+ 'or with more clearly structured multi-line comments.')
+
+ if (line.count('"') - line.count('\\"')) % 2:
+ error(filename, linenum, 'readability/multiline_string', 5,
+ 'Multi-line string ("...") found. This lint script doesn\'t '
+ 'do well with such strings, and may give bogus warnings. '
+ 'Use C++11 raw strings or concatenation instead.')
+
+
+# (non-threadsafe name, thread-safe alternative, validation pattern)
+#
+# The validation pattern is used to eliminate false positives such as:
+# _rand(); // false positive due to substring match.
+# ->rand(); // some member function rand().
+# ACMRandom rand(seed); // some variable named rand.
+# ISAACRandom rand(); // another variable named rand.
+#
+# Basically we require the return value of these functions to be used
+# in some expression context on the same line by matching on some
+# operator before the function name. This eliminates constructors and
+# member function calls.
+_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)'
+_THREADING_LIST = (
+ ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'),
+ ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'),
+ ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'),
+ ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'),
+ ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'),
+ ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'),
+ ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'),
+ ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'),
+ ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'),
+ ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'),
+ ('strtok(', 'strtok_r(',
+ _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'),
+ ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'),
+ )
+
+
+def CheckPosixThreading(filename, clean_lines, linenum, error):
+ """Checks for calls to thread-unsafe functions.
+
+ Much code has been originally written without consideration of
+ multi-threading. Also, engineers are relying on their old experience;
+ they have learned posix before threading extensions were added. These
+ tests guide the engineers to use thread-safe functions (when using
+ posix directly).
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST:
+ # Additional pattern matching check to confirm that this is the
+ # function we are looking for
+ if Search(pattern, line):
+ error(filename, linenum, 'runtime/threadsafe_fn', 2,
+ 'Consider using ' + multithread_safe_func +
+ '...) instead of ' + single_thread_func +
+ '...) for improved thread safety.')
+
+
+def CheckVlogArguments(filename, clean_lines, linenum, error):
+ """Checks that VLOG() is only used for defining a logging level.
+
+ For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and
+ VLOG(FATAL) are not.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line):
+ error(filename, linenum, 'runtime/vlog', 5,
+ 'VLOG() should be used with numeric verbosity level. '
+ 'Use LOG() if you want symbolic severity levels.')
+
+# Matches invalid increment: *count++, which moves pointer instead of
+# incrementing a value.
+_RE_PATTERN_INVALID_INCREMENT = re.compile(
+ r'^\s*\*\w+(\+\+|--);')
+
+
+def CheckInvalidIncrement(filename, clean_lines, linenum, error):
+ """Checks for invalid increment *count++.
+
+ For example following function:
+ void increment_counter(int* count) {
+ *count++;
+ }
+ is invalid, because it effectively does count++, moving pointer, and should
+ be replaced with ++*count, (*count)++ or *count += 1.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ if _RE_PATTERN_INVALID_INCREMENT.match(line):
+ error(filename, linenum, 'runtime/invalid_increment', 5,
+ 'Changing pointer instead of value (or unused value of operator*).')
+
+
+def IsMacroDefinition(clean_lines, linenum):
+ if Search(r'^#define', clean_lines[linenum]):
+ return True
+
+ if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]):
+ return True
+
+ return False
+
+
+def IsForwardClassDeclaration(clean_lines, linenum):
+ return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum])
+
+
+class _BlockInfo(object):
+ """Stores information about a generic block of code."""
+
+ def __init__(self, linenum, seen_open_brace):
+ self.starting_linenum = linenum
+ self.seen_open_brace = seen_open_brace
+ self.open_parentheses = 0
+ self.inline_asm = _NO_ASM
+ self.check_namespace_indentation = False
+
+ def CheckBegin(self, filename, clean_lines, linenum, error):
+ """Run checks that applies to text up to the opening brace.
+
+ This is mostly for checking the text after the class identifier
+ and the "{", usually where the base class is specified. For other
+ blocks, there isn't much to check, so we always pass.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ pass
+
+ def CheckEnd(self, filename, clean_lines, linenum, error):
+ """Run checks that applies to text after the closing brace.
+
+ This is mostly used for checking end of namespace comments.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ pass
+
+ def IsBlockInfo(self):
+ """Returns true if this block is a _BlockInfo.
+
+ This is convenient for verifying that an object is an instance of
+ a _BlockInfo, but not an instance of any of the derived classes.
+
+ Returns:
+ True for this class, False for derived classes.
+ """
+ return self.__class__ == _BlockInfo
+
+
+class _ExternCInfo(_BlockInfo):
+ """Stores information about an 'extern "C"' block."""
+
+ def __init__(self, linenum):
+ _BlockInfo.__init__(self, linenum, True)
+
+
+class _ClassInfo(_BlockInfo):
+ """Stores information about a class."""
+
+ def __init__(self, name, class_or_struct, clean_lines, linenum):
+ _BlockInfo.__init__(self, linenum, False)
+ self.name = name
+ self.is_derived = False
+ self.check_namespace_indentation = True
+ if class_or_struct == 'struct':
+ self.access = 'public'
+ self.is_struct = True
+ else:
+ self.access = 'private'
+ self.is_struct = False
+
+ # Remember initial indentation level for this class. Using raw_lines here
+ # instead of elided to account for leading comments.
+ self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum])
+
+ # Try to find the end of the class. This will be confused by things like:
+ # class A {
+ # } *x = { ...
+ #
+ # But it's still good enough for CheckSectionSpacing.
+ self.last_line = 0
+ depth = 0
+ for i in range(linenum, clean_lines.NumLines()):
+ line = clean_lines.elided[i]
+ depth += line.count('{') - line.count('}')
+ if not depth:
+ self.last_line = i
+ break
+
+ def CheckBegin(self, filename, clean_lines, linenum, error):
+ # Look for a bare ':'
+ if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]):
+ self.is_derived = True
+
+ def CheckEnd(self, filename, clean_lines, linenum, error):
+ # If there is a DISALLOW macro, it should appear near the end of
+ # the class.
+ seen_last_thing_in_class = False
+ for i in xrange(linenum - 1, self.starting_linenum, -1):
+ match = Search(
+ r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' +
+ self.name + r'\)',
+ clean_lines.elided[i])
+ if match:
+ if seen_last_thing_in_class:
+ error(filename, i, 'readability/constructors', 3,
+ match.group(1) + ' should be the last thing in the class')
+ break
+
+ if not Match(r'^\s*$', clean_lines.elided[i]):
+ seen_last_thing_in_class = True
+
+ # Check that closing brace is aligned with beginning of the class.
+ # Only do this if the closing brace is indented by only whitespaces.
+ # This means we will not check single-line class definitions.
+ indent = Match(r'^( *)\}', clean_lines.elided[linenum])
+ if indent and len(indent.group(1)) != self.class_indent:
+ if self.is_struct:
+ parent = 'struct ' + self.name
+ else:
+ parent = 'class ' + self.name
+ error(filename, linenum, 'whitespace/indent', 3,
+ 'Closing brace should be aligned with beginning of %s' % parent)
+
+
+class _NamespaceInfo(_BlockInfo):
+ """Stores information about a namespace."""
+
+ def __init__(self, name, linenum):
+ _BlockInfo.__init__(self, linenum, False)
+ self.name = name or ''
+ self.check_namespace_indentation = True
+
+ def CheckEnd(self, filename, clean_lines, linenum, error):
+ """Check end of namespace comments."""
+ line = clean_lines.raw_lines[linenum]
+
+ # Check how many lines is enclosed in this namespace. Don't issue
+ # warning for missing namespace comments if there aren't enough
+ # lines. However, do apply checks if there is already an end of
+ # namespace comment and it's incorrect.
+ #
+ # TODO(unknown): We always want to check end of namespace comments
+ # if a namespace is large, but sometimes we also want to apply the
+ # check if a short namespace contained nontrivial things (something
+ # other than forward declarations). There is currently no logic on
+ # deciding what these nontrivial things are, so this check is
+ # triggered by namespace size only, which works most of the time.
+ if (linenum - self.starting_linenum < 10
+ and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)):
+ return
+
+ # Look for matching comment at end of namespace.
+ #
+ # Note that we accept C style "/* */" comments for terminating
+ # namespaces, so that code that terminate namespaces inside
+ # preprocessor macros can be cpplint clean.
+ #
+ # We also accept stuff like "// end of namespace <name>." with the
+ # period at the end.
+ #
+ # Besides these, we don't accept anything else, otherwise we might
+ # get false negatives when existing comment is a substring of the
+ # expected namespace.
+ if self.name:
+ # Named namespace
+ if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' +
+ re.escape(self.name) + r'[\*/\.\\\s]*$'),
+ line):
+ error(filename, linenum, 'readability/namespace', 5,
+ 'Namespace should be terminated with "// namespace %s"' %
+ self.name)
+ else:
+ # Anonymous namespace
+ if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line):
+ # If "// namespace anonymous" or "// anonymous namespace (more text)",
+ # mention "// anonymous namespace" as an acceptable form
+ if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line):
+ error(filename, linenum, 'readability/namespace', 5,
+ 'Anonymous namespace should be terminated with "// namespace"'
+ ' or "// anonymous namespace"')
+ else:
+ error(filename, linenum, 'readability/namespace', 5,
+ 'Anonymous namespace should be terminated with "// namespace"')
+
+
+class _PreprocessorInfo(object):
+ """Stores checkpoints of nesting stacks when #if/#else is seen."""
+
+ def __init__(self, stack_before_if):
+ # The entire nesting stack before #if
+ self.stack_before_if = stack_before_if
+
+ # The entire nesting stack up to #else
+ self.stack_before_else = []
+
+ # Whether we have already seen #else or #elif
+ self.seen_else = False
+
+
+class NestingState(object):
+ """Holds states related to parsing braces."""
+
+ def __init__(self):
+ # Stack for tracking all braces. An object is pushed whenever we
+ # see a "{", and popped when we see a "}". Only 3 types of
+ # objects are possible:
+ # - _ClassInfo: a class or struct.
+ # - _NamespaceInfo: a namespace.
+ # - _BlockInfo: some other type of block.
+ self.stack = []
+
+ # Top of the previous stack before each Update().
+ #
+ # Because the nesting_stack is updated at the end of each line, we
+ # had to do some convoluted checks to find out what is the current
+ # scope at the beginning of the line. This check is simplified by
+ # saving the previous top of nesting stack.
+ #
+ # We could save the full stack, but we only need the top. Copying
+ # the full nesting stack would slow down cpplint by ~10%.
+ self.previous_stack_top = []
+
+ # Stack of _PreprocessorInfo objects.
+ self.pp_stack = []
+
+ def SeenOpenBrace(self):
+ """Check if we have seen the opening brace for the innermost block.
+
+ Returns:
+ True if we have seen the opening brace, False if the innermost
+ block is still expecting an opening brace.
+ """
+ return (not self.stack) or self.stack[-1].seen_open_brace
+
+ def InNamespaceBody(self):
+ """Check if we are currently one level inside a namespace body.
+
+ Returns:
+ True if top of the stack is a namespace block, False otherwise.
+ """
+ return self.stack and isinstance(self.stack[-1], _NamespaceInfo)
+
+ def InExternC(self):
+ """Check if we are currently one level inside an 'extern "C"' block.
+
+ Returns:
+ True if top of the stack is an extern block, False otherwise.
+ """
+ return self.stack and isinstance(self.stack[-1], _ExternCInfo)
+
+ def InClassDeclaration(self):
+ """Check if we are currently one level inside a class or struct declaration.
+
+ Returns:
+ True if top of the stack is a class/struct, False otherwise.
+ """
+ return self.stack and isinstance(self.stack[-1], _ClassInfo)
+
+ def InAsmBlock(self):
+ """Check if we are currently one level inside an inline ASM block.
+
+ Returns:
+ True if the top of the stack is a block containing inline ASM.
+ """
+ return self.stack and self.stack[-1].inline_asm != _NO_ASM
+
+ def InTemplateArgumentList(self, clean_lines, linenum, pos):
+ """Check if current position is inside template argument list.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ pos: position just after the suspected template argument.
+ Returns:
+ True if (linenum, pos) is inside template arguments.
+ """
+ while linenum < clean_lines.NumLines():
+ # Find the earliest character that might indicate a template argument
+ line = clean_lines.elided[linenum]
+ match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:])
+ if not match:
+ linenum += 1
+ pos = 0
+ continue
+ token = match.group(1)
+ pos += len(match.group(0))
+
+ # These things do not look like template argument list:
+ # class Suspect {
+ # class Suspect x; }
+ if token in ('{', '}', ';'): return False
+
+ # These things look like template argument list:
+ # template <class Suspect>
+ # template <class Suspect = default_value>
+ # template <class Suspect[]>
+ # template <class Suspect...>
+ if token in ('>', '=', '[', ']', '.'): return True
+
+ # Check if token is an unmatched '<'.
+ # If not, move on to the next character.
+ if token != '<':
+ pos += 1
+ if pos >= len(line):
+ linenum += 1
+ pos = 0
+ continue
+
+ # We can't be sure if we just find a single '<', and need to
+ # find the matching '>'.
+ (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1)
+ if end_pos < 0:
+ # Not sure if template argument list or syntax error in file
+ return False
+ linenum = end_line
+ pos = end_pos
+ return False
+
+ def UpdatePreprocessor(self, line):
+ """Update preprocessor stack.
+
+ We need to handle preprocessors due to classes like this:
+ #ifdef SWIG
+ struct ResultDetailsPageElementExtensionPoint {
+ #else
+ struct ResultDetailsPageElementExtensionPoint : public Extension {
+ #endif
+
+ We make the following assumptions (good enough for most files):
+ - Preprocessor condition evaluates to true from #if up to first
+ #else/#elif/#endif.
+
+ - Preprocessor condition evaluates to false from #else/#elif up
+ to #endif. We still perform lint checks on these lines, but
+ these do not affect nesting stack.
+
+ Args:
+ line: current line to check.
+ """
+ if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line):
+ # Beginning of #if block, save the nesting stack here. The saved
+ # stack will allow us to restore the parsing state in the #else case.
+ self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack)))
+ elif Match(r'^\s*#\s*(else|elif)\b', line):
+ # Beginning of #else block
+ if self.pp_stack:
+ if not self.pp_stack[-1].seen_else:
+ # This is the first #else or #elif block. Remember the
+ # whole nesting stack up to this point. This is what we
+ # keep after the #endif.
+ self.pp_stack[-1].seen_else = True
+ self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack)
+
+ # Restore the stack to how it was before the #if
+ self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if)
+ else:
+ # TODO(unknown): unexpected #else, issue warning?
+ pass
+ elif Match(r'^\s*#\s*endif\b', line):
+ # End of #if or #else blocks.
+ if self.pp_stack:
+ # If we saw an #else, we will need to restore the nesting
+ # stack to its former state before the #else, otherwise we
+ # will just continue from where we left off.
+ if self.pp_stack[-1].seen_else:
+ # Here we can just use a shallow copy since we are the last
+ # reference to it.
+ self.stack = self.pp_stack[-1].stack_before_else
+ # Drop the corresponding #if
+ self.pp_stack.pop()
+ else:
+ # TODO(unknown): unexpected #endif, issue warning?
+ pass
+
+ # TODO(unknown): Update() is too long, but we will refactor later.
+ def Update(self, filename, clean_lines, linenum, error):
+ """Update nesting state with current line.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Remember top of the previous nesting stack.
+ #
+ # The stack is always pushed/popped and not modified in place, so
+ # we can just do a shallow copy instead of copy.deepcopy. Using
+ # deepcopy would slow down cpplint by ~28%.
+ if self.stack:
+ self.previous_stack_top = self.stack[-1]
+ else:
+ self.previous_stack_top = None
+
+ # Update pp_stack
+ self.UpdatePreprocessor(line)
+
+ # Count parentheses. This is to avoid adding struct arguments to
+ # the nesting stack.
+ if self.stack:
+ inner_block = self.stack[-1]
+ depth_change = line.count('(') - line.count(')')
+ inner_block.open_parentheses += depth_change
+
+ # Also check if we are starting or ending an inline assembly block.
+ if inner_block.inline_asm in (_NO_ASM, _END_ASM):
+ if (depth_change != 0 and
+ inner_block.open_parentheses == 1 and
+ _MATCH_ASM.match(line)):
+ # Enter assembly block
+ inner_block.inline_asm = _INSIDE_ASM
+ else:
+ # Not entering assembly block. If previous line was _END_ASM,
+ # we will now shift to _NO_ASM state.
+ inner_block.inline_asm = _NO_ASM
+ elif (inner_block.inline_asm == _INSIDE_ASM and
+ inner_block.open_parentheses == 0):
+ # Exit assembly block
+ inner_block.inline_asm = _END_ASM
+
+ # Consume namespace declaration at the beginning of the line. Do
+ # this in a loop so that we catch same line declarations like this:
+ # namespace proto2 { namespace bridge { class MessageSet; } }
+ while True:
+ # Match start of namespace. The "\b\s*" below catches namespace
+ # declarations even if it weren't followed by a whitespace, this
+ # is so that we don't confuse our namespace checker. The
+ # missing spaces will be flagged by CheckSpacing.
+ namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line)
+ if not namespace_decl_match:
+ break
+
+ new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum)
+ self.stack.append(new_namespace)
+
+ line = namespace_decl_match.group(2)
+ if line.find('{') != -1:
+ new_namespace.seen_open_brace = True
+ line = line[line.find('{') + 1:]
+
+ # Look for a class declaration in whatever is left of the line
+ # after parsing namespaces. The regexp accounts for decorated classes
+ # such as in:
+ # class LOCKABLE API Object {
+ # };
+ class_decl_match = Match(
+ r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?'
+ r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))'
+ r'(.*)$', line)
+ if (class_decl_match and
+ (not self.stack or self.stack[-1].open_parentheses == 0)):
+ # We do not want to accept classes that are actually template arguments:
+ # template <class Ignore1,
+ # class Ignore2 = Default<Args>,
+ # template <Args> class Ignore3>
+ # void Function() {};
+ #
+ # To avoid template argument cases, we scan forward and look for
+ # an unmatched '>'. If we see one, assume we are inside a
+ # template argument list.
+ end_declaration = len(class_decl_match.group(1))
+ if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration):
+ self.stack.append(_ClassInfo(
+ class_decl_match.group(3), class_decl_match.group(2),
+ clean_lines, linenum))
+ line = class_decl_match.group(4)
+
+ # If we have not yet seen the opening brace for the innermost block,
+ # run checks here.
+ if not self.SeenOpenBrace():
+ self.stack[-1].CheckBegin(filename, clean_lines, linenum, error)
+
+ # Update access control if we are inside a class/struct
+ if self.stack and isinstance(self.stack[-1], _ClassInfo):
+ classinfo = self.stack[-1]
+ access_match = Match(
+ r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?'
+ r':(?:[^:]|$)',
+ line)
+ if access_match:
+ classinfo.access = access_match.group(2)
+
+ # Check that access keywords are indented +1 space. Skip this
+ # check if the keywords are not preceded by whitespaces.
+ indent = access_match.group(1)
+ if (len(indent) != classinfo.class_indent + 1 and
+ Match(r'^\s*$', indent)):
+ if classinfo.is_struct:
+ parent = 'struct ' + classinfo.name
+ else:
+ parent = 'class ' + classinfo.name
+ slots = ''
+ if access_match.group(3):
+ slots = access_match.group(3)
+ error(filename, linenum, 'whitespace/indent', 3,
+ '%s%s: should be indented +1 space inside %s' % (
+ access_match.group(2), slots, parent))
+
+ # Consume braces or semicolons from what's left of the line
+ while True:
+ # Match first brace, semicolon, or closed parenthesis.
+ matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line)
+ if not matched:
+ break
+
+ token = matched.group(1)
+ if token == '{':
+ # If namespace or class hasn't seen a opening brace yet, mark
+ # namespace/class head as complete. Push a new block onto the
+ # stack otherwise.
+ if not self.SeenOpenBrace():
+ self.stack[-1].seen_open_brace = True
+ elif Match(r'^extern\s*"[^"]*"\s*\{', line):
+ self.stack.append(_ExternCInfo(linenum))
+ else:
+ self.stack.append(_BlockInfo(linenum, True))
+ if _MATCH_ASM.match(line):
+ self.stack[-1].inline_asm = _BLOCK_ASM
+
+ elif token == ';' or token == ')':
+ # If we haven't seen an opening brace yet, but we already saw
+ # a semicolon, this is probably a forward declaration. Pop
+ # the stack for these.
+ #
+ # Similarly, if we haven't seen an opening brace yet, but we
+ # already saw a closing parenthesis, then these are probably
+ # function arguments with extra "class" or "struct" keywords.
+ # Also pop these stack for these.
+ if not self.SeenOpenBrace():
+ self.stack.pop()
+ else: # token == '}'
+ # Perform end of block checks and pop the stack.
+ if self.stack:
+ self.stack[-1].CheckEnd(filename, clean_lines, linenum, error)
+ self.stack.pop()
+ line = matched.group(2)
+
+ def InnermostClass(self):
+ """Get class info on the top of the stack.
+
+ Returns:
+ A _ClassInfo object if we are inside a class, or None otherwise.
+ """
+ for i in range(len(self.stack), 0, -1):
+ classinfo = self.stack[i - 1]
+ if isinstance(classinfo, _ClassInfo):
+ return classinfo
+ return None
+
+ def CheckCompletedBlocks(self, filename, error):
+ """Checks that all classes and namespaces have been completely parsed.
+
+ Call this when all lines in a file have been processed.
+ Args:
+ filename: The name of the current file.
+ error: The function to call with any errors found.
+ """
+ # Note: This test can result in false positives if #ifdef constructs
+ # get in the way of brace matching. See the testBuildClass test in
+ # cpplint_unittest.py for an example of this.
+ for obj in self.stack:
+ if isinstance(obj, _ClassInfo):
+ error(filename, obj.starting_linenum, 'build/class', 5,
+ 'Failed to find complete declaration of class %s' %
+ obj.name)
+ elif isinstance(obj, _NamespaceInfo):
+ error(filename, obj.starting_linenum, 'build/namespaces', 5,
+ 'Failed to find complete declaration of namespace %s' %
+ obj.name)
+
+
+def CheckForNonStandardConstructs(filename, clean_lines, linenum,
+ nesting_state, error):
+ r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.
+
+ Complain about several constructs which gcc-2 accepts, but which are
+ not standard C++. Warning about these in lint is one way to ease the
+ transition to new compilers.
+ - put storage class first (e.g. "static const" instead of "const static").
+ - "%lld" instead of %qd" in printf-type functions.
+ - "%1$d" is non-standard in printf-type functions.
+ - "\%" is an undefined character escape sequence.
+ - text after #endif is not allowed.
+ - invalid inner-style forward declaration.
+ - >? and <? operators, and their >?= and <?= cousins.
+
+ Additionally, check for constructor/destructor style violations and reference
+ members, as it is very convenient to do so while checking for
+ gcc-2 compliance.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: A callable to which errors are reported, which takes 4 arguments:
+ filename, line number, error level, and message
+ """
+
+ # Remove comments from the line, but leave in strings for now.
+ line = clean_lines.lines[linenum]
+
+ if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line):
+ error(filename, linenum, 'runtime/printf_format', 3,
+ '%q in format strings is deprecated. Use %ll instead.')
+
+ if Search(r'printf\s*\(.*".*%\d+\$', line):
+ error(filename, linenum, 'runtime/printf_format', 2,
+ '%N$ formats are unconventional. Try rewriting to avoid them.')
+
+ # Remove escaped backslashes before looking for undefined escapes.
+ line = line.replace('\\\\', '')
+
+ if Search(r'("|\').*\\(%|\[|\(|{)', line):
+ error(filename, linenum, 'build/printf_format', 3,
+ '%, [, (, and { are undefined character escapes. Unescape them.')
+
+ # For the rest, work with both comments and strings removed.
+ line = clean_lines.elided[linenum]
+
+ if Search(r'\b(const|volatile|void|char|short|int|long'
+ r'|float|double|signed|unsigned'
+ r'|schar|u?int8|u?int16|u?int32|u?int64)'
+ r'\s+(register|static|extern|typedef)\b',
+ line):
+ error(filename, linenum, 'build/storage_class', 5,
+ 'Storage-class specifier (static, extern, typedef, etc) should be '
+ 'at the beginning of the declaration.')
+
+ if Match(r'\s*#\s*endif\s*[^/\s]+', line):
+ error(filename, linenum, 'build/endif_comment', 5,
+ 'Uncommented text after #endif is non-standard. Use a comment.')
+
+ if Match(r'\s*class\s+(\w+\s*::\s*)+\w+\s*;', line):
+ error(filename, linenum, 'build/forward_decl', 5,
+ 'Inner-style forward declarations are invalid. Remove this line.')
+
+ if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?',
+ line):
+ error(filename, linenum, 'build/deprecated', 3,
+ '>? and <? (max and min) operators are non-standard and deprecated.')
+
+ if Search(r'^\s*const\s*string\s*&\s*\w+\s*;', line):
+ # TODO(unknown): Could it be expanded safely to arbitrary references,
+ # without triggering too many false positives? The first
+ # attempt triggered 5 warnings for mostly benign code in the regtest, hence
+ # the restriction.
+ # Here's the original regexp, for the reference:
+ # type_name = r'\w+((\s*::\s*\w+)|(\s*<\s*\w+?\s*>))?'
+ # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;'
+ error(filename, linenum, 'runtime/member_string_references', 2,
+ 'const string& members are dangerous. It is much better to use '
+ 'alternatives, such as pointers or simple constants.')
+
+ # Everything else in this function operates on class declarations.
+ # Return early if the top of the nesting stack is not a class, or if
+ # the class head is not completed yet.
+ classinfo = nesting_state.InnermostClass()
+ if not classinfo or not classinfo.seen_open_brace:
+ return
+
+ # The class may have been declared with namespace or classname qualifiers.
+ # The constructor and destructor will not have those qualifiers.
+ base_classname = classinfo.name.split('::')[-1]
+
+ # Look for single-argument constructors that aren't marked explicit.
+ # Technically a valid construct, but against style.
+ explicit_constructor_match = Match(
+ r'\s+(?:(?:inline|constexpr)\s+)*(explicit\s+)?'
+ r'(?:(?:inline|constexpr)\s+)*%s\s*'
+ r'\(((?:[^()]|\([^()]*\))*)\)'
+ % re.escape(base_classname),
+ line)
+
+ if explicit_constructor_match:
+ is_marked_explicit = explicit_constructor_match.group(1)
+
+ if not explicit_constructor_match.group(2):
+ constructor_args = []
+ else:
+ constructor_args = explicit_constructor_match.group(2).split(',')
+
+ # collapse arguments so that commas in template parameter lists and function
+ # argument parameter lists don't split arguments in two
+ i = 0
+ while i < len(constructor_args):
+ constructor_arg = constructor_args[i]
+ while (constructor_arg.count('<') > constructor_arg.count('>') or
+ constructor_arg.count('(') > constructor_arg.count(')')):
+ constructor_arg += ',' + constructor_args[i + 1]
+ del constructor_args[i + 1]
+ constructor_args[i] = constructor_arg
+ i += 1
+
+ defaulted_args = [arg for arg in constructor_args if '=' in arg]
+ noarg_constructor = (not constructor_args or # empty arg list
+ # 'void' arg specifier
+ (len(constructor_args) == 1 and
+ constructor_args[0].strip() == 'void'))
+ onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg
+ not noarg_constructor) or
+ # all but at most one arg defaulted
+ (len(constructor_args) >= 1 and
+ not noarg_constructor and
+ len(defaulted_args) >= len(constructor_args) - 1))
+ initializer_list_constructor = bool(
+ onearg_constructor and
+ Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0]))
+ copy_constructor = bool(
+ onearg_constructor and
+ Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&'
+ % re.escape(base_classname), constructor_args[0].strip()))
+
+ if (not is_marked_explicit and
+ onearg_constructor and
+ not initializer_list_constructor and
+ not copy_constructor):
+ if defaulted_args:
+ error(filename, linenum, 'runtime/explicit', 5,
+ 'Constructors callable with one argument '
+ 'should be marked explicit.')
+ else:
+ error(filename, linenum, 'runtime/explicit', 5,
+ 'Single-parameter constructors should be marked explicit.')
+ elif is_marked_explicit and not onearg_constructor:
+ if noarg_constructor:
+ error(filename, linenum, 'runtime/explicit', 5,
+ 'Zero-parameter constructors should not be marked explicit.')
+
+
+def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error):
+ """Checks for the correctness of various spacing around function calls.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Since function calls often occur inside if/for/while/switch
+ # expressions - which have their own, more liberal conventions - we
+ # first see if we should be looking inside such an expression for a
+ # function call, to which we can apply more strict standards.
+ fncall = line # if there's no control flow construct, look at whole line
+ for pattern in (r'\bif\s*\((.*)\)\s*{',
+ r'\bfor\s*\((.*)\)\s*{',
+ r'\bwhile\s*\((.*)\)\s*[{;]',
+ r'\bswitch\s*\((.*)\)\s*{'):
+ match = Search(pattern, line)
+ if match:
+ fncall = match.group(1) # look inside the parens for function calls
+ break
+
+ # Except in if/for/while/switch, there should never be space
+ # immediately inside parens (eg "f( 3, 4 )"). We make an exception
+ # for nested parens ( (a+b) + c ). Likewise, there should never be
+ # a space before a ( when it's a function argument. I assume it's a
+ # function argument when the char before the whitespace is legal in
+ # a function name (alnum + _) and we're not starting a macro. Also ignore
+ # pointers and references to arrays and functions coz they're too tricky:
+ # we use a very simple way to recognize these:
+ # " (something)(maybe-something)" or
+ # " (something)(maybe-something," or
+ # " (something)[something]"
+ # Note that we assume the contents of [] to be short enough that
+ # they'll never need to wrap.
+ if ( # Ignore control structures.
+ not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b',
+ fncall) and
+ # Ignore pointers/references to functions.
+ not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
+ # Ignore pointers/references to arrays.
+ not Search(r' \([^)]+\)\[[^\]]+\]', fncall)):
+ if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call
+ error(filename, linenum, 'whitespace/parens', 4,
+ 'Extra space after ( in function call')
+ elif Search(r'\(\s+(?!(\s*\\)|\()', fncall):
+ error(filename, linenum, 'whitespace/parens', 2,
+ 'Extra space after (')
+ if (Search(r'\w\s+\(', fncall) and
+ not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and
+ not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and
+ not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and
+ not Search(r'\bcase\s+\(', fncall)):
+ # TODO(unknown): Space after an operator function seem to be a common
+ # error, silence those for now by restricting them to highest verbosity.
+ if Search(r'\boperator_*\b', line):
+ error(filename, linenum, 'whitespace/parens', 0,
+ 'Extra space before ( in function call')
+ else:
+ error(filename, linenum, 'whitespace/parens', 4,
+ 'Extra space before ( in function call')
+ # If the ) is followed only by a newline or a { + newline, assume it's
+ # part of a control statement (if/while/etc), and don't complain
+ if Search(r'[^)]\s+\)\s*[^{\s]', fncall):
+ # If the closing parenthesis is preceded by only whitespaces,
+ # try to give a more descriptive error message.
+ if Search(r'^\s+\)', fncall):
+ error(filename, linenum, 'whitespace/parens', 2,
+ 'Closing ) should be moved to the previous line')
+ else:
+ error(filename, linenum, 'whitespace/parens', 2,
+ 'Extra space before )')
+
+
+def IsBlankLine(line):
+ """Returns true if the given line is blank.
+
+ We consider a line to be blank if the line is empty or consists of
+ only white spaces.
+
+ Args:
+ line: A line of a string.
+
+ Returns:
+ True, if the given line is blank.
+ """
+ return not line or line.isspace()
+
+
+def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line,
+ error):
+ is_namespace_indent_item = (
+ len(nesting_state.stack) > 1 and
+ nesting_state.stack[-1].check_namespace_indentation and
+ isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and
+ nesting_state.previous_stack_top == nesting_state.stack[-2])
+
+ if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item,
+ clean_lines.elided, line):
+ CheckItemIndentationInNamespace(filename, clean_lines.elided,
+ line, error)
+
+
+def CheckForFunctionLengths(filename, clean_lines, linenum,
+ function_state, error):
+ """Reports for long function bodies.
+
+ For an overview why this is done, see:
+ https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions
+
+ Uses a simplistic algorithm assuming other style guidelines
+ (especially spacing) are followed.
+ Only checks unindented functions, so class members are unchecked.
+ Trivial bodies are unchecked, so constructors with huge initializer lists
+ may be missed.
+ Blank/comment lines are not counted so as to avoid encouraging the removal
+ of vertical space and comments just to get through a lint check.
+ NOLINT *on the last line of a function* disables this check.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ function_state: Current function name and lines in body so far.
+ error: The function to call with any errors found.
+ """
+ lines = clean_lines.lines
+ line = lines[linenum]
+ joined_line = ''
+
+ starting_func = False
+ regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ...
+ match_result = Match(regexp, line)
+ if match_result:
+ # If the name is all caps and underscores, figure it's a macro and
+ # ignore it, unless it's TEST or TEST_F.
+ function_name = match_result.group(1).split()[-1]
+ if function_name == 'TEST' or function_name == 'TEST_F' or (
+ not Match(r'[A-Z_]+$', function_name)):
+ starting_func = True
+
+ if starting_func:
+ body_found = False
+ for start_linenum in xrange(linenum, clean_lines.NumLines()):
+ start_line = lines[start_linenum]
+ joined_line += ' ' + start_line.lstrip()
+ if Search(r'(;|})', start_line): # Declarations and trivial functions
+ body_found = True
+ break # ... ignore
+ elif Search(r'{', start_line):
+ body_found = True
+ function = Search(r'((\w|:)*)\(', line).group(1)
+ if Match(r'TEST', function): # Handle TEST... macros
+ parameter_regexp = Search(r'(\(.*\))', joined_line)
+ if parameter_regexp: # Ignore bad syntax
+ function += parameter_regexp.group(1)
+ else:
+ function += '()'
+ function_state.Begin(function)
+ break
+ if not body_found:
+ # No body for the function (or evidence of a non-function) was found.
+ error(filename, linenum, 'readability/fn_size', 5,
+ 'Lint failed to find start of function body.')
+ elif Match(r'^\}\s*$', line): # function end
+ function_state.Check(error, filename, linenum)
+ function_state.End()
+ elif not Match(r'^\s*$', line):
+ function_state.Count() # Count non-blank/non-comment lines.
+
+
+_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?')
+
+
+def CheckComment(line, filename, linenum, next_line_start, error):
+ """Checks for common mistakes in comments.
+
+ Args:
+ line: The line in question.
+ filename: The name of the current file.
+ linenum: The number of the line to check.
+ next_line_start: The first non-whitespace column of the next line.
+ error: The function to call with any errors found.
+ """
+ commentpos = line.find('//')
+ if commentpos != -1:
+ # Check if the // may be in quotes. If so, ignore it
+ if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0:
+ # Allow one space for new scopes, two spaces otherwise:
+ if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and
+ ((commentpos >= 1 and
+ line[commentpos-1] not in string.whitespace) or
+ (commentpos >= 2 and
+ line[commentpos-2] not in string.whitespace))):
+ error(filename, linenum, 'whitespace/comments', 2,
+ 'At least two spaces is best between code and comments')
+
+ # Checks for common mistakes in TODO comments.
+ comment = line[commentpos:]
+ match = _RE_PATTERN_TODO.match(comment)
+ if match:
+ # One whitespace is correct; zero whitespace is handled elsewhere.
+ leading_whitespace = match.group(1)
+ if len(leading_whitespace) > 1:
+ error(filename, linenum, 'whitespace/todo', 2,
+ 'Too many spaces before TODO')
+
+ username = match.group(2)
+ if not username:
+ error(filename, linenum, 'readability/todo', 2,
+ 'Missing username in TODO; it should look like '
+ '"// TODO(my_username): Stuff."')
+
+ middle_whitespace = match.group(3)
+ # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison
+ if middle_whitespace != ' ' and middle_whitespace != '':
+ error(filename, linenum, 'whitespace/todo', 2,
+ 'TODO(my_username) should be followed by a space')
+
+ # If the comment contains an alphanumeric character, there
+ # should be a space somewhere between it and the // unless
+ # it's a /// or //! Doxygen comment.
+ if (Match(r'//[^ ]*\w', comment) and
+ not Match(r'(///|//\!)(\s+|$)', comment)):
+ error(filename, linenum, 'whitespace/comments', 4,
+ 'Should have a space between // and comment')
+
+
+def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
+ """Checks for the correctness of various spacing issues in the code.
+
+ Things we check for: spaces around operators, spaces after
+ if/for/while/switch, no spaces around parens in function calls, two
+ spaces between code and comment, don't start a block with a blank
+ line, don't end a function with a blank line, don't add a blank line
+ after public/protected/private, don't have too many blank lines in a row.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+
+ # Don't use "elided" lines here, otherwise we can't check commented lines.
+ # Don't want to use "raw" either, because we don't want to check inside C++11
+ # raw strings,
+ raw = clean_lines.lines_without_raw_strings
+ line = raw[linenum]
+
+ # Before nixing comments, check if the line is blank for no good
+ # reason. This includes the first line after a block is opened, and
+ # blank lines at the end of a function (ie, right before a line like '}'
+ #
+ # Skip all the blank line checks if we are immediately inside a
+ # namespace body. In other words, don't issue blank line warnings
+ # for this block:
+ # namespace {
+ #
+ # }
+ #
+ # A warning about missing end of namespace comments will be issued instead.
+ #
+ # Also skip blank line checks for 'extern "C"' blocks, which are formatted
+ # like namespaces.
+ if (IsBlankLine(line) and
+ not nesting_state.InNamespaceBody() and
+ not nesting_state.InExternC()):
+ elided = clean_lines.elided
+ prev_line = elided[linenum - 1]
+ prevbrace = prev_line.rfind('{')
+ # TODO(unknown): Don't complain if line before blank line, and line after,
+ # both start with alnums and are indented the same amount.
+ # This ignores whitespace at the start of a namespace block
+ # because those are not usually indented.
+ if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1:
+ # OK, we have a blank line at the start of a code block. Before we
+ # complain, we check if it is an exception to the rule: The previous
+ # non-empty line has the parameters of a function header that are indented
+ # 4 spaces (because they did not fit in a 80 column line when placed on
+ # the same line as the function name). We also check for the case where
+ # the previous line is indented 6 spaces, which may happen when the
+ # initializers of a constructor do not fit into a 80 column line.
+ exception = False
+ if Match(r' {6}\w', prev_line): # Initializer list?
+ # We are looking for the opening column of initializer list, which
+ # should be indented 4 spaces to cause 6 space indentation afterwards.
+ search_position = linenum-2
+ while (search_position >= 0
+ and Match(r' {6}\w', elided[search_position])):
+ search_position -= 1
+ exception = (search_position >= 0
+ and elided[search_position][:5] == ' :')
+ else:
+ # Search for the function arguments or an initializer list. We use a
+ # simple heuristic here: If the line is indented 4 spaces; and we have a
+ # closing paren, without the opening paren, followed by an opening brace
+ # or colon (for initializer lists) we assume that it is the last line of
+ # a function header. If we have a colon indented 4 spaces, it is an
+ # initializer list.
+ exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',
+ prev_line)
+ or Match(r' {4}:', prev_line))
+
+ if not exception:
+ error(filename, linenum, 'whitespace/blank_line', 2,
+ 'Redundant blank line at the start of a code block '
+ 'should be deleted.')
+ # Ignore blank lines at the end of a block in a long if-else
+ # chain, like this:
+ # if (condition1) {
+ # // Something followed by a blank line
+ #
+ # } else if (condition2) {
+ # // Something else
+ # }
+ if linenum + 1 < clean_lines.NumLines():
+ next_line = raw[linenum + 1]
+ if (next_line
+ and Match(r'\s*}', next_line)
+ and next_line.find('} else ') == -1):
+ error(filename, linenum, 'whitespace/blank_line', 3,
+ 'Redundant blank line at the end of a code block '
+ 'should be deleted.')
+
+ matched = Match(r'\s*(public|protected|private):', prev_line)
+ if matched:
+ error(filename, linenum, 'whitespace/blank_line', 3,
+ 'Do not leave a blank line after "%s:"' % matched.group(1))
+
+ # Next, check comments
+ next_line_start = 0
+ if linenum + 1 < clean_lines.NumLines():
+ next_line = raw[linenum + 1]
+ next_line_start = len(next_line) - len(next_line.lstrip())
+ CheckComment(line, filename, linenum, next_line_start, error)
+
+ # get rid of comments and strings
+ line = clean_lines.elided[linenum]
+
+ # You shouldn't have spaces before your brackets, except maybe after
+ # 'delete []' or 'return []() {};'
+ if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line):
+ error(filename, linenum, 'whitespace/braces', 5,
+ 'Extra space before [')
+
+ # In range-based for, we wanted spaces before and after the colon, but
+ # not around "::" tokens that might appear.
+ if (Search(r'for *\(.*[^:]:[^: ]', line) or
+ Search(r'for *\(.*[^: ]:[^:]', line)):
+ error(filename, linenum, 'whitespace/forcolon', 2,
+ 'Missing space around colon in range-based for loop')
+
+
+def CheckOperatorSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing around operators.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Don't try to do spacing checks for operator methods. Do this by
+ # replacing the troublesome characters with something else,
+ # preserving column position for all other characters.
+ #
+ # The replacement is done repeatedly to avoid false positives from
+ # operators that call operators.
+ while True:
+ match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line)
+ if match:
+ line = match.group(1) + ('_' * len(match.group(2))) + match.group(3)
+ else:
+ break
+
+ # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )".
+ # Otherwise not. Note we only check for non-spaces on *both* sides;
+ # sometimes people put non-spaces on one side when aligning ='s among
+ # many lines (not that this is behavior that I approve of...)
+ if ((Search(r'[\w.]=', line) or
+ Search(r'=[\w.]', line))
+ and not Search(r'\b(if|while|for) ', line)
+ # Operators taken from [lex.operators] in C++11 standard.
+ and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line)
+ and not Search(r'operator=', line)):
+ error(filename, linenum, 'whitespace/operators', 4,
+ 'Missing spaces around =')
+
+ # It's ok not to have spaces around binary operators like + - * /, but if
+ # there's too little whitespace, we get concerned. It's hard to tell,
+ # though, so we punt on this one for now. TODO.
+
+ # You should always have whitespace around binary operators.
+ #
+ # Check <= and >= first to avoid false positives with < and >, then
+ # check non-include lines for spacing around < and >.
+ #
+ # If the operator is followed by a comma, assume it's be used in a
+ # macro context and don't do any checks. This avoids false
+ # positives.
+ #
+ # Note that && is not included here. This is because there are too
+ # many false positives due to RValue references.
+ match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line)
+ if match:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around %s' % match.group(1))
+ elif not Match(r'#.*include', line):
+ # Look for < that is not surrounded by spaces. This is only
+ # triggered if both sides are missing spaces, even though
+ # technically should should flag if at least one side is missing a
+ # space. This is done to avoid some false positives with shifts.
+ match = Match(r'^(.*[^\s<])<[^\s=<,]', line)
+ if match:
+ (_, _, end_pos) = CloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ if end_pos <= -1:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around <')
+
+ # Look for > that is not surrounded by spaces. Similar to the
+ # above, we only trigger if both sides are missing spaces to avoid
+ # false positives with shifts.
+ match = Match(r'^(.*[^-\s>])>[^\s=>,]', line)
+ if match:
+ (_, _, start_pos) = ReverseCloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ if start_pos <= -1:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around >')
+
+ # We allow no-spaces around << when used like this: 10<<20, but
+ # not otherwise (particularly, not when used as streams)
+ #
+ # We also allow operators following an opening parenthesis, since
+ # those tend to be macros that deal with operators.
+ match = Search(r'(operator|[^\s(<])(?:L|UL|LL|ULL|l|ul|ll|ull)?<<([^\s,=<])', line)
+ if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and
+ not (match.group(1) == 'operator' and match.group(2) == ';')):
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around <<')
+
+ # We allow no-spaces around >> for almost anything. This is because
+ # C++11 allows ">>" to close nested templates, which accounts for
+ # most cases when ">>" is not followed by a space.
+ #
+ # We still warn on ">>" followed by alpha character, because that is
+ # likely due to ">>" being used for right shifts, e.g.:
+ # value >> alpha
+ #
+ # When ">>" is used to close templates, the alphanumeric letter that
+ # follows would be part of an identifier, and there should still be
+ # a space separating the template type and the identifier.
+ # type<type<type>> alpha
+ match = Search(r'>>[a-zA-Z_]', line)
+ if match:
+ error(filename, linenum, 'whitespace/operators', 3,
+ 'Missing spaces around >>')
+
+ # There shouldn't be space around unary operators
+ match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line)
+ if match:
+ error(filename, linenum, 'whitespace/operators', 4,
+ 'Extra space for operator %s' % match.group(1))
+
+
+def CheckParenthesisSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing around parentheses.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # No spaces after an if, while, switch, or for
+ match = Search(r' (if\(|for\(|while\(|switch\()', line)
+ if match:
+ error(filename, linenum, 'whitespace/parens', 5,
+ 'Missing space before ( in %s' % match.group(1))
+
+ # For if/for/while/switch, the left and right parens should be
+ # consistent about how many spaces are inside the parens, and
+ # there should either be zero or one spaces inside the parens.
+ # We don't want: "if ( foo)" or "if ( foo )".
+ # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed.
+ match = Search(r'\b(if|for|while|switch)\s*'
+ r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$',
+ line)
+ if match:
+ if len(match.group(2)) != len(match.group(4)):
+ if not (match.group(3) == ';' and
+ len(match.group(2)) == 1 + len(match.group(4)) or
+ not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)):
+ error(filename, linenum, 'whitespace/parens', 5,
+ 'Mismatching spaces inside () in %s' % match.group(1))
+ if len(match.group(2)) not in [0, 1]:
+ error(filename, linenum, 'whitespace/parens', 5,
+ 'Should have zero or one spaces inside ( and ) in %s' %
+ match.group(1))
+
+
+def CheckCommaSpacing(filename, clean_lines, linenum, error):
+ """Checks for horizontal spacing near commas and semicolons.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ raw = clean_lines.lines_without_raw_strings
+ line = clean_lines.elided[linenum]
+
+ # You should always have a space after a comma (either as fn arg or operator)
+ #
+ # This does not apply when the non-space character following the
+ # comma is another comma, since the only time when that happens is
+ # for empty macro arguments.
+ #
+ # We run this check in two passes: first pass on elided lines to
+ # verify that lines contain missing whitespaces, second pass on raw
+ # lines to confirm that those missing whitespaces are not due to
+ # elided comments.
+ if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and
+ Search(r',[^,\s]', raw[linenum])):
+ error(filename, linenum, 'whitespace/comma', 3,
+ 'Missing space after ,')
+
+ # You should always have a space after a semicolon
+ # except for few corner cases
+ # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more
+ # space after ;
+ if Search(r';[^\s};\\)/]', line):
+ error(filename, linenum, 'whitespace/semicolon', 3,
+ 'Missing space after ;')
+
+
+def _IsType(clean_lines, nesting_state, expr):
+ """Check if expression looks like a type name, returns true if so.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ expr: The expression to check.
+ Returns:
+ True, if token looks like a type.
+ """
+ # Keep only the last token in the expression
+ last_word = Match(r'^.*(\b\S+)$', expr)
+ if last_word:
+ token = last_word.group(1)
+ else:
+ token = expr
+
+ # Match native types and stdint types
+ if _TYPES.match(token):
+ return True
+
+ # Try a bit harder to match templated types. Walk up the nesting
+ # stack until we find something that resembles a typename
+ # declaration for what we are looking for.
+ typename_pattern = (r'\b(?:typename|class|struct)\s+' + re.escape(token) +
+ r'\b')
+ block_index = len(nesting_state.stack) - 1
+ while block_index >= 0:
+ if isinstance(nesting_state.stack[block_index], _NamespaceInfo):
+ return False
+
+ # Found where the opening brace is. We want to scan from this
+ # line up to the beginning of the function, minus a few lines.
+ # template <typename Type1, // stop scanning here
+ # ...>
+ # class C
+ # : public ... { // start scanning here
+ last_line = nesting_state.stack[block_index].starting_linenum
+
+ next_block_start = 0
+ if block_index > 0:
+ next_block_start = nesting_state.stack[block_index - 1].starting_linenum
+ first_line = last_line
+ while first_line >= next_block_start:
+ if clean_lines.elided[first_line].find('template') >= 0:
+ break
+ first_line -= 1
+ if first_line < next_block_start:
+ # Didn't find any "template" keyword before reaching the next block,
+ # there are probably no template things to check for this block
+ block_index -= 1
+ continue
+
+ # Look for typename in the specified range
+ for i in xrange(first_line, last_line + 1, 1):
+ if Search(typename_pattern, clean_lines.elided[i]):
+ return True
+ block_index -= 1
+
+ return False
+
+
+def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error):
+ """Checks for horizontal spacing near commas.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Except after an opening paren, or after another opening brace (in case of
+ # an initializer list, for instance), you should have spaces before your
+ # braces when they are delimiting blocks, classes, namespaces etc.
+ # And since you should never have braces at the beginning of a line,
+ # this is an easy test. Except that braces used for initialization don't
+ # follow the same rule; we often don't want spaces before those.
+ match = Match(r'^(.*[^ ({>]){', line)
+
+ if match:
+ # Try a bit harder to check for brace initialization. This
+ # happens in one of the following forms:
+ # Constructor() : initializer_list_{} { ... }
+ # Constructor{}.MemberFunction()
+ # Type variable{};
+ # FunctionCall(type{}, ...);
+ # LastArgument(..., type{});
+ # LOG(INFO) << type{} << " ...";
+ # map_of_type[{...}] = ...;
+ # ternary = expr ? new type{} : nullptr;
+ # OuterTemplate<InnerTemplateConstructor<Type>{}>
+ #
+ # We check for the character following the closing brace, and
+ # silence the warning if it's one of those listed above, i.e.
+ # "{.;,)<>]:".
+ #
+ # To account for nested initializer list, we allow any number of
+ # closing braces up to "{;,)<". We can't simply silence the
+ # warning on first sight of closing brace, because that would
+ # cause false negatives for things that are not initializer lists.
+ # Silence this: But not this:
+ # Outer{ if (...) {
+ # Inner{...} if (...){ // Missing space before {
+ # }; }
+ #
+ # There is a false negative with this approach if people inserted
+ # spurious semicolons, e.g. "if (cond){};", but we will catch the
+ # spurious semicolon with a separate check.
+ leading_text = match.group(1)
+ (endline, endlinenum, endpos) = CloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ trailing_text = ''
+ if endpos > -1:
+ trailing_text = endline[endpos:]
+ for offset in xrange(endlinenum + 1,
+ min(endlinenum + 3, clean_lines.NumLines() - 1)):
+ trailing_text += clean_lines.elided[offset]
+ # We also suppress warnings for `uint64_t{expression}` etc., as the style
+ # guide recommends brace initialization for integral types to avoid
+ # overflow/truncation.
+ if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text)
+ and not _IsType(clean_lines, nesting_state, leading_text)):
+ error(filename, linenum, 'whitespace/braces', 5,
+ 'Missing space before {')
+
+ # Make sure '} else {' has spaces.
+ if Search(r'}else', line):
+ error(filename, linenum, 'whitespace/braces', 5,
+ 'Missing space before else')
+
+ # You shouldn't have a space before a semicolon at the end of the line.
+ # There's a special case for "for" since the style guide allows space before
+ # the semicolon there.
+ if Search(r':\s*;\s*$', line):
+ error(filename, linenum, 'whitespace/semicolon', 5,
+ 'Semicolon defining empty statement. Use {} instead.')
+ elif Search(r'^\s*;\s*$', line):
+ error(filename, linenum, 'whitespace/semicolon', 5,
+ 'Line contains only semicolon. If this should be an empty statement, '
+ 'use {} instead.')
+ elif (Search(r'\s+;\s*$', line) and
+ not Search(r'\bfor\b', line)):
+ error(filename, linenum, 'whitespace/semicolon', 5,
+ 'Extra space before last semicolon. If this should be an empty '
+ 'statement, use {} instead.')
+
+
+def IsDecltype(clean_lines, linenum, column):
+ """Check if the token ending on (linenum, column) is decltype().
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: the number of the line to check.
+ column: end column of the token to check.
+ Returns:
+ True if this token is decltype() expression, False otherwise.
+ """
+ (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column)
+ if start_col < 0:
+ return False
+ if Search(r'\bdecltype\s*$', text[0:start_col]):
+ return True
+ return False
+
+
+def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error):
+ """Checks for additional blank line issues related to sections.
+
+ Currently the only thing checked here is blank line before protected/private.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ class_info: A _ClassInfo objects.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ # Skip checks if the class is small, where small means 25 lines or less.
+ # 25 lines seems like a good cutoff since that's the usual height of
+ # terminals, and any class that can't fit in one screen can't really
+ # be considered "small".
+ #
+ # Also skip checks if we are on the first line. This accounts for
+ # classes that look like
+ # class Foo { public: ... };
+ #
+ # If we didn't find the end of the class, last_line would be zero,
+ # and the check will be skipped by the first condition.
+ if (class_info.last_line - class_info.starting_linenum <= 24 or
+ linenum <= class_info.starting_linenum):
+ return
+
+ matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum])
+ if matched:
+ # Issue warning if the line before public/protected/private was
+ # not a blank line, but don't do this if the previous line contains
+ # "class" or "struct". This can happen two ways:
+ # - We are at the beginning of the class.
+ # - We are forward-declaring an inner class that is semantically
+ # private, but needed to be public for implementation reasons.
+ # Also ignores cases where the previous line ends with a backslash as can be
+ # common when defining classes in C macros.
+ prev_line = clean_lines.lines[linenum - 1]
+ if (not IsBlankLine(prev_line) and
+ not Search(r'\b(class|struct)\b', prev_line) and
+ not Search(r'\\$', prev_line)):
+ # Try a bit harder to find the beginning of the class. This is to
+ # account for multi-line base-specifier lists, e.g.:
+ # class Derived
+ # : public Base {
+ end_class_head = class_info.starting_linenum
+ for i in range(class_info.starting_linenum, linenum):
+ if Search(r'\{\s*$', clean_lines.lines[i]):
+ end_class_head = i
+ break
+ if end_class_head < linenum - 1:
+ error(filename, linenum, 'whitespace/blank_line', 3,
+ '"%s:" should be preceded by a blank line' % matched.group(1))
+
+
+def GetPreviousNonBlankLine(clean_lines, linenum):
+ """Return the most recent non-blank line and its line number.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file contents.
+ linenum: The number of the line to check.
+
+ Returns:
+ A tuple with two elements. The first element is the contents of the last
+ non-blank line before the current line, or the empty string if this is the
+ first non-blank line. The second is the line number of that line, or -1
+ if this is the first non-blank line.
+ """
+
+ prevlinenum = linenum - 1
+ while prevlinenum >= 0:
+ prevline = clean_lines.elided[prevlinenum]
+ if not IsBlankLine(prevline): # if not a blank line...
+ return (prevline, prevlinenum)
+ prevlinenum -= 1
+ return ('', -1)
+
+
+def CheckBraces(filename, clean_lines, linenum, error):
+ """Looks for misplaced braces (e.g. at the end of line).
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ line = clean_lines.elided[linenum] # get rid of comments and strings
+
+ if Match(r'\s*{\s*$', line):
+ # We allow an open brace to start a line in the case where someone is using
+ # braces in a block to explicitly create a new scope, which is commonly used
+ # to control the lifetime of stack-allocated variables. Braces are also
+ # used for brace initializers inside function calls. We don't detect this
+ # perfectly: we just don't complain if the last non-whitespace character on
+ # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the
+ # previous line starts a preprocessor block. We also allow a brace on the
+ # following line if it is part of an array initialization and would not fit
+ # within the 80 character limit of the preceding line.
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ if (not Search(r'[,;:}{(]\s*$', prevline) and
+ not Match(r'\s*#', prevline) and
+ not (GetLineWidth(prevline) > _line_length - 2 and '[]' in prevline)):
+ error(filename, linenum, 'whitespace/braces', 4,
+ '{ should almost always be at the end of the previous line')
+
+ # An else clause should be on the same line as the preceding closing brace.
+ if Match(r'\s*else\b\s*(?:if\b|\{|$)', line):
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ if Match(r'\s*}\s*$', prevline):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'An else should appear on the same line as the preceding }')
+
+ # If braces come on one side of an else, they should be on both.
+ # However, we have to worry about "else if" that spans multiple lines!
+ if Search(r'else if\s*\(', line): # could be multi-line if
+ brace_on_left = bool(Search(r'}\s*else if\s*\(', line))
+ # find the ( after the if
+ pos = line.find('else if')
+ pos = line.find('(', pos)
+ if pos > 0:
+ (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos)
+ brace_on_right = endline[endpos:].find('{') != -1
+ if brace_on_left != brace_on_right: # must be brace after if
+ error(filename, linenum, 'readability/braces', 5,
+ 'If an else has a brace on one side, it should have it on both')
+ elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line):
+ error(filename, linenum, 'readability/braces', 5,
+ 'If an else has a brace on one side, it should have it on both')
+
+ # Likewise, an else should never have the else clause on the same line
+ if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'Else clause should never be on same line as else (use 2 lines)')
+
+ # In the same way, a do/while should never be on one line
+ if Match(r'\s*do [^\s{]', line):
+ error(filename, linenum, 'whitespace/newline', 4,
+ 'do/while clauses should not be on a single line')
+
+ # Check single-line if/else bodies. The style guide says 'curly braces are not
+ # required for single-line statements'. We additionally allow multi-line,
+ # single statements, but we reject anything with more than one semicolon in
+ # it. This means that the first semicolon after the if should be at the end of
+ # its line, and the line after that should have an indent level equal to or
+ # lower than the if. We also check for ambiguous if/else nesting without
+ # braces.
+ if_else_match = Search(r'\b(if\s*\(|else\b)', line)
+ if if_else_match and not Match(r'\s*#', line):
+ if_indent = GetIndentLevel(line)
+ endline, endlinenum, endpos = line, linenum, if_else_match.end()
+ if_match = Search(r'\bif\s*\(', line)
+ if if_match:
+ # This could be a multiline if condition, so find the end first.
+ pos = if_match.end() - 1
+ (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos)
+ # Check for an opening brace, either directly after the if or on the next
+ # line. If found, this isn't a single-statement conditional.
+ if (not Match(r'\s*{', endline[endpos:])
+ and not (Match(r'\s*$', endline[endpos:])
+ and endlinenum < (len(clean_lines.elided) - 1)
+ and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))):
+ while (endlinenum < len(clean_lines.elided)
+ and ';' not in clean_lines.elided[endlinenum][endpos:]):
+ endlinenum += 1
+ endpos = 0
+ if endlinenum < len(clean_lines.elided):
+ endline = clean_lines.elided[endlinenum]
+ # We allow a mix of whitespace and closing braces (e.g. for one-liner
+ # methods) and a single \ after the semicolon (for macros)
+ endpos = endline.find(';')
+ if not Match(r';[\s}]*(\\?)$', endline[endpos:]):
+ # Semicolon isn't the last character, there's something trailing.
+ # Output a warning if the semicolon is not contained inside
+ # a lambda expression.
+ if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$',
+ endline):
+ error(filename, linenum, 'readability/braces', 4,
+ 'If/else bodies with multiple statements require braces')
+ elif endlinenum < len(clean_lines.elided) - 1:
+ # Make sure the next line is dedented
+ next_line = clean_lines.elided[endlinenum + 1]
+ next_indent = GetIndentLevel(next_line)
+ # With ambiguous nested if statements, this will error out on the
+ # if that *doesn't* match the else, regardless of whether it's the
+ # inner one or outer one.
+ if (if_match and Match(r'\s*else\b', next_line)
+ and next_indent != if_indent):
+ error(filename, linenum, 'readability/braces', 4,
+ 'Else clause should be indented at the same level as if. '
+ 'Ambiguous nested if/else chains require braces.')
+ elif next_indent > if_indent:
+ error(filename, linenum, 'readability/braces', 4,
+ 'If/else bodies with multiple statements require braces')
+
+
+def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
+ """Looks for redundant trailing semicolon.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ line = clean_lines.elided[linenum]
+
+ # Block bodies should not be followed by a semicolon. Due to C++11
+ # brace initialization, there are more places where semicolons are
+ # required than not, so we use a whitelist approach to check these
+ # rather than a blacklist. These are the places where "};" should
+ # be replaced by just "}":
+ # 1. Some flavor of block following closing parenthesis:
+ # for (;;) {};
+ # while (...) {};
+ # switch (...) {};
+ # Function(...) {};
+ # if (...) {};
+ # if (...) else if (...) {};
+ #
+ # 2. else block:
+ # if (...) else {};
+ #
+ # 3. const member function:
+ # Function(...) const {};
+ #
+ # 4. Block following some statement:
+ # x = 42;
+ # {};
+ #
+ # 5. Block at the beginning of a function:
+ # Function(...) {
+ # {};
+ # }
+ #
+ # Note that naively checking for the preceding "{" will also match
+ # braces inside multi-dimensional arrays, but this is fine since
+ # that expression will not contain semicolons.
+ #
+ # 6. Block following another block:
+ # while (true) {}
+ # {};
+ #
+ # 7. End of namespaces:
+ # namespace {};
+ #
+ # These semicolons seems far more common than other kinds of
+ # redundant semicolons, possibly due to people converting classes
+ # to namespaces. For now we do not warn for this case.
+ #
+ # Try matching case 1 first.
+ match = Match(r'^(.*\)\s*)\{', line)
+ if match:
+ # Matched closing parenthesis (case 1). Check the token before the
+ # matching opening parenthesis, and don't warn if it looks like a
+ # macro. This avoids these false positives:
+ # - macro that defines a base class
+ # - multi-line macro that defines a base class
+ # - macro that defines the whole class-head
+ #
+ # But we still issue warnings for macros that we know are safe to
+ # warn, specifically:
+ # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P
+ # - TYPED_TEST
+ # - INTERFACE_DEF
+ # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED:
+ #
+ # We implement a whitelist of safe macros instead of a blacklist of
+ # unsafe macros, even though the latter appears less frequently in
+ # google code and would have been easier to implement. This is because
+ # the downside for getting the whitelist wrong means some extra
+ # semicolons, while the downside for getting the blacklist wrong
+ # would result in compile errors.
+ #
+ # In addition to macros, we also don't want to warn on
+ # - Compound literals
+ # - Lambdas
+ # - alignas specifier with anonymous structs
+ # - decltype
+ closing_brace_pos = match.group(1).rfind(')')
+ opening_parenthesis = ReverseCloseExpression(
+ clean_lines, linenum, closing_brace_pos)
+ if opening_parenthesis[2] > -1:
+ line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]]
+ macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix)
+ func = Match(r'^(.*\])\s*$', line_prefix)
+ if ((macro and
+ macro.group(1) not in (
+ 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST',
+ 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED',
+ 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or
+ (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or
+ Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or
+ Search(r'\bdecltype$', line_prefix) or
+ Search(r'\s+=\s*$', line_prefix)):
+ match = None
+ if (match and
+ opening_parenthesis[1] > 1 and
+ Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])):
+ # Multi-line lambda-expression
+ match = None
+
+ else:
+ # Try matching cases 2-3.
+ match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line)
+ if not match:
+ # Try matching cases 4-6. These are always matched on separate lines.
+ #
+ # Note that we can't simply concatenate the previous line to the
+ # current line and do a single match, otherwise we may output
+ # duplicate warnings for the blank line case:
+ # if (cond) {
+ # // blank line
+ # }
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ if prevline and Search(r'[;{}]\s*$', prevline):
+ match = Match(r'^(\s*)\{', line)
+
+ # Check matching closing brace
+ if match:
+ (endline, endlinenum, endpos) = CloseExpression(
+ clean_lines, linenum, len(match.group(1)))
+ if endpos > -1 and Match(r'^\s*;', endline[endpos:]):
+ # Current {} pair is eligible for semicolon check, and we have found
+ # the redundant semicolon, output warning here.
+ #
+ # Note: because we are scanning forward for opening braces, and
+ # outputting warnings for the matching closing brace, if there are
+ # nested blocks with trailing semicolons, we will get the error
+ # messages in reversed order.
+
+ # We need to check the line forward for NOLINT
+ raw_lines = clean_lines.raw_lines
+ ParseNolintSuppressions(filename, raw_lines[endlinenum-1], endlinenum-1,
+ error)
+ ParseNolintSuppressions(filename, raw_lines[endlinenum], endlinenum,
+ error)
+
+ error(filename, endlinenum, 'readability/braces', 4,
+ "You don't need a ; after a }")
+
+
+def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
+ """Look for empty loop/conditional body with only a single semicolon.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ # Search for loop keywords at the beginning of the line. Because only
+ # whitespaces are allowed before the keywords, this will also ignore most
+ # do-while-loops, since those lines should start with closing brace.
+ #
+ # We also check "if" blocks here, since an empty conditional block
+ # is likely an error.
+ line = clean_lines.elided[linenum]
+ matched = Match(r'\s*(for|while|if)\s*\(', line)
+ if matched:
+ # Find the end of the conditional expression.
+ (end_line, end_linenum, end_pos) = CloseExpression(
+ clean_lines, linenum, line.find('('))
+
+ # Output warning if what follows the condition expression is a semicolon.
+ # No warning for all other cases, including whitespace or newline, since we
+ # have a separate check for semicolons preceded by whitespace.
+ if end_pos >= 0 and Match(r';', end_line[end_pos:]):
+ if matched.group(1) == 'if':
+ error(filename, end_linenum, 'whitespace/empty_conditional_body', 5,
+ 'Empty conditional bodies should use {}')
+ else:
+ error(filename, end_linenum, 'whitespace/empty_loop_body', 5,
+ 'Empty loop bodies should use {} or continue')
+
+ # Check for if statements that have completely empty bodies (no comments)
+ # and no else clauses.
+ if end_pos >= 0 and matched.group(1) == 'if':
+ # Find the position of the opening { for the if statement.
+ # Return without logging an error if it has no brackets.
+ opening_linenum = end_linenum
+ opening_line_fragment = end_line[end_pos:]
+ # Loop until EOF or find anything that's not whitespace or opening {.
+ while not Search(r'^\s*\{', opening_line_fragment):
+ if Search(r'^(?!\s*$)', opening_line_fragment):
+ # Conditional has no brackets.
+ return
+ opening_linenum += 1
+ if opening_linenum == len(clean_lines.elided):
+ # Couldn't find conditional's opening { or any code before EOF.
+ return
+ opening_line_fragment = clean_lines.elided[opening_linenum]
+ # Set opening_line (opening_line_fragment may not be entire opening line).
+ opening_line = clean_lines.elided[opening_linenum]
+
+ # Find the position of the closing }.
+ opening_pos = opening_line_fragment.find('{')
+ if opening_linenum == end_linenum:
+ # We need to make opening_pos relative to the start of the entire line.
+ opening_pos += end_pos
+ (closing_line, closing_linenum, closing_pos) = CloseExpression(
+ clean_lines, opening_linenum, opening_pos)
+ if closing_pos < 0:
+ return
+
+ # Now construct the body of the conditional. This consists of the portion
+ # of the opening line after the {, all lines until the closing line,
+ # and the portion of the closing line before the }.
+ if (clean_lines.raw_lines[opening_linenum] !=
+ CleanseComments(clean_lines.raw_lines[opening_linenum])):
+ # Opening line ends with a comment, so conditional isn't empty.
+ return
+ if closing_linenum > opening_linenum:
+ # Opening line after the {. Ignore comments here since we checked above.
+ body = list(opening_line[opening_pos+1:])
+ # All lines until closing line, excluding closing line, with comments.
+ body.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum])
+ # Closing line before the }. Won't (and can't) have comments.
+ body.append(clean_lines.elided[closing_linenum][:closing_pos-1])
+ body = '\n'.join(body)
+ else:
+ # If statement has brackets and fits on a single line.
+ body = opening_line[opening_pos+1:closing_pos-1]
+
+ # Check if the body is empty
+ if not _EMPTY_CONDITIONAL_BODY_PATTERN.search(body):
+ return
+ # The body is empty. Now make sure there's not an else clause.
+ current_linenum = closing_linenum
+ current_line_fragment = closing_line[closing_pos:]
+ # Loop until EOF or find anything that's not whitespace or else clause.
+ while Search(r'^\s*$|^(?=\s*else)', current_line_fragment):
+ if Search(r'^(?=\s*else)', current_line_fragment):
+ # Found an else clause, so don't log an error.
+ return
+ current_linenum += 1
+ if current_linenum == len(clean_lines.elided):
+ break
+ current_line_fragment = clean_lines.elided[current_linenum]
+
+ # The body is empty and there's no else clause until EOF or other code.
+ error(filename, end_linenum, 'whitespace/empty_if_body', 4,
+ ('If statement had no body and no else clause'))
+
+
+def FindCheckMacro(line):
+ """Find a replaceable CHECK-like macro.
+
+ Args:
+ line: line to search on.
+ Returns:
+ (macro name, start position), or (None, -1) if no replaceable
+ macro is found.
+ """
+ for macro in _CHECK_MACROS:
+ i = line.find(macro)
+ if i >= 0:
+ # Find opening parenthesis. Do a regular expression match here
+ # to make sure that we are matching the expected CHECK macro, as
+ # opposed to some other macro that happens to contain the CHECK
+ # substring.
+ matched = Match(r'^(.*\b' + macro + r'\s*)\(', line)
+ if not matched:
+ continue
+ return (macro, len(matched.group(1)))
+ return (None, -1)
+
+
+def CheckCheck(filename, clean_lines, linenum, error):
+ """Checks the use of CHECK and EXPECT macros.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+
+ # Decide the set of replacement macros that should be suggested
+ lines = clean_lines.elided
+ (check_macro, start_pos) = FindCheckMacro(lines[linenum])
+ if not check_macro:
+ return
+
+ # Find end of the boolean expression by matching parentheses
+ (last_line, end_line, end_pos) = CloseExpression(
+ clean_lines, linenum, start_pos)
+ if end_pos < 0:
+ return
+
+ # If the check macro is followed by something other than a
+ # semicolon, assume users will log their own custom error messages
+ # and don't suggest any replacements.
+ if not Match(r'\s*;', last_line[end_pos:]):
+ return
+
+ if linenum == end_line:
+ expression = lines[linenum][start_pos + 1:end_pos - 1]
+ else:
+ expression = lines[linenum][start_pos + 1:]
+ for i in xrange(linenum + 1, end_line):
+ expression += lines[i]
+ expression += last_line[0:end_pos - 1]
+
+ # Parse expression so that we can take parentheses into account.
+ # This avoids false positives for inputs like "CHECK((a < 4) == b)",
+ # which is not replaceable by CHECK_LE.
+ lhs = ''
+ rhs = ''
+ operator = None
+ while expression:
+ matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||'
+ r'==|!=|>=|>|<=|<|\()(.*)$', expression)
+ if matched:
+ token = matched.group(1)
+ if token == '(':
+ # Parenthesized operand
+ expression = matched.group(2)
+ (end, _) = FindEndOfExpressionInLine(expression, 0, ['('])
+ if end < 0:
+ return # Unmatched parenthesis
+ lhs += '(' + expression[0:end]
+ expression = expression[end:]
+ elif token in ('&&', '||'):
+ # Logical and/or operators. This means the expression
+ # contains more than one term, for example:
+ # CHECK(42 < a && a < b);
+ #
+ # These are not replaceable with CHECK_LE, so bail out early.
+ return
+ elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'):
+ # Non-relational operator
+ lhs += token
+ expression = matched.group(2)
+ else:
+ # Relational operator
+ operator = token
+ rhs = matched.group(2)
+ break
+ else:
+ # Unparenthesized operand. Instead of appending to lhs one character
+ # at a time, we do another regular expression match to consume several
+ # characters at once if possible. Trivial benchmark shows that this
+ # is more efficient when the operands are longer than a single
+ # character, which is generally the case.
+ matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression)
+ if not matched:
+ matched = Match(r'^(\s*\S)(.*)$', expression)
+ if not matched:
+ break
+ lhs += matched.group(1)
+ expression = matched.group(2)
+
+ # Only apply checks if we got all parts of the boolean expression
+ if not (lhs and operator and rhs):
+ return
+
+ # Check that rhs do not contain logical operators. We already know
+ # that lhs is fine since the loop above parses out && and ||.
+ if rhs.find('&&') > -1 or rhs.find('||') > -1:
+ return
+
+ # At least one of the operands must be a constant literal. This is
+ # to avoid suggesting replacements for unprintable things like
+ # CHECK(variable != iterator)
+ #
+ # The following pattern matches decimal, hex integers, strings, and
+ # characters (in that order).
+ lhs = lhs.strip()
+ rhs = rhs.strip()
+ match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$'
+ if Match(match_constant, lhs) or Match(match_constant, rhs):
+ # Note: since we know both lhs and rhs, we can provide a more
+ # descriptive error message like:
+ # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42)
+ # Instead of:
+ # Consider using CHECK_EQ instead of CHECK(a == b)
+ #
+ # We are still keeping the less descriptive message because if lhs
+ # or rhs gets long, the error message might become unreadable.
+ error(filename, linenum, 'readability/check', 2,
+ 'Consider using %s instead of %s(a %s b)' % (
+ _CHECK_REPLACEMENT[check_macro][operator],
+ check_macro, operator))
+
+
+def CheckAltTokens(filename, clean_lines, linenum, error):
+ """Check alternative keywords being used in boolean expressions.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Avoid preprocessor lines
+ if Match(r'^\s*#', line):
+ return
+
+ # Last ditch effort to avoid multi-line comments. This will not help
+ # if the comment started before the current line or ended after the
+ # current line, but it catches most of the false positives. At least,
+ # it provides a way to workaround this warning for people who use
+ # multi-line comments in preprocessor macros.
+ #
+ # TODO(unknown): remove this once cpplint has better support for
+ # multi-line comments.
+ if line.find('/*') >= 0 or line.find('*/') >= 0:
+ return
+
+ for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line):
+ error(filename, linenum, 'readability/alt_tokens', 2,
+ 'Use operator %s instead of %s' % (
+ _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1)))
+
+
+def GetLineWidth(line):
+ """Determines the width of the line in column positions.
+
+ Args:
+ line: A string, which may be a Unicode string.
+
+ Returns:
+ The width of the line in column positions, accounting for Unicode
+ combining characters and wide characters.
+ """
+ if isinstance(line, unicode):
+ width = 0
+ for uc in unicodedata.normalize('NFC', line):
+ if unicodedata.east_asian_width(uc) in ('W', 'F'):
+ width += 2
+ elif not unicodedata.combining(uc):
+ width += 1
+ return width
+ else:
+ return len(line)
+
+
+def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
+ error):
+ """Checks rules from the 'C++ style rules' section of cppguide.html.
+
+ Most of these rules are hard to test (naming, comment style), but we
+ do what we can. In particular we check for 2-space indents, line lengths,
+ tab usage, spaces inside code, etc.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ file_extension: The extension (without the dot) of the filename.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+
+ # Don't use "elided" lines here, otherwise we can't check commented lines.
+ # Don't want to use "raw" either, because we don't want to check inside C++11
+ # raw strings,
+ raw_lines = clean_lines.lines_without_raw_strings
+ line = raw_lines[linenum]
+ prev = raw_lines[linenum - 1] if linenum > 0 else ''
+
+ if line.find('\t') != -1:
+ error(filename, linenum, 'whitespace/tab', 1,
+ 'Tab found; better to use spaces')
+
+ # One or three blank spaces at the beginning of the line is weird; it's
+ # hard to reconcile that with 2-space indents.
+ # NOTE: here are the conditions rob pike used for his tests. Mine aren't
+ # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces
+ # if(RLENGTH > 20) complain = 0;
+ # if(match($0, " +(error|private|public|protected):")) complain = 0;
+ # if(match(prev, "&& *$")) complain = 0;
+ # if(match(prev, "\\|\\| *$")) complain = 0;
+ # if(match(prev, "[\",=><] *$")) complain = 0;
+ # if(match($0, " <<")) complain = 0;
+ # if(match(prev, " +for \\(")) complain = 0;
+ # if(prevodd && match(prevprev, " +for \\(")) complain = 0;
+ scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$'
+ classinfo = nesting_state.InnermostClass()
+ initial_spaces = 0
+ cleansed_line = clean_lines.elided[linenum]
+ while initial_spaces < len(line) and line[initial_spaces] == ' ':
+ initial_spaces += 1
+ # There are certain situations we allow one space, notably for
+ # section labels, and also lines containing multi-line raw strings.
+ # We also don't check for lines that look like continuation lines
+ # (of lines ending in double quotes, commas, equals, or angle brackets)
+ # because the rules for how to indent those are non-trivial.
+ if (not Search(r'[",=><] *$', prev) and
+ (initial_spaces == 1 or initial_spaces == 3) and
+ not Match(scope_or_label_pattern, cleansed_line) and
+ not (clean_lines.raw_lines[linenum] != line and
+ Match(r'^\s*""', line))):
+ error(filename, linenum, 'whitespace/indent', 3,
+ 'Weird number of spaces at line-start. '
+ 'Are you using a 2-space indent?')
+
+ if line and line[-1].isspace():
+ error(filename, linenum, 'whitespace/end_of_line', 4,
+ 'Line ends in whitespace. Consider deleting these extra spaces.')
+
+ # Check if the line is a header guard.
+ is_header_guard = False
+ if IsHeaderExtension(file_extension):
+ cppvar = GetHeaderGuardCPPVariable(filename)
+ if (line.startswith('#ifndef %s' % cppvar) or
+ line.startswith('#define %s' % cppvar) or
+ line.startswith('#endif // %s' % cppvar)):
+ is_header_guard = True
+ # #include lines and header guards can be long, since there's no clean way to
+ # split them.
+ #
+ # URLs can be long too. It's possible to split these, but it makes them
+ # harder to cut&paste.
+ #
+ # The "$Id:...$" comment may also get very long without it being the
+ # developers fault.
+ if (not line.startswith('#include') and not is_header_guard and
+ not Match(r'^\s*//.*http(s?)://\S*$', line) and
+ not Match(r'^\s*//\s*[^\s]*$', line) and
+ not Match(r'^// \$Id:.*#[0-9]+ \$$', line)):
+ line_width = GetLineWidth(line)
+ if line_width > _line_length:
+ error(filename, linenum, 'whitespace/line_length', 2,
+ 'Lines should be <= %i characters long' % _line_length)
+
+ if (cleansed_line.count(';') > 1 and
+ # for loops are allowed two ;'s (and may run over two lines).
+ cleansed_line.find('for') == -1 and
+ (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or
+ GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and
+ # It's ok to have many commands in a switch case that fits in 1 line
+ not ((cleansed_line.find('case ') != -1 or
+ cleansed_line.find('default:') != -1) and
+ cleansed_line.find('break;') != -1)):
+ error(filename, linenum, 'whitespace/newline', 0,
+ 'More than one command on the same line')
+
+ # Some more style checks
+ CheckBraces(filename, clean_lines, linenum, error)
+ CheckTrailingSemicolon(filename, clean_lines, linenum, error)
+ CheckEmptyBlockBody(filename, clean_lines, linenum, error)
+ CheckSpacing(filename, clean_lines, linenum, nesting_state, error)
+ CheckOperatorSpacing(filename, clean_lines, linenum, error)
+ CheckParenthesisSpacing(filename, clean_lines, linenum, error)
+ CheckCommaSpacing(filename, clean_lines, linenum, error)
+ CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error)
+ CheckSpacingForFunctionCall(filename, clean_lines, linenum, error)
+ CheckCheck(filename, clean_lines, linenum, error)
+ CheckAltTokens(filename, clean_lines, linenum, error)
+ classinfo = nesting_state.InnermostClass()
+ if classinfo:
+ CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error)
+
+
+_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$')
+# Matches the first component of a filename delimited by -s and _s. That is:
+# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo'
+# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo'
+# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo'
+# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo'
+_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+')
+
+
+def _DropCommonSuffixes(filename):
+ """Drops common suffixes like _test.cc or -inl.h from filename.
+
+ For example:
+ >>> _DropCommonSuffixes('foo/foo-inl.h')
+ 'foo/foo'
+ >>> _DropCommonSuffixes('foo/bar/foo.cc')
+ 'foo/bar/foo'
+ >>> _DropCommonSuffixes('foo/foo_internal.h')
+ 'foo/foo'
+ >>> _DropCommonSuffixes('foo/foo_unusualinternal.h')
+ 'foo/foo_unusualinternal'
+
+ Args:
+ filename: The input filename.
+
+ Returns:
+ The filename with the common suffix removed.
+ """
+ for suffix in ('test.cc', 'regtest.cc', 'unittest.cc',
+ 'inl.h', 'impl.h', 'internal.h'):
+ if (filename.endswith(suffix) and len(filename) > len(suffix) and
+ filename[-len(suffix) - 1] in ('-', '_')):
+ return filename[:-len(suffix) - 1]
+ return os.path.splitext(filename)[0]
+
+
+def _ClassifyInclude(fileinfo, include, is_system):
+ """Figures out what kind of header 'include' is.
+
+ Args:
+ fileinfo: The current file cpplint is running over. A FileInfo instance.
+ include: The path to a #included file.
+ is_system: True if the #include used <> rather than "".
+
+ Returns:
+ One of the _XXX_HEADER constants.
+
+ For example:
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True)
+ _C_SYS_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True)
+ _CPP_SYS_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False)
+ _LIKELY_MY_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'),
+ ... 'bar/foo_other_ext.h', False)
+ _POSSIBLE_MY_HEADER
+ >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False)
+ _OTHER_HEADER
+ """
+ # This is a list of all standard c++ header files, except
+ # those already checked for above.
+ is_cpp_h = include in _CPP_HEADERS
+
+ if is_system:
+ if is_cpp_h:
+ return _CPP_SYS_HEADER
+ else:
+ return _C_SYS_HEADER
+
+ # If the target file and the include we're checking share a
+ # basename when we drop common extensions, and the include
+ # lives in . , then it's likely to be owned by the target file.
+ target_dir, target_base = (
+ os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName())))
+ include_dir, include_base = os.path.split(_DropCommonSuffixes(include))
+ if target_base == include_base and (
+ include_dir == target_dir or
+ include_dir == os.path.normpath(target_dir + '/../public')):
+ return _LIKELY_MY_HEADER
+
+ # If the target and include share some initial basename
+ # component, it's possible the target is implementing the
+ # include, so it's allowed to be first, but we'll never
+ # complain if it's not there.
+ target_first_component = _RE_FIRST_COMPONENT.match(target_base)
+ include_first_component = _RE_FIRST_COMPONENT.match(include_base)
+ if (target_first_component and include_first_component and
+ target_first_component.group(0) ==
+ include_first_component.group(0)):
+ return _POSSIBLE_MY_HEADER
+
+ return _OTHER_HEADER
+
+
+
+def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
+ """Check rules that are applicable to #include lines.
+
+ Strings on #include lines are NOT removed from elided line, to make
+ certain tasks easier. However, to prevent false positives, checks
+ applicable to #include lines in CheckLanguage must be put here.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ include_state: An _IncludeState instance in which the headers are inserted.
+ error: The function to call with any errors found.
+ """
+ fileinfo = FileInfo(filename)
+ line = clean_lines.lines[linenum]
+
+ # "include" should use the new style "foo/bar.h" instead of just "bar.h"
+ # Only do this check if the included header follows google naming
+ # conventions. If not, assume that it's a 3rd party API that
+ # requires special include conventions.
+ #
+ # We also make an exception for Lua headers, which follow google
+ # naming convention but not the include convention.
+ match = Match(r'#include\s*"([^/]+\.h)"', line)
+ if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)):
+ error(filename, linenum, 'build/include', 4,
+ 'Include the directory when naming .h files')
+
+ # we shouldn't include a file more than once. actually, there are a
+ # handful of instances where doing so is okay, but in general it's
+ # not.
+ match = _RE_PATTERN_INCLUDE.search(line)
+ if match:
+ include = match.group(2)
+ is_system = (match.group(1) == '<')
+ duplicate_line = include_state.FindHeader(include)
+ if duplicate_line >= 0:
+ error(filename, linenum, 'build/include', 4,
+ '"%s" already included at %s:%s' %
+ (include, filename, duplicate_line))
+ elif (include.endswith('.cc') and
+ os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)):
+ error(filename, linenum, 'build/include', 4,
+ 'Do not include .cc files from other packages')
+ elif not _THIRD_PARTY_HEADERS_PATTERN.match(include):
+ include_state.include_list[-1].append((include, linenum))
+
+ # We want to ensure that headers appear in the right order:
+ # 1) for foo.cc, foo.h (preferred location)
+ # 2) c system files
+ # 3) cpp system files
+ # 4) for foo.cc, foo.h (deprecated location)
+ # 5) other google headers
+ #
+ # We classify each include statement as one of those 5 types
+ # using a number of techniques. The include_state object keeps
+ # track of the highest type seen, and complains if we see a
+ # lower type after that.
+ error_message = include_state.CheckNextIncludeOrder(
+ _ClassifyInclude(fileinfo, include, is_system))
+ if error_message:
+ error(filename, linenum, 'build/include_order', 4,
+ '%s. Should be: %s.h, c system, c++ system, other.' %
+ (error_message, fileinfo.BaseName()))
+ canonical_include = include_state.CanonicalizeAlphabeticalOrder(include)
+ if not include_state.IsInAlphabeticalOrder(
+ clean_lines, linenum, canonical_include):
+ error(filename, linenum, 'build/include_alpha', 4,
+ 'Include "%s" not in alphabetical order' % include)
+ include_state.SetLastHeader(canonical_include)
+
+
+
+def _GetTextInside(text, start_pattern):
+ r"""Retrieves all the text between matching open and close parentheses.
+
+ Given a string of lines and a regular expression string, retrieve all the text
+ following the expression and between opening punctuation symbols like
+ (, [, or {, and the matching close-punctuation symbol. This properly nested
+ occurrences of the punctuations, so for the text like
+ printf(a(), b(c()));
+ a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'.
+ start_pattern must match string having an open punctuation symbol at the end.
+
+ Args:
+ text: The lines to extract text. Its comments and strings must be elided.
+ It can be single line and can span multiple lines.
+ start_pattern: The regexp string indicating where to start extracting
+ the text.
+ Returns:
+ The extracted text.
+ None if either the opening string or ending punctuation could not be found.
+ """
+ # TODO(unknown): Audit cpplint.py to see what places could be profitably
+ # rewritten to use _GetTextInside (and use inferior regexp matching today).
+
+ # Give opening punctuations to get the matching close-punctuations.
+ matching_punctuation = {'(': ')', '{': '}', '[': ']'}
+ closing_punctuation = set(matching_punctuation.itervalues())
+
+ # Find the position to start extracting text.
+ match = re.search(start_pattern, text, re.M)
+ if not match: # start_pattern not found in text.
+ return None
+ start_position = match.end(0)
+
+ assert start_position > 0, (
+ 'start_pattern must ends with an opening punctuation.')
+ assert text[start_position - 1] in matching_punctuation, (
+ 'start_pattern must ends with an opening punctuation.')
+ # Stack of closing punctuations we expect to have in text after position.
+ punctuation_stack = [matching_punctuation[text[start_position - 1]]]
+ position = start_position
+ while punctuation_stack and position < len(text):
+ if text[position] == punctuation_stack[-1]:
+ punctuation_stack.pop()
+ elif text[position] in closing_punctuation:
+ # A closing punctuation without matching opening punctuations.
+ return None
+ elif text[position] in matching_punctuation:
+ punctuation_stack.append(matching_punctuation[text[position]])
+ position += 1
+ if punctuation_stack:
+ # Opening punctuations left without matching close-punctuations.
+ return None
+ # punctuations match.
+ return text[start_position:position - 1]
+
+
+# Patterns for matching call-by-reference parameters.
+#
+# Supports nested templates up to 2 levels deep using this messy pattern:
+# < (?: < (?: < [^<>]*
+# >
+# | [^<>] )*
+# >
+# | [^<>] )*
+# >
+_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]*
+_RE_PATTERN_TYPE = (
+ r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?'
+ r'(?:\w|'
+ r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|'
+ r'::)+')
+# A call-by-reference parameter ends with '& identifier'.
+_RE_PATTERN_REF_PARAM = re.compile(
+ r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*'
+ r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]')
+# A call-by-const-reference parameter either ends with 'const& identifier'
+# or looks like 'const type& identifier' when 'type' is atomic.
+_RE_PATTERN_CONST_REF_PARAM = (
+ r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT +
+ r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')')
+# Stream types.
+_RE_PATTERN_REF_STREAM_PARAM = (
+ r'(?:.*stream\s*&\s*' + _RE_PATTERN_IDENT + r')')
+
+
+def CheckLanguage(filename, clean_lines, linenum, file_extension,
+ include_state, nesting_state, error):
+ """Checks rules from the 'C++ language rules' section of cppguide.html.
+
+ Some of these rules are hard to test (function overloading, using
+ uint32 inappropriately), but we do the best we can.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ file_extension: The extension (without the dot) of the filename.
+ include_state: An _IncludeState instance in which the headers are inserted.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ # If the line is empty or consists of entirely a comment, no need to
+ # check it.
+ line = clean_lines.elided[linenum]
+ if not line:
+ return
+
+ match = _RE_PATTERN_INCLUDE.search(line)
+ if match:
+ CheckIncludeLine(filename, clean_lines, linenum, include_state, error)
+ return
+
+ # Reset include state across preprocessor directives. This is meant
+ # to silence warnings for conditional includes.
+ match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line)
+ if match:
+ include_state.ResetSection(match.group(1))
+
+ # Make Windows paths like Unix.
+ fullname = os.path.abspath(filename).replace('\\', '/')
+
+ # Perform other checks now that we are sure that this is not an include line
+ CheckCasts(filename, clean_lines, linenum, error)
+ CheckGlobalStatic(filename, clean_lines, linenum, error)
+ CheckPrintf(filename, clean_lines, linenum, error)
+
+ if IsHeaderExtension(file_extension):
+ # TODO(unknown): check that 1-arg constructors are explicit.
+ # How to tell it's a constructor?
+ # (handled in CheckForNonStandardConstructs for now)
+ # TODO(unknown): check that classes declare or disable copy/assign
+ # (level 1 error)
+ pass
+
+ # Check if people are using the verboten C basic types. The only exception
+ # we regularly allow is "unsigned short port" for port.
+ if Search(r'\bshort port\b', line):
+ if not Search(r'\bunsigned short port\b', line):
+ error(filename, linenum, 'runtime/int', 4,
+ 'Use "unsigned short" for ports, not "short"')
+ else:
+ match = Search(r'\b(short|long(?! +double)|long long)\b', line)
+ if match:
+ error(filename, linenum, 'runtime/int', 4,
+ 'Use int16/int64/etc, rather than the C type %s' % match.group(1))
+
+ # Check if some verboten operator overloading is going on
+ # TODO(unknown): catch out-of-line unary operator&:
+ # class X {};
+ # int operator&(const X& x) { return 42; } // unary operator&
+ # The trick is it's hard to tell apart from binary operator&:
+ # class Y { int operator&(const Y& x) { return 23; } }; // binary operator&
+ if Search(r'\boperator\s*&\s*\(\s*\)', line):
+ error(filename, linenum, 'runtime/operator', 4,
+ 'Unary operator& is dangerous. Do not use it.')
+
+ # Check for suspicious usage of "if" like
+ # } if (a == b) {
+ if Search(r'\}\s*if\s*\(', line):
+ error(filename, linenum, 'readability/braces', 4,
+ 'Did you mean "else if"? If not, start a new line for "if".')
+
+ # Check for potential format string bugs like printf(foo).
+ # We constrain the pattern not to pick things like DocidForPrintf(foo).
+ # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str())
+ # TODO(unknown): Catch the following case. Need to change the calling
+ # convention of the whole function to process multiple line to handle it.
+ # printf(
+ # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line);
+ printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(')
+ if printf_args:
+ match = Match(r'([\w.\->()]+)$', printf_args)
+ if match and match.group(1) != '__VA_ARGS__':
+ function_name = re.search(r'\b((?:string)?printf)\s*\(',
+ line, re.I).group(1)
+ error(filename, linenum, 'runtime/printf', 4,
+ 'Potential format string bug. Do %s("%%s", %s) instead.'
+ % (function_name, match.group(1)))
+
+ # Check for potential memset bugs like memset(buf, sizeof(buf), 0).
+ match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line)
+ if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)):
+ error(filename, linenum, 'runtime/memset', 4,
+ 'Did you mean "memset(%s, 0, %s)"?'
+ % (match.group(1), match.group(2)))
+
+ if Search(r'\busing namespace\b', line):
+ error(filename, linenum, 'build/namespaces', 5,
+ 'Do not use namespace using-directives. '
+ 'Use using-declarations instead.')
+
+ # Detect variable-length arrays.
+ match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line)
+ if (match and match.group(2) != 'return' and match.group(2) != 'delete' and
+ match.group(3).find(']') == -1):
+ # Split the size using space and arithmetic operators as delimiters.
+ # If any of the resulting tokens are not compile time constants then
+ # report the error.
+ tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3))
+ is_const = True
+ skip_next = False
+ for tok in tokens:
+ if skip_next:
+ skip_next = False
+ continue
+
+ if Search(r'sizeof\(.+\)', tok): continue
+ if Search(r'arraysize\(\w+\)', tok): continue
+
+ tok = tok.lstrip('(')
+ tok = tok.rstrip(')')
+ if not tok: continue
+ if Match(r'\d+', tok): continue
+ if Match(r'0[xX][0-9a-fA-F]+', tok): continue
+ if Match(r'k[A-Z0-9]\w*', tok): continue
+ if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue
+ if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue
+ # A catch all for tricky sizeof cases, including 'sizeof expression',
+ # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)'
+ # requires skipping the next token because we split on ' ' and '*'.
+ if tok.startswith('sizeof'):
+ skip_next = True
+ continue
+ is_const = False
+ break
+ if not is_const:
+ error(filename, linenum, 'runtime/arrays', 1,
+ 'Do not use variable-length arrays. Use an appropriately named '
+ "('k' followed by CamelCase) compile-time constant for the size.")
+
+ # Check for use of unnamed namespaces in header files. Registration
+ # macros are typically OK, so we allow use of "namespace {" on lines
+ # that end with backslashes.
+ if (IsHeaderExtension(file_extension)
+ and Search(r'\bnamespace\s*{', line)
+ and line[-1] != '\\'):
+ error(filename, linenum, 'build/namespaces', 4,
+ 'Do not use unnamed namespaces in header files. See '
+ 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'
+ ' for more information.')
+
+
+def CheckGlobalStatic(filename, clean_lines, linenum, error):
+ """Check for unsafe global or static objects.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Match two lines at a time to support multiline declarations
+ if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line):
+ line += clean_lines.elided[linenum + 1].strip()
+
+ # Check for people declaring static/global STL strings at the top level.
+ # This is dangerous because the C++ language does not guarantee that
+ # globals with constructors are initialized before the first access, and
+ # also because globals can be destroyed when some threads are still running.
+ # TODO(unknown): Generalize this to also find static unique_ptr instances.
+ # TODO(unknown): File bugs for clang-tidy to find these.
+ match = Match(
+ r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +'
+ r'([a-zA-Z0-9_:]+)\b(.*)',
+ line)
+
+ # Remove false positives:
+ # - String pointers (as opposed to values).
+ # string *pointer
+ # const string *pointer
+ # string const *pointer
+ # string *const pointer
+ #
+ # - Functions and template specializations.
+ # string Function<Type>(...
+ # string Class<Type>::Method(...
+ #
+ # - Operators. These are matched separately because operator names
+ # cross non-word boundaries, and trying to match both operators
+ # and functions at the same time would decrease accuracy of
+ # matching identifiers.
+ # string Class::operator*()
+ if (match and
+ not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and
+ not Search(r'\boperator\W', line) and
+ not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))):
+ if Search(r'\bconst\b', line):
+ error(filename, linenum, 'runtime/string', 4,
+ 'For a static/global string constant, use a C style string '
+ 'instead: "%schar%s %s[]".' %
+ (match.group(1), match.group(2) or '', match.group(3)))
+ else:
+ error(filename, linenum, 'runtime/string', 4,
+ 'Static/global string variables are not permitted.')
+
+ if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or
+ Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)):
+ error(filename, linenum, 'runtime/init', 4,
+ 'You seem to be initializing a member variable with itself.')
+
+
+def CheckPrintf(filename, clean_lines, linenum, error):
+ """Check for printf related issues.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # When snprintf is used, the second argument shouldn't be a literal.
+ match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line)
+ if match and match.group(2) != '0':
+ # If 2nd arg is zero, snprintf is used to calculate size.
+ error(filename, linenum, 'runtime/printf', 3,
+ 'If you can, use sizeof(%s) instead of %s as the 2nd arg '
+ 'to snprintf.' % (match.group(1), match.group(2)))
+
+ # Check if some verboten C functions are being used.
+ if Search(r'\bsprintf\s*\(', line):
+ error(filename, linenum, 'runtime/printf', 5,
+ 'Never use sprintf. Use snprintf instead.')
+ match = Search(r'\b(strcpy|strcat)\s*\(', line)
+ if match:
+ error(filename, linenum, 'runtime/printf', 4,
+ 'Almost always, snprintf is better than %s' % match.group(1))
+
+
+def IsDerivedFunction(clean_lines, linenum):
+ """Check if current line contains an inherited function.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ Returns:
+ True if current line contains a function with "override"
+ virt-specifier.
+ """
+ # Scan back a few lines for start of current function
+ for i in xrange(linenum, max(-1, linenum - 10), -1):
+ match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i])
+ if match:
+ # Look for "override" after the matching closing parenthesis
+ line, _, closing_paren = CloseExpression(
+ clean_lines, i, len(match.group(1)))
+ return (closing_paren >= 0 and
+ Search(r'\boverride\b', line[closing_paren:]))
+ return False
+
+
+def IsOutOfLineMethodDefinition(clean_lines, linenum):
+ """Check if current line contains an out-of-line method definition.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ Returns:
+ True if current line contains an out-of-line method definition.
+ """
+ # Scan back a few lines for start of current function
+ for i in xrange(linenum, max(-1, linenum - 10), -1):
+ if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]):
+ return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None
+ return False
+
+
+def IsInitializerList(clean_lines, linenum):
+ """Check if current line is inside constructor initializer list.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ Returns:
+ True if current line appears to be inside constructor initializer
+ list, False otherwise.
+ """
+ for i in xrange(linenum, 1, -1):
+ line = clean_lines.elided[i]
+ if i == linenum:
+ remove_function_body = Match(r'^(.*)\{\s*$', line)
+ if remove_function_body:
+ line = remove_function_body.group(1)
+
+ if Search(r'\s:\s*\w+[({]', line):
+ # A lone colon tend to indicate the start of a constructor
+ # initializer list. It could also be a ternary operator, which
+ # also tend to appear in constructor initializer lists as
+ # opposed to parameter lists.
+ return True
+ if Search(r'\}\s*,\s*$', line):
+ # A closing brace followed by a comma is probably the end of a
+ # brace-initialized member in constructor initializer list.
+ return True
+ if Search(r'[{};]\s*$', line):
+ # Found one of the following:
+ # - A closing brace or semicolon, probably the end of the previous
+ # function.
+ # - An opening brace, probably the start of current class or namespace.
+ #
+ # Current line is probably not inside an initializer list since
+ # we saw one of those things without seeing the starting colon.
+ return False
+
+ # Got to the beginning of the file without seeing the start of
+ # constructor initializer list.
+ return False
+
+
+def CheckForNonConstReference(filename, clean_lines, linenum,
+ nesting_state, error):
+ """Check for non-const references.
+
+ Separate from CheckLanguage since it scans backwards from current
+ line, instead of scanning forward.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: The function to call with any errors found.
+ """
+ # Do nothing if there is no '&' on current line.
+ line = clean_lines.elided[linenum]
+ if '&' not in line:
+ return
+
+ # If a function is inherited, current function doesn't have much of
+ # a choice, so any non-const references should not be blamed on
+ # derived function.
+ if IsDerivedFunction(clean_lines, linenum):
+ return
+
+ # Don't warn on out-of-line method definitions, as we would warn on the
+ # in-line declaration, if it isn't marked with 'override'.
+ if IsOutOfLineMethodDefinition(clean_lines, linenum):
+ return
+
+ # Long type names may be broken across multiple lines, usually in one
+ # of these forms:
+ # LongType
+ # ::LongTypeContinued &identifier
+ # LongType::
+ # LongTypeContinued &identifier
+ # LongType<
+ # ...>::LongTypeContinued &identifier
+ #
+ # If we detected a type split across two lines, join the previous
+ # line to current line so that we can match const references
+ # accordingly.
+ #
+ # Note that this only scans back one line, since scanning back
+ # arbitrary number of lines would be expensive. If you have a type
+ # that spans more than 2 lines, please use a typedef.
+ if linenum > 1:
+ previous = None
+ if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line):
+ # previous_line\n + ::current_line
+ previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$',
+ clean_lines.elided[linenum - 1])
+ elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line):
+ # previous_line::\n + current_line
+ previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$',
+ clean_lines.elided[linenum - 1])
+ if previous:
+ line = previous.group(1) + line.lstrip()
+ else:
+ # Check for templated parameter that is split across multiple lines
+ endpos = line.rfind('>')
+ if endpos > -1:
+ (_, startline, startpos) = ReverseCloseExpression(
+ clean_lines, linenum, endpos)
+ if startpos > -1 and startline < linenum:
+ # Found the matching < on an earlier line, collect all
+ # pieces up to current line.
+ line = ''
+ for i in xrange(startline, linenum + 1):
+ line += clean_lines.elided[i].strip()
+
+ # Check for non-const references in function parameters. A single '&' may
+ # found in the following places:
+ # inside expression: binary & for bitwise AND
+ # inside expression: unary & for taking the address of something
+ # inside declarators: reference parameter
+ # We will exclude the first two cases by checking that we are not inside a
+ # function body, including one that was just introduced by a trailing '{'.
+ # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare].
+ if (nesting_state.previous_stack_top and
+ not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or
+ isinstance(nesting_state.previous_stack_top, _NamespaceInfo))):
+ # Not at toplevel, not within a class, and not within a namespace
+ return
+
+ # Avoid initializer lists. We only need to scan back from the
+ # current line for something that starts with ':'.
+ #
+ # We don't need to check the current line, since the '&' would
+ # appear inside the second set of parentheses on the current line as
+ # opposed to the first set.
+ if linenum > 0:
+ for i in xrange(linenum - 1, max(0, linenum - 10), -1):
+ previous_line = clean_lines.elided[i]
+ if not Search(r'[),]\s*$', previous_line):
+ break
+ if Match(r'^\s*:\s+\S', previous_line):
+ return
+
+ # Avoid preprocessors
+ if Search(r'\\\s*$', line):
+ return
+
+ # Avoid constructor initializer lists
+ if IsInitializerList(clean_lines, linenum):
+ return
+
+ # We allow non-const references in a few standard places, like functions
+ # called "swap()" or iostream operators like "<<" or ">>". Do not check
+ # those function parameters.
+ #
+ # We also accept & in static_assert, which looks like a function but
+ # it's actually a declaration expression.
+ whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|'
+ r'operator\s*[<>][<>]|'
+ r'static_assert|COMPILE_ASSERT'
+ r')\s*\(')
+ if Search(whitelisted_functions, line):
+ return
+ elif not Search(r'\S+\([^)]*$', line):
+ # Don't see a whitelisted function on this line. Actually we
+ # didn't see any function name on this line, so this is likely a
+ # multi-line parameter list. Try a bit harder to catch this case.
+ for i in xrange(2):
+ if (linenum > i and
+ Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])):
+ return
+
+ decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body
+ for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls):
+ if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and
+ not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)):
+ error(filename, linenum, 'runtime/references', 2,
+ 'Is this a non-const reference? '
+ 'If so, make const or use a pointer: ' +
+ ReplaceAll(' *<', '<', parameter))
+
+
+def CheckCasts(filename, clean_lines, linenum, error):
+ """Various cast related checks.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ # Check to see if they're using an conversion function cast.
+ # I just try to capture the most common basic types, though there are more.
+ # Parameterless conversion functions, such as bool(), are allowed as they are
+ # probably a member operator declaration or default constructor.
+ match = Search(
+ r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b'
+ r'(int|float|double|bool|char|int32|uint32|int64|uint64)'
+ r'(\([^)].*)', line)
+ expecting_function = ExpectingFunctionArgs(clean_lines, linenum)
+ if match and not expecting_function:
+ matched_type = match.group(2)
+
+ # matched_new_or_template is used to silence two false positives:
+ # - New operators
+ # - Template arguments with function types
+ #
+ # For template arguments, we match on types immediately following
+ # an opening bracket without any spaces. This is a fast way to
+ # silence the common case where the function type is the first
+ # template argument. False negative with less-than comparison is
+ # avoided because those operators are usually followed by a space.
+ #
+ # function<double(double)> // bracket + no space = false positive
+ # value < double(42) // bracket + space = true positive
+ matched_new_or_template = match.group(1)
+
+ # Avoid arrays by looking for brackets that come after the closing
+ # parenthesis.
+ if Match(r'\([^()]+\)\s*\[', match.group(3)):
+ return
+
+ # Other things to ignore:
+ # - Function pointers
+ # - Casts to pointer types
+ # - Placement new
+ # - Alias declarations
+ matched_funcptr = match.group(3)
+ if (matched_new_or_template is None and
+ not (matched_funcptr and
+ (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(',
+ matched_funcptr) or
+ matched_funcptr.startswith('(*)'))) and
+ not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and
+ not Search(r'new\(\S+\)\s*' + matched_type, line)):
+ error(filename, linenum, 'readability/casting', 4,
+ 'Using deprecated casting style. '
+ 'Use static_cast<%s>(...) instead' %
+ matched_type)
+
+ if not expecting_function:
+ CheckCStyleCast(filename, clean_lines, linenum, 'static_cast',
+ r'\((int|float|double|bool|char|u?int(16|32|64))\)', error)
+
+ # This doesn't catch all cases. Consider (const char * const)"hello".
+ #
+ # (char *) "foo" should always be a const_cast (reinterpret_cast won't
+ # compile).
+ if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast',
+ r'\((char\s?\*+\s?)\)\s*"', error):
+ pass
+ else:
+ # Check pointer casts for other than string constants
+ CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast',
+ r'\((\w+\s?\*+\s?)\)', error)
+
+ # In addition, we look for people taking the address of a cast. This
+ # is dangerous -- casts can assign to temporaries, so the pointer doesn't
+ # point where you think.
+ #
+ # Some non-identifier character is required before the '&' for the
+ # expression to be recognized as a cast. These are casts:
+ # expression = &static_cast<int*>(temporary());
+ # function(&(int*)(temporary()));
+ #
+ # This is not a cast:
+ # reference_type&(int* function_param);
+ match = Search(
+ r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|'
+ r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line)
+ if match:
+ # Try a better error message when the & is bound to something
+ # dereferenced by the casted pointer, as opposed to the casted
+ # pointer itself.
+ parenthesis_error = False
+ match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line)
+ if match:
+ _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1)))
+ if x1 >= 0 and clean_lines.elided[y1][x1] == '(':
+ _, y2, x2 = CloseExpression(clean_lines, y1, x1)
+ if x2 >= 0:
+ extended_line = clean_lines.elided[y2][x2:]
+ if y2 < clean_lines.NumLines() - 1:
+ extended_line += clean_lines.elided[y2 + 1]
+ if Match(r'\s*(?:->|\[)', extended_line):
+ parenthesis_error = True
+
+ if parenthesis_error:
+ error(filename, linenum, 'readability/casting', 4,
+ ('Are you taking an address of something dereferenced '
+ 'from a cast? Wrapping the dereferenced expression in '
+ 'parentheses will make the binding more obvious'))
+ else:
+ error(filename, linenum, 'runtime/casting', 4,
+ ('Are you taking an address of a cast? '
+ 'This is dangerous: could be a temp var. '
+ 'Take the address before doing the cast, rather than after'))
+
+
+def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error):
+ """Checks for a C-style cast by looking for the pattern.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ cast_type: The string for the C++ cast to recommend. This is either
+ reinterpret_cast, static_cast, or const_cast, depending.
+ pattern: The regular expression used to find C-style casts.
+ error: The function to call with any errors found.
+
+ Returns:
+ True if an error was emitted.
+ False otherwise.
+ """
+ line = clean_lines.elided[linenum]
+ match = Search(pattern, line)
+ if not match:
+ return False
+
+ # Exclude lines with keywords that tend to look like casts
+ context = line[0:match.start(1) - 1]
+ if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context):
+ return False
+
+ # Try expanding current context to see if we one level of
+ # parentheses inside a macro.
+ if linenum > 0:
+ for i in xrange(linenum - 1, max(0, linenum - 5), -1):
+ context = clean_lines.elided[i] + context
+ if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context):
+ return False
+
+ # operator++(int) and operator--(int)
+ if context.endswith(' operator++') or context.endswith(' operator--'):
+ return False
+
+ # A single unnamed argument for a function tends to look like old style cast.
+ # If we see those, don't issue warnings for deprecated casts.
+ remainder = line[match.end(0):]
+ if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)',
+ remainder):
+ return False
+
+ # At this point, all that should be left is actual casts.
+ error(filename, linenum, 'readability/casting', 4,
+ 'Using C-style cast. Use %s<%s>(...) instead' %
+ (cast_type, match.group(1)))
+
+ return True
+
+
+def ExpectingFunctionArgs(clean_lines, linenum):
+ """Checks whether where function type arguments are expected.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+
+ Returns:
+ True if the line at 'linenum' is inside something that expects arguments
+ of function types.
+ """
+ line = clean_lines.elided[linenum]
+ return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or
+ (linenum >= 2 and
+ (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$',
+ clean_lines.elided[linenum - 1]) or
+ Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$',
+ clean_lines.elided[linenum - 2]) or
+ Search(r'\bstd::m?function\s*\<\s*$',
+ clean_lines.elided[linenum - 1]))))
+
+
+_HEADERS_CONTAINING_TEMPLATES = (
+ ('<deque>', ('deque',)),
+ ('<functional>', ('unary_function', 'binary_function',
+ 'plus', 'minus', 'multiplies', 'divides', 'modulus',
+ 'negate',
+ 'equal_to', 'not_equal_to', 'greater', 'less',
+ 'greater_equal', 'less_equal',
+ 'logical_and', 'logical_or', 'logical_not',
+ 'unary_negate', 'not1', 'binary_negate', 'not2',
+ 'bind1st', 'bind2nd',
+ 'pointer_to_unary_function',
+ 'pointer_to_binary_function',
+ 'ptr_fun',
+ 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t',
+ 'mem_fun_ref_t',
+ 'const_mem_fun_t', 'const_mem_fun1_t',
+ 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t',
+ 'mem_fun_ref',
+ )),
+ ('<limits>', ('numeric_limits',)),
+ ('<list>', ('list',)),
+ ('<map>', ('map', 'multimap',)),
+ ('<memory>', ('allocator', 'make_shared', 'make_unique', 'shared_ptr',
+ 'unique_ptr', 'weak_ptr')),
+ ('<queue>', ('queue', 'priority_queue',)),
+ ('<set>', ('set', 'multiset',)),
+ ('<stack>', ('stack',)),
+ ('<string>', ('char_traits', 'basic_string',)),
+ ('<tuple>', ('tuple',)),
+ ('<unordered_map>', ('unordered_map', 'unordered_multimap')),
+ ('<unordered_set>', ('unordered_set', 'unordered_multiset')),
+ ('<utility>', ('pair',)),
+ ('<vector>', ('vector',)),
+
+ # gcc extensions.
+ # Note: std::hash is their hash, ::hash is our hash
+ ('<hash_map>', ('hash_map', 'hash_multimap',)),
+ ('<hash_set>', ('hash_set', 'hash_multiset',)),
+ ('<slist>', ('slist',)),
+ )
+
+_HEADERS_MAYBE_TEMPLATES = (
+ ('<algorithm>', ('copy', 'max', 'min', 'min_element', 'sort',
+ 'transform',
+ )),
+ ('<utility>', ('forward', 'make_pair', 'move', 'swap')),
+ )
+
+_RE_PATTERN_STRING = re.compile(r'\bstring\b')
+
+_re_pattern_headers_maybe_templates = []
+for _header, _templates in _HEADERS_MAYBE_TEMPLATES:
+ for _template in _templates:
+ # Match max<type>(..., ...), max(..., ...), but not foo->max, foo.max or
+ # type::max().
+ _re_pattern_headers_maybe_templates.append(
+ (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'),
+ _template,
+ _header))
+
+# Other scripts may reach in and modify this pattern.
+_re_pattern_templates = []
+for _header, _templates in _HEADERS_CONTAINING_TEMPLATES:
+ for _template in _templates:
+ _re_pattern_templates.append(
+ (re.compile(r'(\<|\b)' + _template + r'\s*\<'),
+ _template + '<>',
+ _header))
+
+
+def FilesBelongToSameModule(filename_cc, filename_h):
+ """Check if these two filenames belong to the same module.
+
+ The concept of a 'module' here is a as follows:
+ foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the
+ same 'module' if they are in the same directory.
+ some/path/public/xyzzy and some/path/internal/xyzzy are also considered
+ to belong to the same module here.
+
+ If the filename_cc contains a longer path than the filename_h, for example,
+ '/absolute/path/to/base/sysinfo.cc', and this file would include
+ 'base/sysinfo.h', this function also produces the prefix needed to open the
+ header. This is used by the caller of this function to more robustly open the
+ header file. We don't have access to the real include paths in this context,
+ so we need this guesswork here.
+
+ Known bugs: tools/base/bar.cc and base/bar.h belong to the same module
+ according to this implementation. Because of this, this function gives
+ some false positives. This should be sufficiently rare in practice.
+
+ Args:
+ filename_cc: is the path for the .cc file
+ filename_h: is the path for the header path
+
+ Returns:
+ Tuple with a bool and a string:
+ bool: True if filename_cc and filename_h belong to the same module.
+ string: the additional prefix needed to open the header file.
+ """
+
+ fileinfo = FileInfo(filename_cc)
+ if not fileinfo.IsSource():
+ return (False, '')
+ filename_cc = filename_cc[:-len(fileinfo.Extension())]
+ matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo.BaseName())
+ if matched_test_suffix:
+ filename_cc = filename_cc[:-len(matched_test_suffix.group(1))]
+ filename_cc = filename_cc.replace('/public/', '/')
+ filename_cc = filename_cc.replace('/internal/', '/')
+
+ if not filename_h.endswith('.h'):
+ return (False, '')
+ filename_h = filename_h[:-len('.h')]
+ if filename_h.endswith('-inl'):
+ filename_h = filename_h[:-len('-inl')]
+ filename_h = filename_h.replace('/public/', '/')
+ filename_h = filename_h.replace('/internal/', '/')
+
+ files_belong_to_same_module = filename_cc.endswith(filename_h)
+ common_path = ''
+ if files_belong_to_same_module:
+ common_path = filename_cc[:-len(filename_h)]
+ return files_belong_to_same_module, common_path
+
+
+def UpdateIncludeState(filename, include_dict, io=codecs):
+ """Fill up the include_dict with new includes found from the file.
+
+ Args:
+ filename: the name of the header to read.
+ include_dict: a dictionary in which the headers are inserted.
+ io: The io factory to use to read the file. Provided for testability.
+
+ Returns:
+ True if a header was successfully added. False otherwise.
+ """
+ headerfile = None
+ try:
+ headerfile = io.open(filename, 'r', 'utf8', 'replace')
+ except IOError:
+ return False
+ linenum = 0
+ for line in headerfile:
+ linenum += 1
+ clean_line = CleanseComments(line)
+ match = _RE_PATTERN_INCLUDE.search(clean_line)
+ if match:
+ include = match.group(2)
+ include_dict.setdefault(include, linenum)
+ return True
+
+
+def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
+ io=codecs):
+ """Reports for missing stl includes.
+
+ This function will output warnings to make sure you are including the headers
+ necessary for the stl containers and functions that you use. We only give one
+ reason to include a header. For example, if you use both equal_to<> and
+ less<> in a .h file, only one (the latter in the file) of these will be
+ reported as a reason to include the <functional>.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ include_state: An _IncludeState instance.
+ error: The function to call with any errors found.
+ io: The IO factory to use to read the header file. Provided for unittest
+ injection.
+ """
+ required = {} # A map of header name to linenumber and the template entity.
+ # Example of required: { '<functional>': (1219, 'less<>') }
+
+ for linenum in xrange(clean_lines.NumLines()):
+ line = clean_lines.elided[linenum]
+ if not line or line[0] == '#':
+ continue
+
+ # String is special -- it is a non-templatized type in STL.
+ matched = _RE_PATTERN_STRING.search(line)
+ if matched:
+ # Don't warn about strings in non-STL namespaces:
+ # (We check only the first match per line; good enough.)
+ prefix = line[:matched.start()]
+ if prefix.endswith('std::') or not prefix.endswith('::'):
+ required['<string>'] = (linenum, 'string')
+
+ for pattern, template, header in _re_pattern_headers_maybe_templates:
+ if pattern.search(line):
+ required[header] = (linenum, template)
+
+ # The following function is just a speed up, no semantics are changed.
+ if not '<' in line: # Reduces the cpu time usage by skipping lines.
+ continue
+
+ for pattern, template, header in _re_pattern_templates:
+ matched = pattern.search(line)
+ if matched:
+ # Don't warn about IWYU in non-STL namespaces:
+ # (We check only the first match per line; good enough.)
+ prefix = line[:matched.start()]
+ if prefix.endswith('std::') or not prefix.endswith('::'):
+ required[header] = (linenum, template)
+
+ # The policy is that if you #include something in foo.h you don't need to
+ # include it again in foo.cc. Here, we will look at possible includes.
+ # Let's flatten the include_state include_list and copy it into a dictionary.
+ include_dict = dict([item for sublist in include_state.include_list
+ for item in sublist])
+
+ # Did we find the header for this file (if any) and successfully load it?
+ header_found = False
+
+ # Use the absolute path so that matching works properly.
+ abs_filename = FileInfo(filename).FullName()
+
+ # For Emacs's flymake.
+ # If cpplint is invoked from Emacs's flymake, a temporary file is generated
+ # by flymake and that file name might end with '_flymake.cc'. In that case,
+ # restore original file name here so that the corresponding header file can be
+ # found.
+ # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h'
+ # instead of 'foo_flymake.h'
+ abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename)
+
+ # include_dict is modified during iteration, so we iterate over a copy of
+ # the keys.
+ header_keys = include_dict.keys()
+ for header in header_keys:
+ (same_module, common_path) = FilesBelongToSameModule(abs_filename, header)
+ fullpath = common_path + header
+ if same_module and UpdateIncludeState(fullpath, include_dict, io):
+ header_found = True
+
+ # If we can't find the header file for a .cc, assume it's because we don't
+ # know where to look. In that case we'll give up as we're not sure they
+ # didn't include it in the .h file.
+ # TODO(unknown): Do a better job of finding .h files so we are confident that
+ # not having the .h file means there isn't one.
+ if filename.endswith('.cc') and not header_found:
+ return
+
+ # All the lines have been processed, report the errors found.
+ for required_header_unstripped in required:
+ template = required[required_header_unstripped][1]
+ if required_header_unstripped.strip('<>"') not in include_dict:
+ error(filename, required[required_header_unstripped][0],
+ 'build/include_what_you_use', 4,
+ 'Add #include ' + required_header_unstripped + ' for ' + template)
+
+
+_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<')
+
+
+def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):
+ """Check that make_pair's template arguments are deduced.
+
+ G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are
+ specified explicitly, and such use isn't intended in any case.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+ match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line)
+ if match:
+ error(filename, linenum, 'build/explicit_make_pair',
+ 4, # 4 = high confidence
+ 'For C++11-compatibility, omit template arguments from make_pair'
+ ' OR use pair directly OR if appropriate, construct a pair directly')
+
+
+def CheckRedundantVirtual(filename, clean_lines, linenum, error):
+ """Check if line contains a redundant "virtual" function-specifier.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ # Look for "virtual" on current line.
+ line = clean_lines.elided[linenum]
+ virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line)
+ if not virtual: return
+
+ # Ignore "virtual" keywords that are near access-specifiers. These
+ # are only used in class base-specifier and do not apply to member
+ # functions.
+ if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or
+ Match(r'^\s+(public|protected|private)\b', virtual.group(3))):
+ return
+
+ # Ignore the "virtual" keyword from virtual base classes. Usually
+ # there is a column on the same line in these cases (virtual base
+ # classes are rare in google3 because multiple inheritance is rare).
+ if Match(r'^.*[^:]:[^:].*$', line): return
+
+ # Look for the next opening parenthesis. This is the start of the
+ # parameter list (possibly on the next line shortly after virtual).
+ # TODO(unknown): doesn't work if there are virtual functions with
+ # decltype() or other things that use parentheses, but csearch suggests
+ # that this is rare.
+ end_col = -1
+ end_line = -1
+ start_col = len(virtual.group(2))
+ for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())):
+ line = clean_lines.elided[start_line][start_col:]
+ parameter_list = Match(r'^([^(]*)\(', line)
+ if parameter_list:
+ # Match parentheses to find the end of the parameter list
+ (_, end_line, end_col) = CloseExpression(
+ clean_lines, start_line, start_col + len(parameter_list.group(1)))
+ break
+ start_col = 0
+
+ if end_col < 0:
+ return # Couldn't find end of parameter list, give up
+
+ # Look for "override" or "final" after the parameter list
+ # (possibly on the next few lines).
+ for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())):
+ line = clean_lines.elided[i][end_col:]
+ match = Search(r'\b(override|final)\b', line)
+ if match:
+ error(filename, linenum, 'readability/inheritance', 4,
+ ('"virtual" is redundant since function is '
+ 'already declared as "%s"' % match.group(1)))
+
+ # Set end_col to check whole lines after we are done with the
+ # first line.
+ end_col = 0
+ if Search(r'[^\w]\s*$', line):
+ break
+
+
+def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error):
+ """Check if line contains a redundant "override" or "final" virt-specifier.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ # Look for closing parenthesis nearby. We need one to confirm where
+ # the declarator ends and where the virt-specifier starts to avoid
+ # false positives.
+ line = clean_lines.elided[linenum]
+ declarator_end = line.rfind(')')
+ if declarator_end >= 0:
+ fragment = line[declarator_end:]
+ else:
+ if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0:
+ fragment = line
+ else:
+ return
+
+ # Check that at most one of "override" or "final" is present, not both
+ if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment):
+ error(filename, linenum, 'readability/inheritance', 4,
+ ('"override" is redundant since function is '
+ 'already declared as "final"'))
+
+
+
+
+# Returns true if we are at a new block, and it is directly
+# inside of a namespace.
+def IsBlockInNameSpace(nesting_state, is_forward_declaration):
+ """Checks that the new block is directly in a namespace.
+
+ Args:
+ nesting_state: The _NestingState object that contains info about our state.
+ is_forward_declaration: If the class is a forward declared class.
+ Returns:
+ Whether or not the new block is directly in a namespace.
+ """
+ if is_forward_declaration:
+ if len(nesting_state.stack) >= 1 and (
+ isinstance(nesting_state.stack[-1], _NamespaceInfo)):
+ return True
+ else:
+ return False
+
+ return (len(nesting_state.stack) > 1 and
+ nesting_state.stack[-1].check_namespace_indentation and
+ isinstance(nesting_state.stack[-2], _NamespaceInfo))
+
+
+def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item,
+ raw_lines_no_comments, linenum):
+ """This method determines if we should apply our namespace indentation check.
+
+ Args:
+ nesting_state: The current nesting state.
+ is_namespace_indent_item: If we just put a new class on the stack, True.
+ If the top of the stack is not a class, or we did not recently
+ add the class, False.
+ raw_lines_no_comments: The lines without the comments.
+ linenum: The current line number we are processing.
+
+ Returns:
+ True if we should apply our namespace indentation check. Currently, it
+ only works for classes and namespaces inside of a namespace.
+ """
+
+ is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments,
+ linenum)
+
+ if not (is_namespace_indent_item or is_forward_declaration):
+ return False
+
+ # If we are in a macro, we do not want to check the namespace indentation.
+ if IsMacroDefinition(raw_lines_no_comments, linenum):
+ return False
+
+ return IsBlockInNameSpace(nesting_state, is_forward_declaration)
+
+
+# Call this method if the line is directly inside of a namespace.
+# If the line above is blank (excluding comments) or the start of
+# an inner namespace, it cannot be indented.
+def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum,
+ error):
+ line = raw_lines_no_comments[linenum]
+ if Match(r'^\s+', line):
+ error(filename, linenum, 'runtime/indentation_namespace', 4,
+ 'Do not indent within a namespace')
+
+
+def ProcessLine(filename, file_extension, clean_lines, line,
+ include_state, function_state, nesting_state, error,
+ extra_check_functions=[]):
+ """Processes a single line in the file.
+
+ Args:
+ filename: Filename of the file that is being processed.
+ file_extension: The extension (dot not included) of the file.
+ clean_lines: An array of strings, each representing a line of the file,
+ with comments stripped.
+ line: Number of line being processed.
+ include_state: An _IncludeState instance in which the headers are inserted.
+ function_state: A _FunctionState instance which counts function lines, etc.
+ nesting_state: A NestingState instance which maintains information about
+ the current stack of nested blocks being parsed.
+ error: A callable to which errors are reported, which takes 4 arguments:
+ filename, line number, error level, and message
+ extra_check_functions: An array of additional check functions that will be
+ run on each source line. Each function takes 4
+ arguments: filename, clean_lines, line, error
+ """
+ raw_lines = clean_lines.raw_lines
+ ParseNolintSuppressions(filename, raw_lines[line], line, error)
+ nesting_state.Update(filename, clean_lines, line, error)
+ CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line,
+ error)
+ if nesting_state.InAsmBlock(): return
+ CheckForFunctionLengths(filename, clean_lines, line, function_state, error)
+ CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)
+ CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error)
+ CheckLanguage(filename, clean_lines, line, file_extension, include_state,
+ nesting_state, error)
+ CheckForNonConstReference(filename, clean_lines, line, nesting_state, error)
+ CheckForNonStandardConstructs(filename, clean_lines, line,
+ nesting_state, error)
+ CheckVlogArguments(filename, clean_lines, line, error)
+ CheckPosixThreading(filename, clean_lines, line, error)
+ CheckInvalidIncrement(filename, clean_lines, line, error)
+ CheckMakePairUsesDeduction(filename, clean_lines, line, error)
+ CheckRedundantVirtual(filename, clean_lines, line, error)
+ CheckRedundantOverrideOrFinal(filename, clean_lines, line, error)
+ for check_fn in extra_check_functions:
+ check_fn(filename, clean_lines, line, error)
+
+def FlagCxx11Features(filename, clean_lines, linenum, error):
+ """Flag those c++11 features that we only allow in certain places.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line)
+
+ # Flag unapproved C++ TR1 headers.
+ if include and include.group(1).startswith('tr1/'):
+ error(filename, linenum, 'build/c++tr1', 5,
+ ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1))
+
+ # Flag unapproved C++11 headers.
+ if include and include.group(1) in ('cfenv',
+ 'condition_variable',
+ 'fenv.h',
+ 'future',
+ 'mutex',
+ 'thread',
+ 'chrono',
+ 'ratio',
+ 'regex',
+ 'system_error',
+ ):
+ error(filename, linenum, 'build/c++11', 5,
+ ('<%s> is an unapproved C++11 header.') % include.group(1))
+
+ # The only place where we need to worry about C++11 keywords and library
+ # features in preprocessor directives is in macro definitions.
+ if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return
+
+ # These are classes and free functions. The classes are always
+ # mentioned as std::*, but we only catch the free functions if
+ # they're not found by ADL. They're alphabetical by header.
+ for top_name in (
+ # type_traits
+ 'alignment_of',
+ 'aligned_union',
+ ):
+ if Search(r'\bstd::%s\b' % top_name, line):
+ error(filename, linenum, 'build/c++11', 5,
+ ('std::%s is an unapproved C++11 class or function. Send c-style '
+ 'an example of where it would make your code more readable, and '
+ 'they may let you use it.') % top_name)
+
+
+def FlagCxx14Features(filename, clean_lines, linenum, error):
+ """Flag those C++14 features that we restrict.
+
+ Args:
+ filename: The name of the current file.
+ clean_lines: A CleansedLines instance containing the file.
+ linenum: The number of the line to check.
+ error: The function to call with any errors found.
+ """
+ line = clean_lines.elided[linenum]
+
+ include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line)
+
+ # Flag unapproved C++14 headers.
+ if include and include.group(1) in ('scoped_allocator', 'shared_mutex'):
+ error(filename, linenum, 'build/c++14', 5,
+ ('<%s> is an unapproved C++14 header.') % include.group(1))
+
+
+def ProcessFileData(filename, file_extension, lines, error,
+ extra_check_functions=[]):
+ """Performs lint checks and reports any errors to the given error function.
+
+ Args:
+ filename: Filename of the file that is being processed.
+ file_extension: The extension (dot not included) of the file.
+ lines: An array of strings, each representing a line of the file, with the
+ last element being empty if the file is terminated with a newline.
+ error: A callable to which errors are reported, which takes 4 arguments:
+ filename, line number, error level, and message
+ extra_check_functions: An array of additional check functions that will be
+ run on each source line. Each function takes 4
+ arguments: filename, clean_lines, line, error
+ """
+ lines = (['// marker so line numbers and indices both start at 1'] + lines +
+ ['// marker so line numbers end in a known way'])
+
+ include_state = _IncludeState()
+ function_state = _FunctionState()
+ nesting_state = NestingState()
+
+ ResetNolintSuppressions()
+
+ CheckForCopyright(filename, lines, error)
+ ProcessGlobalSuppresions(lines)
+ RemoveMultiLineComments(filename, lines, error)
+ clean_lines = CleansedLines(lines)
+
+ if IsHeaderExtension(file_extension):
+ CheckForHeaderGuard(filename, clean_lines, error)
+
+ for line in xrange(clean_lines.NumLines()):
+ ProcessLine(filename, file_extension, clean_lines, line,
+ include_state, function_state, nesting_state, error,
+ extra_check_functions)
+ FlagCxx11Features(filename, clean_lines, line, error)
+ nesting_state.CheckCompletedBlocks(filename, error)
+
+ CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)
+
+ # Check that the .cc file has included its header if it exists.
+ if _IsSourceExtension(file_extension):
+ CheckHeaderFileIncluded(filename, include_state, error)
+
+ # We check here rather than inside ProcessLine so that we see raw
+ # lines rather than "cleaned" lines.
+ CheckForBadCharacters(filename, lines, error)
+
+ CheckForNewlineAtEOF(filename, lines, error)
+
+def ProcessConfigOverrides(filename):
+ """ Loads the configuration files and processes the config overrides.
+
+ Args:
+ filename: The name of the file being processed by the linter.
+
+ Returns:
+ False if the current |filename| should not be processed further.
+ """
+
+ abs_filename = os.path.abspath(filename)
+ cfg_filters = []
+ keep_looking = True
+ while keep_looking:
+ abs_path, base_name = os.path.split(abs_filename)
+ if not base_name:
+ break # Reached the root directory.
+
+ cfg_file = os.path.join(abs_path, "CPPLINT.cfg")
+ abs_filename = abs_path
+ if not os.path.isfile(cfg_file):
+ continue
+
+ try:
+ with open(cfg_file) as file_handle:
+ for line in file_handle:
+ line, _, _ = line.partition('#') # Remove comments.
+ if not line.strip():
+ continue
+
+ name, _, val = line.partition('=')
+ name = name.strip()
+ val = val.strip()
+ if name == 'set noparent':
+ keep_looking = False
+ elif name == 'filter':
+ cfg_filters.append(val)
+ elif name == 'exclude_files':
+ # When matching exclude_files pattern, use the base_name of
+ # the current file name or the directory name we are processing.
+ # For example, if we are checking for lint errors in /foo/bar/baz.cc
+ # and we found the .cfg file at /foo/CPPLINT.cfg, then the config
+ # file's "exclude_files" filter is meant to be checked against "bar"
+ # and not "baz" nor "bar/baz.cc".
+ if base_name:
+ pattern = re.compile(val)
+ if pattern.match(base_name):
+ sys.stderr.write('Ignoring "%s": file excluded by "%s". '
+ 'File path component "%s" matches '
+ 'pattern "%s"\n' %
+ (filename, cfg_file, base_name, val))
+ return False
+ elif name == 'linelength':
+ global _line_length
+ try:
+ _line_length = int(val)
+ except ValueError:
+ sys.stderr.write('Line length must be numeric.')
+ elif name == 'root':
+ global _root
+ _root = val
+ elif name == 'headers':
+ ProcessHppHeadersOption(val)
+ else:
+ sys.stderr.write(
+ 'Invalid configuration option (%s) in file %s\n' %
+ (name, cfg_file))
+
+ except IOError:
+ sys.stderr.write(
+ "Skipping config file '%s': Can't open for reading\n" % cfg_file)
+ keep_looking = False
+
+ # Apply all the accumulated filters in reverse order (top-level directory
+ # config options having the least priority).
+ for filter in reversed(cfg_filters):
+ _AddFilters(filter)
+
+ return True
+
+
+def ProcessFile(filename, vlevel, extra_check_functions=[]):
+ """Does google-lint on a single file.
+
+ Args:
+ filename: The name of the file to parse.
+
+ vlevel: The level of errors to report. Every error of confidence
+ >= verbose_level will be reported. 0 is a good default.
+
+ extra_check_functions: An array of additional check functions that will be
+ run on each source line. Each function takes 4
+ arguments: filename, clean_lines, line, error
+ """
+
+ _SetVerboseLevel(vlevel)
+ _BackupFilters()
+
+ if not ProcessConfigOverrides(filename):
+ _RestoreFilters()
+ return
+
+ lf_lines = []
+ crlf_lines = []
+ try:
+ # Support the UNIX convention of using "-" for stdin. Note that
+ # we are not opening the file with universal newline support
+ # (which codecs doesn't support anyway), so the resulting lines do
+ # contain trailing '\r' characters if we are reading a file that
+ # has CRLF endings.
+ # If after the split a trailing '\r' is present, it is removed
+ # below.
+ if filename == '-':
+ lines = codecs.StreamReaderWriter(sys.stdin,
+ codecs.getreader('utf8'),
+ codecs.getwriter('utf8'),
+ 'replace').read().split('\n')
+ else:
+ lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
+
+ # Remove trailing '\r'.
+ # The -1 accounts for the extra trailing blank line we get from split()
+ for linenum in range(len(lines) - 1):
+ if lines[linenum].endswith('\r'):
+ lines[linenum] = lines[linenum].rstrip('\r')
+ crlf_lines.append(linenum + 1)
+ else:
+ lf_lines.append(linenum + 1)
+
+ except IOError:
+ sys.stderr.write(
+ "Skipping input '%s': Can't open for reading\n" % filename)
+ _RestoreFilters()
+ return
+
+ # Note, if no dot is found, this will give the entire filename as the ext.
+ file_extension = filename[filename.rfind('.') + 1:]
+
+ # When reading from stdin, the extension is unknown, so no cpplint tests
+ # should rely on the extension.
+ if filename != '-' and file_extension not in _valid_extensions:
+ sys.stderr.write('Ignoring %s; not a valid file name '
+ '(%s)\n' % (filename, ', '.join(_valid_extensions)))
+ else:
+ ProcessFileData(filename, file_extension, lines, Error,
+ extra_check_functions)
+
+ # If end-of-line sequences are a mix of LF and CR-LF, issue
+ # warnings on the lines with CR.
+ #
+ # Don't issue any warnings if all lines are uniformly LF or CR-LF,
+ # since critique can handle these just fine, and the style guide
+ # doesn't dictate a particular end of line sequence.
+ #
+ # We can't depend on os.linesep to determine what the desired
+ # end-of-line sequence should be, since that will return the
+ # server-side end-of-line sequence.
+ if lf_lines and crlf_lines:
+ # Warn on every line with CR. An alternative approach might be to
+ # check whether the file is mostly CRLF or just LF, and warn on the
+ # minority, we bias toward LF here since most tools prefer LF.
+ for linenum in crlf_lines:
+ Error(filename, linenum, 'whitespace/newline', 1,
+ 'Unexpected \\r (^M) found; better to use only \\n')
+
+ sys.stdout.write('Done processing %s\n' % filename)
+ _RestoreFilters()
+
+
+def PrintUsage(message):
+ """Prints a brief usage string and exits, optionally with an error message.
+
+ Args:
+ message: The optional error message.
+ """
+ sys.stderr.write(_USAGE)
+ if message:
+ sys.exit('\nFATAL ERROR: ' + message)
+ else:
+ sys.exit(1)
+
+
+def PrintCategories():
+ """Prints a list of all the error-categories used by error messages.
+
+ These are the categories used to filter messages via --filter.
+ """
+ sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES))
+ sys.exit(0)
+
+
+def ParseArguments(args):
+ """Parses the command line arguments.
+
+ This may set the output format and verbosity level as side-effects.
+
+ Args:
+ args: The command line arguments:
+
+ Returns:
+ The list of filenames to lint.
+ """
+ try:
+ (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',
+ 'counting=',
+ 'filter=',
+ 'root=',
+ 'linelength=',
+ 'extensions=',
+ 'headers='])
+ except getopt.GetoptError:
+ PrintUsage('Invalid arguments.')
+
+ verbosity = _VerboseLevel()
+ output_format = _OutputFormat()
+ filters = ''
+ counting_style = ''
+
+ for (opt, val) in opts:
+ if opt == '--help':
+ PrintUsage(None)
+ elif opt == '--output':
+ if val not in ('emacs', 'vs7', 'eclipse'):
+ PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.')
+ output_format = val
+ elif opt == '--verbose':
+ verbosity = int(val)
+ elif opt == '--filter':
+ filters = val
+ if not filters:
+ PrintCategories()
+ elif opt == '--counting':
+ if val not in ('total', 'toplevel', 'detailed'):
+ PrintUsage('Valid counting options are total, toplevel, and detailed')
+ counting_style = val
+ elif opt == '--root':
+ global _root
+ _root = val
+ elif opt == '--linelength':
+ global _line_length
+ try:
+ _line_length = int(val)
+ except ValueError:
+ PrintUsage('Line length must be digits.')
+ elif opt == '--extensions':
+ global _valid_extensions
+ try:
+ _valid_extensions = set(val.split(','))
+ except ValueError:
+ PrintUsage('Extensions must be comma seperated list.')
+ elif opt == '--headers':
+ ProcessHppHeadersOption(val)
+
+ if not filenames:
+ PrintUsage('No files were specified.')
+
+ _SetOutputFormat(output_format)
+ _SetVerboseLevel(verbosity)
+ _SetFilters(filters)
+ _SetCountingStyle(counting_style)
+
+ return filenames
+
+
+def main():
+ filenames = ParseArguments(sys.argv[1:])
+
+ # Change stderr to write with replacement characters so we don't die
+ # if we try to print something containing non-ASCII characters.
+ sys.stderr = codecs.StreamReaderWriter(sys.stderr,
+ codecs.getreader('utf8'),
+ codecs.getwriter('utf8'),
+ 'replace')
+
+ _cpplint_state.ResetErrorCounts()
+ for filename in filenames:
+ ProcessFile(filename, _cpplint_state.verbose_level)
+ _cpplint_state.PrintErrorCounts()
+
+ sys.exit(_cpplint_state.error_count > 0)
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_UT_INCLUDE_APPWINDOW_H__
+#define __ESPLUSPLAYER_UT_INCLUDE_APPWINDOW_H__
+
+#define ECORE_WAYLAND_DISPLAY_TEST 1
+
+#include <cassert>
+#include <chrono>
+#include <thread>
+
+#include "Ecore.h"
+#include "Ecore_Wayland.h"
+#include "Elementary.h"
+#if ECORE_WAYLAND_DISPLAY_TEST
+#include "Ecore_Wl2.h"
+#endif
+
+namespace esplusplayer_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 esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_UT_INCLUDE_APPWINDOW_H__
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_UT_INCLUDE_ES_EVENT_LISTENER_H__
+#define __ESPLUSPLAYER_UT_INCLUDE_ES_EVENT_LISTENER_H__
+
+#include <chrono>
+#include <cstring>
+#include <string>
+#include <thread>
+#include <functional>
+
+#include "core/utils/plusplayer_log.h"
+#include "esplusplayer_capi/esplusplayer_capi.h"
+#include "ut/include/esplusplayer/esreader.hpp"
+
+class EsPlayerEventCallback {
+ public:
+ EsPlayerEventCallback(esplusplayer_handle handle,
+ EsStreamReader* video_reader,
+ EsStreamReader* audio_reader)
+ : handle_(handle),
+ video_reader_(video_reader),
+ audio_reader_(audio_reader) {}
+ ~EsPlayerEventCallback() {
+ if (video_reader_) video_reader_->Stop();
+ if (audio_reader_) audio_reader_->Stop();
+ if (audio_feeding_task_.joinable()) audio_feeding_task_.join();
+ if (video_feeding_task_.joinable()) video_feeding_task_.join();
+ }
+ void SetCallback(void) {
+ esplusplayer_set_error_cb(handle_, OnError, this);
+ esplusplayer_set_buffer_status_cb(handle_, OnBufferStatus, this);
+ esplusplayer_set_buffer_byte_status_cb(handle_, OnBufferByteStatus, this);
+ esplusplayer_set_buffer_time_status_cb(handle_, OnBufferTimeStatus, 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);
+ esplusplayer_set_seek_done_cb(handle_, OnSeekDone, this);
+ esplusplayer_set_ready_to_seek_cb(handle_, OnReadyToSeek, this);
+ esplusplayer_set_media_packet_video_decoded_cb(handle_, OnVideoDecoded,
+ this);
+ esplusplayer_set_closed_caption_cb(handle_, OnClosedCaption, this);
+ esplusplayer_set_flush_done_cb(handle_, OnFlushDone, this);
+ esplusplayer_set_event_cb(handle_, OnEvent, this);
+ esplusplayer_set_video_frame_dropped_cb(handle_, OnVideoFrameDropped, this);
+ }
+
+ static void DataFeedingTask(EsStreamReader* stream,
+ esplusplayer_handle esplayer) {
+ esplusplayer_es_packet pkt;
+ while (true) {
+ memset(&pkt, 0, sizeof(esplusplayer_es_packet));
+ if (!stream->ReadNextPacket(pkt)) {
+ esplusplayer_submit_eos_packet(esplayer, stream->GetType());
+ break;
+ }
+ esplusplayer_submit_packet(esplayer, &pkt);
+ delete[] pkt.buffer;
+ }
+ }
+ static void OnError(const esplusplayer_error_type err_code, void* userdata) {
+ LOG_ENTER;
+ }
+ static void OnResourceConflicted(void* userdata) { LOG_ENTER; }
+ static void OnSeekDone(void* userdata) { LOG_ENTER; }
+ static void OnVideoFrameDropped(const uint64_t count, void*) { LOG_ENTER; }
+ static void OnBufferByteStatus(const esplusplayer_stream_type,
+ const esplusplayer_buffer_status, uint64_t,
+ void* userdata) {
+ LOG_ENTER;
+ }
+ static void OnBufferTimeStatus(const esplusplayer_stream_type,
+ const esplusplayer_buffer_status, uint64_t,
+ void* userdata) {
+ LOG_ENTER;
+ }
+ static void OnVideoDecoded(const esplusplayer_decoded_video_packet*,
+ void* userdata) {
+ LOG_ENTER;
+ }
+ static void OnClosedCaption(const char* data, const int size,
+ void* userdata) {
+ LOG_ENTER;
+ }
+ static void OnFlushDone(void* userdata) { LOG_ENTER; }
+ static void OnEvent(const esplusplayer_event_type type,
+ const esplusplayer_event_msg msg, void* userdata) {
+ EsPlayerEventCallback* cb = static_cast<EsPlayerEventCallback*>(userdata);
+ LOG_ENTER;
+ if (type == ESPLUSPLAYER_EVENT_REQUESTED_FIRST_RENDER_FRAME) {
+ std::unique_lock<std::mutex> lk(cb->first_render_done_m_);
+ cb->first_render_done_ = true;
+ lk.unlock();
+ cb->first_render_done_cv_.notify_all();
+ }
+ }
+ static void OnEos(void* userdata) {
+ EsPlayerEventCallback* cb = static_cast<EsPlayerEventCallback*>(userdata);
+ LOG_ENTER;
+ std::unique_lock<std::mutex> lk(cb->eos_m_);
+ cb->eos_ = true;
+ lk.unlock();
+ cb->eos_cv_.notify_all();
+ }
+
+ static void StartDataFeedingTask(EsPlayerEventCallback* cb,
+ esplusplayer_stream_type type) {
+ std::unique_lock<std::mutex> lk(cb->data_m_);
+ if (type == ESPLUSPLAYER_STREAM_TYPE_AUDIO) {
+ if (cb->audio_reader_ != nullptr && !cb->audio_feeding_task_.joinable()) {
+ cb->ready_audio_data_ = true;
+ cb->audio_feeding_task_ =
+ std::thread(DataFeedingTask, std::ref(cb->audio_reader_),
+ std::ref(cb->handle_));
+ }
+ LOG_INFO("Audio ready to prepare");
+ } else if (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {
+ if (cb->video_reader_ != nullptr && !cb->video_feeding_task_.joinable()) {
+ cb->ready_video_data_ = true;
+ cb->video_feeding_task_ =
+ std::thread(DataFeedingTask, std::ref(cb->video_reader_),
+ std::ref(cb->handle_));
+ }
+ LOG_INFO("Video ready to prepare");
+ }
+ lk.unlock();
+ cb->data_cv_.notify_all();
+ }
+
+ static void OnPrepareDone(bool ret, void* userdata) {
+ EsPlayerEventCallback* cb = static_cast<EsPlayerEventCallback*>(userdata);
+ LOG_ENTER;
+ StartDataFeedingTask(cb, ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+ StartDataFeedingTask(cb, ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ std::unique_lock<std::mutex> lk(cb->prepare_done_m_);
+ cb->prepare_done_ = true;
+ cb->prepare_done_result_ = ret;
+ 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);
+ LOG_ENTER;
+ StartDataFeedingTask(cb, type);
+ }
+ static void OnReadyToSeek(const esplusplayer_stream_type type,
+ const uint64_t offset, void* userdata) {
+ EsPlayerEventCallback* cb = static_cast<EsPlayerEventCallback*>(userdata);
+ LOG_ENTER;
+ 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_);
+ LOG_ENTER;
+ data_cv_.wait_for(lk, std::chrono::seconds(1), [this]() -> bool {
+ return ready_audio_data_ && ready_video_data_;
+ });
+ LOG_LEAVE;
+ lk.unlock();
+ }
+ void WaitAudioStreamData() {
+ std::unique_lock<std::mutex> lk(data_m_);
+ LOG_ENTER;
+ data_cv_.wait_for(lk, std::chrono::seconds(1),
+ [this]() -> bool { return ready_audio_data_; });
+ LOG_LEAVE;
+ lk.unlock();
+ }
+ void WaitVideoStreamData() {
+ std::unique_lock<std::mutex> lk(data_m_);
+ LOG_ENTER;
+ data_cv_.wait_for(lk, std::chrono::seconds(1),
+ [this]() -> bool { return ready_audio_data_; });
+ LOG_LEAVE;
+ lk.unlock();
+ }
+ void WaitForEos() {
+ LOG_ENTER;
+ std::unique_lock<std::mutex> lk(eos_m_);
+ eos_cv_.wait_for(lk, std::chrono::minutes(1),
+ [this]() -> bool { return eos_; });
+ LOG_LEAVE;
+ lk.unlock();
+ }
+
+ void WaitForEos(std::function<bool()> abort_func) {
+ LOG_ENTER;
+ std::unique_lock<std::mutex> lk(eos_m_);
+ while(abort_func() == false && eos_ == false) {
+ eos_cv_.wait_for(lk, std::chrono::seconds(1),
+ [this]() -> bool { return eos_; });
+ }
+ LOG_LEAVE;
+ }
+ bool WaitForPrepareDone() {
+ LOG_ENTER;
+ std::unique_lock<std::mutex> lk(prepare_done_m_);
+ prepare_done_cv_.wait_for(lk, std::chrono::seconds(10),
+ [this]() -> bool { return prepare_done_; });
+ LOG_LEAVE;
+ lk.unlock();
+ return prepare_done_result_;
+ }
+ void WaitForFirstRenderDone() {
+ LOG_ENTER;
+ std::unique_lock<std::mutex> lk(first_render_done_m_);
+ first_render_done_cv_.wait_for(
+ lk, std::chrono::seconds(10),
+ [this]() -> bool { return first_render_done_; });
+ LOG_LEAVE;
+ lk.unlock();
+ }
+
+ private:
+ esplusplayer_handle handle_ = nullptr;
+ EsStreamReader* video_reader_ = nullptr;
+ EsStreamReader* audio_reader_ = nullptr;
+ std::thread video_feeding_task_;
+ std::thread audio_feeding_task_;
+
+ 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;
+ bool prepare_done_result_ = false;
+ std::mutex prepare_done_m_;
+ std::condition_variable prepare_done_cv_;
+
+ bool first_render_done_ = false;
+ std::mutex first_render_done_m_;
+ std::condition_variable first_render_done_cv_;
+};
+
+#endif // __ESPLUSPLAYER_UT_INCLUDE_ES_EVENT_LISTENER_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_UT_INCLUDE_ES_READER_H__
+#define __ESPLUSPLAYER_UT_INCLUDE_ES_READER_H__
+
+
+#include <string>
+#include <fstream>
+#include <json/json.h>
+#include <chrono>
+
+#include "esplusplayer_capi/esplusplayer_capi.h"
+#include "esplusplayer/tclist.h"
+#include "utils/utility.h"
+
+using namespace std;
+using namespace chrono;
+
+// TODO: Use streamreader.hpp after merge this class with it
+class EsStreamReader {
+ public:
+ explicit EsStreamReader(const std::string dirpath,
+ esplusplayer_stream_type type,
+ bool absolutepath = false) {
+ dir_path_ = ContentsRoot::Instance().GetRoot() + dirpath;
+ if (absolutepath) {
+ dir_path_ = dirpath;
+ }
+ es_data_file_ = dir_path_ + "ESP.es";
+ es_info_file_ = dir_path_ + "ESP.info";
+ es_codec_info_json_file_ = dir_path_ + "ESCodecInfo.json";
+ extra_codec_file_ = dir_path_ + "ESP.codec_extradata";
+ type_ = type;
+ pause_requested_ = false;
+ std::cout << "ES data file " << es_data_file_ << std::endl;
+ }
+ ~EsStreamReader() {
+ if (stream_.is_open()) {
+ stream_.close();
+ }
+ }
+
+ bool SetStreamInfo(esplusplayer_handle& esplayer) {
+ if (type_ == ESPLUSPLAYER_STREAM_TYPE_AUDIO) {
+ esplusplayer_audio_stream_info audio_stream;
+ audio_stream.codec_data = nullptr;
+ audio_stream.codec_data_length = 0;
+ GetExtraData_(audio_stream.codec_data, audio_stream.codec_data_length);
+ if (!GetMediaInfo_(audio_stream)) {
+ if (audio_stream.codec_data != nullptr)
+ delete []audio_stream.codec_data;
+ return false;
+ }
+
+ esplusplayer_set_audio_stream_info(esplayer, &audio_stream);
+ if (audio_stream.codec_data != nullptr)
+ delete []audio_stream.codec_data;
+ } else if (type_ == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {
+ esplusplayer_video_stream_info video_stream;
+ video_stream.codec_data = nullptr;
+ video_stream.codec_data_length = 0;
+ GetExtraData_(video_stream.codec_data, video_stream.codec_data_length);
+ if (!GetMediaInfo_(video_stream)) {
+ if (video_stream.codec_data != nullptr)
+ delete []video_stream.codec_data;
+ return false;
+ }
+ esplusplayer_set_video_stream_info(esplayer, &video_stream);
+ if (video_stream.codec_data != nullptr)
+ delete []video_stream.codec_data;
+ }
+ return true;
+ }
+
+ void WaitForRealTimeInterval(esplusplayer_es_packet& pkt) {
+ if (first_read_time.time_since_epoch() == milliseconds(0)) {
+ first_read_time = system_clock::now();
+ first_pts = pkt.pts;
+ return;
+ }
+
+ if (pkt.pts == 0) {
+ LOG_ERROR("pkt.pts is 0. looks like invalid timestamp");
+ }
+
+ const uint64_t margin = 10; // msec
+ if (pkt.pts < first_pts + margin) { // too early, no wait
+ return;
+ }
+
+ do {
+ auto current_time = system_clock::now();
+ if (milliseconds(pkt.pts - first_pts + margin) <=
+ current_time - first_read_time) // already late, no wait
+ return;
+ std::this_thread::sleep_for(std::chrono::milliseconds(margin / 2));
+ } while (stream_.is_open() && !stop_requested_);
+ }
+
+ bool ReadNextPacket(esplusplayer_es_packet& pkt) {
+ if (stop_requested_) return false;
+
+ if (!stream_.is_open()) {
+ stream_ = std::ifstream(es_data_file_, std::ifstream::binary);
+ if (!stream_.is_open()) return false;
+ }
+ if (stream_.eof() || GetFileLeftSize_() < 24) {
+ if (!repeat) {
+ LOG_ERROR("type[ %d ], stream EOF", type_);
+ return false;
+ }
+ last_pts_before_repeat = last_pts + last_duration;
+ stream_.seekg(0, std::ios::beg);
+ LOG_ERROR("stream EOF, seek to 0 to repeat");
+ }
+
+ while (pause_requested_ && !stop_requested_) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ first_read_time = system_clock::now(); // to resume playback immediately
+ // after long pause.
+ first_pts = last_pts;
+ continue;
+ }
+
+ pkt.type = type_;
+ std::uint64_t size;
+ stream_.read(reinterpret_cast<char*>(&pkt.pts), sizeof(pkt.pts));
+ pkt.pts = pkt.pts / 1000000 + last_pts_before_repeat; // ns -> ms
+ last_pts = pkt.pts;
+ stream_.read(reinterpret_cast<char*>(&pkt.duration), sizeof(pkt.duration));
+ last_duration = pkt.duration = pkt.duration / 1000000; // ns -> ms
+ stream_.read(reinterpret_cast<char*>(&size), sizeof(size));
+ pkt.buffer_size = static_cast<uint32_t>(size);
+ if (pkt.buffer_size == 0) return false;
+ pkt.buffer = new char[pkt.buffer_size];
+ stream_.read(reinterpret_cast<char*>(pkt.buffer), pkt.buffer_size);
+ pkt.matroska_color_info = nullptr;
+ // std::cout << "Type: " << type_ << " \tPts: " << pkt.pts << "
+ // \tduration: " << pkt.duration << " \tsize: " << size << std::endl;
+
+ if (use_realtime_interval_to_submit_packet) {
+ WaitForRealTimeInterval(pkt);
+ }
+
+ return true;
+ }
+
+ void ResetReader() {
+ stream_.seekg(0,std::ios::beg);
+ pause_requested_ = false;
+ }
+
+ void Pause() {
+ pause_requested_ = true;
+ std::cout << "Reader paused" << std::endl;
+ }
+
+ void Resume() {
+ pause_requested_ = false;
+ std::cout << "Reader resumed" << std::endl;
+ }
+
+ void Repeat() {
+ LOG_ERROR("Repeat mode");
+ repeat = true;
+ }
+
+ void Stop() {
+ stop_requested_ = true;
+ }
+
+ void SetRealtimePacketSubmit(bool on) {
+ use_realtime_interval_to_submit_packet = on;
+ }
+
+ esplusplayer_stream_type GetType() {
+ return type_;
+ }
+
+private:
+ int GetFileLeftSize_(void) {
+ if (!stream_.is_open()) return 0;
+ int cur = stream_.tellg();
+ stream_.seekg(0, stream_.end);
+ int total = stream_.tellg();
+ stream_.seekg(cur);
+ return total - cur;
+ }
+ bool GetExtraData_(char*& data, unsigned int& size) {
+ auto stream = std::ifstream(extra_codec_file_, std::ifstream::binary);
+ if (!stream.is_open()) return false;
+ stream.read(reinterpret_cast<char*>(&size), sizeof(size));
+ if (size == 0) return false;
+ data = new char[size];
+ stream.read(data, size);
+ stream.close();
+ return true;
+ }
+
+ Json::Value OpenCodecInfoJson_() {
+ Json::CharReaderBuilder jsReader;
+ Json::Value root;
+ std::string err;
+ auto stream = std::ifstream(es_codec_info_json_file_, std::ifstream::in);
+ if (!stream.is_open()) {
+ LOG_ERROR("Fail to open %s", es_codec_info_json_file_.c_str());
+ return root;
+ }
+ if (Json::parseFromStream(jsReader, stream, &root, &err) == false) {
+ LOG_ERROR("json parsing failed [%s]", err.c_str());
+ }
+ LOG_INFO("root.sized [%d]", root.size());
+ return root;
+ }
+
+ bool GetMediaInfo_(esplusplayer_video_stream_info& info) {
+ Json::Value root = OpenCodecInfoJson_();
+ if (!root.empty()) {
+ info.mime_type =
+ utils::Utility::ConvertToMimeType(root["mimetype"].asString());
+ info.width = root["width"].asUInt();
+ info.height = root["height"].asUInt();
+ info.max_width = root["maxwidth"].asUInt();
+ info.max_height = root["maxheight"].asUInt();
+ info.framerate_num = root["r_framerate"]["num"].asUInt();
+ info.framerate_den = root["r_framerate"]["den"].asUInt();
+ return true;
+ }
+
+ auto stream = std::ifstream(es_info_file_, std::ifstream::in);
+ if (!stream.is_open()) {
+ std::cout << "No video es file: " << es_info_file_ << std::endl;
+ return false;
+ }
+
+ uint32_t mime_type;
+ stream >> mime_type >> info.width >> info.height >> info.max_width >> info.max_height >>
+ info.framerate_num >> info.framerate_den;
+ info.mime_type = static_cast<esplusplayer_video_mime_type>(mime_type);
+ std::cout << "mime_type: " << info.mime_type << std::endl;
+ std::cout << "info.width: " << info.width << std::endl;
+ stream.close();
+ return true;
+ }
+
+ bool GetMediaInfo_(esplusplayer_audio_stream_info& info) {
+ Json::Value root = OpenCodecInfoJson_();
+ if (!root.empty()) {
+ info.mime_type = utils::Utility::ConvertToMimeType(
+ root["mimetype"].asString(), root["format"].asString());
+ info.sample_rate = root["rate"].asUInt();
+ info.channels = root["channels"].asUInt();
+ return true;
+ }
+
+ auto stream = std::ifstream(es_info_file_, std::ifstream::in);
+ if (!stream.is_open()) {
+ std::cout << "No audio es file: " << es_info_file_ << std::endl;
+ return false;
+ }
+ uint32_t mime_type;
+ stream >> mime_type >> info.sample_rate >> info.channels;
+ info.mime_type = static_cast<esplusplayer_audio_mime_type>(mime_type);
+ std::cout << "mime_type: " << info.mime_type << std::endl;
+ std::cout << "info.sample_rate: " << info.sample_rate << std::endl;
+ stream.close();
+ return true;
+ }
+
+
+
+ private:
+ std::string dir_path_;
+ std::string es_data_file_;
+ std::string es_info_file_;
+ std::string es_codec_info_json_file_;
+ std::string extra_codec_file_;
+ std::ifstream stream_;
+ esplusplayer_stream_type type_;
+ bool pause_requested_ = false;
+ bool stop_requested_ = false;
+
+ bool repeat = false;
+ uint64_t last_pts = 0;
+ uint64_t last_duration = 0;
+ uint64_t last_pts_before_repeat = 0;
+ bool use_realtime_interval_to_submit_packet = false;
+ system_clock::time_point first_read_time;
+ uint64_t first_pts;
+ uint64_t number_of_read_packet = 0;
+};
+
+#endif // __ESPLUSPLAYER_UT_INCLUDE_ES_READER_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_UT_INCLUDE_ESPLUSPLAYER_TCLIST_H__
+#define __ESPLUSPLAYER_UT_INCLUDE_ESPLUSPLAYER_TCLIST_H__
+
+#include <string>
+#include <vector>
+
+namespace es_tc {
+ static const std::string es_h264_aac = "es_h264_aac/";
+ static const std::string es_hevc_ac3 = "es_hevc_ac3/";
+ static const std::string es_vp9_opus = "es_vp9_opus/";
+ static const std::string es_h264_640_360_aac = "es_h264_640_360_aac/";
+ static std::vector<std::string> tc_list = {
+ es_h264_aac,
+ //es_hevc_ac3,
+ es_vp9_opus,
+ };
+ static std::vector<std::string> inapp_multiview_tc_list = {
+ es_h264_640_360_aac,
+ };
+}
+
+class ContentsRoot {
+public:
+ static ContentsRoot& Instance();
+ virtual ~ContentsRoot() {};
+ bool IsMounted();
+ bool MountTcDirectory();
+ void UnMountTcDirectory();
+ std::string GetRoot() {return root_abs_path;};
+private:
+ explicit ContentsRoot();
+ bool UseUsb();
+ std::string root_abs_path;
+};
+#endif // __ESPLUSPLAYER_UT_INCLUDE_ESPLUSPLAYER_TCLIST_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_CONSTANT_H__
+#define __ESPLUSPLAYER_MIXER_UT_CONSTANT_H__
+
+#include <tbm_type.h>
+
+#include <cstdint>
+#include <type_traits>
+
+#include "mixer/mixer.h"
+#include "mixer/types/buffertype.h"
+#include "mixer/types/planecomponent.h"
+#include "mixer/types/videoplanemanipinfo.h"
+#include "esplusplayer/types/display.h"
+
+namespace esplusplayer_ut {
+using namespace esplusplayer;
+
+VideoPlaneManipulableInfo GetVideoPlaneManipulableInfo(
+ BufferHandleType handle, const PlaneComponent& comp,
+ const std::uint32_t& linesize, const Geometry& geom);
+
+Geometry GetGeometry(const int& x, const int& y, const int& w, const int& h);
+
+CropArea GetCropArea(const double& x, const double& y, const double& w,
+ const double& h);
+
+Mixer::ResolutionInfo GetResolutionInfo(int width, int height, int fnum,
+ int fden);
+
+static const std::uint32_t kDefaultWidth = 1920;
+static const std::uint32_t kDefaultHeight = 1080;
+static const std::uint32_t kSmallWidth = 192;
+static const std::uint32_t kSmallHeight = 108;
+static const std::uint32_t kInvalidWidth = 0;
+static const std::uint32_t kInvalidHeight = 0;
+static const std::uint32_t kExpectedDefaultByteSize =
+ (kDefaultWidth * kDefaultHeight * 3) >> 1;
+
+static const std::uint32_t kDefaultFramerateNum = 30;
+static const std::uint32_t kDefaultFramerateDen = 1;
+static const std::uint32_t kFastFramerateNum = 60;
+static const std::uint32_t kFastFramerateDen = 1;
+static const std::uint32_t kInvalidFramerateNum = 0;
+static const std::uint32_t kInvalidFramerateDen = 0;
+
+static const std::uint32_t kDefaultX = 10;
+static const std::uint32_t kDefaultY = 20;
+static const std::uint32_t kDefaultW = 30;
+static const std::uint32_t kDefaultH = 40;
+
+static const std::uint32_t kDefaultLineSize = kDefaultWidth;
+static const BufferHandleType kDefaultBufferHandle = 1;
+static const BufferHandleType kInvalidBufferHandle = 0;
+static const BufferKeyType kDefaultBufferKey = 1;
+static const BufferKeyType kInvalidBufferKey = 0;
+
+extern BufferDefaultType kDefaultBuffer;
+extern BufferUnionHandleType kDefaultMappedHandle;
+
+static const auto kYComponentSrcVMInfo = GetVideoPlaneManipulableInfo(
+ kDefaultBufferHandle, PlaneComponent::kYComponent, kDefaultLineSize,
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH));
+static const auto kUVComponentSrcVMInfo = GetVideoPlaneManipulableInfo(
+ kDefaultBufferHandle, PlaneComponent::kUVComponent, kDefaultLineSize,
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH));
+
+static const auto kYComponentDestVMInfo = GetVideoPlaneManipulableInfo(
+ kDefaultBufferHandle, PlaneComponent::kYComponent, kDefaultLineSize,
+ GetGeometry(0, 0, kDefaultWidth, kDefaultHeight));
+static const auto kUVComponentDestVMInfo = GetVideoPlaneManipulableInfo(
+ kDefaultBufferHandle, PlaneComponent::kUVComponent, kDefaultLineSize,
+ GetGeometry(0, kDefaultHeight, kDefaultWidth >> 1, kDefaultHeight >> 1));
+
+static const auto kCroppedYComponentDestVMInfo = GetVideoPlaneManipulableInfo(
+ kDefaultBufferHandle, PlaneComponent::kYComponent, kDefaultLineSize,
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH));
+static const auto kCroppedUVComponentDestVMInfo = GetVideoPlaneManipulableInfo(
+ kDefaultBufferHandle, PlaneComponent::kUVComponent, kDefaultLineSize,
+ GetGeometry(kDefaultX >> 1, (kDefaultY >> 1) + kDefaultHeight,
+ kDefaultW >> 1, kDefaultH >> 1));
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_CONSTANT_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_MATCHER_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_MATCHER_H__
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include "mixer/types/videoplanemanipinfo.h"
+
+namespace esplusplayer_ut {
+using namespace esplusplayer;
+
+using ::testing::MakeMatcher;
+using ::testing::Matcher;
+using ::testing::MatcherInterface;
+using ::testing::MatchResultListener;
+
+class IsSameVideoPlaneManipulableInfoMatcher
+ : public MatcherInterface<const VideoPlaneManipulableInfo&> {
+ public:
+ explicit IsSameVideoPlaneManipulableInfoMatcher(
+ const VideoPlaneManipulableInfo& comparer)
+ : comparer_(comparer) {}
+ virtual ~IsSameVideoPlaneManipulableInfoMatcher() {}
+ bool MatchAndExplain(const VideoPlaneManipulableInfo& info,
+ MatchResultListener* listener) const override {
+ if (info.handle != comparer_.handle) {
+ *listener << "handle: " << info.handle << " vs " << comparer_.handle;
+ return false;
+ }
+ if (info.component != comparer_.component) {
+ *listener << "component: " << static_cast<int>(info.component) << " vs "
+ << static_cast<int>(comparer_.component);
+ return false;
+ }
+ if (info.linesize != comparer_.linesize) {
+ *listener << "linesize: " << info.linesize << " vs "
+ << comparer_.linesize;
+ return false;
+ }
+ if (info.rect.x != comparer_.rect.x) {
+ *listener << "rect.x: " << info.rect.x << " vs " << comparer_.rect.x;
+ return false;
+ }
+ if (info.rect.y != comparer_.rect.y) {
+ *listener << "rect.y: " << info.rect.y << " vs " << comparer_.rect.y;
+ return false;
+ }
+ if (info.rect.w != comparer_.rect.w) {
+ *listener << "rect.w: " << info.rect.w << " vs " << comparer_.rect.w;
+ return false;
+ }
+ if (info.rect.h != comparer_.rect.h) {
+ *listener << "rect.h: " << info.rect.h << " vs " << comparer_.rect.h;
+ return false;
+ }
+ return true;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "is same with your param";
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "is not same";
+ }
+
+ private:
+ const VideoPlaneManipulableInfo& comparer_;
+};
+
+Matcher<const VideoPlaneManipulableInfo&> IsSameVideoPlaneManipulableInfo(
+ const VideoPlaneManipulableInfo& comparer);
+} // namespace esplusplayer_ut
+
+using ::testing::PrintToString;
+
+template <typename... Args>
+std::string StringFormat(const std::string& format, Args... args) {
+ size_t size = snprintf(nullptr, 0, format.c_str(), args...) +
+ 1; // Extra space for '\0'
+ if (size <= 0) {
+ throw std::runtime_error("Error during formatting.");
+ }
+ std::unique_ptr<char[]> buf(new char[size]);
+ snprintf(buf.get(), size, format.c_str(), args...);
+ return std::string(buf.get(),
+ buf.get() + size - 1); // We don't want the '\0' inside
+}
+
+MATCHER_P(IsSameGeometry, geom,
+ StringFormat("%s in (x, y, w, h) [%d, %d, %d, %d]",
+ negation ? "isn't" : "is", geom.x, geom.y, geom.w,
+ geom.h)) {
+ if (geom.x != arg.x) return false;
+ if (geom.y != arg.y) return false;
+ if (geom.w != arg.w) return false;
+ if (geom.h != arg.h) return false;
+ return true;
+}
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_MATCHER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_FAKE_BUFFER_H__
+#define __ESPLUSPLAYER_MIXER_UT_FAKE_BUFFER_H__
+
+#include <gmock/gmock.h>
+
+#include <cstdint>
+#include <memory>
+
+using ::testing::MakePolymorphicAction;
+using ::testing::PolymorphicAction;
+
+namespace esplusplayer_ut {
+struct FakeBuffer {
+ FakeBuffer(const std::uint32_t& size)
+ : ptr(std::unique_ptr<char[]>(new char[size])), size(size) {}
+ std::unique_ptr<char[]> ptr = nullptr;
+ const std::uint32_t size = 0;
+};
+using FakeBufferPtr = std::unique_ptr<FakeBuffer>;
+
+class CreateFakeBufferAction {
+ public:
+ explicit CreateFakeBufferAction(FakeBufferPtr& buffer) : buffer_(buffer) {}
+ template <typename Result, typename ArgumentTuple>
+ Result Perform(const ArgumentTuple& args) const {
+ auto size = std::get<0>(args);
+ if (size == 0) return;
+ buffer_.reset(new FakeBuffer(size));
+ }
+
+ private:
+ FakeBufferPtr& buffer_;
+};
+
+static PolymorphicAction<CreateFakeBufferAction> CreateFakeBuffer(
+ FakeBufferPtr& buffer) {
+ return MakePolymorphicAction(CreateFakeBufferAction(buffer));
+}
+
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_FAKE_BUFFER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_BUFFER_OBJECT_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_BUFFER_OBJECT_H__
+
+#include <gmock/gmock.h>
+
+#include "mixer/constant.h"
+#include "mixer/interfaces/accessiblebuffer.h"
+#include "mixer/interfaces/bufferobject.h"
+#include "mixer/mock/movable.h"
+
+namespace esplusplayer_ut {
+using namespace esplusplayer;
+class MockBufferObject : public BufferObject {
+ public:
+ MOCK_CONST_METHOD0(GetBufferHandle, BufferHandleType());
+ MOCK_CONST_METHOD0(Export, BufferKeyType());
+ MOCK_CONST_METHOD0(GetSize, std::uint32_t());
+};
+
+class MockAccessibleBufferObject : public BufferObject,
+ public AccessibleBuffer {
+ public:
+ MOCK_CONST_METHOD0(GetReadableAddress_, Mover<PhyAddrAccessorPtr>());
+ MOCK_CONST_METHOD0(GetWritableAddress_, Mover<PhyAddrAccessorPtr>());
+ MOCK_CONST_METHOD0(GetBufferHandle, BufferHandleType());
+ MOCK_CONST_METHOD0(Export, BufferKeyType());
+ MOCK_CONST_METHOD0(GetSize, std::uint32_t());
+ PhyAddrAccessorPtr GetReadableAddress() const {
+ return std::move(GetReadableAddress_().get());
+ }
+ PhyAddrAccessorPtr GetWritableAddress() const {
+ return std::move(GetWritableAddress_().get());
+ }
+};
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_BUFFER_OBJECT_H__
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_MEMORY_ALLOCATOR_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_MEMORY_ALLOCATOR_H__
+
+#include <gmock/gmock.h>
+
+#include "mixer/constant.h"
+#include "mixer/interfaces/memoryallocator.h"
+
+namespace esplusplayer_ut {
+using namespace esplusplayer;
+class MockMemoryAllocator : public MemoryAllocator {
+ public:
+ MOCK_CONST_METHOD1(Allocate, BufferObject*(const std::uint32_t&));
+};
+} // namespace esplusplayer_ut
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_MEMORY_ALLOCATOR_H__
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_PHYSICAL_ADDRESS_ACCESSOR_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_PHYSICAL_ADDRESS_ACCESSOR_H__
+
+#include <gmock/gmock.h>
+
+#include "mixer/interfaces/phyaddraccessor.h"
+
+namespace esplusplayer {
+class MockPhyAddrAccessor : public PhysicalAddressAccessor {
+ public:
+ virtual ~MockPhyAddrAccessor() = default;
+ MOCK_METHOD0(GetAddress, BufferPhysicalAddrType());
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_PHYSICAL_ADDRESS_ACCESSOR_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_H__
+
+#include <gmock/gmock.h>
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "mixer/constant.h"
+#include "mixer/interfaces/renderableobject.h"
+#include "mixer/interfaces/videoplanemanipulator.h"
+#include "mixer/types/videoplanemanipinfo.h"
+#include "esplusplayer/types/display.h"
+
+namespace esplusplayer_ut {
+
+class MockRenderableObject : public RenderableObject {
+ public:
+ MOCK_CONST_METHOD0(GetVideoPlaneManipInfo,
+ const std::vector<VideoPlaneManipulableInfo>());
+ MOCK_CONST_METHOD0(IsValid, bool());
+ MOCK_CONST_METHOD0(GetWidth, std::uint32_t());
+ MOCK_CONST_METHOD0(GetHeight, std::uint32_t());
+ MOCK_CONST_METHOD0(GetSize, std::uint32_t());
+ MOCK_METHOD3(Render, bool(const VideoPlaneManipulator* const,
+ const std::vector<VideoPlaneManipulableInfo>&,
+ const Geometry&));
+ MOCK_METHOD4(Fill, bool(const VideoPlaneColorManipulator* const,
+ const PlaneComponent&, const std::uint32_t&,
+ const Geometry&));
+ MOCK_CONST_METHOD1(Export, bool(BufferKeyType&));
+};
+
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_FACTORY_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_FACTORY_H__
+
+#include <gmock/gmock.h>
+
+#include <cstdint>
+
+#include "mixer/constant.h"
+#include "mixer/interfaces/renderableobj_factory.h"
+
+namespace esplusplayer_ut {
+class MockRenderableObjectFactory : public RenderableObjectFactory {
+ public:
+ MOCK_CONST_METHOD2(CreateRenderableObject,
+ RenderableObject*(const std::uint32_t width,
+ const std::uint32_t height));
+};
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_FACTORY_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERER_EVENT_LISTENER_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERER_EVENT_LISTENER_H__
+
+#include <gmock/gmock.h>
+
+#include "mixer/constant.h"
+#include "mixer/renderer.h"
+
+namespace esplusplayer_ut {
+class MockRendererEventListener : public RendererEventListener {
+ public:
+ MOCK_METHOD1(OnRenderingRelease, bool(const BufferKeyType&));
+};
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERER_EVENT_LISTENER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEOPLANECOLLECTION_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEOPLANECOLLECTION_H__
+
+#include <gmock/gmock.h>
+
+#include "mixer/constant.h"
+#include "mixer/interfaces/videoplanecollection.h"
+
+namespace esplusplayer_ut {
+class MockVideoPlaneCollection : public VideoPlaneCollection {
+ public:
+ MOCK_CONST_METHOD0(GetVideoPlaneManipInfo,
+ const std::vector<VideoPlaneManipulableInfo>());
+};
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEOPLANECOLLECTION_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEO_PLANE_MANIPULATOR_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEO_PLANE_MANIPULATOR_H__
+
+#include <cstdint>
+
+#include "mixer/constant.h"
+#include "mixer/interfaces/videoplanemanipulator.h"
+
+namespace esplusplayer_ut {
+using namespace esplusplayer;
+
+class MockVideoPlaneManipulator : public VideoPlaneManipulator {
+ public:
+ MOCK_CONST_METHOD2(Do, bool(const VideoPlaneManipulableInfo&,
+ const VideoPlaneManipulableInfo&));
+};
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEO_PLANE_MANIPULATOR_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_MOCK_VIDEO_PLANE_SCALER_H__
+#define __ESPLUSPLAYER_MIXER_MOCK_VIDEO_PLANE_SCALER_H__
+
+#include <gmock/gmock.h>
+
+#include "mixer/constant.h"
+#include "mixer/interfaces/videoplanescaler.h"
+
+namespace esplusplayer {
+class MockVideoPlaneScaler : public VideoPlaneScaler {
+ public:
+ virtual ~MockVideoPlaneScaler() = default;
+ MOCK_CONST_METHOD0(GetScaleManipulator, const VideoPlaneManipulator* const());
+};
+} // namespace esplusplayer
+
+#endif // __ESPLUSPLAYER_MIXER_MOCK_VIDEO_PLANE_SCALER_H__
\ No newline at end of file
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_MOVABLE_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_MOVABLE_H__
+
+#include <cassert>
+
+namespace esplusplayer_ut {
+
+template <typename T>
+class Mover {
+ public:
+ Mover(T&& object) : object(std::move(object)), valid(true) {}
+
+ Mover(const Mover<T>& other)
+ : object(const_cast<T&&>(other.object)), valid(true) {
+ assert(other.valid);
+ other.valid = false;
+ }
+
+ Mover& operator=(const Mover& other) {
+ assert(other.valid);
+ object = const_cast<T&&>(other.object);
+ other.valid = false;
+ valid = true;
+ }
+
+ T& get() {
+ assert(valid);
+ return object;
+ }
+
+ const T& get() const {
+ assert(valid);
+ return *object;
+ }
+
+ private:
+ T object;
+ mutable bool valid;
+};
+
+template <typename T>
+inline Mover<T> Movable(T&& object) {
+ return Mover<T>(std::move(object));
+}
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_MOVABLE_H__
--- /dev/null
+#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_MOVE_OBJECT_WRAPPER_H__
+#define __ESPLUSPLAYER_MIXER_UT_MOCK_MOVE_OBJECT_WRAPPER_H__
+
+#include <iostream>
+
+namespace esplusplayer_ut {
+template <typename T>
+class MoveObjectWrapper {
+ public:
+ explicit MoveObjectWrapper(T* obj) : obj_(obj) {}
+ virtual ~MoveObjectWrapper() {
+ if (moved_ == false) {
+ delete obj_;
+ std::cout << "CALLED dtor" << std::endl;
+ }
+ }
+ MoveObjectWrapper<T>& operator=(T* obj) { obj_ = obj; }
+ T& Get() { return *obj_; }
+ T* Move() {
+ moved_ = true;
+ return obj_;
+ }
+
+ private:
+ T* obj_ = nullptr;
+ bool moved_ = false;
+};
+} // namespace esplusplayer_ut
+
+#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_MOVE_OBJECT_WRAPPER_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#ifndef __ESPLUSPLAYER_UT_INCLUDE_ESCOMMON_H__
+#define __ESPLUSPLAYER_UT_INCLUDE_ESCOMMON_H__
+
+#include <chrono>
+#include <cstdlib>
+#include <fstream>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "esplusplayer/espacket.h"
+#include "ut/include/appwindow.h"
+
+namespace pp = esplusplayer;
+
+namespace utils {
+inline bool Exists(const std::string &path) {
+ std::ifstream ifile(path);
+ return static_cast<bool>(ifile);
+}
+} // namespace utils
+
+namespace {
+class Environment {
+ public:
+ Environment() {
+ appwindow_.reset(new esplusplayer_ut::AppWindow(0, 0, 1920, 1080));
+ }
+ void *Window() { return appwindow_->GetWindow().obj; }
+ void *EcoreWindow() { return appwindow_->GetEcoreWL2Window(); }
+
+ private:
+ std::shared_ptr<esplusplayer_ut::AppWindow> appwindow_;
+};
+
+class ESPacketDownloader {
+ public:
+ static void Init() {
+ if (utils::Exists("/tmp/esdata/")) {
+ return;
+ }
+ std::cout << "Download ESData via http - path : /tmp/esdata" << std::endl;
+ int ret = std::system(
+ "function download(){ "
+ "if [[ $1 =~ '/' ]]; "
+ "then "
+ "for file in `curl http://168.219.244.23/WebAPITest/esdata$2$1 "
+ "| grep \"\\[\" "
+ "| grep -v '\\?\\|PAR\\|ICO|index' "
+ "| awk -F\"href=\" '{print $2}' "
+ "| awk -F\\\" '{print $2}'`; "
+ "do "
+ "download $file $2$1; "
+ "done "
+ "else "
+ "curl http://168.219.244.23/WebAPITest/esdata/$2$1 --create-dirs -o "
+ "/tmp/esdata$2$1; "
+ "fi "
+ "} && download /");
+ if (!ret) { // wget download success
+ return;
+ }
+ }
+};
+} // namespace
+
+namespace es {
+inline std::uint64_t ConvertNsToMs(std::uint64_t ms) {
+ constexpr std::uint64_t ns_unit = 1000000;
+ return ms / ns_unit;
+}
+struct Packet {
+ std::uint64_t pts;
+ std::uint64_t duration;
+ std::uint64_t size;
+ std::shared_ptr<char> data;
+
+ pp::EsPacketPtr MakeEsPacket(pp::StreamType type) {
+ return std::move(
+ pp::EsPacket::Create(type, data, static_cast<std::uint32_t>(size),
+ // pts/1000000,duration/1000000));
+ ConvertNsToMs(pts), ConvertNsToMs(duration)));
+ }
+
+ static pp::EsPacketPtr MakeEosPacket(pp::StreamType type) {
+ return std::move(pp::EsPacket::CreateEos(type));
+ }
+};
+
+struct CodecExtraData {
+ std::uint32_t size;
+ std::shared_ptr<char> data;
+};
+using PacketPtr = std::shared_ptr<Packet>;
+using CodecExtraDataPtr = std::shared_ptr<CodecExtraData>;
+
+template <typename TRACK, typename TRACKTYPE>
+struct VideoInfo {
+ std::string mimetype;
+ int width, height;
+ int maxwidth, maxheight;
+ int framerate_num, framerate_den;
+
+ void ExtractMediaInfoFrom(std::ifstream &stream) {
+ stream >> mimetype >> width >> height >> maxwidth >> maxheight >>
+ framerate_num >> framerate_den;
+ }
+
+ TRACK GetTrack() {
+ TRACK t;
+ t.active = true;
+ t.index = 0;
+ t.type = TRACKTYPE::kTrackTypeVideo;
+ t.mimetype = mimetype;
+ t.width = width;
+ t.height = height;
+ t.maxwidth = maxwidth;
+ t.maxheight = maxheight;
+ t.framerate_num = framerate_num;
+ t.framerate_den = framerate_den;
+ return t;
+ }
+};
+
+template <typename TRACK, typename TRACKTYPE>
+struct AudioInfo {
+ std::string mimetype;
+ int samplerate;
+ int channels;
+ int version;
+
+ void ExtractMediaInfoFrom(std::ifstream &stream) {
+ stream >> mimetype >> samplerate >> channels >> version;
+ }
+ TRACK GetTrack() {
+ TRACK t;
+ t.active = true;
+ t.index = 0;
+ t.type = TRACKTYPE::kTrackTypeAudio;
+ t.mimetype = mimetype;
+ t.sample_rate = samplerate;
+ t.channels = channels;
+ t.version = version;
+ return t;
+ }
+};
+
+template <typename TRACKTYPE>
+class StreamReader {
+ public:
+ using Ptr = std::shared_ptr<StreamReader<TRACKTYPE>>;
+ friend Ptr;
+ static Ptr Create(const std::string &dirpath, const TRACKTYPE &type) {
+ return Ptr(new StreamReader(dirpath, type));
+ }
+ virtual ~StreamReader() {
+ if (stream_.is_open()) {
+ stream_.close();
+ }
+ }
+
+ private:
+ explicit StreamReader(const std::string dirpath, const TRACKTYPE &type)
+ : type_(type) {
+ dirpath_ = "/tmp/esdata/" + dirpath;
+ if (utils::Exists(dirpath_) == false) {
+ throw std::runtime_error(dirpath_ + "doesn't exist");
+ }
+ stream_ = std::ifstream(dirpath_ + "/ESP.es", std::ifstream::binary);
+ }
+
+ public:
+ CodecExtraDataPtr GetExtraData() {
+ std::string path = dirpath_ + "/ESP.codec_extradata";
+ auto stream = std::ifstream(path, std::ifstream::binary);
+ auto extradata = CodecExtraDataPtr(new CodecExtraData);
+ stream.read(reinterpret_cast<char *>(&extradata->size),
+ sizeof extradata->size);
+ using DataPtr = std::shared_ptr<char>;
+ extradata->data = DataPtr(new char[extradata->size]);
+ stream.read(reinterpret_cast<char *>(extradata->data.get()),
+ extradata->size);
+ stream.close();
+ return extradata;
+ }
+
+ PacketPtr ReadNextPacket() {
+ if (stream_.eof()) {
+ return nullptr;
+ }
+ auto pkt = PacketPtr(new Packet);
+ stream_.read(reinterpret_cast<char *>(&pkt->pts), sizeof pkt->pts);
+ stream_.read(reinterpret_cast<char *>(&pkt->duration),
+ sizeof pkt->duration);
+ stream_.read(reinterpret_cast<char *>(&pkt->size), sizeof pkt->size);
+ using DataPtr = std::unique_ptr<char>;
+ pkt->data = DataPtr(new char[pkt->size]);
+ stream_.read(reinterpret_cast<char *>(pkt->data.get()), pkt->size);
+ return pkt;
+ }
+
+ template <typename T>
+ T GetMediaInfo() {
+ std::string path = dirpath_ + "/ESP.info";
+ T media;
+ auto stream = std::ifstream(path, std::ifstream::in);
+ media.ExtractMediaInfoFrom(stream);
+ stream.close();
+ return media;
+ }
+
+ TRACKTYPE GetTrackType() { return type_; }
+
+ private:
+ std::string dirpath_;
+ std::ifstream stream_;
+ TRACKTYPE type_;
+};
+
+} // namespace es
+#endif // __ESPLUSPLAYER_UT_INCLUDE_ESCOMMON_H__
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#ifndef __ESPLUSPLAYER_UT_INCLUDE_MOCK_VIDEOSINK_H__
+#define __ESPLUSPLAYER_UT_INCLUDE_MOCK_VIDEOSINK_H__
+
+#include "core/utils/plusplayer_log.h"
+#include "ivideo-sink.hpp"
+#include "utility.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+namespace utils {
+
+static int num_of_video_sinks = 0;
+class MockVideoSinkBuilder;
+
+class MockIVideoSink : public IVideoSink {
+ public:
+ MOCK_METHOD7(start,
+ errno_t(const struct wl_display* _display,
+ const struct tizen_video_object* _video_object,
+ const struct wl_surface* _video_surface,
+ const struct tizen_policy* _tizen_policy,
+ const int _desktop_id, const tztv_source_e _source_type,
+ const int _video_out_rsc_id));
+ MOCK_METHOD2(setPreResolution,
+ errno_t(const avoc_tpt_resolution_s& res_data, bool sync_call));
+ MOCK_METHOD3(setColorSpaceInfo,
+ errno_t(const avoc_video_data_format_e videoformat,
+ const avoc_color_space_info* color_space_info,
+ bool sync_call));
+ MOCK_METHOD2(setResolution,
+ errno_t(const avoc_tpt_resolution_s& res_data, bool sync_call));
+ MOCK_METHOD3(setMdcvMetadata, errno_t(const void* mdcv_metadata,
+ const int length, bool sync_call));
+ MOCK_METHOD2(setBitrateLevel,
+ errno_t(const int bitrate_level, bool sync_call));
+ MOCK_METHOD2(setHDRCompatInfo, errno_t(uint32_t data, bool sync_call));
+ MOCK_METHOD2(setWelcomeMode,
+ errno_t(const avoc_setting_on_off_e onoff, bool sync_call));
+ MOCK_METHOD3(setHdmiMetaData,
+ errno_t(const avoc_hdmi_meta_s* hdmi_metadata,
+ const avoc_setting_on_off_e onoff, bool sync_call));
+ MOCK_METHOD2(setLocalDimmingDisable,
+ errno_t(const int onoff, bool sync_call));
+ MOCK_METHOD1(notifyChannelChange, errno_t(bool sync_call));
+ MOCK_METHOD0(stop, errno_t());
+ MOCK_METHOD1(setSrcBufferType,
+ errno_t(enum VideoSink_SrcBufferTypes buffer_type));
+ MOCK_METHOD1(renderFrame, errno_t(void* frame_info));
+ MOCK_METHOD1(setKeepCropRatio, errno_t(bool onoff));
+ MOCK_METHOD1(setMute, errno_t(bool onoff));
+ MOCK_METHOD1(setSync, errno_t(bool onoff));
+ MOCK_METHOD1(setStill, errno_t(enum VideoSink_StillModes mode));
+ MOCK_METHOD1(setFlowMode, errno_t(bool onoff));
+ MOCK_METHOD4(setCrop, errno_t(int x, int y, int w, int h));
+ MOCK_METHOD1(getFramedropCount, errno_t(unsigned int* pDropCnt));
+ MOCK_METHOD4(isHwRotationSupported,
+ errno_t(int h_res, int v_res, int framerateHZ,
+ bool* hwRotation));
+
+ void Bind(std::unique_ptr<IVideoSink>&& org_video_sink) {
+ org_video_sink_ = std::move(org_video_sink);
+ ON_CALL(*this, start(_, _, _, _, _, _, _))
+ .WillByDefault(Invoke(org_video_sink_.get(), &IVideoSink::start));
+ ON_CALL(*this, setPreResolution(_, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setPreResolution));
+ ON_CALL(*this, setColorSpaceInfo(_, _, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setColorSpaceInfo));
+ ON_CALL(*this, setResolution(_, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setResolution));
+ ON_CALL(*this, setMdcvMetadata(_, _, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setMdcvMetadata));
+ ON_CALL(*this, setBitrateLevel(_, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setBitrateLevel));
+ ON_CALL(*this, setHDRCompatInfo(_, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setHDRCompatInfo));
+ ON_CALL(*this, setWelcomeMode(_, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setWelcomeMode));
+ ON_CALL(*this, setHdmiMetaData(_, _, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setHdmiMetaData));
+ ON_CALL(*this, setLocalDimmingDisable(_, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setLocalDimmingDisable));
+ ON_CALL(*this, notifyChannelChange(_))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::notifyChannelChange));
+ ON_CALL(*this, stop())
+ .WillByDefault(Invoke(org_video_sink_.get(), &IVideoSink::stop));
+ ON_CALL(*this, setSrcBufferType(_))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setSrcBufferType));
+ ON_CALL(*this, renderFrame(_))
+ .WillByDefault(Invoke(org_video_sink_.get(), &IVideoSink::renderFrame));
+ ON_CALL(*this, setKeepCropRatio(_))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::setKeepCropRatio));
+ ON_CALL(*this, setMute(_))
+ .WillByDefault(Invoke(
+ [this](bool on)->int{ int ret = org_video_sink_.get()->setMute(on); if(on==false){isFirstVideoUnmute=true;};return ret;}));
+ ON_CALL(*this, setSync(_))
+ .WillByDefault(Invoke(org_video_sink_.get(), &IVideoSink::setSync));
+ ON_CALL(*this, setStill(_))
+ .WillByDefault(Invoke(org_video_sink_.get(), &IVideoSink::setStill));
+ ON_CALL(*this, setFlowMode(_))
+ .WillByDefault(Invoke(org_video_sink_.get(), &IVideoSink::setFlowMode));
+ ON_CALL(*this, setCrop(_, _, _, _))
+ .WillByDefault(Invoke(org_video_sink_.get(), &IVideoSink::setCrop));
+ ON_CALL(*this, getFramedropCount(_))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::getFramedropCount));
+ ON_CALL(*this, isHwRotationSupported(_, _, _, _))
+ .WillByDefault(
+ Invoke(org_video_sink_.get(), &IVideoSink::isHwRotationSupported));
+ }
+
+ friend class MockVideoSinkBuilder;
+ MockIVideoSink() {
+ isFirstVideoUnmute = false;
+ num_of_video_sinks++;
+ LOG_ERROR("num_of_video_sinks: %d, new_video_sink: %p", num_of_video_sinks,
+ this);
+ };
+ ~MockIVideoSink() {
+ num_of_video_sinks--;
+ LOG_ERROR("num_of_video_sinks: %d, new_video_sink: %p", num_of_video_sinks,
+ this);
+ };
+
+ public:
+ std::unique_ptr<IVideoSink> org_video_sink_;
+ Watcher<bool> isFirstVideoUnmute;
+};
+
+class MockVideoSinkBuilder : public IVideoSinkBuilder {
+ public:
+ IVideoSink* buildNewVideoSinkHandle() override {
+ LOG_ERROR("num_of_video_sinks: %d, new_video_sink: %p", num_of_video_sinks,
+ new_video_sink);
+ assert(new_video_sink);
+ std::unique_ptr<IVideoSink> org_video_sink =
+ std::unique_ptr<IVideoSink>(org_builder->buildNewVideoSinkHandle());
+ new_video_sink->Bind(std::move(org_video_sink));
+ return new_video_sink;
+ }
+ static std::shared_ptr<MockVideoSinkBuilder> injectNewBuilder() {
+ // create and delete IVideoSink instance once to load original builder
+ // inside IVideoSink class
+ if (mock_builder != nullptr) {
+ LOG_ERROR("mock_builder already created");
+ return mock_builder;
+ }
+
+ IVideoSink* org_video_sink = IVideoSink::createInstance();
+ delete org_video_sink;
+
+ org_builder = std::shared_ptr<IVideoSinkBuilder>(IVideoSink::builder);
+ mock_builder =
+ std::make_shared<MockVideoSinkBuilder>();
+ IVideoSink::injectBuilder((IVideoSinkBuilder*)mock_builder.get());
+ return mock_builder;
+ }
+
+ void prepareNewVideoSink() {
+ LOG_ERROR("num_of_video_sinks: %d, new_video_sink: %p", num_of_video_sinks,
+ new_video_sink);
+ new_video_sink = new (std::nothrow)NiceMock<MockIVideoSink>();
+ }
+
+ MockVideoSinkBuilder(){};
+ ~MockVideoSinkBuilder(){};
+ static std::shared_ptr<IVideoSinkBuilder> org_builder;
+ static std::shared_ptr<MockVideoSinkBuilder> mock_builder;
+ static NiceMock<MockIVideoSink>* new_video_sink;
+};
+std::shared_ptr<IVideoSinkBuilder> MockVideoSinkBuilder::org_builder = nullptr;
+std::shared_ptr<MockVideoSinkBuilder> MockVideoSinkBuilder::mock_builder = nullptr;
+NiceMock<MockIVideoSink>* MockVideoSinkBuilder::new_video_sink = nullptr;
+
+} // namespace utils
+
+#endif // __ESPLUSPLAYER_UT_INCLUDE_MOCK_VIDEOSINK_H__
--- /dev/null
+//
+// @ Copyright [2019] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#ifndef __ESPLUSPLAYER_UT_INCLUDE_PLUSPLAYER_UTILITY_H__
+#define __ESPLUSPLAYER_UT_INCLUDE_PLUSPLAYER_UTILITY_H__
+
+#include <Ecore_Evas.h>
+
+#include "gmock/gmock.h"
+
+#include "core/utils/plusplayer_log.h"
+#include "esplusplayer_capi/esplusplayer_capi.h"
+#include "mixer_capi/mixer_capi.h"
+#include "esplusplayer/types/display.h"
+#include "ut/include/appwindow.h"
+
+#include <condition_variable>
+#include <functional>
+#include <future>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <vector>
+
+#define DEFAULT_TIMEOUT_FOR_PREPARING (95 * 1000)
+#define DEFAULT_TIMEOUT_FOR_SEEKING (25 * 1000)
+#define EOS_WAIT_TIME 10000
+
+class IAudioControl;
+class DiagnosisAudioControl;
+
+namespace utils {
+class Utility;
+using evas_h = Evas_Object*;
+
+enum EsType {
+ kAudio = 0x01,
+ kVideo = 0x02,
+ kBoth = (kAudio | kVideo)
+};
+
+class Utility {
+ public:
+ static Utility& Instance();
+ static void ThreadSleep(long ms);
+ static void Kill();
+ static std::string getKeyboardInput(int timeoutMsec);
+ static std::string Execute(
+ std::string cmd); // execute system command and get back result string
+ static std::vector<std::string> Split(std::string in, char delimiter);
+ static bool IsChipset(const char* name);
+ static esplusplayer_video_mime_type ConvertToMimeType(
+ const std::string &mimetype) {
+ static std::unordered_map<std::string, esplusplayer_video_mime_type>
+ kMimeTypeMap = {
+ {"video/x-h264", ESPLUSPLAYER_VIDEO_MIME_TYPE_H264},
+ {"video/x-h265", ESPLUSPLAYER_VIDEO_MIME_TYPE_HEVC},
+ {"video/x-vp9", ESPLUSPLAYER_VIDEO_MIME_TYPE_VP9},
+ };
+ if (kMimeTypeMap.count(mimetype) == 0)
+ return ESPLUSPLAYER_VIDEO_MIME_TYPE_UNKNOWN;
+ return kMimeTypeMap.at(mimetype);
+ }
+
+ static esplusplayer_audio_mime_type ConvertToMimeType(
+ const std::string &mimetype, const std::string &format) {
+ if (mimetype.compare("audio/x-raw") == 0) {
+ static std::unordered_map<std::string, esplusplayer_audio_mime_type>
+ kPcmFormatMap = {
+ {"S16LE", ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_S16LE},
+ };
+ if (kPcmFormatMap.count(format) == 0)
+ return ESPLUSPLAYER_AUDIO_MIME_TYPE_UNKNOWN;
+ return kPcmFormatMap.at(format);
+ }
+
+ static std::unordered_map<std::string, esplusplayer_audio_mime_type>
+ kMimeTypeMap = {
+ {"audio/x-opus", ESPLUSPLAYER_AUDIO_MIME_TYPE_OPUS},
+ };
+ if (kMimeTypeMap.count(mimetype) == 0)
+ return ESPLUSPLAYER_AUDIO_MIME_TYPE_UNKNOWN;
+ return kMimeTypeMap.at(mimetype);
+ }
+ virtual ~Utility();
+
+ const char* GetCurrentTestName(void);
+
+#ifndef IS_AUDIO_PRODUCT
+
+#if 0
+ esplusplayer::PlusPlayer::Ptr GetOpenedMixPlusPlayer(std::string& uri,
+ esplusplayer::Mixer* mixer,
+ esplusplayer::Geometry& roi);
+
+ esplusplayer::PlusPlayer::Ptr GetPreparedMixPlusPlayer(
+ std::string& uri, esplusplayer::Mixer* mixer, esplusplayer::Geometry& roi);
+
+ esplusplayer::PlusPlayer::Ptr GetStartedMixPlusPlayer(
+ std::string& uri, esplusplayer::Mixer* mixer, esplusplayer::Geometry& roi);
+#endif
+
+ esplusplayer_handle GetOpenedMixESPP(mixer_handle mixer,
+ esplusplayer::Geometry& roi);
+
+ esplusplayer_handle GetPreparedMixESPP(std::string& uri, mixer_handle mixer,
+ esplusplayer::Geometry& roi);
+
+ esplusplayer_handle GetStartedMixESPP(std::string& uri, mixer_handle mixer,
+ esplusplayer::Geometry& roi);
+#endif
+ esplusplayer_handle GetOpenedESPP(esplusplayer::Geometry& roi);
+
+ esplusplayer_handle GetPreparedESPP(std::string& uri,
+ esplusplayer::Geometry& roi);
+
+ esplusplayer_handle GetStartedESPP(std::string& uri, esplusplayer::Geometry& roi);
+
+ bool PrepareESPP(esplusplayer_handle player, std::string& uri,
+ EsType type = EsType::kBoth);
+
+ void FeedingEsPacket(esplusplayer_handle player,
+ const esplusplayer_stream_type type,
+ const std::string& uri);
+
+ void DestroyESPP(esplusplayer_handle player);
+
+ evas_h GetWindow() const;
+
+ int CaptureYUV(int capture_width, int capture_height, char* ybuff,
+ char* cbuff, int ysize, int csize,
+ std::string result_file_path);
+
+ int CaptureJPG(const int capture_width, const int capture_height, char* ybuff,
+ char* cbuff, const int ysize, const int csize,
+ std::string img_file_path);
+
+ int CheckYUV(int x, int y);
+ bool IsAudioDisconnected();
+ bool IsAudioConnected() {return !IsAudioDisconnected();}
+ bool IsAudioMute();
+ bool IsAudioUnmute() {return !IsAudioMute();}
+ int GetPlayingTimeForManualTestInMsec();
+
+ Utility(const Utility& rhs) = delete;
+ Utility& operator=(const Utility& rhs) = delete;
+
+ private:
+ explicit Utility();
+
+ private:
+ std::unique_ptr<esplusplayer_ut::AppWindow> appwindow_;
+ IAudioControl* audioControl = nullptr;
+ DiagnosisAudioControl* audioDiagnoser = nullptr;
+};
+
+enum class WatcherStatus {
+ kSuccess,
+ kFail
+};
+
+enum PrepareStatus {
+ kReady,
+ kSuccess,
+ kFail
+};
+
+static const int TIMEOUT_10_SEC = 10 * 1000;
+
+template <typename T>
+class Watcher {
+ public:
+ Watcher() = default;
+ ~Watcher() {
+ this->SendSignalToWait();
+ std::unique_lock<std::mutex>(this->destructor_mutex_);
+ }
+
+ explicit Watcher(T&& t) : value_(t) { LOGD("object : 0x%p", this); }
+ Watcher(Watcher<T>&& t) : value_(std::move(std::forward<T>(t))) {
+ LOGD("object&& : 0x%p", this);
+ }
+
+ std::future<WatcherStatus> WaitForChange(T expected, int timeout_ms) {
+ LOG_DEBUG("WaitForChange ::Start ");
+ return std::async(std::launch::async, &Watcher::Evaluate, std::ref(*this),
+ expected, timeout_ms);
+ }
+
+ void SendSignalToWait() { cv_.notify_one(); }
+
+ T GetValue() { return value_; }
+
+ Watcher& operator=(const T& rhs) {
+ std::unique_lock<std::mutex> locker(this->mutex_);
+ value_ = rhs;
+ this->SendSignalToWait();
+ // LOGD("operator=(const T& rhs)");
+ return *this;
+ }
+
+ Watcher& operator=(const Watcher& rhs) {
+ std::unique_lock<std::mutex> locker(this->mutex_);
+ value_ = rhs.value_;
+ this->SendSignalToWait();
+ // LOGD("operator=(const Watcher& rhs)");
+ return *this;
+ }
+
+ bool operator==(const Watcher& rhs) const {
+ return this->value_ == rhs.value_;
+ }
+
+ bool operator!=(const Watcher& rhs) const { return !(this == rhs); }
+
+ bool operator==(const T& rhs) const { return this->value_ == rhs; }
+
+ bool operator!=(const T& rhs) const { return !(this->value_ == rhs); }
+
+ bool operator<(const T& rhs) const { return this->value_ < rhs; }
+
+ bool operator>(const T& rhs) const { return this->value_ > rhs; }
+
+ bool operator>=(const T& rhs) const { return this->value_ >= rhs; }
+
+ bool operator<=(const T& rhs) const { return this->value_ <= rhs; }
+
+ operator T() { return value_; }
+
+ private:
+ static WatcherStatus Evaluate(Watcher<T>& w, T expected, int timeout_ms) {
+ std::unique_lock<std::mutex>(w.destructor_mutex_);
+ auto until = std::chrono::system_clock::now() +
+ std::chrono::milliseconds(timeout_ms);
+ std::cv_status status = std::cv_status::timeout;
+
+ while (true) {
+ std::unique_lock<std::mutex> locker(w.mutex_);
+
+ if (w.value_ == expected) {
+ return WatcherStatus::kSuccess;
+ }
+
+ status = w.cv_.wait_until(locker, until);
+
+ if (status == std::cv_status::timeout) {
+ return WatcherStatus::kFail;
+ }
+ }
+
+ return WatcherStatus::kFail;
+ }
+
+ private:
+ T value_;
+ std::mutex mutex_;
+ std::mutex destructor_mutex_;
+ std::condition_variable cv_;
+};
+
+} // namespace utils
+
+#endif // __ESPLUSPLAYER_UT_INCLUDE_PLUSPLAYER_UTILITY_H__
--- /dev/null
+#include "esplusplayer/tclist.h"
+
+#include <unistd.h>
+#include <filesystem>
+#include <fstream>
+#include <memory>
+
+#include "core/utils/plusplayer_log.h"
+
+static std::unique_ptr<ContentsRoot> ptr = nullptr;
+ContentsRoot& ContentsRoot::Instance() {
+ if (ptr.get() == nullptr) ptr.reset(new ContentsRoot());
+ return *(ptr.get());
+}
+
+ContentsRoot::ContentsRoot() { root_abs_path = "/tmp/esdata/"; }
+
+bool ContentsRoot::IsMounted() {
+ if (std::filesystem::exists("/tmp/esdata/bunny") == false) {
+ LOG_ERROR("/tmp/esdata/bunny doesn't exist");
+ return false;
+ }
+ return true;
+}
+
+bool ContentsRoot::MountTcDirectory() {
+ static const std::string tc_tmp_dir = "/tmp/esdata/";
+ LOG_DEBUG("MountTCDirectory for CG() system result : %d",
+ system("mkdir -p /tmp/esdata/"));
+ LOG_DEBUG("MountTCDirectory for CG() system result : %d",
+ system("mount -t cifs -o ro,username=cloudgame,pass=1q2w3e4r "
+ "//168.219.244.23/cloudgame/www/esdata /tmp/esdata"));
+ if (IsMounted()) {
+ root_abs_path = tc_tmp_dir;
+ return true;
+ }
+
+ return UseUsb();
+}
+
+void ContentsRoot::UnMountTcDirectory() {
+ if (IsMounted()) {
+ LOG_DEBUG("UnMountTcDirectory() system result : %d",
+ std::system("/usr/bin/umount -f -l -a -t cifs"));
+ }
+ root_abs_path = "";
+}
+
+bool ContentsRoot::UseUsb() {
+ const std::string tc_usb_dir1 = "/opt/media/USBDriveA1/esdata/";
+ const std::string tc_usb_dir2 = "/opt/media/USBDriveA/esdata/";
+ int ret = access(tc_usb_dir1.c_str(), F_OK);
+ if (ret == 0) {
+ root_abs_path = tc_usb_dir1;
+ LOG_DEBUG("UseUsb for test : %d, %s", ret, root_abs_path.c_str());
+ return true;
+ }
+
+ ret = access(tc_usb_dir2.c_str(), F_OK);
+ if (ret == 0) {
+ root_abs_path = tc_usb_dir2;
+ LOG_DEBUG("UseUsb for test : %d, %s", ret, root_abs_path.c_str());
+ return true;
+ }
+
+ LOG_ERROR("No USB Directory( %s, %s )", tc_usb_dir1.c_str(),
+ tc_usb_dir2.c_str());
+ return false;
+}
--- /dev/null
+
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <stdio.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <future>
+#include <map>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include "esplusplayer_capi/esplusplayer_capi.h"
+#include "gmock/gmock.h"
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+#include "ut/include/appwindow.h"
+#include "ut/include/esplusplayer/eseventlistener.hpp"
+#include "ut/include/esplusplayer/esreader.hpp"
+#include "ut/include/esplusplayer/tclist.h"
+#include "ut/include/streamreader.hpp"
+
+using namespace esplusplayer;
+
+class EsTest : public ::testing::Test {
+ public:
+ EsTest() { std::cout << "EsTest()" << std::endl; }
+ ~EsTest() { std::cout << "~EsTest()" << std::endl; }
+
+ virtual void SetUp() override { std::cout << "SetUp()" << std::endl; }
+
+ virtual void TearDown() override { std::cout << "TearDown()" << std::endl; }
+};
+
+class EsBasicTest : public ::testing::TestWithParam<std::string> {
+ public:
+ EsBasicTest() { std::cout << "EsBasicTest()" << std::endl; }
+ ~EsBasicTest() { std::cout << "~EsBasicTest()" << std::endl; }
+
+ static void SetUpTestCase() {
+ gst_init_check(nullptr, nullptr, nullptr);
+ window_ = new Environment();
+ ESPacketDownloader::Init();
+ esplayer_ = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer_);
+ std::cout << "SetUpTestCase()" << std::endl;
+ }
+ static void TearDownTestCase() {
+ if (window_) {
+ delete window_;
+ window_ = nullptr;
+ }
+ esplusplayer_destroy(esplayer_);
+ esplayer_ = nullptr;
+ std::cout << "TearDownTestCase()" << std::endl;
+ }
+
+ virtual void SetUp() override {
+ uri_ = GetParam();
+ std::cout << "uri_: " << uri_ << std::endl;
+ video_reader_ =
+ new EsStreamReader(uri_ + "video/", ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ audio_reader_ =
+ new EsStreamReader(uri_ + "audio/", ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+ callback_ =
+ new EsPlayerEventCallback(esplayer_, video_reader_, audio_reader_);
+ callback_->SetCallback();
+
+ std::cout << "SetUp()" << std::endl;
+ }
+
+ virtual void TearDown() override {
+ if (nullptr != esplayer_) {
+ ASSERT_EQ(esplusplayer_close(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ }
+ delete callback_;
+ delete video_reader_;
+ delete audio_reader_;
+
+ std::cout << "TearDown()" << std::endl;
+ }
+
+ public:
+ std::string uri_;
+ EsStreamReader* video_reader_;
+ EsStreamReader* audio_reader_;
+ static esplusplayer_handle esplayer_;
+ EsPlayerEventCallback* callback_;
+ static Environment* window_;
+};
+Environment* EsBasicTest::window_ = nullptr;
+esplusplayer_handle EsBasicTest::esplayer_ = nullptr;
+#if 0
+TEST_F(EsTest, vdapi_basic_esplusplayer_create_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_open_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_close_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_stop_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_stop(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_get_error_string_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ const char* error = new char[100];
+ error =
+ esplusplayer_get_error_string(ESPLUSPLAYER_ERROR_TYPE_NOT_SUPPORTED_FILE);
+ std::cout << "error type" << error << std::endl;
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_set_tz_use_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_tz_use(esplayer, true),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_set_video_frame_buffer_type_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_video_frame_buffer_type(
+ esplayer, ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_NONE),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_set_buffer_size_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ esplusplayer_set_buffer_size(esplayer,
+ ESPLUSPLAYER_BUFFER_AUDIO_MAX_BYTE_SIZE, 10240);
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_submit_encrypted_packet_p_1) {
+ esplusplayer_handle esplayer = NULL;
+ ASSERT_EQ(esplusplayer_submit_encrypted_packet(esplayer, NULL, NULL),
+ ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_submit_trust_zone_packet_p_1) {
+ esplusplayer_handle esplayer = NULL;
+ ASSERT_EQ(esplusplayer_submit_trust_zone_packet(esplayer, NULL, 0),
+ ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_submit_eos_packet_p_1) {
+ esplusplayer_handle esplayer = NULL;
+ ASSERT_EQ(
+ esplusplayer_submit_eos_packet(esplayer, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_decoded_buffer_destroy_p_1) {
+ esplusplayer_handle esplayer = NULL;
+ ASSERT_EQ(esplusplayer_decoded_buffer_destroy(esplayer, NULL),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_set_unlimited_max_buffer_mode_p_1) {
+ esplusplayer_handle esplayer = NULL;
+ ASSERT_EQ(esplusplayer_set_unlimited_max_buffer_mode(esplayer),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_PARAMETER);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_get_state_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::cout << "get state" << std::endl;
+ esplusplayer_state ret = ESPLUSPLAYER_STATE_NONE;
+ ret = esplusplayer_get_state(esplayer);
+ ASSERT_EQ(ret, ESPLUSPLAYER_STATE_IDLE);
+ std::cout << "get state" << ret << std::endl;
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest, vdapi_basic_esplusplayer_set_low_latency_mode_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ esplusplayer_set_low_latency_mode(esplayer,
+ ESPLUSPLAYER_LOW_LATENCY_MODE_NONE);
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(EsTest,
+ vdapi_basic_esplusplayer_esplusplayer_set_video_frame_peek_mode_p_1) {
+ esplusplayer_handle esplayer = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer);
+ ASSERT_EQ(esplusplayer_open(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ esplusplayer_set_video_frame_peek_mode(esplayer);
+ ASSERT_EQ(esplusplayer_close(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_destroy(esplayer), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_audio_mute_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_set_audio_mute(esplayer_, true),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_app_info_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ char a[30], appid[] = "9Ur5IzDKqV.TizenYouTube";
+ char v[20], version[] = "3.0";
+ char t[20], type[] = "MSE";
+ esplusplayer_app_info appinfo;
+ strncpy(a, appid, sizeof(a) - 1);
+ a[sizeof(a) - 1] = 0x00;
+ strncpy(v, version, sizeof(v) - 1);
+ v[sizeof(v) - 1] = 0x00;
+ strncpy(t, type, sizeof(t) - 1);
+ t[sizeof(t) - 1] = 0x00;
+ appinfo.id = a;
+ appinfo.version = v;
+ appinfo.type = t;
+ ASSERT_EQ(esplusplayer_set_app_info(esplayer_, &appinfo),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_app_info_ex_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ char a[30], appid[] = "org.tizen.netflix-app";
+ char v[20], version[] = "5.3.30";
+ char t[20], type[] = "PVOD";
+ char r[20], runtitle[] = "Netflix";
+ esplusplayer_app_info_ex appinfo;
+ strncpy(a, appid, sizeof(a) - 1);
+ a[sizeof(a) - 1] = 0x00;
+ strncpy(v, version, sizeof(v) - 1);
+ v[sizeof(v) - 1] = 0x00;
+ strncpy(t, type, sizeof(t) - 1);
+ t[sizeof(t) - 1] = 0x00;
+ strncpy(r, runtitle, sizeof(r) - 1);
+ r[sizeof(r) - 1] = 0x00;
+ appinfo.id = a;
+ appinfo.version = v;
+ appinfo.type = t;
+ appinfo.runtitle = r;
+ ASSERT_EQ(esplusplayer_set_app_info_ex(esplayer_, &appinfo),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_close_p_2) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_prepare_async_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_start_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_pause_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ std::cout << "Pause player" << std::endl;
+ ASSERT_EQ(esplusplayer_pause(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_resume_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ std::cout << "Pause player" << std::endl;
+ ASSERT_EQ(esplusplayer_pause(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ std::cout << "resume player" << std::endl;
+ ASSERT_EQ(esplusplayer_resume(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_deactive_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(esplusplayer_deactivate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_deactivate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+#endif
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_active_deactivate_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ esplusplayer_audio_stream_info audio_stream1;
+ audio_stream1.codec_data = nullptr;
+ audio_stream1.codec_data_length = 0;
+ audio_stream1.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_PCM_S16LE;
+ audio_stream1.bitrate = 0;
+ audio_stream1.sample_rate = 48000;
+ audio_stream1.channels = 2;
+
+ esplusplayer_set_audio_stream_info(esplayer_, &audio_stream1);
+
+
+ esplusplayer_set_low_latency_mode(esplayer_, ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_PREROLL);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(esplusplayer_deactivate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ esplusplayer_audio_stream_info audio_stream;
+ audio_stream.codec_data = nullptr;
+ audio_stream.codec_data_length = 0;
+ audio_stream.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_EAC3;
+ audio_stream.bitrate = 448;
+ audio_stream.sample_rate = 48000;
+ audio_stream.channels = 6;
+
+ esplusplayer_set_audio_stream_info(esplayer_, &audio_stream);
+
+
+ //audio_reader_->ResetReader();
+ ASSERT_EQ(esplusplayer_activate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+#if 0
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_active_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(esplusplayer_deactivate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_deactivate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ video_reader_->ResetReader();
+ audio_reader_->ResetReader();
+ ASSERT_EQ(esplusplayer_activate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_activate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_get_playing_time_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ uint64_t cur_time1 = 0;
+ uint64_t cur_time2 = 0;
+ ASSERT_EQ(esplusplayer_get_playing_time(esplayer_, &cur_time1),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::cout << "current time is" << cur_time1 << std::endl;
+ ASSERT_EQ(esplusplayer_get_playing_time(esplayer_, &cur_time2),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::cout << "current time is" << cur_time2 << std::endl;
+ ASSERT_LE(cur_time1, cur_time2);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_seek_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(esplusplayer_seek(esplayer_, 0), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_playback_rate_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(esplusplayer_set_playback_rate(esplayer_, 2.0, true),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_flush_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(esplusplayer_flush(esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_flush(esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_resume(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_volume_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ int vol = 80;
+ int vol1 = 0;
+ ASSERT_EQ(esplusplayer_set_volume(esplayer_, vol),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(esplusplayer_get_volume(esplayer_, &vol1),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(vol, vol1);
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_get_volume_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ int vol = 80;
+ int vol1 = 0;
+ ASSERT_EQ(esplusplayer_set_volume(esplayer_, vol),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(esplusplayer_get_volume(esplayer_, &vol1),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(vol, vol1);
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_get_adaptive_info_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ uint64_t count = 0;
+ ASSERT_EQ(esplusplayer_get_adaptive_info(
+ esplayer_, static_cast<void*>(&count),
+ ESPLUSPLAYER_ADAPT_INFO_TYPE_DROPPED_FRAMES),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::cout << "count = " << count << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_submit_data_type_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_submit_data_type(
+ esplayer_, ESPLUSPLAYER_SUBMIT_DATA_TYPE_CLEAN_DATA),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_render_video_frame_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_video_frame_peek_mode(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ ASSERT_EQ(esplusplayer_pause(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_seek(esplayer_, 0), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ ASSERT_EQ(esplusplayer_render_video_frame(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ ASSERT_EQ(esplusplayer_resume(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_render_time_offset_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_set_low_latency_mode(
+ esplayer_, ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_PREROLL),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ int64_t set_offset = 10;
+ ASSERT_EQ(esplusplayer_set_render_time_offset(
+ esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO, set_offset),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ int64_t get_offset = 0;
+ ASSERT_EQ(esplusplayer_get_render_time_offset(
+ esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO, &get_offset),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(set_offset, get_offset);
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_render_time_offset_p_2) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_set_low_latency_mode(
+ esplayer_, ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_PREROLL),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ int64_t set_offset = 10;
+ ASSERT_EQ(esplusplayer_set_render_time_offset(
+ esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO, set_offset),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ int64_t get_offset = 0;
+ ASSERT_EQ(esplusplayer_get_render_time_offset(
+ esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO, &get_offset),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(set_offset, get_offset);
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_render_time_offset_n_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ int64_t set_offset = 10;
+ ASSERT_EQ(esplusplayer_set_render_time_offset(
+ esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO, set_offset),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ int64_t get_offset = 0;
+ ASSERT_EQ(esplusplayer_get_render_time_offset(
+ esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO, &get_offset),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_render_time_offset_n_2) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_set_low_latency_mode(
+ esplayer_, ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ int64_t set_offset = 10;
+ ASSERT_EQ(esplusplayer_set_render_time_offset(
+ esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO, set_offset),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ int64_t get_offset = 0;
+ ASSERT_EQ(esplusplayer_get_render_time_offset(
+ esplayer_, ESPLUSPLAYER_STREAM_TYPE_VIDEO, &get_offset),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_switch_audio_stream_onthefly_p_1) {
+ if (uri_.find("aac") == std::string::npos) return;
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ esplusplayer_audio_stream_info audio_stream;
+ memset(&audio_stream, 0, sizeof(esplusplayer_audio_stream_info));
+ audio_stream.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_AC3;
+ audio_stream.sample_rate = 48000;
+ audio_stream.channels = 2;
+ ASSERT_EQ(esplusplayer_switch_audio_stream_onthefly(esplayer_, &audio_stream),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::cout << "set audio stream info after flush" << std::endl;
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_switch_audio_stream_onthefly_n_1) {
+ if (uri_.find("aac") == std::string::npos) return;
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+
+ esplusplayer_audio_stream_info audio_stream;
+ memset(&audio_stream, 0, sizeof(esplusplayer_audio_stream_info));
+ audio_stream.mime_type = ESPLUSPLAYER_AUDIO_MIME_TYPE_OPUS;
+ audio_stream.sample_rate = 48000;
+ audio_stream.channels = 2;
+ ASSERT_EQ(esplusplayer_switch_audio_stream_onthefly(esplayer_, &audio_stream),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_video_codec_type_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_video_codec_type(esplayer_,
+ ESPLUSPLAYER_VIDEO_CODEC_TYPE_HW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_set_audio_codec_type_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_audio_codec_type(esplayer_,
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_HW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_get_virtual_rsc_id_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+
+ int virtual_id = -1;
+ ASSERT_EQ(esplusplayer_get_virtual_rsc_id(
+ esplayer_, ESPLUSPLAYER_RSC_TYPE_VIDEO_RENDERER, &virtual_id),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(virtual_id != -1);
+ std::cout << "BasicTest, Play, END" << std::endl;
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_init_audio_easing_info_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ const esplusplayer_target_audio_easing_info init_info = {
+ 0, 1000, ESPLUSPLAYER_AUDIO_EASING_LINEAR};
+ ASSERT_EQ(esplusplayer_init_audio_easing_info(esplayer_, 100, 0, &init_info),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_init_audio_easing_info_n_1) {
+ const esplusplayer_target_audio_easing_info init_info = {
+ 0, 1000, ESPLUSPLAYER_AUDIO_EASING_LINEAR};
+ ASSERT_EQ(esplusplayer_init_audio_easing_info(esplayer_, 100, 0, &init_info),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_update_audio_easing_info_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ const esplusplayer_target_audio_easing_info init_info = {
+ 0, 1000, ESPLUSPLAYER_AUDIO_EASING_LINEAR};
+ ASSERT_EQ(esplusplayer_init_audio_easing_info(esplayer_, 100, 0, &init_info),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ const esplusplayer_target_audio_easing_info update_info = {
+ 100, 1000, ESPLUSPLAYER_AUDIO_EASING_LINEAR};
+ ASSERT_EQ(esplusplayer_update_audio_easing_info(esplayer_, &update_info),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_update_audio_easing_info_n_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ const esplusplayer_target_audio_easing_info update_info = {
+ 100, 1000, ESPLUSPLAYER_AUDIO_EASING_LINEAR};
+ ASSERT_EQ(esplusplayer_update_audio_easing_info(esplayer_, &update_info),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_get_audio_easing_info_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ uint32_t init_volume = 100;
+ uint32_t init_elapsed_time = 50;
+ const esplusplayer_target_audio_easing_info info = {
+ 50, 1000, ESPLUSPLAYER_AUDIO_EASING_LINEAR};
+ ASSERT_EQ(esplusplayer_init_audio_easing_info(esplayer_, init_volume,
+ init_elapsed_time, &info),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ uint32_t cur_volume = 0;
+ uint32_t elapsed_time = 0;
+ esplusplayer_target_audio_easing_info get_info;
+ ASSERT_EQ(esplusplayer_get_audio_easing_info(esplayer_, &cur_volume,
+ &elapsed_time, &get_info),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(init_volume, cur_volume);
+ EXPECT_EQ(init_elapsed_time, elapsed_time);
+ EXPECT_EQ(info.volume, get_info.volume);
+ EXPECT_EQ(info.duration, get_info.duration);
+ EXPECT_EQ(info.type, get_info.type);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_get_audio_easing_info_n_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ uint32_t cur_volume = 0;
+ uint32_t elapsed_time = 0;
+ esplusplayer_target_audio_easing_info get_info;
+ ASSERT_EQ(esplusplayer_get_audio_easing_info(esplayer_, &cur_volume,
+ &elapsed_time, &get_info),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_start_audio_easing_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+
+ const esplusplayer_target_audio_easing_info info = {
+ 50, 1000, ESPLUSPLAYER_AUDIO_EASING_LINEAR};
+ ASSERT_EQ(esplusplayer_init_audio_easing_info(esplayer_, 100, 0, &info),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_start_audio_easing(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_start_audio_easing_n_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(esplusplayer_start_audio_easing(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_stop_audio_easing_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+
+ const esplusplayer_target_audio_easing_info info = {
+ 50, 1000, ESPLUSPLAYER_AUDIO_EASING_LINEAR};
+ ASSERT_EQ(esplusplayer_init_audio_easing_info(esplayer_, 100, 0, &info),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_start_audio_easing(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(esplusplayer_stop_audio_easing(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_P(EsBasicTest, vdapi_basic_esplusplayer_stop_audio_easing_n_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(esplusplayer_stop_audio_easing(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+}
+
+TEST_P(EsBasicTest,
+ vdapi_basic_esplusplayer_set_alternative_audio_resource_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+
+ ASSERT_EQ(esplusplayer_set_audio_codec_type(esplayer_,
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_alternative_audio_resource(
+ esplayer_, ESPLUSPLAYER_AUDIO_RESOURCE_SIMPLE_MIX_OUT),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+}
+
+TEST_P(EsBasicTest,
+ vdapi_basic_esplusplayer_set_alternative_audio_resource_p_2) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ ASSERT_EQ(esplusplayer_deactivate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ ASSERT_EQ(esplusplayer_set_audio_codec_type(esplayer_,
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_alternative_audio_resource(
+ esplayer_, ESPLUSPLAYER_AUDIO_RESOURCE_SIMPLE_MIX_OUT),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ audio_reader_->ResetReader();
+ ASSERT_EQ(esplusplayer_activate(esplayer_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+}
+#endif
+INSTANTIATE_TEST_CASE_P(ESPlusplayer, EsBasicTest,
+ ::testing::ValuesIn(es_tc::tc_list));
--- /dev/null
+
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <stdio.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <future>
+#include <map>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include "gmock/gmock.h"
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+
+#include "esplusplayer_capi/esplusplayer_capi.h"
+#include "esplusplayer_capi/esplusplayer_internal.h"
+#include "ut/include/appwindow.h"
+#include "ut/include/esplusplayer/eseventlistener.hpp"
+#include "ut/include/esplusplayer/esreader.hpp"
+//#include "ut/include/esplusplayer/tclist.h"
+#include "ut/include/streamreader.hpp"
+
+using namespace esplusplayer;
+namespace es_tc_diaplay {
+ static const std::string es_h264_aac = "es_h264_aac/";
+ static const std::string es_hevc_ac3 = "es_hevc_ac3/";
+ static const std::string es_vp9_opus = "es_vp9_opus/";
+ std::vector<std::string> tc_list = {
+ es_h264_aac,
+ //es_hevc_ac3,
+ es_vp9_opus,
+ };
+}
+
+class EsDisplayTest : public ::testing::TestWithParam<std::string> {
+ public:
+ EsDisplayTest() { std::cout << "EsDisplayTest()" << std::endl; }
+ ~EsDisplayTest() { std::cout << "~EsDisplayTest()" << std::endl; }
+
+ static void SetUpTestCase() {
+ gst_init_check(nullptr, nullptr, nullptr);
+ window_ = new Environment();
+ ESPacketDownloader::Init();
+ std::cout << "SetUpTestCase()" << std::endl;
+ }
+ static void TearDownTestCase() {
+ if (window_) {
+ delete window_;
+ window_ = nullptr;
+ }
+ std::cout << "TearDownTestCase()" << std::endl;
+ }
+
+ virtual void SetUp() override {
+ uri_ = GetParam();
+ std::cout << "uri_: " << uri_ << std::endl;
+ video_reader_ =
+ new EsStreamReader(uri_ + "video/", ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ audio_reader_ =
+ new EsStreamReader(uri_ + "audio/", ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+ esplayer_ = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer_);
+ callback_ =
+ new EsPlayerEventCallback(esplayer_, video_reader_, audio_reader_);
+ callback_->SetCallback();
+
+ std::cout << "SetUp()" << std::endl;
+ }
+
+ virtual void TearDown() override {
+ if (nullptr != esplayer_) {
+ ASSERT_EQ(esplusplayer_stop(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_close(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ }
+ delete callback_;
+ delete video_reader_;
+ delete audio_reader_;
+
+ std::cout << "TearDown()" << std::endl;
+ }
+
+ public:
+ std::string uri_;
+ EsStreamReader* video_reader_;
+ EsStreamReader* audio_reader_;
+ esplusplayer_handle esplayer_;
+ EsPlayerEventCallback* callback_;
+ static Environment* window_;
+};
+Environment* EsDisplayTest::window_ = nullptr;
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_display_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_ecore_display_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_ecore_display(
+ esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->EcoreWindow(), 0, 0, 1920, 1080),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_surface_display_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ unsigned int surfaceid = 1;
+ ASSERT_EQ(esplusplayer_set_surface_display(esplayer_,
+ ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ surfaceid, 0, 0, 1920, 1080),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_display_mode_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_display_mode(esplayer_,
+ ESPLUSPLAYER_DISPLAY_MODE_FULL_SCREEN),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_display_roi_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_display_mode(esplayer_,
+ ESPLUSPLAYER_DISPLAY_MODE_DST_ROI),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_display_roi(esplayer_,0,0,600,500),ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_display_visible_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_display_visible(esplayer_,false),ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_video_roi_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_video_roi(esplayer_,0,0,0.5,0.5),ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_display_rotation_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_display_rotation(esplayer_,ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_90),ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_get_display_rotation_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ esplusplayer_display_rotation_type rotation_set = ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_90;
+ esplusplayer_display_rotation_type rotation_get = ESPLUSPLAYER_DISPLAY_ROTATION_TYPE_NONE;
+ ASSERT_EQ(esplusplayer_set_display_rotation(esplayer_,rotation_set),ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ esplusplayer_get_display_rotation(esplayer_,&rotation_get);
+ ASSERT_EQ(rotation_set,rotation_get);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_video_rotation_p_1) {
+ ASSERT_EQ(
+ esplusplayer_set_video_stream_rotation_info(esplayer_,ESPLUSPLAYER_VIDEO_ROTATION_90),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_video_rotation_p_2) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_video_stream_rotation_info(esplayer_,ESPLUSPLAYER_VIDEO_ROTATION_90),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_set_video_rotation_p_3) {
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(
+ esplusplayer_set_video_stream_rotation_info(esplayer_,ESPLUSPLAYER_VIDEO_ROTATION_270),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+}
+
+TEST_P(EsDisplayTest, vdapi_display_esplusplayer_get_video_rotation_p_1) {
+ esplusplayer_video_stream_rotation_type rotation_set = ESPLUSPLAYER_VIDEO_ROTATION_270;
+ esplusplayer_video_stream_rotation_type rotation_get = ESPLUSPLAYER_VIDEO_ROTATION_NONE;
+ ASSERT_EQ(esplusplayer_open(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_video_stream_rotation_info(esplayer_, rotation_set),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader_->SetStreamInfo(esplayer_));
+ ASSERT_TRUE(audio_reader_->SetStreamInfo(esplayer_));
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ASSERT_EQ(
+ esplusplayer_get_video_stream_rotation_info(esplayer_,&rotation_get),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(rotation_set,rotation_get);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+}
+
+INSTANTIATE_TEST_CASE_P(ESPlusplayer, EsDisplayTest,
+ ::testing::ValuesIn(es_tc_diaplay::tc_list));
--- /dev/null
+
+#include <stdio.h>
+
+#include <condition_variable>
+#include <mutex>
+
+#include "esplusplayer_capi/esplusplayer_capi.h"
+#include "esplusplayer_capi/esplusplayer_internal.h"
+#include "gmock/gmock.h"
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+#include "ut/include/esplusplayer/eseventlistener.hpp"
+#include "ut/include/esplusplayer/esreader.hpp"
+#include "ut/include/streamreader.hpp"
+
+using namespace esplusplayer;
+
+#define AAC_PATH "es_h264_aac/audio/"
+#define OPUS_PATH "es_vp9_opus/audio/"
+
+class EsDualAudioTest : public ::testing::Test {
+ public:
+ EsDualAudioTest() { std::cout << "EsDualAudioTest()" << std::endl; }
+ ~EsDualAudioTest() { std::cout << "~EsDualAudioTest()" << std::endl; }
+
+ static void SetUpTestCase() {
+ gst_init_check(nullptr, nullptr, nullptr);
+ ESPacketDownloader::Init();
+
+ esplayer1_ = esplusplayer_create();
+ esplayer2_ = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer1_);
+ ASSERT_NE(nullptr, esplayer2_);
+ std::cout << "SetUpTestCase()" << std::endl;
+ }
+
+ static void TearDownTestCase() {
+ esplusplayer_destroy(esplayer1_);
+ esplusplayer_destroy(esplayer2_);
+ esplayer1_ = nullptr;
+ esplayer2_ = nullptr;
+ std::cout << "TearDownTestCase()" << std::endl;
+ }
+
+ virtual void SetUp() override {
+ audio_reader1_ =
+ new EsStreamReader(AAC_PATH, ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+ callback1_ = new EsPlayerEventCallback(esplayer1_, nullptr, audio_reader1_);
+ callback1_->SetCallback();
+
+ audio_reader2_ =
+ new EsStreamReader(OPUS_PATH, ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+ callback2_ = new EsPlayerEventCallback(esplayer2_, nullptr, audio_reader2_);
+ callback2_->SetCallback();
+ }
+
+ virtual void TearDown() override {
+ if (nullptr != esplayer1_) {
+ ASSERT_EQ(esplusplayer_close(esplayer1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ }
+ if (nullptr != esplayer2_) {
+ ASSERT_EQ(esplusplayer_close(esplayer2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ }
+ delete callback1_;
+ delete callback2_;
+ delete audio_reader1_;
+ delete audio_reader2_;
+ }
+
+ public:
+ static esplusplayer_handle esplayer1_;
+ static esplusplayer_handle esplayer2_;
+ EsPlayerEventCallback* callback1_;
+ EsPlayerEventCallback* callback2_;
+ EsStreamReader* audio_reader1_;
+ EsStreamReader* audio_reader2_;
+};
+esplusplayer_handle EsDualAudioTest::esplayer1_ = nullptr;
+esplusplayer_handle EsDualAudioTest::esplayer2_ = nullptr;
+
+TEST_F(EsDualAudioTest, audio_preloading) {
+ ASSERT_EQ(esplusplayer_open(esplayer1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(audio_reader1_->SetStreamInfo(esplayer1_));
+ ASSERT_EQ(esplusplayer_set_audio_preloading(esplayer1_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer1_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback1_->WaitForPrepareDone();
+
+ ASSERT_EQ(esplusplayer_open(esplayer2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(audio_reader2_->SetStreamInfo(esplayer2_));
+ ASSERT_EQ(esplusplayer_set_audio_codec_type(
+ esplayer2_, ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_audio_preloading(esplayer2_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer2_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback2_->WaitForPrepareDone();
+
+ ASSERT_EQ(esplusplayer_start(esplayer1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ sleep(1);
+
+ ASSERT_EQ(esplusplayer_deactivate(esplayer1_, ESPLUSPLAYER_STREAM_TYPE_AUDIO), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_start(esplayer2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ sleep(1);
+}
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <stdio.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <future>
+#include <map>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include "esplusplayer_capi/esplusplayer_capi.h"
+#include "gmock/gmock.h"
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+#include "ut/include/appwindow.h"
+#include "ut/include/esplusplayer/eseventlistener.hpp"
+#include "ut/include/esplusplayer/esreader.hpp"
+#include "ut/include/esplusplayer/tclist.h"
+#include "ut/include/streamreader.hpp"
+
+using namespace esplusplayer;
+
+class EsInAppMultiViewTest : public ::testing::TestWithParam<std::string> {
+ public:
+ EsInAppMultiViewTest() { std::cout << "EsInAppMultiViewTest()" << std::endl; }
+ ~EsInAppMultiViewTest() { std::cout << "~EsInAppMultiViewTest()" << std::endl; }
+
+ static void SetUpTestCase() {
+ gst_init_check(nullptr, nullptr, nullptr);
+ window_ = new Environment();
+ ESPacketDownloader::Init();
+ esplayer1_ = esplusplayer_create();
+ esplayer2_ = esplusplayer_create();
+ ASSERT_NE(nullptr, esplayer1_);
+ ASSERT_NE(nullptr, esplayer2_);
+ std::cout << "SetUpTestCase()" << std::endl;
+ }
+ static void TearDownTestCase() {
+ if (window_) {
+ delete window_;
+ window_ = nullptr;
+ }
+ esplusplayer_destroy(esplayer1_);
+ esplusplayer_destroy(esplayer2_);
+ esplayer1_ = nullptr;
+ esplayer2_ = nullptr;
+ std::cout << "TearDownTestCase()" << std::endl;
+ }
+
+ virtual void SetUp() override {
+ uri_ = GetParam();
+ std::cout << "uri_: " << uri_ << std::endl;
+ video_reader1_ =
+ new EsStreamReader(uri_ + "video/", ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ callback1_ =
+ new EsPlayerEventCallback(esplayer1_, video_reader1_, nullptr);
+ callback1_->SetCallback();
+
+ video_reader2_ =
+ new EsStreamReader(uri_ + "video/", ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ callback2_ =
+ new EsPlayerEventCallback(esplayer2_, video_reader2_, nullptr);
+ callback2_->SetCallback();
+
+ std::cout << "SetUp()" << std::endl;
+ }
+
+ virtual void TearDown() override {
+ if (nullptr != esplayer1_) {
+ ASSERT_EQ(esplusplayer_close(esplayer1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ }
+ if (nullptr != esplayer2_) {
+ ASSERT_EQ(esplusplayer_close(esplayer2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ }
+ delete callback1_;
+ delete callback2_;
+ delete video_reader1_;
+ delete video_reader2_;
+
+ std::cout << "TearDown()" << std::endl;
+ }
+
+ public:
+ std::string uri_;
+ EsStreamReader* video_reader1_;
+ EsStreamReader* video_reader2_;
+ static esplusplayer_handle esplayer1_;
+ static esplusplayer_handle esplayer2_;
+ EsPlayerEventCallback* callback1_;
+ EsPlayerEventCallback* callback2_;
+ static Environment* window_;
+};
+Environment* EsInAppMultiViewTest::window_ = nullptr;
+esplusplayer_handle EsInAppMultiViewTest::esplayer1_ = nullptr;
+esplusplayer_handle EsInAppMultiViewTest::esplayer2_ = nullptr;
+
+#if 0
+TEST_P(EsInAppMultiViewTest, vdapi_inappmultiview_esplusplayer_start_p_1) {
+ ASSERT_EQ(esplusplayer_open(esplayer1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer1_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_display_mode(esplayer1_,
+ ESPLUSPLAYER_DISPLAY_MODE_DST_ROI),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_display_roi(esplayer1_,0,0,640,360), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader1_->SetStreamInfo(esplayer1_));
+ ASSERT_EQ(esplusplayer_set_resource_allocate_policy(esplayer1_, ESPLUSPLAYER_RSC_ALLOC_EXCLUSIVE_NO_EXPLICIT), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback1_->WaitForPrepareDone();
+
+ ASSERT_EQ(esplusplayer_open(esplayer2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_display(esplayer2_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ window_->Window()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_display_mode(esplayer2_,
+ ESPLUSPLAYER_DISPLAY_MODE_DST_ROI),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_set_display_roi(esplayer2_,640,0,640,360), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(video_reader2_->SetStreamInfo(esplayer2_));
+ ASSERT_EQ(esplusplayer_set_resource_allocate_policy(esplayer2_, ESPLUSPLAYER_RSC_ALLOC_EXCLUSIVE_NO_EXPLICIT), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback2_->WaitForPrepareDone();
+
+ ASSERT_EQ(esplusplayer_start(esplayer1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_start(esplayer2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+
+ std::cout << "EsInAppMultiViewTest, Play, END" << std::endl;
+}
+#endif
+
+INSTANTIATE_TEST_CASE_P(ESPlusplayer, EsInAppMultiViewTest,
+ ::testing::ValuesIn(es_tc::inapp_multiview_tc_list));
--- /dev/null
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include <chrono>
+#include <thread>
+
+#include "esplusplayer_capi/esplusplayer_capi.h"
+
+class EsVideoSetStreamTest : public ::testing::Test {};
+
+TEST_F(EsVideoSetStreamTest, MJPEG) {
+ esplusplayer_video_stream_info vstream_info;
+ vstream_info.codec_data = nullptr;
+ vstream_info.codec_data_length = 0;
+ vstream_info.mime_type = ESPLUSPLAYER_VIDEO_MIME_TYPE_MJPEG;
+ vstream_info.width = 1920;
+ vstream_info.height = 1080;
+ vstream_info.max_width = 1920;
+ vstream_info.max_height = 1080;
+ vstream_info.framerate_num = 30;
+ vstream_info.framerate_den = 1;
+
+ auto* handle = esplusplayer_create();
+ esplusplayer_open(handle);
+ esplusplayer_set_video_stream_info(handle, &vstream_info);
+ esplusplayer_prepare_async(handle);
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ esplusplayer_close(handle);
+ esplusplayer_destroy(handle);
+}
+
+TEST_F(EsVideoSetStreamTest, UHD_MJPEG) {
+ esplusplayer_video_stream_info vstream_info;
+ vstream_info.codec_data = nullptr;
+ vstream_info.codec_data_length = 0;
+ vstream_info.mime_type = ESPLUSPLAYER_VIDEO_MIME_TYPE_MJPEG;
+ vstream_info.width = 3840;
+ vstream_info.height = 2160;
+ vstream_info.max_width = 3840;
+ vstream_info.max_height = 2160;
+ vstream_info.framerate_num = 30;
+ vstream_info.framerate_den = 1;
+
+ auto* handle = esplusplayer_create();
+ esplusplayer_open(handle);
+ esplusplayer_set_video_stream_info(handle, &vstream_info);
+ esplusplayer_prepare_async(handle);
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ esplusplayer_close(handle);
+ esplusplayer_destroy(handle);
+}
\ No newline at end of file
--- /dev/null
+#include "mixer/constant.h"
+
+namespace esplusplayer_ut {
+
+VideoPlaneManipulableInfo GetVideoPlaneManipulableInfo(
+ BufferHandleType handle, const PlaneComponent& comp,
+ const std::uint32_t& linesize, const Geometry& geom) {
+ VideoPlaneManipulableInfo ret;
+ ret.handle = handle;
+ ret.component = comp;
+ ret.linesize = linesize;
+ ret.rect = geom;
+ return ret;
+}
+
+Geometry GetGeometry(const int& x, const int& y, const int& w, const int& h) {
+ Geometry geom;
+ geom.x = x;
+ geom.y = y;
+ geom.w = w;
+ geom.h = h;
+ return geom;
+}
+
+CropArea GetCropArea(const double& x, const double& y, const double& w,
+ const double& h) {
+ CropArea croparea;
+ croparea.scale_x = x;
+ croparea.scale_y = y;
+ croparea.scale_w = w;
+ croparea.scale_h = h;
+ return croparea;
+}
+
+Mixer::ResolutionInfo GetResolutionInfo(int width, int height, int fnum,
+ int fden) {
+ Mixer::ResolutionInfo rinfo;
+ rinfo.width = width;
+ rinfo.height = height;
+ rinfo.framerate_num = fnum;
+ rinfo.framerate_den = fden;
+ return rinfo;
+}
+
+static std::uint32_t _kDefaultBuffer = 0;
+BufferDefaultType kDefaultBuffer =
+ reinterpret_cast<BufferDefaultType>(&_kDefaultBuffer);
+
+BufferUnionHandleType kDefaultMappedHandle = {.u32 = kDefaultBufferHandle};
+
+} // namespace esplusplayer_ut
\ No newline at end of file
--- /dev/null
+#include "mixer/matcher.h"
+
+namespace esplusplayer_ut {
+
+Matcher<const VideoPlaneManipulableInfo&> IsSameVideoPlaneManipulableInfo(
+ const VideoPlaneManipulableInfo& comparer) {
+ return MakeMatcher(new IsSameVideoPlaneManipulableInfoMatcher(comparer));
+}
+
+} // namespace esplusplayer_ut
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include <gtest/gtest.h>
+
+#include <iostream>
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer_capi/mixer_capi.h"
+#include "ut/include/utils/utility.h"
+#include "ut/include/streamreader.hpp"
+
+using namespace esplusplayer;
+using namespace esplusplayer_ut;
+using namespace utils;
+using namespace std;
+
+using utils::Utility;
+std::string es_uri_1 = "youtube/";
+std::string es_uri_2 = "bunny/";
+std::string es_vp8_uri = "es_vp8_vorbis/";
+
+//max playing duration : 10 sec => see EsStreamReader::ReadNextPacket()
+constexpr int kPlayingTime = 2; // (sec)
+
+//------------------------------------------------------------
+class MixerEsppScenarioTestF : public ::testing::Test {
+ public:
+ explicit MixerEsppScenarioTestF(void)
+ : util_(Utility::Instance()),
+ mixer_(nullptr),
+ player1_(nullptr),
+ player2_(nullptr),
+ player3_(nullptr),
+ player4_(nullptr){};
+ ~MixerEsppScenarioTestF(void){};
+
+ static void SetUpTestCase() {
+ ESPacketDownloader::Init();
+ std::cout << "SetUpTestCase()" << std::endl;
+ }
+
+ static void TearDownTestCase() {}
+
+ virtual void SetUp(void) override {
+ LOG_ERROR("%s", util_.GetCurrentTestName());
+ mixer_ = mixer_create();
+ mixer_set_display(mixer_, MIXER_DISPLAY_TYPE_OVERLAY, util_.GetWindow());
+#if 1
+ roi1_.x = 20;
+ roi1_.y = 20;
+ roi1_.w = 720;
+ roi1_.h = 480;
+
+ roi2_.x = 1000;
+ roi2_.y = 20;
+ roi2_.w = 720;
+ roi2_.h = 480;
+
+ roi3_.x = 1000;
+ roi3_.y = 520;
+ roi3_.w = 720;
+ roi3_.h = 480;
+
+ roi4_.x = 20;
+ roi4_.y = 520;
+ roi4_.w = 720;
+ roi4_.h = 480;
+#else
+ roi1_.x = 20;
+ roi1_.y = 20;
+ roi1_.w = 1180;
+ roi1_.h = 720;
+
+ roi2_.x = 1220;
+ roi2_.y = 20;
+ roi2_.w = 640;
+ roi2_.h = 480;
+
+ roi3_.x = 1220;
+ roi3_.y = 520;
+ roi3_.w = 640;
+ roi3_.h = 480;
+#endif
+ }
+
+ virtual void TearDown(void) override {
+ util_.DestroyESPP(player1_);
+ util_.DestroyESPP(player2_);
+ util_.DestroyESPP(player3_);
+ util_.DestroyESPP(player4_);
+ mixer_destroy(mixer_);
+ player1_ = nullptr;
+ player2_ = nullptr;
+ player3_ = nullptr;
+ player4_ = nullptr;
+ mixer_ = nullptr;
+ LOG_ERROR("%s", util_.GetCurrentTestName());
+ }
+
+ public:
+ Utility& util_;
+ mixer_handle mixer_;
+ esplusplayer_handle player1_;
+ esplusplayer_handle player2_;
+ esplusplayer_handle player3_;
+ esplusplayer_handle player4_;
+ Geometry roi1_;
+ Geometry roi2_;
+ Geometry roi3_;
+ Geometry roi4_;
+};
+
+#if 1 // normal mixer test
+TEST_F(MixerEsppScenarioTestF, Basic) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+ player3_ = util_.GetOpenedMixESPP(mixer_, roi3_);
+ ASSERT_NE(player3_, nullptr);
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer_, player1_), MIXER_ERROR_TYPE_NONE);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player3_, es_uri_1));
+
+ EXPECT_EQ(esplusplayer_start(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, DetachAttach) {
+ player1_ = util_.GetStartedMixESPP(es_uri_1, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetStartedMixESPP(es_uri_2, mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ player3_ = util_.GetStartedMixESPP(es_uri_1, mixer_, roi3_);
+ ASSERT_NE(player3_, nullptr);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, DetachAttach1) {
+ player1_ = util_.GetStartedMixESPP(es_uri_1, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetStartedMixESPP(es_uri_1, mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ player3_ = util_.GetStartedMixESPP(es_uri_2, mixer_, roi1_);
+ ASSERT_NE(player3_, nullptr);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, SetROI) {
+ player1_ = util_.GetStartedMixESPP(es_uri_1, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetStartedMixESPP(es_uri_2, mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(esplusplayer_set_display_roi(player1_, roi2_.x, roi2_.y, roi2_.w,
+ roi2_.h),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_display_roi(player2_, roi1_.x, roi1_.y, roi1_.w,
+ roi1_.h),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(mixer_commit(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, SetROI1) {
+ player1_ = util_.GetStartedMixESPP(es_uri_1, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetStartedMixESPP(es_uri_2, mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(esplusplayer_set_display_roi(player1_, roi2_.x, roi2_.y, roi2_.w,
+ roi2_.h),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_display_roi(player2_, roi3_.x, roi3_.y, roi3_.w,
+ roi3_.h),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(mixer_commit(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, CheckMaxMixedPlayer) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+ player3_ = util_.GetOpenedMixESPP(mixer_, roi3_);
+ ASSERT_NE(player3_, nullptr);
+ esplusplayer_handle player4 = util_.GetOpenedMixESPP(mixer_, roi3_);
+ ASSERT_NE(player4, nullptr);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player3_, es_uri_1));
+
+ EXPECT_FALSE(util_.PrepareESPP(player4, es_uri_1));
+
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ util_.DestroyESPP(player4);
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, SetAudioFocus) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer_, player1_), MIXER_ERROR_TYPE_NONE);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_uri_2));
+
+ EXPECT_EQ(esplusplayer_start(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer_, player2_), MIXER_ERROR_TYPE_NONE);
+ util_.FeedingEsPacket(player2_, ESPLUSPLAYER_STREAM_TYPE_AUDIO, es_uri_2);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, MultiAudioTest) {
+ EXPECT_EQ(mixer_disable_audio_focus_setting(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ player3_ = util_.GetOpenedMixESPP(mixer_, roi3_);
+ ASSERT_NE(player3_, nullptr);
+
+ EXPECT_EQ(esplusplayer_set_audio_codec_type(player2_,
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_audio_codec_type(player3_,
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_uri_2));
+ EXPECT_TRUE(util_.PrepareESPP(player3_, es_uri_1));
+
+ EXPECT_EQ(esplusplayer_start(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, MultiAudioTest2) {
+ EXPECT_EQ(mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_DISABLE),
+ MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(mixer_disable_audio_focus_setting(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ player3_ = util_.GetOpenedMixESPP(mixer_, roi3_);
+ ASSERT_NE(player3_, nullptr);
+
+ EXPECT_EQ(esplusplayer_set_alternative_video_resource(player2_, 1),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_video_codec_type(player3_,
+ ESPLUSPLAYER_VIDEO_CODEC_TYPE_SW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_audio_codec_type(player2_,
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_audio_codec_type(player3_,
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_SW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_uri_2));
+ EXPECT_TRUE(util_.PrepareESPP(player3_, es_uri_1));
+
+ /* audio hw decoder + mmaudiosink */
+ EXPECT_EQ(esplusplayer_start(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ /* audio sw decoder + pulsesink */
+ EXPECT_EQ(esplusplayer_start(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ /* audio sw decoder + pulsesink */
+ EXPECT_EQ(esplusplayer_start(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(esplusplayer_deactivate(player1_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_deactivate(player3_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ /* audio hw decoder + mmaudiosink */
+ EXPECT_EQ(esplusplayer_set_audio_codec_type(player3_,
+ ESPLUSPLAYER_AUDIO_CODEC_TYPE_HW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_activate(player3_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ util_.FeedingEsPacket(player3_, ESPLUSPLAYER_STREAM_TYPE_AUDIO, es_uri_1);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(esplusplayer_deactivate(player2_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_deactivate(player3_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ /* audio sw decoder + alsasink */
+ EXPECT_EQ(esplusplayer_activate(player2_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ util_.FeedingEsPacket(player2_, ESPLUSPLAYER_STREAM_TYPE_AUDIO, es_uri_2);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, SetAudioFocus1) {
+ player1_ = util_.GetStartedMixESPP(es_uri_1, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetStartedMixESPP(es_uri_2, mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, SetAudioFocus2) {
+ player1_ = util_.GetStartedMixESPP(es_uri_1, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer_, nullptr),
+ MIXER_ERROR_TYPE_INVALID_OPERATION);
+
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, MixerNPlayer) {
+ mixer_set_display_mode(mixer_, MIXER_DISPLAY_MODE_DST_ROI);
+ mixer_set_display_roi(mixer_, roi2_.x, roi2_.y, roi2_.w, roi2_.h);
+ mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_DISABLE);
+ mixer_disable_audio_focus_setting(mixer_);
+ mixer_set_alternative_video_scaler(mixer_);
+
+ player1_ = util_.GetOpenedESPP(roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ Geometry roi3;
+ roi3.x = 0;
+ roi3.y = 0;
+ roi3.w = roi2_.w;
+ roi3.h = roi2_.h;
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi3);
+ ASSERT_NE(player2_, nullptr);
+ esplusplayer_set_alternative_video_resource(player2_, 1);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_uri_1, utils::EsType::kBoth));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_uri_2, utils::EsType::kVideo));
+
+ esplusplayer_start(player1_);
+ esplusplayer_start(player2_);
+
+ mixer_start(mixer_);
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_EQ(esplusplayer_deactivate(player1_, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_deactivate(player2_, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(esplusplayer_set_display(player1_, ESPLUSPLAYER_DISPLAY_TYPE_MIXER,
+ mixer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_display(
+ player2_, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY, util_.GetWindow()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ esplusplayer_set_alternative_video_resource(player1_, 1);
+ EXPECT_EQ(esplusplayer_activate(player1_, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ esplusplayer_set_display_roi(player1_, roi3.x, roi3.y, roi3.w, roi3.h);
+ mixer_commit(mixer_);
+ util_.FeedingEsPacket(player1_, ESPLUSPLAYER_STREAM_TYPE_VIDEO, es_uri_1);
+
+ esplusplayer_set_alternative_video_resource(player2_, 0);
+ EXPECT_EQ(esplusplayer_activate(player2_, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ esplusplayer_set_display_mode(player2_, ESPLUSPLAYER_DISPLAY_MODE_DST_ROI);
+ esplusplayer_set_display_roi(player2_, roi1_.x, roi1_.y, roi1_.w, roi1_.h);
+ util_.FeedingEsPacket(player2_, ESPLUSPLAYER_STREAM_TYPE_VIDEO, es_uri_2);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ esplusplayer_stop(player1_);
+ esplusplayer_stop(player2_);
+ mixer_stop(mixer_);
+}
+
+TEST_F(MixerEsppScenarioTestF, NDecH264PlayerX2) {
+ mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_NDECODER);
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer_, player1_), MIXER_ERROR_TYPE_NONE);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_uri_1));
+
+ EXPECT_EQ(esplusplayer_start(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, NDecH264PlayerX4) {
+ mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_NDECODER);
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+ player3_ = util_.GetOpenedMixESPP(mixer_, roi3_);
+ ASSERT_NE(player3_, nullptr);
+ player4_ = util_.GetOpenedMixESPP(mixer_, roi4_);
+ ASSERT_NE(player4_, nullptr);
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer_, player1_), MIXER_ERROR_TYPE_NONE);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player3_, es_uri_1));
+ EXPECT_TRUE(util_.PrepareESPP(player4_, es_uri_1));
+
+ EXPECT_EQ(esplusplayer_start(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player4_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player4_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, NDecVp8PlayerX2) {
+ mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_NDECODER);
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer_, player1_), MIXER_ERROR_TYPE_NONE);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_vp8_uri));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_vp8_uri));
+
+ EXPECT_EQ(esplusplayer_start(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(MixerEsppScenarioTestF, NDecVp8PlayerX4) {
+ mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_NDECODER);
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetOpenedMixESPP(mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+ player3_ = util_.GetOpenedMixESPP(mixer_, roi3_);
+ ASSERT_NE(player3_, nullptr);
+ player4_ = util_.GetOpenedMixESPP(mixer_, roi4_);
+ ASSERT_NE(player4_, nullptr);
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer_, player1_), MIXER_ERROR_TYPE_NONE);
+
+ EXPECT_TRUE(util_.PrepareESPP(player1_, es_vp8_uri));
+ EXPECT_TRUE(util_.PrepareESPP(player2_, es_vp8_uri));
+ EXPECT_TRUE(util_.PrepareESPP(player3_, es_vp8_uri));
+ EXPECT_TRUE(util_.PrepareESPP(player4_, es_vp8_uri));
+
+ EXPECT_EQ(esplusplayer_start(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player4_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player3_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player4_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+#endif
--- /dev/null
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <memory>
+
+#include "mixer/constant.h"
+#include "mixer/matcher.h"
+#include "mixer/mixedframe.h"
+#include "mixer/mock/fakebuffer.h"
+#include "mixer/mock/mock_bufferobject.h"
+#include "mixer/mock/mock_memallocator.h"
+#include "mixer/mock/mock_phyaddraccessor.h"
+#include "mixer/mock/mock_vpmanipulator.h"
+#include "mixer/mock/movable.h"
+#include "mixer/mock/moveobj_wrapper.h"
+
+using namespace esplusplayer;
+using namespace esplusplayer_ut;
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::AtLeast;
+using ::testing::ByRef;
+using ::testing::DoAll;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Invoke;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::SizeIs;
+
+/********************************************************************************************
+ * [DEFAULT CLASS] DefaultMixedFrameTestOption
+ */
+
+class DefaultMixedFrameTestOption {
+ public:
+ explicit DefaultMixedFrameTestOption() = default;
+ virtual ~DefaultMixedFrameTestOption() = default;
+
+ protected:
+ virtual void Init_() {
+ ON_CALL(GetMemoryAllocator_(), Allocate(_))
+ .WillByDefault(DoAll(
+ SaveArg<0>(&allocated_size_), CreateFakeBuffer(fakebuffer_),
+ Invoke(
+ this,
+ &DefaultMixedFrameTestOption::DelegateDefaultForBufferObject_),
+ Return(RelaseBufferObject_())));
+ }
+
+ protected:
+ MockMemoryAllocator& GetMemoryAllocator_() { return memalloc_; }
+ MockAccessibleBufferObject& GetBufferObject_() { return bufferobj_.Get(); }
+ MockAccessibleBufferObject* RelaseBufferObject_() {
+ return bufferobj_.Move();
+ }
+ MockPhyAddrAccessor& GetReadablePhyAddrAccessor_() {
+ return r_phyaddraccessor_.Get();
+ }
+ MockPhyAddrAccessor& GetWritablePhyAddrAccessor_() {
+ return w_phyaddraccessor_.Get();
+ }
+ MockAccessibleBufferObject::PhyAddrAccessorPtr
+ MoveReadablePhyAddrAccessor_() {
+ return MockAccessibleBufferObject::PhyAddrAccessorPtr(
+ r_phyaddraccessor_.Move());
+ }
+ MockAccessibleBufferObject::PhyAddrAccessorPtr
+ MoveWritablePhyAddrAccessor_() {
+ return MockAccessibleBufferObject::PhyAddrAccessorPtr(
+ w_phyaddraccessor_.Move());
+ }
+
+ private:
+ virtual void DelegateDefaultForBufferObject_(const std::uint32_t size) {
+ ON_CALL(GetBufferObject_(), GetReadableAddress_())
+ .WillByDefault(
+ Return(Movable(MockAccessibleBufferObject::PhyAddrAccessorPtr(
+ MoveReadablePhyAddrAccessor_()))));
+ ON_CALL(GetBufferObject_(), GetWritableAddress_())
+ .WillByDefault(
+ Return(Movable(MockAccessibleBufferObject::PhyAddrAccessorPtr(
+ MoveWritablePhyAddrAccessor_()))));
+ ON_CALL(GetBufferObject_(), GetBufferHandle())
+ .WillByDefault(Return(kDefaultBufferHandle));
+ ON_CALL(GetBufferObject_(), Export())
+ .WillByDefault(Return(kDefaultBufferKey));
+ ON_CALL(GetBufferObject_(), GetSize())
+ .WillByDefault(Return(allocated_size_));
+
+ ON_CALL(GetReadablePhyAddrAccessor_(), GetAddress())
+ .WillByDefault(Return(fakebuffer_->ptr.get()));
+ ON_CALL(GetWritablePhyAddrAccessor_(), GetAddress())
+ .WillByDefault(Return(fakebuffer_->ptr.get()));
+ }
+
+ private:
+ std::uint32_t allocated_size_;
+ FakeBufferPtr fakebuffer_;
+
+ MoveObjectWrapper<MockPhyAddrAccessor> r_phyaddraccessor_{
+ new MockPhyAddrAccessor()};
+ MoveObjectWrapper<MockPhyAddrAccessor> w_phyaddraccessor_{
+ new MockPhyAddrAccessor()};
+ MoveObjectWrapper<MockAccessibleBufferObject> bufferobj_{
+ new MockAccessibleBufferObject()};
+ MockMemoryAllocator memalloc_;
+};
+
+/********************************************************************************************
+ * InvalidMixedFrameTest
+ */
+
+class InvalidMixedFrameTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ mixed_frame_ = MixedFrame::Create(nullptr, kInvalidWidth, kInvalidHeight);
+ ASSERT_FALSE(mixed_frame_->IsValid());
+ }
+ virtual void TearDown() override {}
+
+ protected:
+ MixedFramePtr& GetMixedFrame() { return mixed_frame_; }
+ const MockVideoPlaneManipulator& GetVideoPlaneManipulator_() const {
+ return vieoplane_manip_;
+ }
+
+ private:
+ MixedFramePtr mixed_frame_;
+ MockVideoPlaneManipulator vieoplane_manip_;
+};
+
+TEST_F(InvalidMixedFrameTest, GetVideoPlaneManipInfo) {
+ EXPECT_EQ(0, GetMixedFrame()->GetVideoPlaneManipInfo().size());
+}
+
+TEST_F(InvalidMixedFrameTest, GetSize) {
+ EXPECT_EQ(0, GetMixedFrame()->GetSize());
+}
+
+TEST_F(InvalidMixedFrameTest, Render) {
+ EXPECT_FALSE(GetMixedFrame()->Render(
+ &GetVideoPlaneManipulator_(),
+ {kYComponentSrcVMInfo, kUVComponentSrcVMInfo},
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH)));
+}
+
+TEST_F(InvalidMixedFrameTest, Export) {
+ BufferKeyType key;
+ EXPECT_FALSE(GetMixedFrame()->Export(key));
+}
+
+/********************************************************************************************
+ * MixedFrameConstructionTest
+ */
+class MixedFrameConstructionTest : public ::testing::Test,
+ public DefaultMixedFrameTestOption {
+ public:
+ using MockBufferObjectPtr = std::unique_ptr<MockBufferObject>;
+ virtual ~MixedFrameConstructionTest() {}
+
+ virtual void SetUp() override { Init_(); }
+ virtual void TearDown() override {}
+};
+
+TEST_F(MixedFrameConstructionTest, IsValid_WithInvalidAllocator) {
+ EXPECT_FALSE(
+ MixedFrame::Create(nullptr, kDefaultWidth, kDefaultHeight)->IsValid());
+}
+
+TEST_F(MixedFrameConstructionTest, IsValid_WithInvalidResolution) {
+ EXPECT_FALSE(
+ MixedFrame::Create(&GetMemoryAllocator_(), kInvalidWidth, kInvalidHeight)
+ ->IsValid());
+}
+
+TEST_F(MixedFrameConstructionTest, IsValid_WithAllocateNullPtr) {
+ EXPECT_CALL(GetMemoryAllocator_(), Allocate(kExpectedDefaultByteSize))
+ .WillOnce(Return(nullptr));
+
+ EXPECT_FALSE(
+ MixedFrame::Create(&GetMemoryAllocator_(), kDefaultWidth, kDefaultHeight)
+ ->IsValid());
+}
+
+TEST_F(MixedFrameConstructionTest, IsValid_WithZeroSizeAllocated) {
+ EXPECT_CALL(GetMemoryAllocator_(), Allocate(kExpectedDefaultByteSize))
+ .Times(1);
+ EXPECT_CALL(GetBufferObject_(), GetSize()).WillOnce(Return(0));
+
+ EXPECT_FALSE(
+ MixedFrame::Create(&GetMemoryAllocator_(), kDefaultWidth, kDefaultHeight)
+ ->IsValid());
+}
+
+TEST_F(MixedFrameConstructionTest, IsValid_WithReturnWritableNullPtr) {
+ EXPECT_CALL(GetMemoryAllocator_(), Allocate(kExpectedDefaultByteSize))
+ .Times(1);
+ EXPECT_CALL(GetBufferObject_(), GetSize()).Times(1);
+ EXPECT_CALL(GetBufferObject_(), GetWritableAddress_())
+ .WillOnce(Return(Movable(AccessibleBuffer::PhyAddrAccessorPtr(nullptr))));
+
+ EXPECT_TRUE(
+ MixedFrame::Create(&GetMemoryAllocator_(), kDefaultWidth, kDefaultHeight)
+ ->IsValid());
+}
+
+TEST_F(MixedFrameConstructionTest, IsValid) {
+ EXPECT_CALL(GetMemoryAllocator_(), Allocate(kExpectedDefaultByteSize))
+ .Times(1);
+ EXPECT_CALL(GetBufferObject_(), GetSize()).Times(1);
+ EXPECT_CALL(GetBufferObject_(), GetWritableAddress_()).Times(1);
+ EXPECT_CALL(GetWritablePhyAddrAccessor_(), GetAddress()).Times(2);
+
+ EXPECT_TRUE(
+ MixedFrame::Create(&GetMemoryAllocator_(), kDefaultWidth, kDefaultHeight)
+ ->IsValid());
+}
+
+/********************************************************************************************
+ * MixedFrameTest
+ */
+
+class MixedFrameTest : public ::testing::Test,
+ public DefaultMixedFrameTestOption {
+ public:
+ explicit MixedFrameTest() = default;
+ virtual ~MixedFrameTest() = default;
+
+ virtual void SetUp() override {
+ Init_();
+
+ ON_CALL(GetVideoPlaneManipulator_(), Do(_, _)).WillByDefault(Return(true));
+
+ EXPECT_CALL(GetMemoryAllocator_(), Allocate(_)).Times(1);
+
+ EXPECT_CALL(GetBufferObject_(), GetSize()).Times(AtLeast(1));
+ EXPECT_CALL(GetBufferObject_(), GetWritableAddress_()).Times(AtLeast(1));
+
+ EXPECT_CALL(GetWritablePhyAddrAccessor_(), GetAddress()).Times(2);
+
+ mixed_frame_ = MixedFrame::Create(&GetMemoryAllocator_(), kDefaultWidth,
+ kDefaultHeight);
+ ASSERT_TRUE(mixed_frame_->IsValid());
+ }
+ virtual void TearDown() override {}
+
+ protected:
+ MockVideoPlaneManipulator& GetVideoPlaneManipulator_() {
+ return vieoplane_manip_;
+ }
+ MixedFramePtr& GetMixedFrame() { return mixed_frame_; }
+
+ private:
+ MixedFramePtr mixed_frame_;
+ MockVideoPlaneManipulator vieoplane_manip_;
+};
+
+TEST_F(MixedFrameTest, GetVideoPlaneManipInfo) {
+ EXPECT_CALL(GetBufferObject_(), GetBufferHandle());
+ EXPECT_THAT(
+ GetMixedFrame()->GetVideoPlaneManipInfo(),
+ AllOf(SizeIs(2),
+ ElementsAre(
+ IsSameVideoPlaneManipulableInfo(kYComponentDestVMInfo),
+ IsSameVideoPlaneManipulableInfo(kUVComponentDestVMInfo))));
+}
+
+TEST_F(MixedFrameTest, GetWidth) {
+ EXPECT_EQ(kDefaultWidth, GetMixedFrame()->GetWidth());
+}
+
+TEST_F(MixedFrameTest, GetHeight) {
+ EXPECT_EQ(kDefaultHeight, GetMixedFrame()->GetHeight());
+}
+
+TEST_F(MixedFrameTest, GetSize) {
+ EXPECT_EQ(kExpectedDefaultByteSize, GetMixedFrame()->GetSize());
+}
+
+TEST_F(MixedFrameTest, Render) {
+ ::testing::Sequence s1;
+ EXPECT_CALL(GetBufferObject_(), GetBufferHandle()).Times(2);
+
+ EXPECT_CALL(GetVideoPlaneManipulator_(),
+ Do(IsSameVideoPlaneManipulableInfo(kYComponentSrcVMInfo),
+ IsSameVideoPlaneManipulableInfo(kCroppedYComponentDestVMInfo)))
+ .InSequence(s1);
+ EXPECT_CALL(
+ GetVideoPlaneManipulator_(),
+ Do(IsSameVideoPlaneManipulableInfo(kUVComponentSrcVMInfo),
+ IsSameVideoPlaneManipulableInfo(kCroppedUVComponentDestVMInfo)))
+ .InSequence(s1);
+
+ EXPECT_TRUE(GetMixedFrame()->Render(
+ &GetVideoPlaneManipulator_(),
+ {kYComponentSrcVMInfo, kUVComponentSrcVMInfo},
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH)));
+}
+
+TEST_F(MixedFrameTest, Render_WithNullOperator) {
+ EXPECT_FALSE(GetMixedFrame()->Render(
+ nullptr, {kYComponentSrcVMInfo, kUVComponentSrcVMInfo},
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH)));
+}
+
+TEST_F(MixedFrameTest, Render_WithFirstOpFailed) {
+ EXPECT_CALL(GetBufferObject_(), GetBufferHandle()).Times(2);
+
+ EXPECT_CALL(GetVideoPlaneManipulator_(),
+ Do(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kYComponent),
+ _))
+ .WillOnce(Return(false));
+ EXPECT_CALL(GetVideoPlaneManipulator_(),
+ Do(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kUVComponent),
+ _))
+ .Times(1);
+
+ EXPECT_FALSE(GetMixedFrame()->Render(
+ &GetVideoPlaneManipulator_(),
+ {kYComponentSrcVMInfo, kUVComponentSrcVMInfo},
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH)));
+}
+
+TEST_F(MixedFrameTest, Render_WithSecondOpFailed) {
+ EXPECT_CALL(GetBufferObject_(), GetBufferHandle()).Times(2);
+
+ EXPECT_CALL(GetVideoPlaneManipulator_(),
+ Do(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kYComponent),
+ _))
+ .Times(1);
+ EXPECT_CALL(GetVideoPlaneManipulator_(),
+ Do(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kUVComponent),
+ _))
+ .WillOnce(Return(false));
+
+ EXPECT_FALSE(GetMixedFrame()->Render(
+ &GetVideoPlaneManipulator_(),
+ {kYComponentSrcVMInfo, kUVComponentSrcVMInfo},
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH)));
+}
+
+TEST_F(MixedFrameTest, Export) {
+ EXPECT_CALL(GetBufferObject_(), Export());
+ BufferKeyType key;
+ EXPECT_TRUE(GetMixedFrame()->Export(key));
+ EXPECT_EQ(kDefaultBufferKey, key);
+}
\ No newline at end of file
--- /dev/null
+
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include <gtest/gtest.h>
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/mixer.h"
+#include "mixer/mixer_eventlistener.h"
+#include "mixer/mixerticket.h"
+#include "mixer/mixerticket_eventlistener.h"
+#include "ut/include/appwindow.h"
+#include "ut/include/utils/utility.h"
+
+using namespace esplusplayer;
+using namespace esplusplayer_ut;
+using namespace utils;
+
+class MixerMockPlayer {
+ public:
+ MixerMockPlayer() { listener_ = new MyTicketListener(this); }
+ MixerMockPlayer(int x, int y, int w, int h) {
+ listener_ = new MyTicketListener(this);
+ display_info_.geometry.x = x;
+ display_info_.geometry.y = y;
+ display_info_.geometry.w = w;
+ display_info_.geometry.h = h;
+ }
+ ~MixerMockPlayer() { delete listener_; }
+
+ class MyTicketListener : public MixerTicketEventListener {
+ public:
+ explicit MyTicketListener(MixerMockPlayer* handler) : handler_(handler){};
+ bool OnAudioFocusChanged(bool active) {
+ LOG_INFO("My OnAudioFocusChanged [%d] player [%p]", active, handler_);
+ return true;
+ }
+
+ bool OnUpdateDisplayInfo(const DisplayInfo& cur_info,
+ DisplayInfo* new_info) {
+ *new_info = handler_->display_info_;
+ LOG_INFO("OnUpdateDisplayInfo x[%d] y[%d]",
+ handler_->display_info_.geometry.x,
+ handler_->display_info_.geometry.y);
+ return true;
+ }
+ MixerMockPlayer* handler_ = nullptr;
+ };
+
+ public:
+ MixerTicketEventListener* listener_ = nullptr;
+ DisplayInfo display_info_;
+};
+
+class MixerMockListener : public MixerEventListener {
+ public:
+ MixerMockListener() {}
+ ~MixerMockListener() {}
+ void OnError() {
+ LOG_INFO(" listener [%p]", this);
+ }
+ void OnResourceConflicted() {
+ LOG_INFO("MyOnResourceConflicted listener[%p]", this);
+ }
+};
+
+TEST(MixerTest, MixerCreate) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ ASSERT_NE(mixer, nullptr);
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerTicketCreate) {
+ MixerMockPlayer p1;
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ASSERT_NE(ticket, nullptr);
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerDisableAutoRscAlloc_1) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ EXPECT_TRUE(mixer->SetRscAllocMode(RscAllocMode::kDisable));
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerDisableAutoRscAlloc_2) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerMockPlayer p1;
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_FALSE(mixer->SetRscAllocMode(RscAllocMode::kDisable));
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerDisableAudioFocusSetting_1) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ EXPECT_TRUE(mixer->DisableAudioFocusSetting());
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerDisableAudioFocusSetting_2) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerMockPlayer p1;
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_FALSE(mixer->DisableAudioFocusSetting());
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerSetAudioFocus_1) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerMockPlayer p1;
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_TRUE(mixer->SetAudioFocus(&p1));
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerSetAudioFocus_2) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerMockPlayer p1, p2;
+
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ MixerTicket* ticket2 = mixer->CreateTicket(&p2);
+
+ ticket->RegisterListener(p1.listener_);
+ ticket2->RegisterListener(p2.listener_);
+
+ EXPECT_TRUE(mixer->SetAudioFocus(&p2));
+
+ delete ticket;
+ delete ticket2;
+
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerSetAudioFocus_3) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ mixer->DisableAudioFocusSetting();
+ MixerMockPlayer p1;
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_FALSE(mixer->SetAudioFocus(&p1));
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerSetDisplay) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ EXPECT_TRUE(mixer->SetDisplay(DisplayType::kOverlay,
+ Utility::Instance().GetWindow()));
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerCommit) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerMockPlayer p1(1, 1, 1, 1);
+ MixerMockPlayer p2(2, 2, 2, 2);
+
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ MixerTicket* ticket2 = mixer->CreateTicket(&p2);
+
+ ticket->RegisterListener(p1.listener_);
+ ticket2->RegisterListener(p2.listener_);
+
+ EXPECT_TRUE(mixer->Commit());
+
+ delete ticket;
+ delete ticket2;
+
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerSetDisplayRoi) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerMockPlayer p1;
+
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+
+ ticket->RegisterListener(p1.listener_);
+ Geometry geometry;
+ geometry.x = 11;
+ geometry.y = 11;
+ geometry.w = 22;
+ geometry.h = 22;
+ EXPECT_TRUE(mixer->SetDisplayRoi(geometry));
+
+ delete ticket;
+
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerSetDisplayMode) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerMockPlayer p1;
+
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_TRUE(mixer->SetDisplayMode(DisplayMode::kDstRoi));
+
+ delete ticket;
+
+ mixer.reset();
+}
+
+TEST(MixerTest, MixerRegisterListener) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerMockListener* listener = new MixerMockListener;
+ EXPECT_TRUE(mixer->RegisterListener(listener));
+ mixer.reset();
+ delete listener;
+}
+
+TEST(MixerTest, MixerStart) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ EXPECT_TRUE(mixer->SetDisplay(DisplayType::kOverlay,
+ Utility::Instance().GetWindow()));
+ Geometry geometry;
+ geometry.x = 0;
+ geometry.y = 0;
+ geometry.w = 1920;
+ geometry.h = 1080;
+ EXPECT_TRUE(mixer->SetDisplayRoi(geometry));
+ MixerMockListener* listener = new MixerMockListener;
+ EXPECT_TRUE(mixer->RegisterListener(listener));
+ EXPECT_TRUE(mixer->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ EXPECT_TRUE(mixer->Stop());
+ mixer.reset();
+ delete listener;
+}
+
+TEST(MixerTest, MixerSetAlternativeVideoScaler) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ EXPECT_TRUE(mixer->SetAlternativeVideoScaler());
+ EXPECT_TRUE(mixer->SetDisplay(DisplayType::kOverlay,
+ Utility::Instance().GetWindow()));
+ Geometry geometry;
+ geometry.x = 0;
+ geometry.y = 0;
+ geometry.w = 1920;
+ geometry.h = 1080;
+ EXPECT_TRUE(mixer->SetDisplayRoi(geometry));
+ MixerMockListener* listener = new MixerMockListener;
+ EXPECT_TRUE(mixer->RegisterListener(listener));
+ EXPECT_TRUE(mixer->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ EXPECT_TRUE(mixer->Stop());
+ mixer.reset();
+ delete listener;
+}
\ No newline at end of file
--- /dev/null
+
+
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include <gtest/gtest.h>
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer_capi/mixer_capi.h"
+#include "ut/include/appwindow.h"
+#include "ut/include/utils/utility.h"
+#include "mixer/mixerticket_eventlistener.h"
+#include "mixer/mixer.h"
+#include "mixer/mixer_eventlistener.h"
+#include "mixer/mixerticket.h"
+#include "mixer/mixerticket_eventlistener.h"
+
+using namespace esplusplayer;
+using namespace esplusplayer_ut;
+using namespace utils;
+
+class CMixerMockPlayer {
+ public:
+ CMixerMockPlayer() { listener_ = new MyTicketListener(this); }
+ CMixerMockPlayer(int x, int y, int w, int h) {
+ listener_ = new MyTicketListener(this);
+ display_info_.geometry.x = x;
+ display_info_.geometry.y = y;
+ display_info_.geometry.w = w;
+ display_info_.geometry.h = h;
+ }
+ ~CMixerMockPlayer() { delete listener_; }
+
+ class MyTicketListener : public MixerTicketEventListener {
+ public:
+ explicit MyTicketListener(CMixerMockPlayer* handler) : handler_(handler){};
+ bool OnAudioFocusChanged(bool active) {
+ LOG_INFO("My OnAudioFocusChanged [%d] player [%p]", active, handler_);
+ return true;
+ }
+
+ bool OnUpdateDisplayInfo(const DisplayInfo& cur_info,
+ DisplayInfo* new_info) {
+ *new_info = handler_->display_info_;
+ LOG_INFO("OnUpdateDisplayInfo x[%d] y[%d]",
+ handler_->display_info_.geometry.x,
+ handler_->display_info_.geometry.y);
+ return true;
+ }
+ CMixerMockPlayer* handler_ = nullptr;
+ };
+
+ public:
+ MixerTicketEventListener* listener_ = nullptr;
+ DisplayInfo display_info_;
+};
+
+TEST(CMixerTest, vdapi_mixer_create_p_1) {
+ mixer_handle mixer = mixer_create();
+ ASSERT_NE(mixer, nullptr);
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_create_ticket_p_1) {
+ CMixerMockPlayer p1;
+ mixer_handle mixer = mixer_create();
+ MixerTicket* ticket = (MixerTicket*)mixer_create_ticket(mixer, &p1);
+ ASSERT_NE(ticket, nullptr);
+ delete ticket;
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_rsc_alloc_mode_p_1) {
+ mixer_handle mixer = mixer_create();
+ EXPECT_EQ(mixer_set_rsc_alloc_mode(mixer, MIXER_RSC_ALLOC_MODE_DISABLE),
+ MIXER_ERROR_TYPE_NONE);
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_rsc_alloc_mode_n_1) {
+ mixer_handle mixer = mixer_create();
+ CMixerMockPlayer p1;
+ MixerTicket* ticket = (MixerTicket*)mixer_create_ticket(mixer, &p1);
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_EQ(mixer_set_rsc_alloc_mode(mixer, MIXER_RSC_ALLOC_MODE_DISABLE),
+ MIXER_ERROR_TYPE_INVALID_OPERATION);
+ delete ticket;
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_disable_audio_focus_setting_p_1) {
+ mixer_handle mixer = mixer_create();
+ EXPECT_EQ(mixer_disable_audio_focus_setting(mixer), MIXER_ERROR_TYPE_NONE);
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_disable_audio_focus_setting_n_1) {
+ mixer_handle mixer = mixer_create();
+ CMixerMockPlayer p1;
+ MixerTicket* ticket = (MixerTicket*)mixer_create_ticket(mixer, &p1);
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_EQ(mixer_disable_audio_focus_setting(mixer),
+ MIXER_ERROR_TYPE_INVALID_OPERATION);
+ delete ticket;
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_audio_focus_p_1) {
+ mixer_handle mixer = mixer_create();
+ CMixerMockPlayer p1;
+ MixerTicket* ticket = (MixerTicket*)mixer_create_ticket(mixer, &p1);
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_EQ(mixer_set_audio_focus(mixer, &p1), MIXER_ERROR_TYPE_NONE);
+ delete ticket;
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_audio_focus_p_2) {
+ mixer_handle mixer = mixer_create();
+ CMixerMockPlayer p1, p2;
+
+ MixerTicket* ticket = (MixerTicket*)mixer_create_ticket(mixer, &p1);
+ MixerTicket* ticket2 = (MixerTicket*)mixer_create_ticket(mixer, &p2);
+
+ ticket->RegisterListener(p1.listener_);
+ ticket2->RegisterListener(p2.listener_);
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer, &p2), MIXER_ERROR_TYPE_NONE);
+
+ delete ticket;
+ delete ticket2;
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_audio_focus_n_2) {
+ mixer_handle mixer = mixer_create();
+ mixer_disable_audio_focus_setting(mixer);
+ CMixerMockPlayer p1;
+ MixerTicket* ticket = (MixerTicket*)mixer_create_ticket(mixer, &p1);
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_EQ(mixer_set_audio_focus(mixer, &p1),
+ MIXER_ERROR_TYPE_INVALID_OPERATION);
+ delete ticket;
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_display_p_1) {
+ mixer_handle mixer = mixer_create();
+ EXPECT_EQ(mixer_set_display(mixer, MIXER_DISPLAY_TYPE_OVERLAY,
+ Utility::Instance().GetWindow()),
+ MIXER_ERROR_TYPE_NONE);
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_commit_p_1) {
+ mixer_handle mixer = mixer_create();
+ CMixerMockPlayer p1(1, 1, 1, 1);
+ CMixerMockPlayer p2(2, 2, 2, 2);
+
+ MixerTicket* ticket = (MixerTicket*)mixer_create_ticket(mixer, &p1);
+ MixerTicket* ticket2 = (MixerTicket*)mixer_create_ticket(mixer, &p2);
+
+ ticket->RegisterListener(p1.listener_);
+ ticket2->RegisterListener(p2.listener_);
+
+ EXPECT_EQ(mixer_commit(mixer), MIXER_ERROR_TYPE_NONE);
+
+ delete ticket;
+ delete ticket2;
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_display_roi_p_1) {
+ mixer_handle mixer = mixer_create();
+ CMixerMockPlayer p1;
+
+ MixerTicket* ticket = (MixerTicket*)mixer_create_ticket(mixer, &p1);
+
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_EQ(mixer_set_display_roi(mixer, 11, 11, 22, 22),
+ MIXER_ERROR_TYPE_NONE);
+
+ delete ticket;
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_display_mode_p_1) {
+ mixer_handle mixer = mixer_create();
+ CMixerMockPlayer p1;
+
+ MixerTicket* ticket = (MixerTicket*)mixer_create_ticket(mixer, &p1);
+
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_EQ(mixer_set_display_mode(mixer, MIXER_DISPLAY_MODE_DST_ROI),
+ MIXER_ERROR_TYPE_NONE);
+
+ delete ticket;
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_start_p_1) {
+ mixer_handle mixer = mixer_create();
+ mixer_set_display(mixer, MIXER_DISPLAY_TYPE_OVERLAY,
+ Utility::Instance().GetWindow());
+ mixer_set_display_roi(mixer, 0, 0, 1920, 1080);
+ EXPECT_EQ(mixer_start(mixer), MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ EXPECT_EQ(mixer_stop(mixer), MIXER_ERROR_TYPE_NONE);
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_alternative_video_scaler_p_1) {
+ mixer_handle mixer = mixer_create();
+ EXPECT_EQ(mixer_set_alternative_video_scaler(mixer), MIXER_ERROR_TYPE_NONE);
+ mixer_set_display(mixer, MIXER_DISPLAY_TYPE_OVERLAY,
+ Utility::Instance().GetWindow());
+ mixer_set_display_roi(mixer, 0, 0, 1920, 1080);
+
+ EXPECT_EQ(mixer_start(mixer), MIXER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ EXPECT_EQ(mixer_stop(mixer), MIXER_ERROR_TYPE_NONE);
+ mixer_destroy(mixer);
+}
+
+TEST(CMixerTest, vdapi_mixer_set_on_resource_conflict_cb_p_1) {
+ mixer_handle mixer = mixer_create();
+ EXPECT_EQ(mixer_set_resource_conflicted_cb(mixer, nullptr, nullptr),
+ MIXER_ERROR_TYPE_NONE);
+ mixer_destroy(mixer);
+}
+
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include <gtest/gtest.h>
+
+#include "esplusplayer_capi/esplusplayer_capi.h"
+#include "mixer_capi/mixer_capi.h"
+#include "ut/include/utils/utility.h"
+#include "ut/include/streamreader.hpp"
+
+using namespace esplusplayer;
+using namespace esplusplayer_ut;
+using namespace utils;
+using namespace std;
+
+using utils::Utility;
+std::string uri_1 = "youtube/";
+std::string uri_2 = "bunny/";
+
+// max playing duration : 10 sec => see EsStreamReader::ReadNextPacket()
+constexpr int kEsPlayingTime = 1; // (sec)
+
+class CMixerEsppTestF : public ::testing::Test {
+ public:
+ explicit CMixerEsppTestF(void)
+ : util_(Utility::Instance()),
+ mixer_(nullptr),
+ player1_(nullptr),
+ player2_(nullptr),
+ player3_(nullptr),
+ player4_(nullptr){};
+ ~CMixerEsppTestF(void){};
+
+ static void SetUpTestCase() {
+ ESPacketDownloader::Init();
+ std::cout << "SetUpTestCase()" << std::endl;
+ }
+
+ static void TearDownTestCase() {}
+
+ virtual void SetUp(void) override {
+ LOG_ERROR("%s", util_.GetCurrentTestName());
+ mixer_ = mixer_create();
+ mixer_set_display(mixer_, MIXER_DISPLAY_TYPE_OVERLAY, util_.GetWindow());
+
+ roi1_.x = 20;
+ roi1_.y = 20;
+ roi1_.w = 960;
+ roi1_.h = 540;
+
+ roi2_.x = 1000;
+ roi2_.y = 20;
+ roi2_.w = 720;
+ roi2_.h = 480;
+
+ roi3_.x = 20;
+ roi3_.y = 520;
+ roi3_.w = 720;
+ roi3_.h = 480;
+
+ roi4_.x = 1000;
+ roi4_.y = 520;
+ roi4_.w = 720;
+ roi4_.h = 480;
+ }
+
+ virtual void TearDown(void) override {
+ util_.DestroyESPP(player1_);
+ util_.DestroyESPP(player2_);
+ util_.DestroyESPP(player3_);
+ util_.DestroyESPP(player4_);
+ mixer_destroy(mixer_);
+ player1_ = nullptr;
+ player2_ = nullptr;
+ player3_ = nullptr;
+ player4_ = nullptr;
+ mixer_ = nullptr;
+ LOG_ERROR("%s", util_.GetCurrentTestName());
+ }
+
+ public:
+ Utility& util_;
+ mixer_handle mixer_;
+ esplusplayer_handle player1_;
+ esplusplayer_handle player2_;
+ esplusplayer_handle player3_;
+ esplusplayer_handle player4_;
+ Geometry roi1_;
+ Geometry roi2_;
+ Geometry roi3_;
+ Geometry roi4_;
+};
+
+TEST(CMixerEsppTest, vdapi_mixer_create_destroy_p_1) {
+ mixer_handle mixer = mixer_create();
+ ASSERT_NE(mixer, nullptr);
+ EXPECT_EQ(mixer_destroy(mixer), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_start_stop_p_1) {
+ player1_ = util_.GetPreparedMixESPP(uri_2, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetPreparedMixESPP(uri_2, mixer_, roi2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_EQ(esplusplayer_start(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_start(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(mixer_start(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kEsPlayingTime));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player2_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_get_max_allowed_number_of_player_p_1) {
+ int max_num = mixer_get_max_allowed_number_of_player(mixer_);
+ constexpr int MAX_NUM = 3;
+ EXPECT_EQ(MAX_NUM, max_num);
+}
+
+TEST(CMixerEsppTest, vdapi_mixer_set_display_p_1) {
+ mixer_handle mixer = mixer_create();
+ ASSERT_NE(mixer, nullptr);
+ Utility& util = Utility::Instance();
+ EXPECT_EQ(
+ mixer_set_display(mixer, MIXER_DISPLAY_TYPE_OVERLAY, util.GetWindow()),
+ MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(mixer_destroy(mixer), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_set_display_mode_p_1) {
+ EXPECT_EQ(mixer_set_display_mode(mixer_, MIXER_DISPLAY_MODE_DST_ROI),
+ MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_set_display_roi_p_1) {
+ EXPECT_EQ(mixer_set_display_mode(mixer_, MIXER_DISPLAY_MODE_DST_ROI),
+ MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(mixer_set_display_roi(mixer_, 0, 0, 1920, 1080),
+ MIXER_ERROR_TYPE_NONE);
+
+ player1_ = util_.GetStartedMixESPP(uri_2, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ mixer_start(mixer_);
+ std::this_thread::sleep_for(std::chrono::seconds(kEsPlayingTime));
+
+ EXPECT_EQ(mixer_set_display_roi(mixer_, roi2_.x, roi2_.y, roi2_.w, roi2_.h),
+ MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kEsPlayingTime));
+
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_set_rsc_alloc_mode_p_1) {
+ EXPECT_EQ(mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_DISABLE),
+ MIXER_ERROR_TYPE_NONE);
+ mixer_stop(mixer_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_set_rsc_alloc_mode_n_1) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_DISABLE),
+ MIXER_ERROR_TYPE_INVALID_OPERATION);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_esplusplayer_set_video_codec_type_p_1) {
+ EXPECT_EQ(mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_DISABLE),
+ MIXER_ERROR_TYPE_NONE);
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(esplusplayer_set_video_codec_type(
+ player1_, ESPLUSPLAYER_VIDEO_CODEC_TYPE_HW_N_DECODING),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ mixer_stop(mixer_);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_esplusplayer_set_video_codec_type_p_2) {
+ EXPECT_EQ(mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_DISABLE),
+ MIXER_ERROR_TYPE_NONE);
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(esplusplayer_set_video_codec_type(player1_,
+ ESPLUSPLAYER_VIDEO_CODEC_TYPE_HW),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ mixer_stop(mixer_);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_esplusplayer_set_video_codec_type_n_1) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(esplusplayer_set_video_codec_type(
+ player1_, ESPLUSPLAYER_VIDEO_CODEC_TYPE_HW_N_DECODING),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ mixer_stop(mixer_);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_esplusplayer_set_video_codec_type_n_2) {
+ player1_ = util_.GetOpenedESPP(roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(esplusplayer_set_video_codec_type(
+ player1_, ESPLUSPLAYER_VIDEO_CODEC_TYPE_HW_N_DECODING),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ mixer_stop(mixer_);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_esplusplayer_set_video_codec_type_n_3) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(esplusplayer_set_video_codec_type(player1_,
+ ESPLUSPLAYER_VIDEO_CODEC_TYPE_HW),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ mixer_stop(mixer_);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_esplusplayer_set_alternative_video_resource_p_1) {
+ EXPECT_EQ(mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_DISABLE),
+ MIXER_ERROR_TYPE_NONE);
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(esplusplayer_set_alternative_video_resource(player1_, 0),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_esplusplayer_set_alternative_video_resource_n_1) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(esplusplayer_set_alternative_video_resource(player1_, 0),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_esplusplayer_set_alternative_video_resource_n_2) {
+ EXPECT_EQ(mixer_set_rsc_alloc_mode(mixer_, MIXER_RSC_ALLOC_MODE_DEFAULT),
+ MIXER_ERROR_TYPE_NONE);
+ player1_ = util_.GetPreparedMixESPP(uri_2, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ EXPECT_EQ(esplusplayer_deactivate(player1_, ESPLUSPLAYER_STREAM_TYPE_VIDEO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_alternative_video_resource(player1_, 0),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+
+ mixer_stop(mixer_);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_disable_audio_focus_setting_p_1) {
+ EXPECT_EQ(mixer_disable_audio_focus_setting(mixer_), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_disable_audio_focus_setting_p_2) {
+ EXPECT_EQ(mixer_disable_audio_focus_setting(mixer_), MIXER_ERROR_TYPE_NONE);
+ player1_ = util_.GetPreparedMixESPP(uri_2, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ mixer_start(mixer_);
+
+ EXPECT_EQ(esplusplayer_deactivate(player1_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ MIXER_ERROR_TYPE_NONE);
+
+ mixer_stop(mixer_);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_disable_audio_focus_setting_n_1) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ EXPECT_EQ(mixer_disable_audio_focus_setting(mixer_),
+ MIXER_ERROR_TYPE_INVALID_OPERATION);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_esplusplayer_deactivate_p_1) {
+ EXPECT_EQ(mixer_disable_audio_focus_setting(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ player1_ = util_.GetPreparedMixESPP(uri_2, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(esplusplayer_deactivate(player1_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_esplusplayer_deactivate_n_1) {
+ player1_ = util_.GetPreparedMixESPP(uri_2, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_EQ(esplusplayer_deactivate(player1_, ESPLUSPLAYER_STREAM_TYPE_AUDIO),
+ ESPLUSPLAYER_ERROR_TYPE_INVALID_OPERATION);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_set_alternative_video_scaler_p_1) {
+ EXPECT_EQ(mixer_set_alternative_video_scaler(mixer_),
+ MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_set_audio_focus_p_1) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+
+ EXPECT_EQ(mixer_set_audio_focus(mixer_, player1_), MIXER_ERROR_TYPE_NONE);
+
+ EXPECT_EQ(esplusplayer_close(player1_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(mixer_stop(mixer_), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_commit_p_1) {
+ player1_ = util_.GetStartedMixESPP(uri_2, mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ mixer_start(mixer_);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kEsPlayingTime));
+
+ esplusplayer_set_display_roi(player1_, roi3_.x, roi3_.y, roi3_.w, roi3_.h);
+ EXPECT_EQ(mixer_commit(mixer_), MIXER_ERROR_TYPE_NONE);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kEsPlayingTime));
+
+ mixer_stop(mixer_);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_set_resolution_p_1) {
+ EXPECT_EQ(mixer_set_resolution(mixer_, 1920, 1080, 30, 1), MIXER_ERROR_TYPE_NONE);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_create_ticket_p_1) {
+ player1_ = util_.GetOpenedMixESPP(mixer_, roi1_);
+ ASSERT_NE(player1_, nullptr);
+ void* ticket = mixer_create_ticket(mixer_, player1_);
+ ASSERT_NE(ticket, nullptr);
+ esplusplayer_close(player1_);
+}
+
+TEST_F(CMixerEsppTestF, vdapi_mixer_set_resource_conflicted_cb_p_1) {
+ EXPECT_EQ(mixer_set_resource_conflicted_cb(mixer_, nullptr, nullptr),
+ MIXER_ERROR_TYPE_NONE);
+}
+
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include <gtest/gtest.h>
+
+#include <iostream>
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/mixer.h"
+#include "mixer/mixer_eventlistener.h"
+#include "mixer/mixerticket.h"
+#include "mixer/mixerticket_eventlistener.h"
+#include "ut/include/utils/utility.h"
+
+using namespace esplusplayer;
+using namespace esplusplayer_ut;
+using namespace utils;
+using namespace std;
+
+using utils::Utility;
+std::string dashuri =
+ "http://10.88.105.104/WebAPITest/DASH/SRCN_DASH_TC/DA540400/"
+ "DA540400_at4dash.mpd";
+std::string httpuri = "http://media.w3.org/2010/05/bunny/trailer.mp4";
+std::string hlsuri = "http://10.88.105.104/WebAPITest/HLS/Clean/normal.m3u8";
+std::string fhd1 =
+ "http://10.88.105.104/WebAPITest/http/0622_content/BTN_ISO1_1080_30fps.mp4";
+std::string fhd2 =
+ "http://10.88.105.104/WebAPITest/http/0622_content/BTN_ISO2_1080_30fps.mp4";
+std::string fhd3 =
+ "http://10.88.105.104/WebAPITest/http/0622_content/"
+ "BTN_ISO3_1080_30fps.mp4";
+constexpr int kPlayingTime = 5; // sec
+
+//------------------------------------------------------------
+class MixerScenarioTestF : public ::testing::Test {
+ public:
+ explicit MixerScenarioTestF(void)
+ : util_(Utility::Instance()),
+ mixer_(nullptr),
+ player1_(nullptr),
+ player2_(nullptr),
+ player3_(nullptr){};
+ ~MixerScenarioTestF(void){};
+
+ virtual void SetUp(void) override {
+ LOG_ERROR("%s", util_.GetCurrentTestName());
+ mixer_ = Mixer::Create();
+ mixer_->SetDisplay(DisplayType::kOverlay, util_.GetWindow());
+#if 1
+ geometry1_.x = 20;
+ geometry1_.y = 20;
+ geometry1_.w = 960;
+ geometry1_.h = 540;
+
+ geometry2_.x = 1000;
+ geometry2_.y = 20;
+ geometry2_.w = 720;
+ geometry2_.h = 480;
+
+ geometry3_.x = 20;
+ geometry3_.y = 520;
+ geometry3_.w = 720;
+ geometry3_.h = 480;
+
+ geometry4_.x = 1000;
+ geometry4_.y = 520;
+ geometry4_.w = 720;
+ geometry4_.h = 480;
+#else
+ geometry1_.x = 20;
+ geometry1_.y = 20;
+ geometry1_.w = 1180;
+ geometry1_.h = 720;
+
+ geometry2_.x = 1220;
+ geometry2_.y = 20;
+ geometry2_.w = 640;
+ geometry2_.h = 480;
+
+ geometry3_.x = 1220;
+ geometry3_.y = 520;
+ geometry3_.w = 640;
+ geometry3_.h = 480;
+#endif
+ }
+
+ virtual void TearDown(void) override {
+ util_.DestroyPlayer(player1_);
+ util_.DestroyPlayer(player2_);
+ util_.DestroyPlayer(player3_);
+ mixer_.reset();
+ LOG_ERROR("%s", util_.GetCurrentTestName());
+ }
+
+ public:
+ Utility& util_;
+ std::unique_ptr<Mixer> mixer_;
+ PlusPlayer::Ptr player1_;
+ PlusPlayer::Ptr player2_;
+ PlusPlayer::Ptr player3_;
+ PlusPlayer::Ptr player4_;
+ Geometry geometry1_;
+ Geometry geometry2_;
+ Geometry geometry3_;
+ Geometry geometry4_;
+};
+
+#if 0 // prepare async test
+TEST(MixerScenarioTest, OnePlayer) {
+ AppWindow appwin(0, 0, 1920, 1080);
+ // create player1
+ auto player1 = esplusplayer::PlusPlayer::Create();
+
+ shared_ptr<FakeUserData> user_data = std::make_shared<FakeUserData>();
+ shared_ptr<PlusPlayerMockEventListener> mock_eventlistener =
+ std::make_shared<PlusPlayerMockEventListener>();
+ shared_ptr<PlusPlayerFakeEventListener> fake_eventlistener =
+ std::make_shared<PlusPlayerFakeEventListener>();
+ mock_eventlistener->Bind(
+ std::dynamic_pointer_cast<EventListener>(fake_eventlistener));
+
+ ASSERT_NE(player1, nullptr);
+ EXPECT_TRUE(player1->Open(hlsuri.c_str()));
+ player1->RegisterListener(mock_eventlistener.get(), (void*)user_data.get());
+ // set mixer mode
+ EXPECT_TRUE(
+ player1->SetDisplay(DisplayType::kOverlay, appwin.GetWindow().obj));
+ Geometry geometry1;
+ geometry1.x = 0;
+ geometry1.y = 0;
+ geometry1.w = 1920;
+ geometry1.h = 1080;
+ EXPECT_TRUE(player1->SetDisplayMode(DisplayMode::kDstRoi));
+ EXPECT_TRUE(player1->SetDisplayRoi(geometry1));
+ EXPECT_TRUE(player1->PrepareAsync());
+ ASSERT_EQ(
+ user_data->onPrepareDone
+ .WaitForChange(PrepareStatus::kSuccess, DEFAULT_TIMEOUT_FOR_PREPARING)
+ .get(),
+ WatcherStatus::kSuccess);
+ EXPECT_TRUE(player1->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+ EXPECT_TRUE(player1->Stop());
+ EXPECT_TRUE(player1->Close());
+ player1.reset();
+}
+
+TEST(MixerScenarioTest, Basic) {
+ shared_ptr<FakeUserData> user_data1 = std::make_shared<FakeUserData>();
+ shared_ptr<PlusPlayerMockEventListener> mock_eventlistener1 =
+ std::make_shared<PlusPlayerMockEventListener>();
+ shared_ptr<PlusPlayerFakeEventListener> fake_eventlistener1 =
+ std::make_shared<PlusPlayerFakeEventListener>();
+ mock_eventlistener1->Bind(
+ std::dynamic_pointer_cast<EventListener>(fake_eventlistener1));
+
+ shared_ptr<FakeUserData> user_data2 = std::make_shared<FakeUserData>();
+ shared_ptr<PlusPlayerMockEventListener> mock_eventlistener2 =
+ std::make_shared<PlusPlayerMockEventListener>();
+ shared_ptr<PlusPlayerFakeEventListener> fake_eventlistener2 =
+ std::make_shared<PlusPlayerFakeEventListener>();
+ mock_eventlistener2->Bind(
+ std::dynamic_pointer_cast<EventListener>(fake_eventlistener2));
+
+ shared_ptr<FakeUserData> user_data3 = std::make_shared<FakeUserData>();
+ shared_ptr<PlusPlayerMockEventListener> mock_eventlistener3 =
+ std::make_shared<PlusPlayerMockEventListener>();
+ shared_ptr<PlusPlayerFakeEventListener> fake_eventlistener3 =
+ std::make_shared<PlusPlayerFakeEventListener>();
+ mock_eventlistener3->Bind(
+ std::dynamic_pointer_cast<EventListener>(fake_eventlistener3));
+
+ AppWindow appwin(0, 0, 1920, 1080);
+
+ // create mixer
+ auto mixer = Mixer::Create();
+ ASSERT_NE(mixer, nullptr);
+ EXPECT_TRUE(mixer->SetDisplay(DisplayType::kOverlay, appwin.GetWindow().obj));
+
+ // create player1
+ auto player1 = esplusplayer::PlusPlayer::Create();
+ ASSERT_NE(player1, nullptr);
+ EXPECT_TRUE(player1->Open(hlsuri.c_str()));
+ player1->RegisterListener(mock_eventlistener1.get(), (void*)user_data1.get());
+ // set mixer mode
+ EXPECT_TRUE(player1->SetDisplay(DisplayType::kMixer, mixer.get()));
+ Geometry geometry1;
+ geometry1.x = 20;
+ geometry1.y = 20;
+ geometry1.w = 1180;
+ geometry1.h = 720;
+ EXPECT_TRUE(player1->SetDisplayRoi(geometry1));
+ // set audio focus
+ EXPECT_TRUE(mixer->SetAudioFocus(player1.get()));
+ EXPECT_TRUE(player1->PrepareAsync());
+
+ // create player2
+ auto player2 = esplusplayer::PlusPlayer::Create();
+ EXPECT_NE(player2, nullptr);
+ EXPECT_TRUE(player2->Open(hlsuri.c_str()));
+ player2->RegisterListener(mock_eventlistener2.get(), (void*)user_data2.get());
+ // set mixer mode
+ EXPECT_TRUE(player2->SetDisplay(DisplayType::kMixer, mixer.get()));
+ Geometry geometry2;
+ geometry2.x = 1220;
+ geometry2.y = 20;
+ geometry2.w = 640;
+ geometry2.h = 480;
+ EXPECT_TRUE(player2->SetDisplayRoi(geometry2));
+ EXPECT_TRUE(player2->PrepareAsync());
+
+ // create player3
+ auto player3 = esplusplayer::PlusPlayer::Create();
+ EXPECT_NE(player3, nullptr);
+ EXPECT_TRUE(player3->Open(hlsuri.c_str()));
+ player3->RegisterListener(mock_eventlistener3.get(), (void*)user_data3.get());
+ // set mixer mode
+ EXPECT_TRUE(player3->SetDisplay(DisplayType::kMixer, mixer.get()));
+ Geometry geometry3;
+ geometry3.x = 1220;
+ geometry3.y = 520;
+ geometry3.w = 640;
+ geometry3.h = 480;
+ EXPECT_TRUE(player3->SetDisplayRoi(geometry3));
+ EXPECT_TRUE(player3->PrepareAsync());
+
+ ASSERT_EQ(
+ user_data1->onPrepareDone
+ .WaitForChange(PrepareStatus::kSuccess, DEFAULT_TIMEOUT_FOR_PREPARING)
+ .get(),
+ WatcherStatus::kSuccess);
+
+ ASSERT_EQ(
+ user_data2->onPrepareDone
+ .WaitForChange(PrepareStatus::kSuccess, DEFAULT_TIMEOUT_FOR_PREPARING)
+ .get(),
+ WatcherStatus::kSuccess);
+
+ ASSERT_EQ(
+ user_data3->onPrepareDone
+ .WaitForChange(PrepareStatus::kSuccess, DEFAULT_TIMEOUT_FOR_PREPARING)
+ .get(),
+ WatcherStatus::kSuccess);
+
+ EXPECT_TRUE(player1->Start());
+ EXPECT_TRUE(player2->Start());
+ EXPECT_TRUE(player3->Start());
+
+ EXPECT_TRUE(mixer->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer->Stop());
+ EXPECT_TRUE(player1->Stop());
+ EXPECT_TRUE(player2->Stop());
+ EXPECT_TRUE(player3->Stop());
+
+ EXPECT_TRUE(player1->Close());
+ EXPECT_TRUE(player2->Close());
+ EXPECT_TRUE(player3->Close());
+
+ player1.reset();
+ player2.reset();
+ player3.reset();
+ mixer.reset();
+}
+#endif
+#if 0 // memory check TC
+class MixerScenarioMemTestF : public ::testing::Test {
+ public:
+ explicit MixerScenarioMemTestF(void) : util_(Utility::Instance()){};
+ ~MixerScenarioMemTestF(void){};
+
+ virtual void SetUp(void) override {
+ LOG_ERROR("%s", util_.GetCurrentTestName());
+ geometry1_.x = 20;
+ geometry1_.y = 20;
+ geometry1_.w = 960;
+ geometry1_.h = 540;
+
+ geometry2_.x = 1000;
+ geometry2_.y = 20;
+ geometry2_.w = 720;
+ geometry2_.h = 480;
+
+ geometry3_.x = 1000;
+ geometry3_.y = 520;
+ geometry3_.w = 720;
+ geometry3_.h = 480;
+ }
+
+ virtual void TearDown(void) override {
+ LOG_ERROR("%s", util_.GetCurrentTestName());
+ }
+
+ public:
+ Utility& util_;
+ Geometry geometry1_;
+ Geometry geometry2_;
+ Geometry geometry3_;
+};
+
+TEST(MixerScenarioMemTestF, RealNothing) {
+ std::this_thread::sleep_for(std::chrono::seconds(20));
+}
+
+TEST_F(MixerScenarioMemTestF, OnlyWindow) {
+ std::this_thread::sleep_for(std::chrono::seconds(20));
+}
+
+TEST_F(MixerScenarioMemTestF, OnePlayer) {
+ // create player1
+ auto player1_ = util_.GetOpenedPlusPlayer(hlsuri);
+ ASSERT_NE(player1_, nullptr);
+ EXPECT_TRUE(player1_->Prepare());
+ EXPECT_TRUE(player1_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(40));
+ EXPECT_TRUE(player1_->Stop());
+ EXPECT_TRUE(player1_->Close());
+ player1_.reset();
+}
+
+TEST_F(MixerScenarioMemTestF, MixerThreePlayer) {
+ auto mixer_ = Mixer::Create();
+ mixer_->SetDisplay(DisplayType::kOverlay, util_.GetWindow());
+ auto player1_ =
+ util_.GetOpenedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+ auto player2_ =
+ util_.GetOpenedMixPlusPlayer(hlsuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+ auto player3_ =
+ util_.GetOpenedMixPlusPlayer(hlsuri, mixer_.get(), geometry3_);
+ ASSERT_NE(player3_, nullptr);
+
+ EXPECT_TRUE(mixer_->SetAudioFocus(player1_.get()));
+
+ EXPECT_TRUE(player1_->Prepare());
+ EXPECT_TRUE(player2_->Prepare());
+ EXPECT_TRUE(player3_->Prepare());
+
+ EXPECT_TRUE(player1_->Start());
+ EXPECT_TRUE(player2_->Start());
+ EXPECT_TRUE(player3_->Start());
+
+ EXPECT_TRUE(mixer_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(40));
+
+ EXPECT_TRUE(mixer_->Stop());
+ EXPECT_TRUE(player1_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+ EXPECT_TRUE(player3_->Stop());
+
+ EXPECT_TRUE(player1_->Close());
+ EXPECT_TRUE(player2_->Close());
+ EXPECT_TRUE(player3_->Close());
+
+ player1_.reset();
+ player2_.reset();
+ player3_.reset();
+
+ mixer_.reset();
+}
+
+TEST_F(MixerScenarioMemTestF, MixerOnePlayer) {
+ auto mixer_ = Mixer::Create();
+ mixer_->SetDisplay(DisplayType::kOverlay, util_.GetWindow());
+ auto player1_ =
+ util_.GetOpenedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+
+ EXPECT_TRUE(mixer_->SetAudioFocus(player1_.get()));
+
+ EXPECT_TRUE(player1_->Prepare());
+
+ EXPECT_TRUE(player1_->Start());
+
+ EXPECT_TRUE(mixer_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(40));
+
+ EXPECT_TRUE(mixer_->Stop());
+ EXPECT_TRUE(player1_->Stop());
+
+ EXPECT_TRUE(player1_->Close());
+
+ player1_.reset();
+ mixer_.reset();
+}
+#endif
+
+#if 1 // normal mixer test
+TEST_F(MixerScenarioTestF, Basic) {
+ player1_ = util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_TRUE(mixer_->SetAudioFocus(player1_.get()));
+
+ EXPECT_TRUE(player1_->Prepare());
+ EXPECT_TRUE(player2_->Prepare());
+
+ EXPECT_TRUE(player1_->Start());
+ EXPECT_TRUE(player2_->Start());
+
+ EXPECT_TRUE(mixer_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer_->Stop());
+ EXPECT_TRUE(player1_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+}
+
+TEST_F(MixerScenarioTestF, SinglePlay) {
+ player1_ = util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+
+ EXPECT_TRUE(mixer_->SetAudioFocus(player1_.get()));
+
+ EXPECT_TRUE(player1_->Prepare());
+
+ EXPECT_TRUE(player1_->Start());
+
+ EXPECT_TRUE(mixer_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer_->Stop());
+ EXPECT_TRUE(player1_->Stop());
+}
+
+#if 0 // n-decoding test ut : need to call n-decoding mode set api of player
+TEST_F(MixerScenarioTestF, MaxPlay) {
+ player1_ = util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+ player3_ = util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player3_, nullptr);
+ player4_ = util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player4_, nullptr);
+
+ EXPECT_TRUE(mixer_->SetAudioFocus(player1_.get()));
+
+ EXPECT_TRUE(player1_->Prepare());
+ EXPECT_TRUE(player2_->Prepare());
+ EXPECT_TRUE(player3_->Prepare());
+ EXPECT_TRUE(player4_->Prepare());
+
+ EXPECT_TRUE(player1_->Start());
+ EXPECT_TRUE(player2_->Start());
+ EXPECT_TRUE(player3_->Start());
+ EXPECT_TRUE(player4_->Start());
+
+ EXPECT_TRUE(mixer_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer_->Stop());
+ EXPECT_TRUE(player1_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+ EXPECT_TRUE(player3_->Stop());
+ EXPECT_TRUE(player4_->Stop());
+}
+#endif
+
+TEST_F(MixerScenarioTestF, DetachAttach) {
+ player1_ = util_.GetStartedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetStartedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_TRUE(mixer_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+ EXPECT_TRUE(player1_->Stop());
+
+ player3_ = util_.GetStartedMixPlusPlayer(dashuri, mixer_.get(), geometry3_);
+ ASSERT_NE(player3_, nullptr);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+ EXPECT_TRUE(player3_->Stop());
+}
+
+TEST_F(MixerScenarioTestF, DetachAttach1) {
+ player1_ = util_.GetStartedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetStartedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_TRUE(mixer_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+ EXPECT_TRUE(player1_->Stop());
+
+ player3_ = util_.GetStartedMixPlusPlayer(dashuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player3_, nullptr);
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+ EXPECT_TRUE(player3_->Stop());
+}
+
+TEST_F(MixerScenarioTestF, SetROI) {
+ player1_ = util_.GetStartedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetStartedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_TRUE(mixer_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(player1_->SetDisplayRoi(geometry2_));
+ EXPECT_TRUE(player2_->SetDisplayRoi(geometry1_));
+ EXPECT_TRUE(mixer_->Commit());
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer_->Stop());
+ EXPECT_TRUE(player1_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+}
+
+TEST_F(MixerScenarioTestF, SetROI1) {
+ player1_ = util_.GetStartedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetStartedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_TRUE(mixer_->Start());
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(player1_->SetDisplayRoi(geometry2_));
+ EXPECT_TRUE(player2_->SetDisplayRoi(geometry3_));
+ EXPECT_TRUE(mixer_->Commit());
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer_->Stop());
+ EXPECT_TRUE(player1_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+}
+
+TEST_F(MixerScenarioTestF, CheckMaxMixedPlayer) {
+ player1_ = util_.GetOpenedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+ player2_ = util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+ player3_ = util_.GetOpenedMixPlusPlayer(dashuri, mixer_.get(), geometry3_);
+ ASSERT_NE(player3_, nullptr);
+ auto player4 =
+ util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry3_);
+ ASSERT_NE(player4, nullptr);
+
+ EXPECT_TRUE(player1_->Prepare());
+ EXPECT_TRUE(player2_->Prepare());
+ EXPECT_TRUE(player3_->Prepare());
+
+ EXPECT_FALSE(player4->Prepare());
+
+ EXPECT_TRUE(player4->Close());
+ EXPECT_TRUE(mixer_->Stop());
+}
+
+TEST_F(MixerScenarioTestF, SetAudioFocus) {
+ player1_ = util_.GetOpenedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetOpenedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_TRUE(mixer_->SetAudioFocus(player1_.get()));
+
+ EXPECT_TRUE(player1_->Prepare());
+ EXPECT_TRUE(player2_->Prepare());
+
+ EXPECT_TRUE(player1_->Start());
+ EXPECT_TRUE(player2_->Start());
+
+ EXPECT_TRUE(mixer_->Start());
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer_->SetAudioFocus(player2_.get()));
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(mixer_->SetAudioFocus(player1_.get()));
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(player1_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+ EXPECT_TRUE(mixer_->Stop());
+}
+
+TEST_F(MixerScenarioTestF, SetAudioFocus1) {
+ player1_ = util_.GetStartedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+
+ player2_ = util_.GetStartedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_TRUE(mixer_->Start());
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(player1_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+ EXPECT_TRUE(mixer_->Stop());
+}
+
+TEST_F(MixerScenarioTestF, SetAudioFocus2) {
+ player1_ = util_.GetStartedMixPlusPlayer(hlsuri, mixer_.get(), geometry1_);
+ ASSERT_NE(player1_, nullptr);
+
+ EXPECT_FALSE(mixer_->SetAudioFocus(nullptr));
+ player2_ = util_.GetStartedMixPlusPlayer(httpuri, mixer_.get(), geometry2_);
+ ASSERT_NE(player2_, nullptr);
+
+ EXPECT_TRUE(mixer_->Start());
+
+ std::this_thread::sleep_for(std::chrono::seconds(kPlayingTime));
+
+ EXPECT_TRUE(player1_->Stop());
+ EXPECT_TRUE(player2_->Stop());
+ EXPECT_TRUE(mixer_->Stop());
+}
+#endif
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2020] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include <gtest/gtest.h>
+
+#include "core/utils/plusplayer_log.h"
+#include "mixer/mixer.h"
+#include "mixer/mixer_eventlistener.h"
+#include "mixer/mixerticket.h"
+#include "mixer/mixerticket_eventlistener.h"
+#include "ut/include/appwindow.h"
+
+using namespace esplusplayer;
+using namespace esplusplayer_ut;
+
+class MixerTicketMockPlayer {
+ public:
+ MixerTicketMockPlayer() { listener_ = new MyTicketListener(this); }
+ MixerTicketMockPlayer(int x, int y, int w, int h) {
+ listener_ = new MyTicketListener(this);
+ display_info_.geometry.x = x;
+ display_info_.geometry.y = y;
+ display_info_.geometry.w = w;
+ display_info_.geometry.h = h;
+ }
+ ~MixerTicketMockPlayer() { delete listener_; }
+
+ class MyTicketListener : public MixerTicketEventListener {
+ public:
+ explicit MyTicketListener(MixerTicketMockPlayer* handler)
+ : handler_(handler){};
+ bool OnAudioFocusChanged(bool active) {
+ LOG_INFO("My OnAudioFocusChanged [%d] player [%p]", active, handler_);
+ return true;
+ }
+
+ bool OnUpdateDisplayInfo(const DisplayInfo& cur_info,
+ DisplayInfo* new_info) {
+ *new_info = handler_->display_info_;
+ LOG_INFO("OnUpdateDisplayInfo x[%d] y[%d]",
+ handler_->display_info_.geometry.x,
+ handler_->display_info_.geometry.y);
+ return true;
+ }
+ MixerTicketMockPlayer* handler_ = nullptr;
+ };
+
+ public:
+ MixerTicketEventListener* listener_ = nullptr;
+ DisplayInfo display_info_;
+};
+
+class MyMixerListener : public MixerEventListener {
+ public:
+ MyMixerListener() {}
+ ~MyMixerListener() {}
+ void OnError() {
+ LOG_INFO(" listener [%p]", this);
+ }
+ void OnResourceConflicted() {
+ LOG_INFO("MyOnResourceConflicted listener[%p]", this);
+ }
+};
+
+TEST(MixerticketTest, MixerTicketRenderBasic) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1, p2;
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ MixerTicket* ticket2 = mixer->CreateTicket(&p2);
+ // EXPECT_TRUE(ticket->Render());
+ // EXPECT_TRUE(ticket2->Render());
+ delete ticket;
+ delete ticket2;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTicketRegisterListener) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1;
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ EXPECT_TRUE(ticket->RegisterListener(p1.listener_));
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTicketPrepare) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1;
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ticket->RegisterListener(p1.listener_);
+ EXPECT_TRUE(ticket->Prepare());
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTiecketGetAvailableResourceType_1) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1;
+
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+
+ ticket->RegisterListener(p1.listener_);
+ ResourceType type;
+ EXPECT_TRUE(
+ ticket->GetAvailableResourceType(ResourceCategory::kVideoDecoder, &type));
+
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTiecketGetAvailableResourceType_2) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1;
+
+ mixer->SetRscAllocMode(RscAllocMode::kDisable);
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+
+ ticket->RegisterListener(p1.listener_);
+ ResourceType type;
+ EXPECT_FALSE(
+ ticket->GetAvailableResourceType(ResourceCategory::kVideoDecoder, &type));
+
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTiecketAlloc) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1;
+
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+
+ ticket->RegisterListener(p1.listener_);
+ ResourceType type;
+ EXPECT_TRUE(
+ ticket->GetAvailableResourceType(ResourceCategory::kVideoDecoder, &type));
+ EXPECT_TRUE(ticket->Alloc(ResourceCategory::kVideoDecoder, type));
+
+ EXPECT_TRUE(
+ ticket->GetAvailableResourceType(ResourceCategory::kVideoDecoder, &type));
+ EXPECT_TRUE(ticket->Alloc(ResourceCategory::kVideoDecoder, type));
+
+ EXPECT_TRUE(
+ ticket->GetAvailableResourceType(ResourceCategory::kVideoDecoder, &type));
+ EXPECT_TRUE(ticket->Alloc(ResourceCategory::kVideoDecoder, type));
+
+ EXPECT_FALSE(
+ ticket->GetAvailableResourceType(ResourceCategory::kVideoDecoder, &type));
+ EXPECT_FALSE(ticket->Alloc(ResourceCategory::kVideoDecoder, type));
+
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTiecketDeallocResource) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1, p2, p3;
+
+ MixerTicket* ticket1 = mixer->CreateTicket(&p1);
+
+ ResourceType type;
+ EXPECT_TRUE(ticket1->GetAvailableResourceType(ResourceCategory::kVideoDecoder,
+ &type));
+ EXPECT_TRUE(ticket1->Alloc(ResourceCategory::kVideoDecoder, type));
+
+ delete ticket1;
+
+ MixerTicket* ticket2 = mixer->CreateTicket(&p2);
+ EXPECT_TRUE(ticket2->GetAvailableResourceType(ResourceCategory::kVideoDecoder,
+ &type));
+ EXPECT_TRUE(ticket2->Alloc(ResourceCategory::kVideoDecoder, type));
+
+ MixerTicket* ticket3 = mixer->CreateTicket(&p3);
+ EXPECT_TRUE(ticket3->GetAvailableResourceType(ResourceCategory::kVideoDecoder,
+ &type));
+ EXPECT_TRUE(ticket3->Alloc(ResourceCategory::kVideoDecoder, type));
+
+ delete ticket2;
+ delete ticket3;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTiecketIsAudioFocusHandler_1) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1;
+
+ mixer->DisableAudioFocusSetting();
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ticket->RegisterListener(p1.listener_);
+
+ EXPECT_FALSE(ticket->IsAudioFocusHandler());
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTiecketIsAudioFocusHandler_2) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1;
+
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ticket->RegisterListener(p1.listener_);
+
+ EXPECT_TRUE(ticket->IsAudioFocusHandler());
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTiecketIsRscAllocHandler_1) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1;
+
+ mixer->SetRscAllocMode(RscAllocMode::kDisable);
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ticket->RegisterListener(p1.listener_);
+
+ EXPECT_FALSE(ticket->IsRscAllocHandler());
+ delete ticket;
+ mixer.reset();
+}
+
+TEST(MixerticketTest, MixerTiecketIsRscAllocHandler_2) {
+ std::unique_ptr<Mixer> mixer = Mixer::Create();
+ MixerTicketMockPlayer p1;
+
+ MixerTicket* ticket = mixer->CreateTicket(&p1);
+ ticket->RegisterListener(p1.listener_);
+
+ EXPECT_TRUE(ticket->IsRscAllocHandler());
+ delete ticket;
+ mixer.reset();
+}
\ No newline at end of file
--- /dev/null
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "mixer/constant.h"
+#include "mixer/matcher.h"
+#include "mixer/mock/mock_memallocator.h"
+#include "mixer/mock/mock_renderableobj.h"
+#include "mixer/mock/mock_renderableobj_factory.h"
+#include "mixer/mock/mock_renderer_evtlistener.h"
+#include "mixer/mock/mock_vpcollection.h"
+#include "mixer/mock/mock_vpmanipulator.h"
+#include "mixer/mock/mock_vpscaler.h"
+#include "mixer/renderer.h"
+
+using namespace esplusplayer;
+using namespace esplusplayer_ut;
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::AtLeast;
+using ::testing::DoAll;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Return;
+using ::testing::ReturnPointee;
+using ::testing::SetArgReferee;
+using ::testing::SizeIs;
+
+/********************************************************************************************
+ * InvalidRendererTest
+ */
+class InvalidRendererTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ ON_CALL(GetRenderableObjectFactory(), CreateRenderableObject(_, _))
+ .WillByDefault(Return(nullptr));
+
+ EXPECT_CALL(GetRenderableObjectFactory(),
+ CreateRenderableObject(kInvalidWidth, kInvalidHeight));
+
+ renderer_ = std::unique_ptr<Renderer>(new Renderer(
+ GetRenderableObjectFactory(),
+ GetResolutionInfo(kInvalidWidth, kInvalidHeight, kInvalidFramerateNum,
+ kInvalidFramerateDen),
+ nullptr));
+
+ ASSERT_FALSE(renderer_->IsValid());
+ }
+ virtual void TearDown() override {}
+
+ protected:
+ Renderer& GetRenderer() { return *renderer_; }
+
+ const MockRenderableObjectFactory& GetRenderableObjectFactory() const {
+ return mock_mixed_frame_factory_;
+ }
+ const MockVideoPlaneScaler& GetVideoPlaneScaler() const {
+ return mock_vpscaler_;
+ }
+ const MockVideoPlaneCollection* GetVideoPlaneCollection() const {
+ return &videoplanecollection_;
+ }
+
+ private:
+ std::unique_ptr<Renderer> renderer_;
+ MockRenderableObjectFactory mock_mixed_frame_factory_;
+ MockVideoPlaneScaler mock_vpscaler_;
+ MockVideoPlaneCollection videoplanecollection_;
+};
+
+TEST_F(InvalidRendererTest, Start) { EXPECT_FALSE(GetRenderer().Start()); }
+
+TEST_F(InvalidRendererTest, Stop) { EXPECT_FALSE(GetRenderer().Stop()); }
+
+TEST_F(InvalidRendererTest, ChangeRenderingSpeed) {
+ EXPECT_FALSE(
+ GetRenderer().ChangeRenderingSpeed(kFastFramerateNum, kFastFramerateDen));
+}
+
+TEST_F(InvalidRendererTest, Render) {
+ EXPECT_FALSE(GetRenderer().Render(
+ &GetVideoPlaneScaler(), GetVideoPlaneCollection(),
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH)));
+}
+
+/********************************************************************************************
+ * RendererConstructionTest
+ */
+class RendererConstructionTest : public ::testing::Test {
+ public:
+ explicit RendererConstructionTest() {}
+ virtual ~RendererConstructionTest() {}
+ virtual void SetUp() override {
+ mock_mixed_frame_ = new MockRenderableObject();
+
+ ON_CALL(GetRenderableObject(), IsValid()).WillByDefault(Return(true));
+
+ ON_CALL(GetRenderableObjectFactory(), CreateRenderableObject(_, _))
+ .WillByDefault(Return(mock_mixed_frame_));
+ }
+ virtual void TearDown() override {}
+
+ protected:
+ MockRenderableObject& GetRenderableObject() { return *mock_mixed_frame_; }
+ const MockRenderableObjectFactory& GetRenderableObjectFactory() const {
+ return mock_mixed_frame_factory_;
+ }
+ MockRendererEventListener* GetListener() { return nullptr; }
+
+ private:
+ MockRenderableObject* mock_mixed_frame_;
+ MockRenderableObjectFactory mock_mixed_frame_factory_;
+};
+
+TEST_F(RendererConstructionTest, IsValid) {
+ EXPECT_CALL(GetRenderableObject(), IsValid()).Times(AtLeast(1));
+ EXPECT_CALL(GetRenderableObjectFactory(),
+ CreateRenderableObject(kDefaultWidth, kDefaultHeight))
+ .Times(1);
+
+ Renderer renderer(
+ GetRenderableObjectFactory(),
+ GetResolutionInfo(kDefaultWidth, kDefaultHeight, kDefaultFramerateNum,
+ kDefaultFramerateDen),
+ GetListener());
+
+ EXPECT_TRUE(renderer.IsValid());
+}
+
+TEST_F(RendererConstructionTest, IsValid_WithReturnNullMixedFrame) {
+ // FIXME(bayle.park): memleak will occur by mock_mixed_frame_
+ EXPECT_CALL(GetRenderableObjectFactory(),
+ CreateRenderableObject(kDefaultWidth, kDefaultHeight))
+ .WillOnce(Return(nullptr));
+
+ Renderer renderer(
+ GetRenderableObjectFactory(),
+ GetResolutionInfo(kDefaultWidth, kDefaultHeight, kDefaultFramerateNum,
+ kDefaultFramerateDen),
+ GetListener());
+
+ EXPECT_FALSE(renderer.IsValid());
+}
+
+TEST_F(RendererConstructionTest, IsValid_WithInvalidMixedFrame) {
+ EXPECT_CALL(GetRenderableObject(), IsValid()).WillRepeatedly(Return(false));
+ EXPECT_CALL(GetRenderableObjectFactory(),
+ CreateRenderableObject(kDefaultWidth, kDefaultHeight))
+ .Times(1);
+
+ Renderer renderer(
+ GetRenderableObjectFactory(),
+ GetResolutionInfo(kDefaultWidth, kDefaultHeight, kDefaultFramerateNum,
+ kDefaultFramerateDen),
+ GetListener());
+
+ EXPECT_FALSE(renderer.IsValid());
+}
+
+/********************************************************************************************
+ * RendererTest
+ */
+class RendererTest : public ::testing::Test {
+ public:
+ explicit RendererTest() {}
+ virtual ~RendererTest() {}
+ virtual void SetUp() override {
+ mock_mixed_frame_ = new MockRenderableObject();
+
+ ON_CALL(GetRenderableObject(), GetVideoPlaneManipInfo())
+ .WillByDefault(Return(std::vector<VideoPlaneManipulableInfo>(
+ {kYComponentDestVMInfo, kUVComponentDestVMInfo})));
+ ON_CALL(GetRenderableObject(), IsValid()).WillByDefault(Return(true));
+ ON_CALL(GetRenderableObject(), GetWidth())
+ .WillByDefault(Return(kDefaultWidth));
+ ON_CALL(GetRenderableObject(), GetHeight())
+ .WillByDefault(Return(kDefaultHeight));
+ ON_CALL(GetRenderableObject(), GetSize())
+ .WillByDefault(Return(kExpectedDefaultByteSize));
+ ON_CALL(GetRenderableObject(), Render(_, _, _)).WillByDefault(Return(true));
+ ON_CALL(GetRenderableObject(), Export(_))
+ .WillByDefault(
+ DoAll(SetArgReferee<0>(kDefaultBufferKey), Return(true)));
+
+ ON_CALL(*GetVideoPlaneCollection(), GetVideoPlaneManipInfo())
+ .WillByDefault(Return(std::vector<VideoPlaneManipulableInfo>(
+ {kYComponentDestVMInfo, kUVComponentDestVMInfo})));
+
+ ON_CALL(GetRenderableObjectFactory(), CreateRenderableObject(_, _))
+ .WillByDefault(Return(mock_mixed_frame_));
+
+ ON_CALL(GetVideoPlaneScaler(), GetScaleManipulator())
+ .WillByDefault(Return(&GetScaler()));
+
+ ON_CALL(GetScaler(), Do(_, _)).WillByDefault(Return(true));
+
+ ON_CALL(*GetListener(), OnRenderingRelease(_)).WillByDefault(Return(true));
+
+ EXPECT_CALL(GetRenderableObjectFactory(),
+ CreateRenderableObject(kDefaultWidth, kDefaultHeight));
+
+ renderer_ = std::unique_ptr<Renderer>(new Renderer(
+ GetRenderableObjectFactory(),
+ GetResolutionInfo(kDefaultWidth, kDefaultHeight, kDefaultFramerateNum,
+ kDefaultFramerateDen),
+ GetListener()));
+
+ EXPECT_CALL(GetRenderableObject(), IsValid());
+ ASSERT_TRUE(renderer_->IsValid());
+ }
+ virtual void TearDown() override {}
+
+ protected:
+ Renderer& GetRenderer() { return *renderer_; }
+
+ MockRenderableObject& GetRenderableObject() { return *mock_mixed_frame_; }
+ const MockVideoPlaneScaler& GetVideoPlaneScaler() const {
+ return mock_vpscaler_;
+ }
+ const MockVideoPlaneManipulator& GetScaler() const { return scaler_; }
+ const MockRenderableObjectFactory& GetRenderableObjectFactory() const {
+ return mock_mixed_frame_factory_;
+ }
+ MockRendererEventListener* GetListener() { return &listener_; }
+ const MockVideoPlaneCollection* GetVideoPlaneCollection() const {
+ return &videoplanecollection_;
+ }
+
+ private:
+ std::unique_ptr<Renderer> renderer_;
+ MockVideoPlaneScaler mock_vpscaler_;
+ MockVideoPlaneManipulator scaler_;
+ MockRenderableObject* mock_mixed_frame_;
+ MockRenderableObjectFactory mock_mixed_frame_factory_;
+ MockRendererEventListener listener_;
+ MockVideoPlaneCollection videoplanecollection_;
+};
+
+TEST_F(RendererTest, Start) {
+ EXPECT_CALL(GetRenderableObject(), IsValid()).Times(AtLeast(1));
+
+ EXPECT_TRUE(GetRenderer().Start());
+}
+
+TEST_F(RendererTest, Start_WithTwiceCall) {
+ EXPECT_CALL(GetRenderableObject(), IsValid()).Times(AtLeast(1));
+
+ EXPECT_TRUE(GetRenderer().Start());
+ EXPECT_FALSE(GetRenderer().Start());
+}
+
+TEST_F(RendererTest, Stop) {
+ EXPECT_CALL(GetRenderableObject(), IsValid()).Times(AtLeast(1));
+
+ EXPECT_FALSE(GetRenderer().Stop());
+}
+
+TEST_F(RendererTest, Stop_AfterStart) {
+ EXPECT_CALL(GetRenderableObject(), IsValid()).Times(AtLeast(1));
+
+ EXPECT_TRUE(GetRenderer().Start());
+ EXPECT_TRUE(GetRenderer().Stop());
+}
+
+TEST_F(RendererTest, ChangeResolution) {
+ auto* new_frame = new MockRenderableObject();
+ EXPECT_CALL(*new_frame, IsValid()).WillOnce(Return(true));
+ EXPECT_CALL(GetRenderableObjectFactory(),
+ CreateRenderableObject(kSmallWidth, kSmallHeight))
+ .WillOnce(Return(new_frame));
+
+ EXPECT_TRUE(GetRenderer().ChangeResolution(GetRenderableObjectFactory(),
+ kSmallWidth, kSmallHeight));
+}
+
+TEST_F(RendererTest, ChangeResolution_WithInvalidResolution) {
+ EXPECT_FALSE(GetRenderer().ChangeResolution(GetRenderableObjectFactory(),
+ kInvalidWidth, kInvalidHeight));
+ EXPECT_FALSE(GetRenderer().ChangeResolution(GetRenderableObjectFactory(),
+ kSmallWidth, kInvalidHeight));
+ EXPECT_FALSE(GetRenderer().ChangeResolution(GetRenderableObjectFactory(),
+ kInvalidWidth, kSmallHeight));
+}
+
+TEST_F(RendererTest, ChangeResolution_WithSameResolution) {
+ EXPECT_FALSE(GetRenderer().ChangeResolution(GetRenderableObjectFactory(),
+ kDefaultWidth, kDefaultHeight));
+}
+
+TEST_F(RendererTest, ChangeResolution_WithReturnNullFrame) {
+ EXPECT_CALL(GetRenderableObjectFactory(),
+ CreateRenderableObject(kSmallWidth, kSmallHeight))
+ .WillOnce(Return(nullptr));
+
+ EXPECT_FALSE(GetRenderer().ChangeResolution(GetRenderableObjectFactory(),
+ kSmallWidth, kSmallHeight));
+}
+
+TEST_F(RendererTest, ChangeResolution_WithInvalidNewFrame) {
+ auto* new_frame = new MockRenderableObject();
+ EXPECT_CALL(*new_frame, IsValid()).WillOnce(Return(false));
+ EXPECT_CALL(GetRenderableObjectFactory(),
+ CreateRenderableObject(kSmallWidth, kSmallHeight))
+ .WillOnce(Return(new_frame));
+
+ EXPECT_FALSE(GetRenderer().ChangeResolution(GetRenderableObjectFactory(),
+ kSmallWidth, kSmallHeight));
+}
+
+TEST_F(RendererTest, ChangeRenderingSpeed) {
+ EXPECT_CALL(GetRenderableObject(), IsValid()).Times(AtLeast(1));
+
+ EXPECT_TRUE(
+ GetRenderer().ChangeRenderingSpeed(kFastFramerateNum, kFastFramerateDen));
+}
+
+TEST_F(RendererTest, ChangeRenderingSpeed_WithInvalidFramerate) {
+ EXPECT_FALSE(GetRenderer().ChangeRenderingSpeed(kInvalidFramerateNum,
+ kInvalidFramerateDen));
+ EXPECT_FALSE(GetRenderer().ChangeRenderingSpeed(kFastFramerateNum,
+ kInvalidFramerateDen));
+ EXPECT_FALSE(GetRenderer().ChangeRenderingSpeed(kInvalidFramerateNum,
+ kFastFramerateDen));
+}
+
+TEST_F(RendererTest, Render) {
+ EXPECT_CALL(
+ GetRenderableObject(),
+ Render(
+ Eq(&GetScaler()),
+ AllOf(SizeIs(2),
+ ElementsAre(
+ IsSameVideoPlaneManipulableInfo(kYComponentDestVMInfo),
+ IsSameVideoPlaneManipulableInfo(kUVComponentDestVMInfo))),
+ AllOf(Field(&Geometry::x, Eq(kDefaultX)),
+ Field(&Geometry::y, Eq(kDefaultY)),
+ Field(&Geometry::w, Eq(kDefaultW)),
+ Field(&Geometry::h, Eq(kDefaultH)))))
+ .Times(1);
+ EXPECT_CALL(*GetVideoPlaneCollection(), GetVideoPlaneManipInfo()).Times(1);
+ EXPECT_CALL(GetRenderableObject(), IsValid()).Times(AtLeast(1));
+ EXPECT_CALL(GetVideoPlaneScaler(), GetScaleManipulator()).Times(1);
+
+ EXPECT_TRUE(GetRenderer().Render(
+ &GetVideoPlaneScaler(), GetVideoPlaneCollection(),
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH)));
+}
+
+TEST_F(RendererTest, Render_WithNullCollection) {
+ EXPECT_FALSE(GetRenderer().Render(
+ &GetVideoPlaneScaler(), nullptr,
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH)));
+}
+
+TEST_F(RendererTest, Render_WithNullScaler) {
+ EXPECT_CALL(GetRenderableObject(), Render(_, _, _)).Times(0);
+
+ EXPECT_FALSE(GetRenderer().Render(
+ nullptr, GetVideoPlaneCollection(),
+ GetGeometry(kDefaultX, kDefaultY, kDefaultW, kDefaultH)));
+}
\ No newline at end of file
--- /dev/null
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "mixer/constant.h"
+#include "mixer/mock/mock_bufferobject.h"
+#include "mixer/tizen/tizenbuffermgr.h"
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Field;
+using ::testing::NotNull;
+using ::testing::Return;
+
+namespace {
+using namespace esplusplayer;
+struct MockTBM {
+ public:
+ class Fake {
+ public:
+ MOCK_METHOD3(BoAlloc, BufferDefaultType(tbm_bufmgr, int, int));
+ MOCK_METHOD1(BoUnRef, void(BufferDefaultType));
+ MOCK_METHOD2(BoImport, BufferDefaultType(tbm_bufmgr, BufferKeyType));
+ MOCK_METHOD2(GACopy, int(tbm_bufmgr, GraphicsGABltRopInfo*));
+ MOCK_METHOD2(GAScale, int(tbm_bufmgr, GraphicsGAScaleInfo*));
+ MOCK_METHOD2(GAFill, int(tbm_bufmgr, GraphicsGAFillRectInfo*));
+ };
+
+ static Fake* instance;
+
+ static void BoUnRef(BufferDefaultType bo) { instance->BoUnRef(bo); }
+
+ static BufferDefaultType BoAlloc(tbm_bufmgr bufmgr, int size, int flag) {
+ return instance->BoAlloc(bufmgr, size, flag);
+ }
+ static BufferDefaultType BoImport(tbm_bufmgr bufmgr, BufferKeyType key) {
+ return instance->BoImport(bufmgr, key);
+ }
+
+ static int GAScale(tbm_bufmgr bufmgr, GraphicsGAScaleInfo* info) {
+ return instance->GAScale(bufmgr, info);
+ }
+
+ static int GACopy(tbm_bufmgr bufmgr, GraphicsGABltRopInfo* info) {
+ return instance->GACopy(bufmgr, info);
+ }
+
+ static int GAFill(tbm_bufmgr bufmgr, GraphicsGAFillRectInfo* info) {
+ return instance->GAFill(bufmgr, info);
+ }
+};
+
+MockTBM::Fake* MockTBM::instance = nullptr;
+} // namespace
+
+namespace esplusplayer_ut {
+
+using TizenBufferManager = BufferManagerWithType<MockTBM>;
+
+class TizenBufferManagerTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ MockTBM::instance = &GetTBM();
+
+ ON_CALL(GetTBM(), BoAlloc(_, _, _)).WillByDefault(Return(kDefaultBuffer));
+ ON_CALL(GetTBM(), BoImport(_, _)).WillByDefault(Return(kDefaultBuffer));
+ }
+ virtual void TearDown() override {}
+
+ protected:
+ MockTBM::Fake& GetTBM() { return fake_tbm_; }
+ TizenBufferManager& GetBufferManager() { return bufmgr_; }
+
+ private:
+ MockTBM::Fake fake_tbm_;
+ TizenBufferManager bufmgr_;
+};
+
+TEST_F(TizenBufferManagerTest, IsValid) {
+ EXPECT_TRUE(GetBufferManager().IsValid());
+}
+
+TEST_F(TizenBufferManagerTest, Allocate) {
+ EXPECT_CALL(GetTBM(), BoAlloc(NotNull(), kExpectedDefaultByteSize,
+ TBM_BO_SCANOUT | (1 << 17)));
+ EXPECT_CALL(GetTBM(), BoUnRef(NotNull()));
+
+ auto* bufferobj = GetBufferManager().Allocate(kExpectedDefaultByteSize);
+ EXPECT_NE(nullptr, bufferobj);
+ if (bufferobj) delete bufferobj;
+}
+
+TEST_F(TizenBufferManagerTest, Allocate_WithNullBO) {
+ EXPECT_CALL(GetTBM(), BoAlloc(NotNull(), kExpectedDefaultByteSize,
+ TBM_BO_SCANOUT | (1 << 17)))
+ .WillOnce(Return(nullptr));
+
+ EXPECT_EQ(nullptr, GetBufferManager().Allocate(kExpectedDefaultByteSize));
+}
+
+TEST_F(TizenBufferManagerTest, Allocate_WithInvalidBufferSize) {
+ EXPECT_EQ(nullptr, GetBufferManager().Allocate(kInvalidBufferSize));
+}
+
+TEST_F(TizenBufferManagerTest, Import) {
+ EXPECT_CALL(GetTBM(), BoImport(NotNull(), _));
+ EXPECT_CALL(GetTBM(), BoUnRef(NotNull()));
+
+ EXPECT_NE(nullptr, GetBufferManager().Import(kDefaultBufferHandle));
+}
+
+TEST_F(TizenBufferManagerTest, Import_WithNullBO) {
+ EXPECT_CALL(GetTBM(), BoImport(NotNull(), _)).WillOnce(Return(nullptr));
+
+ EXPECT_EQ(nullptr, GetBufferManager().Import(kDefaultBufferHandle));
+}
+
+TEST_F(TizenBufferManagerTest, GetScaleManipulator) {
+ EXPECT_NE(nullptr, GetBufferManager().GetScaleManipulator());
+}
+
+class TizenBufferManagerScalerTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ MockTBM::instance = &GetTBM();
+ ON_CALL(GetTBM(), GAScale(_, _)).WillByDefault(Return(true));
+ }
+ virtual void TearDown() override {}
+
+ protected:
+ MockTBM::Fake& GetTBM() { return fake_tbm_; }
+ const VideoPlaneManipulator& GetScaler() {
+ return *bufmgr_.GetScaleManipulator();
+ }
+
+ private:
+ MockTBM::Fake fake_tbm_;
+ TizenBufferManager bufmgr_;
+};
+
+TEST_F(TizenBufferManagerScalerTest, Do) {
+ EXPECT_CALL(
+ GetTBM(),
+ GAScale(
+ NotNull(),
+ AllOf(
+ Field(&GraphicsGAScaleInfo::ga_mode, GRAPHICS_GA_SCALE_MODE),
+ Field(&GraphicsGAScaleInfo::rop_mode, GRAPHICS_GA_ROP_COPY),
+ Field(&GraphicsGAScaleInfo::ga_op_type, GRAPHICS_GA_SCALE),
+ Field(&GraphicsGAScaleInfo::pre_alphamode, 0),
+ Field(&GraphicsGAScaleInfo::ca_value, 0),
+ Field(&GraphicsGAScaleInfo::rop_on_off, 0),
+ Field(&GraphicsGAScaleInfo::src_handle,
+ kYComponentSrcVMInfo.handle),
+ Field(&GraphicsGAScaleInfo::src_hbytesize,
+ kYComponentSrcVMInfo.linesize),
+ Field(
+ &GraphicsGAScaleInfo::src_rect,
+ AllOf(
+ Field(&GraphicsRectInfo::x, kYComponentSrcVMInfo.rect.x),
+ Field(&GraphicsRectInfo::y, kYComponentSrcVMInfo.rect.y),
+ Field(&GraphicsRectInfo::w, kYComponentSrcVMInfo.rect.w),
+ Field(&GraphicsRectInfo::h,
+ kYComponentSrcVMInfo.rect.h))),
+ Field(&GraphicsGAScaleInfo::dst_handle,
+ kYComponentDestVMInfo.handle),
+ Field(&GraphicsGAScaleInfo::dst_hbytesize,
+ kYComponentDestVMInfo.linesize),
+ Field(
+ &GraphicsGAScaleInfo::dst_rect,
+ AllOf(
+ Field(&GraphicsRectInfo::x, kYComponentDestVMInfo.rect.x),
+ Field(&GraphicsRectInfo::y, kYComponentDestVMInfo.rect.y),
+ Field(&GraphicsRectInfo::w, kYComponentDestVMInfo.rect.w),
+ Field(&GraphicsRectInfo::h,
+ kYComponentDestVMInfo.rect.h))))));
+
+ EXPECT_TRUE(GetScaler().Do(kYComponentSrcVMInfo, kYComponentDestVMInfo));
+}
+
+TEST_F(TizenBufferManagerScalerTest, Do_WithDifferentComponent) {
+ EXPECT_FALSE(GetScaler().Do(kYComponentSrcVMInfo, kUVComponentDestVMInfo));
+}
+} // namespace plusplayer_ut
--- /dev/null
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <iostream>
+
+#include "mixer/constant.h"
+#include "mixer/tizen/tizenbufferobj.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace {
+using namespace esplusplayer;
+
+struct MockTBM {
+ public:
+ class Fake {
+ public:
+ MOCK_METHOD1(BoRef, BufferDefaultType(BufferDefaultType));
+ MOCK_METHOD1(BoUnRef, void(BufferDefaultType));
+ MOCK_METHOD1(BoSize, int(BufferDefaultType));
+ MOCK_METHOD2(BoGetHandle, BufferUnionHandleType(BufferDefaultType, int));
+ MOCK_METHOD1(BoExport, BufferKeyType(BufferDefaultType));
+ };
+
+ static Fake* instance;
+
+ static BufferDefaultType BoRef(BufferDefaultType bo) {
+ return instance->BoRef(bo);
+ }
+
+ static void BoUnRef(BufferDefaultType bo) { instance->BoUnRef(bo); }
+
+ static int BoSize(BufferDefaultType bo) { return instance->BoSize(bo); }
+
+ static BufferUnionHandleType BoGetHandle(BufferDefaultType bo, int device) {
+ return instance->BoGetHandle(bo, device);
+ }
+ static BufferKeyType BoExport(BufferDefaultType bo) {
+ return instance->BoExport(bo);
+ }
+};
+
+MockTBM::Fake* MockTBM::instance = nullptr;
+} // namespace
+
+namespace esplusplayer_ut {
+
+using TizenBufferObject = BufferObjectWithType<MockTBM>;
+
+class InvalidBufferObjectWithTypeTest : public ::testing::Test {
+ public:
+ explicit InvalidBufferObjectWithTypeTest() : bufferobj_(nullptr) {}
+ virtual ~InvalidBufferObjectWithTypeTest() = default;
+ virtual void SetUp() override {}
+ virtual void TearDown() override {}
+
+ protected:
+ TizenBufferObject& GetBufferObject() { return bufferobj_; }
+
+ private:
+ TizenBufferObject bufferobj_;
+};
+
+TEST_F(InvalidBufferObjectWithTypeTest, IsValid) {
+ EXPECT_FALSE(GetBufferObject().IsValid());
+}
+
+TEST_F(InvalidBufferObjectWithTypeTest, GetBufferHandle) {
+ EXPECT_EQ(esplusplayer::kInvalidBufferHandle,
+ GetBufferObject().GetBufferHandle());
+}
+
+TEST_F(InvalidBufferObjectWithTypeTest, Export) {
+ EXPECT_EQ(esplusplayer::kInvalidBufferKey, GetBufferObject().Export());
+}
+
+TEST_F(InvalidBufferObjectWithTypeTest, GetSize) {
+ EXPECT_EQ(esplusplayer::kInvalidBufferSize, GetBufferObject().GetSize());
+}
+
+class BufferObjectWithTypeTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ MockTBM::instance = &GetTBM();
+
+ ON_CALL(GetTBM(), BoRef(_)).WillByDefault(Return(kDefaultBuffer));
+ ON_CALL(GetTBM(), BoSize(_))
+ .WillByDefault(Return(kExpectedDefaultByteSize));
+ ON_CALL(GetTBM(), BoGetHandle(_, _))
+ .WillByDefault(Return(kDefaultMappedHandle));
+ ON_CALL(GetTBM(), BoExport(_)).WillByDefault(Return(kDefaultBufferKey));
+
+ EXPECT_CALL(GetTBM(), BoRef(_)).Times(1);
+ EXPECT_CALL(GetTBM(), BoUnRef(_)).Times(1);
+
+ bufferobj_ = std::unique_ptr<TizenBufferObject>(
+ new TizenBufferObject(kDefaultBuffer));
+ }
+ virtual void TearDown() override {}
+
+ protected:
+ MockTBM::Fake& GetTBM() { return fake_tbm_; }
+ TizenBufferObject& GetBufferObject() { return *bufferobj_; }
+
+ private:
+ MockTBM::Fake fake_tbm_;
+ std::unique_ptr<TizenBufferObject> bufferobj_;
+};
+
+TEST_F(BufferObjectWithTypeTest, IsValid) {
+ EXPECT_TRUE(GetBufferObject().IsValid());
+}
+
+TEST_F(BufferObjectWithTypeTest, GetBufferHandle) {
+ EXPECT_CALL(GetTBM(), BoGetHandle(_, _)).Times(1);
+ EXPECT_EQ(kDefaultBufferHandle, GetBufferObject().GetBufferHandle());
+}
+
+TEST_F(BufferObjectWithTypeTest, Export) {
+ EXPECT_CALL(GetTBM(), BoExport(_)).Times(1);
+ EXPECT_EQ(kDefaultBufferKey, GetBufferObject().Export());
+}
+
+TEST_F(BufferObjectWithTypeTest, GetSize) {
+ EXPECT_CALL(GetTBM(), BoSize(_)).Times(1);
+ EXPECT_EQ(kExpectedDefaultByteSize, GetBufferObject().GetSize());
+}
+} // namespace esplusplayer_ut
\ No newline at end of file
--- /dev/null
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+
+#include "mixer/constant.h"
+#include "mixer/matcher.h"
+#include "mixer/mock/mock_bufferobject.h"
+#include "mixer/mock/movable.h"
+#include "mixer/mock/moveobj_wrapper.h"
+#include "mixer/videoplane.h"
+
+using ::testing::AllOf;
+using ::testing::AnyNumber;
+using ::testing::Field;
+using ::testing::Return;
+
+namespace esplusplayer_ut {
+
+template <typename PlaneType>
+class DefaultVideoPlaneOptionWithBufferObject {
+ public:
+ using BufferObjectPtr = typename PlaneType::BufferObjectPtr;
+ virtual ~DefaultVideoPlaneOptionWithBufferObject() = default;
+
+ protected:
+ virtual void Init_() {
+ ON_CALL(GetBufferObject(), GetBufferHandle())
+ .WillByDefault(Return(kDefaultBufferHandle));
+ ON_CALL(GetBufferObject(), Export())
+ .WillByDefault(Return(kDefaultBufferKey));
+ ON_CALL(GetBufferObject(), GetSize())
+ .WillByDefault(Return(kDefaultWidth * kDefaultHeight));
+
+ EXPECT_CALL(GetBufferObject(), GetBufferHandle()).Times(AnyNumber());
+ EXPECT_CALL(GetBufferObject(), GetSize()).Times(AnyNumber());
+
+ ASSERT_TRUE(y_plane_->IsValid());
+ }
+
+ protected:
+ PlaneType& GetPlane() { return *y_plane_.get(); }
+ MockBufferObject& GetBufferObject() { return bufferobj_.Get(); }
+ MockBufferObject* MoveBufferObject() { return bufferobj_.Move(); }
+
+ private:
+ MoveObjectWrapper<MockBufferObject> bufferobj_{new MockBufferObject()};
+ std::unique_ptr<PlaneType> y_plane_{new PlaneType(
+ BufferObjectPtr(MoveBufferObject()), kDefaultWidth, kDefaultHeight)};
+};
+
+class YComponentVideoPlaneTest
+ : public ::testing::Test,
+ public DefaultVideoPlaneOptionWithBufferObject<YComponentVideoPlane> {
+ public:
+ virtual void SetUp() override { Init_(); }
+ virtual void TearDown() override {}
+};
+
+TEST_F(YComponentVideoPlaneTest, GetVideoPlaneManipulableInfo) {
+ EXPECT_THAT(
+ GetPlane().GetVideoPlaneManipulableInfo(),
+ AllOf(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kYComponent),
+ Field(&VideoPlaneManipulableInfo::handle, kDefaultBufferHandle),
+ Field(&VideoPlaneManipulableInfo::linesize, kDefaultWidth),
+ Field(&VideoPlaneManipulableInfo::rect,
+ IsSameGeometry(
+ GetGeometry(0, 0, kDefaultWidth, kDefaultHeight)))));
+}
+
+TEST_F(YComponentVideoPlaneTest, GetVideoPlaneManipulableInfo_AfterCrop) {
+ GetPlane().SetCropArea(GetCropArea(0.1, 0.1, 0.9, 0.9));
+
+ EXPECT_THAT(
+ GetPlane().GetVideoPlaneManipulableInfo(),
+ AllOf(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kYComponent),
+ Field(&VideoPlaneManipulableInfo::handle, kDefaultBufferHandle),
+ Field(&VideoPlaneManipulableInfo::linesize, kDefaultWidth),
+ Field(&VideoPlaneManipulableInfo::rect,
+ IsSameGeometry(GetGeometry(
+ kDefaultWidth * 0.1, kDefaultHeight * 0.1,
+ kDefaultWidth * 0.9, kDefaultHeight * 0.9)))));
+}
+
+class UVComponentVideoPlaneTest
+ : public ::testing::Test,
+ public DefaultVideoPlaneOptionWithBufferObject<UVComponentVideoPlane> {
+ public:
+ virtual void SetUp() override {
+ Init_();
+ ON_CALL(GetBufferObject(), GetSize())
+ .WillByDefault(Return(kDefaultWidth * kDefaultHeight / 2));
+ }
+ virtual void TearDown() override {}
+};
+
+TEST_F(UVComponentVideoPlaneTest, GetVideoPlaneManipulableInfo) {
+ EXPECT_THAT(
+ GetPlane().GetVideoPlaneManipulableInfo(),
+ AllOf(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kUVComponent),
+ Field(&VideoPlaneManipulableInfo::handle, kDefaultBufferHandle),
+ Field(&VideoPlaneManipulableInfo::linesize, kDefaultWidth),
+ Field(&VideoPlaneManipulableInfo::rect,
+ IsSameGeometry(GetGeometry(0, 0, kDefaultWidth / 2,
+ kDefaultHeight / 2)))));
+}
+
+TEST_F(UVComponentVideoPlaneTest, GetVideoPlaneManipulableInfo_AfterCrop) {
+ GetPlane().SetCropArea(GetCropArea(0.1, 0.1, 0.9, 0.9));
+
+ EXPECT_THAT(
+ GetPlane().GetVideoPlaneManipulableInfo(),
+ AllOf(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kUVComponent),
+ Field(&VideoPlaneManipulableInfo::handle, kDefaultBufferHandle),
+ Field(&VideoPlaneManipulableInfo::linesize, kDefaultWidth),
+ Field(&VideoPlaneManipulableInfo::rect,
+ IsSameGeometry(GetGeometry(
+ kDefaultWidth / 2 * 0.1, kDefaultHeight / 2 * 0.1,
+ kDefaultWidth / 2 * 0.9, kDefaultHeight / 2 * 0.9)))));
+}
+
+class YUVComponentVideoPlaneTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ ON_CALL(GetBufferObject(), GetBufferHandle())
+ .WillByDefault(Return(kDefaultBufferHandle));
+ ON_CALL(GetBufferObject(), Export())
+ .WillByDefault(Return(kDefaultBufferKey));
+ ON_CALL(GetBufferObject(), GetSize())
+ .WillByDefault(Return(kDefaultWidth * kDefaultHeight * 1.5));
+
+ EXPECT_CALL(GetBufferObject(), GetBufferHandle()).Times(AnyNumber());
+ EXPECT_CALL(GetBufferObject(), GetSize()).Times(AnyNumber());
+
+ ASSERT_TRUE(GetYPlane().IsValid());
+ ASSERT_TRUE(GetUVPlane().IsValid());
+ }
+ virtual void TearDown() override {}
+
+ protected:
+ YComponentVideoPlaneWithSharedMemory& GetYPlane() { return *y_plane_.get(); }
+ UVComponentVideoPlaneWithSharedMemory& GetUVPlane() {
+ return *uv_plane_.get();
+ }
+ MockBufferObject& GetBufferObject() {
+ return dynamic_cast<MockBufferObject&>(*bufferobj_.get());
+ }
+
+ private:
+ std::shared_ptr<BufferObject> bufferobj_{new MockBufferObject()};
+ std::unique_ptr<YComponentVideoPlaneWithSharedMemory> y_plane_{
+ new YComponentVideoPlaneWithSharedMemory(bufferobj_, kDefaultWidth,
+ kDefaultHeight)};
+ std::unique_ptr<UVComponentVideoPlaneWithSharedMemory> uv_plane_{
+ new UVComponentVideoPlaneWithSharedMemory(bufferobj_, kDefaultWidth,
+ kDefaultHeight)};
+};
+
+TEST_F(YUVComponentVideoPlaneTest, GetVideoPlaneManipulableInfo) {
+ EXPECT_THAT(
+ GetYPlane().GetVideoPlaneManipulableInfo(),
+ AllOf(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kYComponent),
+ Field(&VideoPlaneManipulableInfo::handle, kDefaultBufferHandle),
+ Field(&VideoPlaneManipulableInfo::linesize, kDefaultWidth),
+ Field(&VideoPlaneManipulableInfo::rect,
+ IsSameGeometry(
+ GetGeometry(0, 0, kDefaultWidth, kDefaultHeight)))));
+ EXPECT_THAT(
+ GetUVPlane().GetVideoPlaneManipulableInfo(),
+ AllOf(
+ Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kUVComponent),
+ Field(&VideoPlaneManipulableInfo::handle, kDefaultBufferHandle),
+ Field(&VideoPlaneManipulableInfo::linesize, kDefaultWidth),
+ Field(&VideoPlaneManipulableInfo::rect,
+ IsSameGeometry(GetGeometry(0, kDefaultHeight, kDefaultWidth / 2,
+ kDefaultHeight / 2)))));
+}
+
+TEST_F(YUVComponentVideoPlaneTest, GetVideoPlaneManipulableInfo_AfterCrop) {
+ GetYPlane().SetCropArea(GetCropArea(0.1, 0.1, 0.9, 0.9));
+ GetUVPlane().SetCropArea(GetCropArea(0.1, 0.1, 0.9, 0.9));
+
+ EXPECT_THAT(
+ GetYPlane().GetVideoPlaneManipulableInfo(),
+ AllOf(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kYComponent),
+ Field(&VideoPlaneManipulableInfo::handle, kDefaultBufferHandle),
+ Field(&VideoPlaneManipulableInfo::linesize, kDefaultWidth),
+ Field(&VideoPlaneManipulableInfo::rect,
+ IsSameGeometry(GetGeometry(
+ kDefaultWidth * 0.1, kDefaultHeight * 0.1,
+ kDefaultWidth * 0.9, kDefaultHeight * 0.9)))));
+ EXPECT_THAT(
+ GetUVPlane().GetVideoPlaneManipulableInfo(),
+ AllOf(Field(&VideoPlaneManipulableInfo::component,
+ PlaneComponent::kUVComponent),
+ Field(&VideoPlaneManipulableInfo::handle, kDefaultBufferHandle),
+ Field(&VideoPlaneManipulableInfo::linesize, kDefaultWidth),
+ Field(&VideoPlaneManipulableInfo::rect,
+ IsSameGeometry(GetGeometry(
+ kDefaultWidth / 2 * 0.1,
+ kDefaultHeight / 2 * 0.1 + kDefaultHeight,
+ kDefaultWidth / 2 * 0.9, kDefaultHeight / 2 * 0.9)))));
+}
+
+} // namespace esplusplayer_ut
\ No newline at end of file
--- /dev/null
+
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+#include <avoc.h>
+#include <sys/prctl.h>
+#include <condition_variable>
+#include <iostream>
+
+#include "esplusplayer/eseventlistener.hpp"
+#include "esplusplayer/tclist.h"
+#include "esplusplayer_capi/esplusplayer_capi.h"
+#include "gmock/gmock.h"
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+#include "utils/mock_videosink.hpp"
+#include "utils/utility.h"
+
+using namespace testing;
+/// TODO:Need to make cloud tc list file
+namespace es_tc_cloudgame {
+
+struct test_parms_s_ {
+ std::string uri_;
+ // int video_sleep_time_; // Not required if the ES stream has valid PTS
+ // int audio_sleep_time_;
+};
+
+std::string H264_FHD_60P_OPUS_48K = "h264_fhd_60p_opus_48k/";
+std::string VP9_FHD_60P_OPUS_48K = "vp9_fhd_60p_opus_48k/";
+std::string VP9_NOAUDIO_60fps = "stadia_vp9/";
+std::string H264_FHD_30P_PCM_48K =
+ "h264_fhd_30p_pcm_48k/"; // Xbox case, sometimes they send 30fps
+std::string H264_NOAUDIO_60fps_fhd = "stadia_h264/";
+std::string H264_NOAUDIO_60fps_uhd = "stadia_h264_uhd/";
+std::string H264_NOAUDIO_60fps_hd =
+ "h264_hd_60fps/"; // GFN Adaptive streaming case
+std::string HEVC_NOAUDIO_60fps_fhd = "hevc_fhd_60fps/";
+std::string HEVC_NOAUDIO_60fps_uhd = "hevc_4k_60fps/"; // GFN 4K case
+
+std::vector<test_parms_s_*> CreateInputData() {
+ test_parms_s_* pTestData1 = new test_parms_s_();
+ test_parms_s_* pTestData2 = new test_parms_s_();
+ test_parms_s_* pTestData3 = new test_parms_s_();
+ test_parms_s_* pTestData4 = new test_parms_s_();
+ test_parms_s_* pTestData5 = new test_parms_s_();
+ test_parms_s_* pTestData6 = new test_parms_s_();
+ test_parms_s_* pTestData7 = new test_parms_s_();
+ test_parms_s_* pTestData8 = new test_parms_s_();
+ test_parms_s_* pTestData9 = new test_parms_s_();
+
+ pTestData1->uri_ = H264_FHD_60P_OPUS_48K;
+ pTestData2->uri_ = VP9_FHD_60P_OPUS_48K;
+ pTestData3->uri_ = VP9_NOAUDIO_60fps;
+ pTestData4->uri_ = H264_FHD_30P_PCM_48K;
+ pTestData5->uri_ = H264_NOAUDIO_60fps_fhd;
+ pTestData6->uri_ = H264_NOAUDIO_60fps_uhd;
+ pTestData7->uri_ = H264_NOAUDIO_60fps_hd;
+ pTestData8->uri_ = HEVC_NOAUDIO_60fps_fhd;
+ pTestData9->uri_ = HEVC_NOAUDIO_60fps_uhd;
+
+ std::vector<test_parms_s_*> values;
+ values.push_back(pTestData1);
+ values.push_back(pTestData2);
+ values.push_back(pTestData3);
+ values.push_back(pTestData4);
+ values.push_back(pTestData5);
+ values.push_back(pTestData6);
+ values.push_back(pTestData7);
+ values.push_back(pTestData8);
+ values.push_back(pTestData9);
+
+ return values;
+}
+
+static std::vector<
+ std::tuple<int, esplusplayer_low_latency_mode, avoc_cloud_game_inputlag_e>>
+ game_modes = {
+ {0 /*Input Lag mode*/,
+ ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY,
+ AVOC_CLOUD_GAME_INPUTLAG_DEFAULT},
+ {0, ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE,
+ AVOC_CLOUD_GAME_INPUTLAG_SEAMLESS_RESOLUTION_CHANGE},
+ {0,
+ ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE_WITH_FIXED_RESOLUTION,
+ AVOC_CLOUD_GAME_INPUTLAG_LOWEST},
+ {1 /*Picture quality*/,
+ ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY,
+ AVOC_CLOUD_GAME_INPUTLAG_DEFAULT},
+ {1, ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE,
+ AVOC_CLOUD_GAME_INPUTLAG_PICTURE_QUALITY},
+ {1,
+ ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE_WITH_FIXED_RESOLUTION,
+ AVOC_CLOUD_GAME_INPUTLAG_PICTURE_QUALITY}};
+
+} // namespace es_tc_cloudgame
+
+esplusplayer::Geometry geometry;
+using namespace utils;
+using utils::Utility;
+
+#define GTEST_SKIP_TC \
+ { \
+ std::cout << "Skip this TC: " \
+ << EsCloudGameBase::util_.GetCurrentTestName() << std::endl; \
+ SUCCEED(); \
+ return; \
+ } // TODO: Update Gtest version to use GTEST_SKIP_TC
+
+MATCHER_P(isGameMode, expected_value, "") {
+ return (arg.game_mode == expected_value);
+}
+
+void SetLowLatencyMode(esplusplayer_handle esplayer) {
+ ASSERT_NE(nullptr, esplayer);
+ int ret = esplusplayer_set_low_latency_mode(
+ esplayer, ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC);
+ ASSERT_EQ(ret, ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ret = esplusplayer_set_low_latency_mode(
+ esplayer, ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE);
+ ASSERT_EQ(ret, ESPLUSPLAYER_ERROR_TYPE_NONE);
+}
+
+class EsCloudGameBase {
+ public:
+ EsCloudGameBase() : util_(Utility::Instance()) {}
+ ~EsCloudGameBase() {}
+
+ static void SetUpTestCaseBase() {
+ gst_init_check(nullptr, nullptr, nullptr);
+ is_test_contents_ready_ = ContentsRoot::Instance().MountTcDirectory();
+ if (is_test_contents_ready_ == false) {
+ LOG_ERROR("Failed to get test contents");
+ }
+
+ mock_video_sink_builder_ = MockVideoSinkBuilder::injectNewBuilder();
+ avoc_get_cloud_game_mode(&last_game_mode);
+ }
+
+ static void TearDownTestCaseBase() {
+ ContentsRoot::Instance().UnMountTcDirectory();
+ is_test_contents_ready_ = false;
+ avoc_set_cloud_game_mode(last_game_mode);
+ }
+
+ virtual es_tc_cloudgame::test_parms_s_* GetTestInput() = 0;
+
+ virtual void SetUpBase() {
+ if (is_test_contents_ready_ == false) {
+ skip_test = true;
+ return;
+ }
+ test_input_ = GetTestInput();
+ LOG_INFO("%s, uri = %s", util_.GetCurrentTestName(),
+ test_input_->uri_.c_str());
+
+ if (Utility::IsChipset("KANTSU2E") || Utility::IsChipset("X22UD")) {
+ if (test_input_->uri_.compare(es_tc_cloudgame::H264_NOAUDIO_60fps_uhd) == 0) {
+ LOG_ERROR("This chipset doesn't support H264 4K 60fps");
+ skip_test = true;
+ return;
+ }
+ }
+
+ video_reader_ = new EsStreamReader(test_input_->uri_ + "video_00/",
+ ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ audio_reader_ = new EsStreamReader(test_input_->uri_ + "audio_00/",
+ ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+
+ video_reader_->SetRealtimePacketSubmit(true);
+ audio_reader_->SetRealtimePacketSubmit(true);
+
+ esplayer_ = util_.GetOpenedESPP(geometry);
+ ASSERT_NE(nullptr, esplayer_);
+
+ callback_ =
+ new EsPlayerEventCallback(esplayer_, video_reader_, audio_reader_);
+ callback_->SetCallback();
+
+ video_reader_->SetStreamInfo(esplayer_);
+ audio_reader_->SetStreamInfo(esplayer_);
+
+ mock_video_sink_builder_->prepareNewVideoSink();
+ }
+
+ virtual void TearDownBase() {
+ esplusplayer_stop(esplayer_);
+ esplusplayer_close(esplayer_);
+ delete callback_;
+ delete video_reader_;
+ delete audio_reader_;
+
+ util_.DestroyESPP(esplayer_);
+ esplayer_ = nullptr;
+ video_reader_ = nullptr;
+ audio_reader_ = nullptr;
+ callback_ = nullptr;
+ skip_test = false;
+ }
+
+ public:
+ Utility& util_;
+ static bool is_test_contents_ready_;
+ bool skip_test = false;
+ es_tc_cloudgame::test_parms_s_* test_input_;
+ EsStreamReader* video_reader_ = nullptr;
+ EsStreamReader* audio_reader_ = nullptr;
+ esplusplayer_handle esplayer_ = nullptr;
+ EsPlayerEventCallback* callback_ = nullptr;
+ static std::shared_ptr<MockVideoSinkBuilder> mock_video_sink_builder_;
+ static int last_game_mode;
+};
+std::shared_ptr<MockVideoSinkBuilder>
+ EsCloudGameBase::mock_video_sink_builder_ = nullptr;
+bool EsCloudGameBase::is_test_contents_ready_ = false;
+int EsCloudGameBase::last_game_mode = 0;
+
+//////////////// EsCloudGameTestForCodecs ///////////////////
+class EsCloudGameTestForCodecs
+ : public EsCloudGameBase,
+ public ::testing::TestWithParam<es_tc_cloudgame::test_parms_s_*> {
+ public:
+ EsCloudGameTestForCodecs() {}
+ ~EsCloudGameTestForCodecs() {}
+
+ static void SetUpTestCase() { SetUpTestCaseBase(); }
+
+ static void TearDownTestCase() { TearDownTestCaseBase(); }
+
+ virtual void SetUp() override { SetUpBase(); }
+
+ virtual void TearDown() override { TearDownBase(); }
+ virtual es_tc_cloudgame::test_parms_s_* GetTestInput() override {
+ return GetParam();
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ ESPlusplayer, EsCloudGameTestForCodecs,
+ ::testing::ValuesIn(es_tc_cloudgame::CreateInputData()));
+
+TEST_P(EsCloudGameTestForCodecs,
+ vdapi_cg_esplusplayer_set_low_latency_mode_p_1) {
+ if (skip_test) GTEST_SKIP_TC;
+ LOG_INFO(
+ "Test ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE for all supported "
+ "codecs(vp9, h264, h265) on cloud game service now.");
+ avoc_set_cloud_game_mode(0);
+
+ Expectation setPreResolution =
+ EXPECT_CALL(
+ *MockVideoSinkBuilder::new_video_sink,
+ setPreResolution(
+ isGameMode(AVOC_CLOUD_GAME_INPUTLAG_SEAMLESS_RESOLUTION_CHANGE),
+ _))
+ .Times(1);
+ Expectation setResolution =
+ EXPECT_CALL(
+ *MockVideoSinkBuilder::new_video_sink,
+ setResolution(
+ isGameMode(AVOC_CLOUD_GAME_INPUTLAG_SEAMLESS_RESOLUTION_CHANGE),
+ _))
+ .Times(1)
+ .After(setPreResolution);
+ Expectation otherRenderFrames =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, renderFrame(_))
+ .Times(AnyNumber())
+ .After(setResolution);
+ Expectation firstRenderFrame =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, renderFrame(_))
+ .Times(1)
+ .After(setResolution)
+ .RetiresOnSaturation();
+ Expectation setMute =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, setMute(false))
+ .Times(1)
+ .After(firstRenderFrame);
+
+ ASSERT_EQ(esplusplayer_set_low_latency_mode(
+ esplayer_, ESPLUSPLAYER_LOW_LATENCY_MODE_ENABLE_GAME_MODE),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ std::this_thread::sleep_for(std::chrono::milliseconds(
+ util_.GetPlayingTimeForManualTestInMsec()));
+}
+
+/////////////// EsCloudGameTestForGameModes //////////////////////
+class EsCloudGameTestForGameModes
+ : public EsCloudGameBase,
+ public ::testing::TestWithParam<std::tuple<
+ int, esplusplayer_low_latency_mode, avoc_cloud_game_inputlag_e>> {
+ public:
+ EsCloudGameTestForGameModes() {}
+ ~EsCloudGameTestForGameModes() {}
+
+ static void SetUpTestCase() { SetUpTestCaseBase(); }
+
+ static void TearDownTestCase() { TearDownTestCaseBase(); }
+
+ virtual void SetUp() override {
+ auto param = GetParam();
+ clip_op_mode_ = std::get<int>(param);
+ espp_low_latency_mode_ = std::get<esplusplayer_low_latency_mode>(param);
+ avoc_game_mode_ = std::get<avoc_cloud_game_inputlag_e>(param);
+ LOG_INFO(
+ "clip_op_mode_[ %d ], espp_low_latency_mode_[ %d ], avoc_game_mode_[ "
+ "%d ]",
+ clip_op_mode_, espp_low_latency_mode_, avoc_game_mode_);
+ SetUpBase();
+ }
+
+ virtual void TearDown() override { TearDownBase(); }
+
+ virtual es_tc_cloudgame::test_parms_s_* GetTestInput() override {
+ static es_tc_cloudgame::test_parms_s_* pTestData = nullptr;
+ if (pTestData) return pTestData;
+
+ pTestData = new es_tc_cloudgame::test_parms_s_();
+ pTestData->uri_ = es_tc_cloudgame::H264_NOAUDIO_60fps_fhd;
+ return pTestData;
+ }
+
+ int clip_op_mode_;
+ esplusplayer_low_latency_mode espp_low_latency_mode_;
+ avoc_cloud_game_inputlag_e avoc_game_mode_;
+};
+
+INSTANTIATE_TEST_CASE_P(ESPlusplayer, EsCloudGameTestForGameModes,
+ ::testing::ValuesIn(es_tc_cloudgame::game_modes));
+
+TEST_P(EsCloudGameTestForGameModes,
+ vdapi_cg_esplusplayer_set_low_latency_mode_p_2) {
+ if (skip_test) GTEST_SKIP_TC;
+ LOG_INFO(
+ "Test initial playback for all the combination of game modes and "
+ "clip_op_modes until first video rendering");
+ avoc_set_cloud_game_mode(clip_op_mode_);
+
+ Expectation setPreResolution =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink,
+ setPreResolution(isGameMode(avoc_game_mode_), _))
+ .Times(1);
+ Expectation setResolution =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink,
+ setResolution(isGameMode(avoc_game_mode_), _))
+ .Times(1)
+ .After(setPreResolution);
+ Expectation otherRenderFrames =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, renderFrame(_))
+ .Times(AnyNumber())
+ .After(setResolution);
+ Expectation firstRenderFrame =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, renderFrame(_))
+ .Times(1)
+ .After(setResolution)
+ .RetiresOnSaturation();
+ Expectation setMute =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, setMute(false))
+ .Times(1)
+ .After(firstRenderFrame);
+
+ ASSERT_EQ(
+ esplusplayer_set_low_latency_mode(esplayer_, espp_low_latency_mode_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForFirstRenderDone();
+}
+
+TEST_P(EsCloudGameTestForGameModes,
+ vdapi_cg_esplusplayer_set_low_latency_mode_p_3) {
+ if (skip_test) GTEST_SKIP_TC;
+ LOG_INFO(
+ "Test clip_op_mode switching(inputlagmode <-> picturequality) for all "
+ "the combination of gamemodes and clip_op_modes during the playback");
+ avoc_set_cloud_game_mode((clip_op_mode_ == 1 ? 0 : 1));
+ ASSERT_EQ(esplusplayer_set_low_latency_mode(
+ esplayer_, ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_SYNC),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(
+ esplusplayer_set_low_latency_mode(esplayer_, espp_low_latency_mode_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForPrepareDone();
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_EQ(MockVideoSinkBuilder::new_video_sink->isFirstVideoUnmute
+ .WaitForChange(true, 3000)
+ .get(),
+ WatcherStatus::kSuccess); // Ready to test
+
+ utils::Watcher<bool> isUnmuteAfterGameModeSet(false);
+
+ if (espp_low_latency_mode_ ==
+ ESPLUSPLAYER_LOW_LATENCY_MODE_DISABLE_VIDEO_QUALITY) {
+ // Old game mode : Not requires video mute
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, setMute(_)).Times(0);
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, setResolution(_, _))
+ .Times(0);
+ } else {
+ // New game mode : Requires Video mute to switch avoc "inputlag mode" <->
+ // "AI upscale mode"
+
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, renderFrame(_))
+ .Times(AnyNumber());
+ Expectation firstMute =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, setMute(true))
+ .Times(1);
+ Expectation setResolution =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink,
+ setResolution(isGameMode(avoc_game_mode_), _))
+ .Times(1)
+ .After(firstMute);
+ Expectation firstRenderFrame =
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, renderFrame(_))
+ .Times(AtLeast(1))
+ .After(setResolution)
+ .RetiresOnSaturation();
+ EXPECT_CALL(*MockVideoSinkBuilder::new_video_sink, setMute(false))
+ .Times(1)
+ .After(firstRenderFrame)
+ .WillOnce(Invoke([&](bool) -> int {
+ isUnmuteAfterGameModeSet = true;
+ return 0;
+ }));
+ }
+
+ avoc_set_cloud_game_mode(clip_op_mode_);
+ isUnmuteAfterGameModeSet.WaitForChange(true, 3000).get();
+}
+
+/////////////// EsCloudGameTestForGameApps //////////////////////
+class EsCloudGameTestForGameApps
+ : public EsCloudGameBase,
+ public ::testing::TestWithParam<std::string> {
+ public:
+ EsCloudGameTestForGameApps() {}
+ ~EsCloudGameTestForGameApps() {}
+ static void SetUpTestCase() { SetUpTestCaseBase(); }
+ static void TearDownTestCase() { TearDownTestCaseBase(); }
+ virtual void SetUp() override {
+ LOG_ERROR("Change process name to actual cloud game app name(%s)",
+ GetParam().c_str());
+ prctl(PR_SET_NAME, (char*)(GetParam().c_str()), 0, 0, 0);
+ SetUpBase();
+ }
+ virtual void TearDown() override {
+ char buf[128] = {
+ 0,
+ };
+ prctl(PR_GET_NAME, buf, 0, 0, 0);
+ LOG_ERROR("Process name(%s)", buf);
+ TearDownBase();
+ }
+
+ virtual es_tc_cloudgame::test_parms_s_* GetTestInput() override {
+ static es_tc_cloudgame::test_parms_s_* pTestData = nullptr;
+ if (pTestData) return pTestData;
+ pTestData = new es_tc_cloudgame::test_parms_s_();
+ pTestData->uri_ = es_tc_cloudgame::H264_FHD_60P_OPUS_48K;
+ return pTestData;
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(ESPlusplayer, EsCloudGameTestForGameApps,
+ ::testing::ValuesIn(std::vector<std::string>{
+ "GHI3a0zMSx.XboxGamePass", "w0wZu3xkQq.GeForceNOW",
+ "CmeyUlnhYY.LunaWebApp", "MiUDVawh7P.UtomikTizen",
+ "notacloudgameapp"}));
+
+/*
+Manual test
+dlogutil PLUSPLAYER:* RSC_CENTER:* | grep -E
+"GetComponentName|GetDedicated|PMRscStrategyMultiTaskDefault.cpp|PMPolicyGameHub.cpp"
+esplusplayer_ut --gtest_also_run_disabled_tests --gtest_filter=*dedicated*
+*/
+TEST_P(EsCloudGameTestForGameApps,
+ vdapi_cg_esplusplayer_dedicated_resource_allocation) {
+ if (is_test_contents_ready_ == false) GTEST_SKIP_TC;
+ LOG_INFO(
+ "Manual test : Test game mode playback with actual cloud game app IDs."
+ "If this model has GameHome, dedicated main decoders must be occupied");
+ // TODO: prepare extra player and test the resource confliction
+
+ esplusplayer_app_info info = {(char*)(GetParam().c_str()), (char*)"1.0",
+ (char*)"MSE"};
+ esplusplayer_set_app_info(esplayer_, &info);
+ SetLowLatencyMode(esplayer_);
+ ASSERT_EQ(esplusplayer_prepare_async(esplayer_),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ ASSERT_TRUE(callback_->WaitForPrepareDone());
+}
+
+static vector<string> targetThreads = {
+ "video_appsrc", "audio_appsrc", "OmxAudioThread", "omxaudiodec",
+ "omx_outbuffer", "omx_inbuffer", "omx_poll", "render_thread",
+};
+
+bool VerifyBoostingResult(vector<string>& target_threads, string policy,
+ string prio) {
+ string cmd = "AffinityReplacer --atp | grep ";
+ cmd += to_string((int)getpid());
+
+ string cmd_result = Utility::Execute(cmd);
+ vector<string> split_result = Utility::Split(cmd_result, ' ');
+ bool comparision_result = true;
+ for_each(target_threads.begin(), target_threads.end(),
+ [&](string& target_thread_name) {
+ auto actual_thread_name_itr = find_if(
+ split_result.begin(), split_result.end(), [&](string& str) {
+ size_t ret = str.find(target_thread_name);
+ return (ret != string::npos);
+ });
+ ASSERT_TRUE(actual_thread_name_itr != split_result.end())
+ << "Can't find " << target_thread_name;
+ string actual_policy = *(actual_thread_name_itr + 1);
+ string actual_prio = *(actual_thread_name_itr + 2);
+ if (policy.compare(actual_policy) != 0) {
+ LOG_ERROR("Thread[ %s ], policy[ %s ] != actual_policy[ %s ]",
+ (*actual_thread_name_itr).c_str(), policy.c_str(),
+ actual_policy.c_str());
+ comparision_result = false;
+ }
+ if (prio.compare(actual_prio) != 0) {
+ LOG_ERROR("Thread[ %s ], prio[ %s ] != actual_prio[ %s ]",
+ (*actual_thread_name_itr).c_str(), prio.c_str(),
+ actual_prio.c_str());
+ comparision_result = false;
+ }
+ });
+ return comparision_result;
+};
+
+TEST_P(EsCloudGameTestForGameApps,
+ vdapi_cg_esplusplayer_game_mode_thread_boosting) {
+ if (is_test_contents_ready_ == false) GTEST_SKIP_TC;
+ LOG_INFO("Evaluate the result of thread-boost on game mode");
+ SetLowLatencyMode(esplayer_);
+ esplusplayer_prepare_async(esplayer_);
+ ASSERT_TRUE(callback_->WaitForPrepareDone());
+ ASSERT_EQ(esplusplayer_start(esplayer_), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ callback_->WaitForFirstRenderDone();
+ std::this_thread::sleep_for(std::chrono::seconds(
+ 5)); // boosting executed by another process just after first rendering
+ ASSERT_TRUE(
+ VerifyBoostingResult(targetThreads, "2", "-6") ||
+ VerifyBoostingResult(
+ targetThreads, "2",
+ "-36")); // 2,-6 : Realtime priority (-36 only for atsc3.0 models)
+}
+
+/////////////// EsCloudGameRecordedStream //////////////////////
+
+class EsCloudGameRecordedStream : public ::testing::Test {
+ public:
+ EsCloudGameRecordedStream() : util_(Utility::Instance()) {}
+ ~EsCloudGameRecordedStream() {}
+
+ static void SetUpTestCase() { gst_init_check(nullptr, nullptr, nullptr); }
+
+ static void TearDownTestCase() {}
+
+ static bool verifyPath(string& path) {
+ ifstream stream = ifstream(path + "ESP.es", ifstream::in);
+ if (!stream.is_open()) {
+ cout << path << ", failll" << endl;
+ path = "/tmp/" + path;
+ stream = ifstream(path + "ESP.es", ifstream::in);
+ }
+ bool ret = stream.is_open();
+ if (ret) stream.close();
+ return ret;
+ }
+
+ virtual void SetUp() {
+ LOG_INFO("%s", util_.GetCurrentTestName());
+ esplayer_ = util_.GetOpenedESPP(geometry);
+ ASSERT_NE(nullptr, esplayer_);
+
+ string path = "./videodump.";
+ if (verifyPath(path)) {
+ video_reader_ =
+ new EsStreamReader(path, ESPLUSPLAYER_STREAM_TYPE_VIDEO, true);
+ }
+
+ path = "./audiodump.";
+ if (verifyPath(path)) {
+ audio_reader_ =
+ new EsStreamReader(path, ESPLUSPLAYER_STREAM_TYPE_AUDIO, true);
+ }
+ ASSERT_TRUE(audio_reader_ || video_reader_)
+ << "audio_reader_:" << audio_reader_
+ << "video_reader_:" << video_reader_;
+
+ callback_ =
+ new EsPlayerEventCallback(esplayer_, video_reader_, audio_reader_);
+ callback_->SetCallback();
+ if (video_reader_) video_reader_->SetStreamInfo(esplayer_);
+ if (audio_reader_) audio_reader_->SetStreamInfo(esplayer_);
+ }
+
+ virtual void TearDown() {
+ esplusplayer_stop(esplayer_);
+ esplusplayer_close(esplayer_);
+ delete video_reader_;
+ delete audio_reader_;
+ delete callback_;
+
+ util_.DestroyESPP(esplayer_);
+ esplayer_ = nullptr;
+ video_reader_ = nullptr;
+ audio_reader_ = nullptr;
+ callback_ = nullptr;
+ }
+
+ public:
+ Utility& util_;
+ EsStreamReader* video_reader_ = nullptr;
+ EsStreamReader* audio_reader_ = nullptr;
+ esplusplayer_handle esplayer_ = nullptr;
+ EsPlayerEventCallback* callback_ = nullptr;
+};
+
+// The testcase to play recorded video stream,
+// 1. How to record video stream
+// a. sh-3.2# touch /tmp/vsdump;chsmack -a '_' /tmp/vsdump;
+// b. Launch cloud game app (ex. Xbox)
+// c. Play and Stop the game
+// d. Now you can see the 3 recorded files (/tmp/videodump* )
+// e. copy it to the other storage if you want to keep it
+// 2. How to play
+// a. sh-3.2# rm -f /tmp/videodump
+// b. move to the directory where the videodump* files are placed.
+// c. sh-3.2# esplusplayer_ut --gtest_also_run_disabled_tests
+// --gtest_filter=*recorded*
+// d. It's controllable by 'p' : pause, 'r' : resume, 'q' : quit
+TEST_F(EsCloudGameRecordedStream,
+ DISABLED_vdapi_cg_esplusplayer_play_recorded_stream) {
+ SetLowLatencyMode(esplayer_);
+ if (video_reader_) {
+ video_reader_->SetRealtimePacketSubmit(true);
+ video_reader_->Repeat();
+ }
+ if (audio_reader_) {
+ audio_reader_->SetRealtimePacketSubmit(true);
+ audio_reader_->Repeat();
+ }
+
+ esplusplayer_prepare_async(esplayer_);
+ callback_->WaitForPrepareDone();
+ esplusplayer_start(esplayer_);
+
+ auto PauseReader = [&]() {
+ if (video_reader_) video_reader_->Pause();
+ if (audio_reader_) audio_reader_->Pause();
+ };
+
+ auto ResumeReader = [&]() {
+ if (video_reader_) video_reader_->Resume();
+ if (audio_reader_) audio_reader_->Resume();
+ };
+
+ callback_->WaitForEos([&]() -> bool {
+ string in = Utility::getKeyboardInput(100);
+ if (in.size() == 0) return false;
+ if (in.c_str()[0] == 'q') return true;
+ if (in.c_str()[0] == 'p') PauseReader();
+ if (in.c_str()[0] == 'r') ResumeReader();
+ return false;
+ });
+}
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "esplusplayer/espacket.h"
+
+namespace pp = plusplayer;
+
+namespace {
+void MakeDummyMatroskaColor(pp::MatroskaColor& color_info) {
+ color_info.matrix_coefficients = 1;
+ color_info.bits_per_channel = 1;
+ color_info.chroma_subsampling_horizontal = 1;
+ color_info.chroma_subsampling_vertical = 1;
+ color_info.cb_subsampling_horizontal = 1;
+ color_info.cb_subsampling_vertical = 1;
+ color_info.chroma_siting_horizontal = 1;
+ color_info.chroma_siting_vertical = 1;
+ color_info.range = 1;
+ color_info.transfer_characteristics = 1;
+ color_info.primaries = 1;
+ color_info.max_cll = 1;
+ color_info.max_fall = 1;
+ color_info.metadata.primary_r_chromaticity_x = 0.5;
+ color_info.metadata.primary_r_chromaticity_y = 0.5;
+ color_info.metadata.primary_g_chromaticity_x = 0.5;
+ color_info.metadata.primary_g_chromaticity_y = 0.5;
+ color_info.metadata.primary_b_chromaticity_x = 0.5;
+ color_info.metadata.primary_b_chromaticity_y = 0.5;
+ color_info.metadata.white_point_chromaticity_x = 0.5;
+ color_info.metadata.white_point_chromaticity_y = 0.5;
+ color_info.metadata.luminance_max = 0.5;
+ color_info.metadata.luminance_min = 0.5;
+}
+bool IsSameMatroskaColor(const pp::MatroskaColor& color_info1,
+ const pp::MatroskaColor& color_info2) {
+ return color_info1.matrix_coefficients == color_info2.matrix_coefficients &&
+ color_info1.bits_per_channel == color_info2.bits_per_channel &&
+ color_info1.chroma_subsampling_horizontal ==
+ color_info2.chroma_subsampling_horizontal &&
+ color_info1.chroma_subsampling_vertical ==
+ color_info2.chroma_subsampling_vertical &&
+ color_info1.cb_subsampling_horizontal ==
+ color_info2.cb_subsampling_horizontal &&
+ color_info1.cb_subsampling_vertical ==
+ color_info2.cb_subsampling_vertical &&
+ color_info1.chroma_siting_horizontal ==
+ color_info2.chroma_siting_horizontal &&
+ color_info1.chroma_siting_vertical ==
+ color_info2.chroma_siting_vertical &&
+ color_info1.range == color_info2.range &&
+ color_info1.transfer_characteristics ==
+ color_info2.transfer_characteristics &&
+ color_info1.primaries == color_info2.primaries &&
+ color_info1.max_cll == color_info2.max_cll &&
+ color_info1.max_fall == color_info2.max_fall &&
+ color_info1.metadata.primary_r_chromaticity_x ==
+ color_info2.metadata.primary_r_chromaticity_x &&
+ color_info1.metadata.primary_r_chromaticity_y ==
+ color_info2.metadata.primary_r_chromaticity_y &&
+ color_info1.metadata.primary_g_chromaticity_x ==
+ color_info2.metadata.primary_g_chromaticity_x &&
+ color_info1.metadata.primary_g_chromaticity_y ==
+ color_info2.metadata.primary_g_chromaticity_y &&
+ color_info1.metadata.primary_b_chromaticity_x ==
+ color_info2.metadata.primary_b_chromaticity_x &&
+ color_info1.metadata.primary_b_chromaticity_y ==
+ color_info2.metadata.primary_b_chromaticity_y &&
+ color_info1.metadata.white_point_chromaticity_x ==
+ color_info2.metadata.white_point_chromaticity_x &&
+ color_info1.metadata.white_point_chromaticity_y ==
+ color_info2.metadata.white_point_chromaticity_y &&
+ color_info1.metadata.luminance_max ==
+ color_info2.metadata.luminance_max &&
+ color_info1.metadata.luminance_min ==
+ color_info2.metadata.luminance_min;
+}
+} // namespace
+
+TEST(EsPacket, DISABLED_MatroskaColor_EmptyData) {
+ auto espacket = pp::EsPacket::Create();
+ ASSERT_FALSE(espacket->HasMatroskaColorInfo());
+}
+
+TEST(EsPacket, DISABLED_MatroskaColor_HasData) {
+ auto espacket = pp::EsPacket::Create();
+ pp::MatroskaColor dummy_color_info;
+ ::MakeDummyMatroskaColor(dummy_color_info);
+ espacket->SetMatroskaColorInfo(dummy_color_info);
+ ASSERT_TRUE(espacket->HasMatroskaColorInfo());
+}
+
+TEST(EsPacket, DISABLED_MatroskaColor_GetData) {
+ auto espacket = pp::EsPacket::Create();
+ pp::MatroskaColor dummy_color_info;
+ ::MakeDummyMatroskaColor(dummy_color_info);
+ espacket->SetMatroskaColorInfo(dummy_color_info);
+ ASSERT_TRUE(espacket->HasMatroskaColorInfo());
+ auto saved_data = espacket->GetMatroskaColorInfo();
+ ASSERT_TRUE(::IsSameMatroskaColor(saved_data, dummy_color_info));
+}
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <future>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "esplusplayer/esplusplayer.h"
+#include "ut/include/streamreader.hpp"
+
+namespace es {
+namespace utils {
+pp::EsPacketPtr MakeEsPacketFromPacket(const es::PacketPtr &pkt,
+ pp::StreamType type) {
+ pp::EsPacketPtr buffer = nullptr;
+ if (pkt == nullptr) {
+ buffer = es::Packet::MakeEosPacket(type);
+ } else {
+ buffer = pkt->MakeEsPacket(type);
+ }
+ return std::move(buffer);
+}
+void MakeDummyMatroskaColor(pp::MatroskaColor &color_info) {
+ color_info.matrix_coefficients = 1;
+ color_info.bits_per_channel = 2;
+ color_info.chroma_subsampling_horizontal = 3;
+ color_info.chroma_subsampling_vertical = 4;
+ color_info.cb_subsampling_horizontal = 3;
+ color_info.cb_subsampling_vertical = 4;
+ color_info.chroma_siting_horizontal = 3;
+ color_info.chroma_siting_vertical = 4;
+ color_info.range = 1;
+ color_info.transfer_characteristics = 1;
+ color_info.primaries = 1;
+ color_info.max_cll = 1;
+ color_info.max_fall = 1;
+ color_info.metadata.primary_r_chromaticity_x = 0.1;
+ color_info.metadata.primary_r_chromaticity_y = 0.2;
+ color_info.metadata.primary_g_chromaticity_x = 0.3;
+ color_info.metadata.primary_g_chromaticity_y = 0.4;
+ color_info.metadata.primary_b_chromaticity_x = 0.5;
+ color_info.metadata.primary_b_chromaticity_y = 0.6;
+ color_info.metadata.white_point_chromaticity_x = 0.7;
+ color_info.metadata.white_point_chromaticity_y = 0.8;
+ color_info.metadata.luminance_max = 0.9;
+ color_info.metadata.luminance_min = 1.0;
+}
+} // namespace utils
+} // namespace es
+
+class EsPlayerMockEventListener : public pp::EsEventListener {
+ public:
+ MOCK_METHOD2_T(OnError, void(const pp::ErrorType, UserData userdata));
+ MOCK_METHOD1_T(OnResourceConflicted, void(UserData userdata));
+ MOCK_METHOD1_T(OnSeekDone, void(UserData userdata));
+ MOCK_METHOD1_T(OnEos, void(UserData userdata));
+ MOCK_METHOD2_T(OnPrepareDone, void(bool, UserData userdata));
+ MOCK_METHOD5_T(OnBufferStatus,
+ void(const pp::StreamType &, const pp::BufferStatus &,
+ const uint64_t, const uint64_t, UserData userdata));
+ MOCK_METHOD2_T(OnReadyToPrepare,
+ void(const pp::StreamType &, UserData userdata));
+ MOCK_METHOD3_T(OnReadyToSeek, void(const pp::StreamType &, const uint64_t,
+ UserData userdata));
+
+ void Bind(std::shared_ptr<pp::EsEventListener> &&eventlistener) {
+ using ::testing::_;
+ using ::testing::Invoke;
+ eventlistener_ = eventlistener;
+ ON_CALL(*this, OnError(_, _))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &pp::EsEventListener::OnError));
+ ON_CALL(*this, OnResourceConflicted(_))
+ .WillByDefault(Invoke(eventlistener_.get(),
+ &pp::EsEventListener::OnResourceConflicted));
+ ON_CALL(*this, OnSeekDone(_))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &pp::EsEventListener::OnSeekDone));
+ ON_CALL(*this, OnEos(_))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &pp::EsEventListener::OnEos));
+ ON_CALL(*this, OnPrepareDone(_, _))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &pp::EsEventListener::OnPrepareDone));
+ ON_CALL(*this, OnBufferStatus(_, _, _, _, _))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &pp::EsEventListener::OnBufferStatus));
+ ON_CALL(*this, OnReadyToPrepare(_, _))
+ .WillByDefault(Invoke(eventlistener_.get(),
+ &pp::EsEventListener::OnReadyToPrepare));
+ ON_CALL(*this, OnReadyToSeek(_, _, _))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &pp::EsEventListener::OnReadyToSeek));
+ }
+
+ private:
+ std::shared_ptr<pp::EsEventListener> eventlistener_;
+};
+
+class EsPlayerTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ env_ = new Environment();
+ ESPacketDownloader::Init();
+ }
+
+ static void TearDownTestCase() {
+ if (env_) {
+ delete env_;
+ env_ = nullptr;
+ }
+ }
+
+ void SetUp() override {
+ gst_init_check(nullptr, nullptr, nullptr);
+ submit_stopped_ = false;
+ player_ = GetCreatePlayer();
+
+ mock_eventlistener_ = std::make_shared<EsPlayerMockEventListener>();
+ fake_eventlistener_ = std::make_shared<EsPlayerFakeEventListener>(this);
+ mock_eventlistener_->Bind(
+ std::dynamic_pointer_cast<pp::EsEventListener>(fake_eventlistener_));
+
+ player_->RegisterListener(mock_eventlistener_.get(), nullptr);
+
+ v_streamreader_ = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/video_00/", pp::kTrackTypeVideo);
+ a_streamreader_ = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/audio_00/", pp::kTrackTypeAudio);
+ }
+
+ void TearDown() override {
+ ClosePlayer();
+ if (video_task_.valid()) {
+ video_task_.wait();
+ }
+ if (audio_task_.valid()) {
+ audio_task_.wait();
+ }
+ mock_eventlistener_.reset();
+ fake_eventlistener_.reset();
+ v_streamreader_.reset();
+ a_streamreader_.reset();
+ is_tzdata_ = false;
+ has_matroska_color_info = false;
+ submit_stopped_ = false;
+ }
+
+ pp::EsPlusPlayer::Ptr GetCreatePlayer() {
+ auto esplayer = pp::EsPlusPlayer::Create();
+ esplayer->Open();
+ esplayer->SetDisplay(pp::DisplayType::kOverlay, env_->Window());
+ return std::move(esplayer);
+ }
+
+ void ClosePlayer() {
+ if (player_) {
+ player_->Close();
+ player_.reset();
+ }
+ }
+
+ pp::VideoStreamPtr GetVideoStream(const pp::Track &track) {
+ auto video_stream = pp::VideoStream::Create();
+ video_stream->SetMimeType(pp::VideoMimeType::kHEVC);
+ video_stream->SetMaxWidth(track.maxwidth);
+ video_stream->SetMaxHeight(track.maxheight);
+ video_stream->SetWidth(track.width);
+ video_stream->SetHeight(track.height);
+ video_stream->SetFramerate(track.framerate_num, track.framerate_den);
+ video_stream->SetCodecData(track.codec_data, track.codec_data_len);
+ return std::move(video_stream);
+ }
+
+ void SetVideoStream() {
+ auto videoinfo =
+ v_streamreader_
+ ->GetMediaInfo<es::VideoInfo<pp::Track, pp::TrackType>>();
+ auto videoextradata = v_streamreader_->GetExtraData();
+ auto videotrack = videoinfo.GetTrack();
+ videotrack.codec_data = videoextradata->data;
+ videotrack.codec_data_len = videoextradata->size;
+
+ auto video_stream = GetVideoStream(videotrack);
+ ASSERT_TRUE(player_->SetStream(video_stream));
+ }
+
+ pp::AudioStreamPtr GetAudioStream(const pp::Track &track) {
+ auto audio_stream = pp::AudioStream::Create();
+ if (!track.mimetype.compare("audio/mpeg"))
+ audio_stream->SetMimeType(pp::AudioMimeType::kAAC);
+ else
+ audio_stream->SetMimeType(pp::AudioMimeType::kAC3);
+ audio_stream->SetSamplerate(track.sample_rate);
+ audio_stream->SetChannels(track.channels);
+ return std::move(audio_stream);
+ }
+
+ void SetAudioStream() {
+ auto audioinfo =
+ a_streamreader_
+ ->GetMediaInfo<es::AudioInfo<pp::Track, pp::TrackType>>();
+ auto audiotrack = audioinfo.GetTrack();
+
+ auto audio_stream = GetAudioStream(audiotrack);
+ ASSERT_TRUE(player_->SetStream(audio_stream));
+ }
+
+ void SetMatroskaColorInfo() { has_matroska_color_info = true; }
+
+ void SetTrustZoneData() {
+ ASSERT_TRUE(player_->SetSubmitDataType(pp::SubmitDataType::kTrustZoneData));
+ is_tzdata_ = true;
+ }
+
+ private:
+ class EsPlayerFakeEventListener : public pp::EsEventListener {
+ public:
+ explicit EsPlayerFakeEventListener(EsPlayerTest *handler)
+ : handler_(handler) {
+ assert(handler);
+ }
+
+ void OnError(const pp::ErrorType &err_code, UserData userdata) override {
+ std::cout << "OnError" << std::endl;
+ }
+ void OnResourceConflicted(UserData userdata) override {
+ std::cout << "OnResourceConflicted" << std::endl;
+ }
+ void OnSeekDone(UserData userdata) override {
+ std::cout << "OnSeekDone" << std::endl;
+ }
+ void OnEos(UserData userdata) override {
+ std::unique_lock<std::mutex> lk(eos_m_);
+ std::cout << "OnEos" << std::endl;
+ eos_ = true;
+ lk.unlock();
+ eos_cv_.notify_all();
+ ASSERT_TRUE(handler_->player_->Stop());
+ }
+ void OnPrepareDone(bool ret, UserData userdata) override {
+ std::cout << "OnPrepareDone" << std::endl;
+ ASSERT_TRUE(handler_->player_->Start());
+ }
+ void OnBufferStatus(const pp::StreamType &type,
+ const pp::BufferStatus &status,
+ const uint64_t byte_size, const uint64_t time_size,
+ UserData userdata) override {
+ auto buffer_status =
+ status == pp::BufferStatus::kUnderrun ? "underrun" : "overrun";
+ std::cout << "OnBufferStatus " << buffer_status << std::endl;
+ }
+ void OnReadyToPrepare(const pp::StreamType &type,
+ UserData userdata) override {
+ std::cout << "OnReadyToPrepare" << std::endl;
+
+ auto a_streaming_task_fn = [this](
+ es::StreamReader<pp::TrackType>::Ptr &streamreader, bool is_tzdata) {
+ while (true) {
+ auto pkt = streamreader->ReadNextPacket();
+ auto buffer = es::utils::MakeEsPacketFromPacket(
+ pkt, static_cast<pp::StreamType>(streamreader->GetTrackType()));
+ if (is_tzdata)
+ SubmitTzEsPacket(buffer);
+ else
+ SubmitEsPacket(buffer);
+ if (pkt == nullptr) break;
+ }
+ };
+ auto v_streaming_task_fn = [this](
+ es::StreamReader<pp::TrackType>::Ptr &streamreader, bool is_tzdata,
+ bool has_color_info) {
+ while (true) {
+ auto pkt = streamreader->ReadNextPacket();
+ auto buffer = es::utils::MakeEsPacketFromPacket(
+ pkt, static_cast<pp::StreamType>(streamreader->GetTrackType()));
+ if (has_color_info && buffer->GetPts() == 0) {
+ pp::MatroskaColor color_info;
+ es::utils::MakeDummyMatroskaColor(color_info);
+ buffer->SetMatroskaColorInfo(color_info);
+ }
+ if (is_tzdata)
+ SubmitTzEsPacket(buffer);
+ else
+ SubmitEsPacket(buffer);
+ if (pkt == nullptr) break;
+ }
+ };
+ if (type == pp::StreamType::kVideo) {
+ handler_->video_task_ =
+ std::async(std::launch::async, v_streaming_task_fn,
+ std::ref(handler_->v_streamreader_),
+ handler_->is_tzdata_, handler_->has_matroska_color_info);
+ } else if (type == pp::StreamType::kAudio) {
+ handler_->audio_task_ = std::async(
+ std::launch::async, a_streaming_task_fn,
+ std::ref(handler_->a_streamreader_), handler_->is_tzdata_);
+ }
+ }
+ void OnReadyToSeek(const pp::StreamType &type, const uint64_t offset,
+ UserData userdata) override {
+ std::cout << "OnReadyToSeek" << std::endl;
+ }
+
+ void WaitForEos() {
+ std::unique_lock<std::mutex> lk(eos_m_);
+ eos_cv_.wait_for(lk, std::chrono::minutes(1),
+ [this]() -> bool { return eos_; });
+ eos_ = false;
+ lk.unlock();
+ }
+
+ void SubmitEsPacket(const pp::EsPacketPtr &packet) {
+ using PacketSubmitStatus = pp::PacketSubmitStatus;
+ PacketSubmitStatus status = PacketSubmitStatus::kNotPrepared;
+ while (status != PacketSubmitStatus::kSuccess) {
+ if (handler_->submit_stopped_ == true) break;
+ status = handler_->player_->SubmitPacket(packet);
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ }
+ }
+
+ void SubmitTzEsPacket(const pp::EsPacketPtr &packet) {
+ using PacketSubmitStatus = pp::PacketSubmitStatus;
+ PacketSubmitStatus status = PacketSubmitStatus::kNotPrepared;
+ while (status != PacketSubmitStatus::kSuccess) {
+ if (handler_->submit_stopped_ == true) break;
+ status = handler_->player_->SubmitTrustZonePacket(packet);
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ }
+ }
+
+ private:
+ bool eos_ = false;
+ std::mutex eos_m_;
+ std::condition_variable eos_cv_;
+ EsPlayerTest *handler_{nullptr};
+ };
+
+ public:
+ static Environment *env_;
+ std::atomic<bool> submit_stopped_;
+ std::shared_ptr<pp::EsPlusPlayer> player_;
+ std::shared_ptr<EsPlayerMockEventListener> mock_eventlistener_;
+ std::shared_ptr<EsPlayerFakeEventListener> fake_eventlistener_;
+ es::StreamReader<pp::TrackType>::Ptr v_streamreader_;
+ es::StreamReader<pp::TrackType>::Ptr a_streamreader_;
+ std::future<void> video_task_;
+ std::future<void> audio_task_;
+ bool has_matroska_color_info = false;
+ bool is_tzdata_ = false;
+};
+
+Environment *EsPlayerTest::env_ = nullptr;
+
+TEST_F(EsPlayerTest, Play) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ EXPECT_CALL(*mock_eventlistener_, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener_, OnPrepareDone(true, _));
+ EXPECT_CALL(*mock_eventlistener_, OnEos(_)).Times(1);
+
+ SetVideoStream();
+ SetAudioStream();
+
+ ASSERT_TRUE(player_->PrepareAsync());
+
+ fake_eventlistener_->WaitForEos();
+}
+
+TEST_F(EsPlayerTest, PlayWithDrm) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ EXPECT_CALL(*mock_eventlistener_, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener_, OnPrepareDone(true, _));
+ EXPECT_CALL(*mock_eventlistener_, OnEos(_)).Times(1);
+
+ SetVideoStream();
+ SetAudioStream();
+ SetTrustZoneData();
+
+ ASSERT_TRUE(player_->PrepareAsync());
+
+ fake_eventlistener_->WaitForEos();
+}
+
+TEST_F(EsPlayerTest, PlayWithMatroskaColor) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ EXPECT_CALL(*mock_eventlistener_, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener_, OnPrepareDone(true, _));
+
+ SetVideoStream();
+ SetAudioStream();
+
+ SetMatroskaColorInfo();
+
+ ASSERT_TRUE(player_->PrepareAsync());
+
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+
+ ASSERT_TRUE(player_->Stop());
+ submit_stopped_ = true;
+}
+
+TEST_F(EsPlayerTest, PlayWithDrmAndMatroskaColor) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ EXPECT_CALL(*mock_eventlistener_, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener_, OnPrepareDone(true, _));
+
+ SetVideoStream();
+ SetAudioStream();
+
+ SetMatroskaColorInfo();
+ SetTrustZoneData();
+
+ ASSERT_TRUE(player_->PrepareAsync());
+
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+
+ ASSERT_TRUE(player_->Stop());
+ submit_stopped_ = true;
+}
+
+TEST_F(EsPlayerTest, Get_Adaptive_Info) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ SetVideoStream();
+ SetAudioStream();
+
+ EXPECT_CALL(*mock_eventlistener_, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener_, OnPrepareDone(true, _));
+
+ ASSERT_TRUE(player_->PrepareAsync());
+
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+
+ uint64_t *pvalue = new uint64_t;
+ ASSERT_TRUE(player_->GetAdaptiveInfo(
+ (void *)pvalue, pp::PlayerAdaptiveInfo::kVideoDroppedFrames));
+ std::cout << "Dropped frames: " << *pvalue << std::endl;
+
+ ASSERT_TRUE(player_->Stop());
+ submit_stopped_ = true;
+}
+
+TEST_F(EsPlayerTest, SetGetVolume) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ SetVideoStream();
+ SetAudioStream();
+
+ EXPECT_CALL(*mock_eventlistener_, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener_, OnPrepareDone(true, _));
+
+ const int kVolume = 80;
+ ASSERT_TRUE(player_->SetVolume(kVolume));
+
+ ASSERT_TRUE(player_->PrepareAsync());
+
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ int volume = 0;
+ ASSERT_TRUE(player_->GetVolume(&volume));
+ std::cout << "volume: " << volume << std::endl;
+ ASSERT_EQ(volume, kVolume);
+
+ ASSERT_TRUE(player_->Stop());
+ submit_stopped_ = true;
+}
+
+TEST_F(EsPlayerTest, VideoActivateDeactivate) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ SetVideoStream();
+ SetAudioStream();
+
+ EXPECT_CALL(*mock_eventlistener_, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener_, OnPrepareDone(true, _));
+
+ const int kVolume = 80;
+ ASSERT_TRUE(player_->SetVolume(kVolume));
+
+ ASSERT_TRUE(player_->PrepareAsync());
+
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ EXPECT_TRUE(player_->Deactivate(pp::StreamType::kVideo));
+ // SetVideoStream();
+ EXPECT_TRUE(player_->Activate(pp::StreamType::kVideo));
+ std::this_thread::sleep_for(std::chrono::seconds(15));
+
+ ASSERT_TRUE(player_->Stop());
+ submit_stopped_ = true;
+}
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <stdio.h>
+
+#include <map>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+
+#include "core/decoderinputbuffer.h"
+#include "core/decoderinputbuffer_listener.h"
+#include "core/track_util.h"
+#include "core/utils/plusplayer_log.h"
+#include "esplayer/esplayer.h"
+#include "esplusplayer/esplusplayer.h"
+#include "tracksource/tracksource.h"
+#include "ut/include/appwindow.h"
+
+using namespace esplusplayer;
+
+static constexpr int kMaxSizeOfQueue = 3;
+static constexpr int kTrackTypes[] = {kTrackTypeAudio, kTrackTypeVideo,
+ kTrackTypeSubtitle};
+
+class Feeder2 : public DecoderInputBufferListener {
+ public:
+ Feeder2() noexcept {}
+ ~Feeder2() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> lock(state_m_);
+ if (state_ != State::kActivated) {
+ LOG_INFO("Already stopped. just destroy feeder");
+ return;
+ }
+ stop_ = true;
+ for (auto type : kTrackTypes) {
+ feed_buf_[type].buffer_cv.notify_one();
+ if (feed_buf_[type].task.valid()) feed_buf_[type].task.wait();
+ }
+ state_ = State::kNone;
+ player_ = nullptr;
+ LOG_LEAVE;
+ return;
+ }
+
+ void OnRecv(DecoderInputBufferPtr inbuffer) {
+#if 1 // Convert Raw Buffer
+ StreamType type = static_cast<StreamType>(inbuffer->GetType());
+ GstBuffer *gstbuffer = static_cast<GstBuffer *>(inbuffer->Release());
+ uint64_t pts = GST_BUFFER_PTS(gstbuffer) / 1000000;
+ uint64_t duration = GST_BUFFER_DURATION(gstbuffer) / 1000000;
+ GstMapInfo info;
+
+ gst_buffer_map(gstbuffer, &info, GST_MAP_READ);
+ // LOG_INFO("type [%d] pts [%lld] duration [%lld] size [%d]", type, pts,
+ // duration, info.size);
+
+ std::shared_ptr<char> data(new char[info.size],
+ std::default_delete<char[]>());
+ for (gsize i = 0; i < info.size; i++) {
+ data.get()[i] = info.data[i];
+ }
+ auto inputbuffer = EsPacket::Create(type, data, info.size, pts, duration);
+ gst_buffer_unmap(gstbuffer, &info);
+
+ Push_(std::move(inputbuffer));
+#else
+ Push_(std::move(inbuffer));
+#endif
+ }
+
+ bool Start(esplusplayer::EsPlusPlayer *player) {
+ LOG_ENTER;
+ assert(player);
+ std::lock_guard<std::mutex> lock(state_m_);
+ if (state_ == State::kActivated) {
+ LOG_INFO("do nothing, task already activated, call stop if you need");
+ return false;
+ }
+ player_ = player;
+ stop_ = false;
+ for (auto type : kTrackTypes) {
+ feed_buf_[type].type = static_cast<TrackType>(type);
+ feed_buf_[type].task = std::async(std::launch::async, &Feeder2::Task_,
+ this, &feed_buf_[type]);
+ feed_buf_[type].activated = true;
+ }
+ state_ = State::kActivated;
+ LOG_LEAVE;
+ return true;
+ }
+
+ bool Stop() {
+ LOG_ENTER;
+ std::lock_guard<std::mutex> lock(state_m_);
+ if (state_ != State::kActivated) {
+ LOG_INFO("Already stopped. just destroy feeder");
+ return true;
+ }
+ stop_ = true;
+ for (auto type : kTrackTypes) {
+ feed_buf_[type].buffer_cv.notify_one();
+ if (feed_buf_[type].task.valid()) feed_buf_[type].task.wait();
+ }
+ state_ = State::kNone;
+ player_ = nullptr;
+ LOG_LEAVE;
+ return true;
+ }
+
+ bool SetEos() {
+ LOG_ENTER;
+ for (auto type : kTrackTypes) {
+ auto inbuffer = EsPacket::CreateEos(static_cast<StreamType>(type));
+ Push_(std::move(inbuffer));
+ }
+ LOG_LEAVE;
+ return true;
+ }
+
+ bool Flush(TrackType type) {
+ LOG_ENTER;
+ if (type >= kTrackTypeMax) {
+ return false;
+ }
+ std::lock_guard<std::mutex> lock(feed_buf_[type].buffer_m);
+ feed_buf_[type].buffer_cv.notify_all();
+ // bool ret =
+ // decoderinputbuffer_util::FlushQueue(feed_buf_[type].inbufferqueue);
+ bool ret = true;
+ while (!feed_buf_[type].inbufferqueue.empty()) {
+ feed_buf_[type].inbufferqueue.pop();
+ }
+ LOG_LEAVE;
+ return ret;
+ }
+
+ private:
+ bool Push_(EsPacketPtr inbuffer) {
+ TrackType type = static_cast<TrackType>(inbuffer->GetType());
+
+ if (type >= kTrackTypeMax) {
+ LOG_INFO("invalid type , failed to push");
+ return false;
+ }
+ if (stop_) {
+ // LOG_INFO("stopped, failed to push");
+ return false;
+ }
+ {
+ std::unique_lock<std::mutex> lock(feed_buf_[type].buffer_m);
+ if (feed_buf_[type].activated) {
+ feed_buf_[type].inbufferqueue.push(std::move(inbuffer));
+ feed_buf_[type].buffer_cv.notify_one();
+ }
+ if (feed_buf_[type].inbufferqueue.size() > kMaxSizeOfQueue) {
+ feed_buf_[type].buffer_cv.wait(lock);
+ }
+ }
+ return true;
+ }
+
+ private:
+ struct FeedBuffer {
+ bool activated = false;
+ TrackType type = kTrackTypeMax;
+ std::mutex buffer_m;
+ std::condition_variable buffer_cv;
+ std::queue<EsPacketPtr> inbufferqueue;
+ std::future<void> task;
+ };
+
+ void Task_(FeedBuffer *feed_buf) {
+ LOG_INFO("ENTER , TASK type[%d]", feed_buf->type);
+ if (!feed_buf) {
+ assert(0 && "feed_buf is null, can't create feeder task");
+ return;
+ }
+ LOG_INFO("FeederTask is Created , TaskID type[%d]", feed_buf->type);
+ while (!stop_) {
+ std::unique_lock<std::mutex> lock(feed_buf->buffer_m);
+ if (feed_buf->inbufferqueue.empty()) {
+ feed_buf->buffer_cv.wait(lock);
+ } else {
+ if (player_->SubmitPacket(feed_buf->inbufferqueue.front()) !=
+ PacketSubmitStatus::kSuccess) {
+ lock.unlock();
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ } else {
+ feed_buf->inbufferqueue.pop();
+ lock.unlock();
+ feed_buf->buffer_cv.notify_one();
+ }
+ }
+ }
+ std::lock_guard<std::mutex> lock(feed_buf->buffer_m);
+ feed_buf->activated = false;
+ // decoderinputbuffer_util::FlushQueue(feed_buf->inbufferqueue);
+ while (!feed_buf->inbufferqueue.empty()) {
+ feed_buf->inbufferqueue.pop();
+ }
+ LOG_INFO("LEAVE , TASK TYPE[%d]", feed_buf->type);
+ }
+
+ enum class State {
+ kNone,
+ kActivated,
+ };
+
+ private:
+ esplusplayer::EsPlusPlayer *player_ = nullptr;
+ State state_ = State::kNone;
+ bool stop_ = false;
+ std::mutex state_m_;
+ FeedBuffer feed_buf_[kTrackTypeMax];
+};
+
+class EsPlayerMockEventListener1 : public EsEventListener {
+ public:
+ MOCK_METHOD2_T(OnError, void(const ErrorType, UserData userdata));
+ MOCK_METHOD1_T(OnResourceConflicted, void(UserData userdata));
+ MOCK_METHOD1_T(OnSeekDone, void(UserData userdata));
+ MOCK_METHOD1_T(OnEos, void(UserData userdata));
+ MOCK_METHOD2_T(OnPrepareDone, void(bool, UserData userdata));
+ MOCK_METHOD5_T(OnBufferStatus,
+ void(const StreamType &, const BufferStatus &, const uint64_t,
+ const uint64_t, UserData userdata));
+ MOCK_METHOD2_T(OnReadyToPrepare, void(const StreamType &, UserData userdata));
+ MOCK_METHOD3_T(OnReadyToSeek,
+ void(const StreamType &, const uint64_t, UserData userdata));
+
+ void Bind(std::shared_ptr<EsEventListener> &&eventlistener) {
+ using ::testing::_;
+ using ::testing::Invoke;
+ eventlistener_ = eventlistener;
+ ON_CALL(*this, OnError(_, _))
+ .WillByDefault(Invoke(eventlistener_.get(), &EsEventListener::OnError));
+ ON_CALL(*this, OnResourceConflicted(_))
+ .WillByDefault(Invoke(eventlistener_.get(),
+ &EsEventListener::OnResourceConflicted));
+ ON_CALL(*this, OnSeekDone(_))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &EsEventListener::OnSeekDone));
+ ON_CALL(*this, OnEos(_))
+ .WillByDefault(Invoke(eventlistener_.get(), &EsEventListener::OnEos));
+ ON_CALL(*this, OnPrepareDone(_, _))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &EsEventListener::OnPrepareDone));
+ ON_CALL(*this, OnBufferStatus(_, _, _, _, _))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &EsEventListener::OnBufferStatus));
+ ON_CALL(*this, OnReadyToPrepare(_, _))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &EsEventListener::OnReadyToPrepare));
+ ON_CALL(*this, OnReadyToSeek(_, _, _))
+ .WillByDefault(
+ Invoke(eventlistener_.get(), &EsEventListener::OnReadyToSeek));
+ }
+
+ private:
+ std::shared_ptr<EsEventListener> eventlistener_;
+};
+
+class EsPlayerFakeEventListener1 : public EsEventListener {
+ public:
+ EsPlayerFakeEventListener1()
+ : eos_(false), ready_audio_data_(false), ready_video_data_(false) {}
+
+ void OnError(const ErrorType &err_code, UserData userdata) override {
+ std::cout << "OnError" << std::endl;
+ }
+ void OnResourceConflicted(UserData userdata) override {
+ std::cout << "OnResourceConflicted" << std::endl;
+ }
+ void OnSeekDone(UserData userdata) override {
+ std::cout << "OnSeekDone" << std::endl;
+ }
+ void OnEos(UserData userdata) override {
+ std::unique_lock<std::mutex> lk(eos_m_);
+ std::cout << "OnEos" << std::endl;
+ eos_ = true;
+ lk.unlock();
+ eos_cv_.notify_all();
+ }
+ void OnPrepareDone(bool ret, UserData userdata) override {
+ std::cout << "OnPrepareDone" << std::endl;
+ }
+ void OnBufferStatus(const StreamType &type, const BufferStatus &status,
+ const uint64_t byte_size, const uint64_t time_size,
+ UserData userdata) override {
+ auto buffer_status =
+ status == BufferStatus::kUnderrun ? "underrun" : "overrun";
+ std::cout << "OnBufferStatus " << buffer_status << std::endl;
+ }
+ void OnReadyToPrepare(const StreamType &type, UserData userdata) override {
+ std::cout << "OnReadyToPrepare" << std::endl;
+ std::unique_lock<std::mutex> lk(data_m_);
+ if (type == StreamType::kAudio)
+ ready_audio_data_ = true;
+ else if (type == StreamType::kVideo)
+ ready_video_data_ = true;
+ lk.unlock();
+ data_cv_.notify_all();
+ }
+ void OnReadyToSeek(const StreamType &type, const uint64_t offset,
+ UserData userdata) override {
+ std::cout << "OnReadyToSeek" << std::endl;
+ std::unique_lock<std::mutex> lk(data_m_);
+ if (type == StreamType::kAudio)
+ ready_audio_data_ = true;
+ else if (type == StreamType::kVideo)
+ ready_video_data_ = true;
+ lk.unlock();
+ 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_;
+ });
+ ready_audio_data_ = false;
+ ready_video_data_ = false;
+ 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_; });
+ eos_ = false;
+ std::cout << "WaitForEos stop" << std::endl;
+ lk.unlock();
+ }
+
+ private:
+ bool eos_;
+ std::mutex eos_m_;
+ std::condition_variable eos_cv_;
+ bool ready_audio_data_;
+ bool ready_video_data_;
+ std::mutex data_m_;
+ std::condition_variable data_cv_;
+};
+
+class EsPlayerTest2 : public ::testing::Test {
+ public:
+ EsPlayerTest2() {}
+
+ void SetUp() override {
+ gst_init_check(nullptr, nullptr, nullptr);
+ // std::string url("http://10.88.105.104/WebAPITest/HLS/Clean/normal.m3u8");
+ std::string url(
+ "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/"
+ "bipbop_16x9_variant.m3u8");
+ TypeFinder typefinder(url);
+ StreamingProperty property;
+ if (!typefinder.Probe()) {
+ return;
+ }
+ tracksource_ = TrackSource::CreateCompositor();
+ tracksource_->AddSource(&typefinder, property);
+ if (!tracksource_->Prepare()) {
+ LOG_ERROR("tracksource prepare was failed");
+ return;
+ }
+ feeder_.reset(new Feeder2());
+ tracksource_->RegisterListener(feeder_.get());
+ }
+
+ void TearDown() override {
+ feeder_.reset();
+ tracksource_.reset();
+ }
+
+ std::shared_ptr<Feeder2> GetFeeder2() { return feeder_; }
+ std::shared_ptr<TrackSource> GetTrackSource() { return tracksource_; }
+
+ private:
+ std::shared_ptr<Feeder2> feeder_;
+ std::shared_ptr<TrackSource> tracksource_;
+};
+
+VideoStreamPtr GetVideoStream() {
+ auto video_stream = VideoStream::Create();
+ video_stream->SetMimeType(VideoMimeType::kH264);
+ video_stream->SetWidth(640);
+ video_stream->SetHeight(352);
+ video_stream->SetFramerate(30, 1);
+ return std::move(video_stream);
+}
+
+AudioStreamPtr GetAudioStream() {
+ auto audio_stream = AudioStream::Create();
+ audio_stream->SetMimeType(AudioMimeType::kAAC);
+ audio_stream->SetSamplerate(44100);
+ audio_stream->SetChannels(2);
+ return std::move(audio_stream);
+}
+
+AudioStreamPtr GetAudioStream2() {
+ auto audio_stream = AudioStream::Create();
+ audio_stream->SetMimeType(AudioMimeType::kAAC);
+ audio_stream->SetSamplerate(22050);
+ audio_stream->SetChannels(2);
+ return std::move(audio_stream);
+}
+
+TEST_F(EsPlayerTest2, Play) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ auto mock_eventlistener = std::make_shared<EsPlayerMockEventListener1>();
+ auto fake_eventlistener = std::make_shared<EsPlayerFakeEventListener1>();
+ mock_eventlistener->Bind(
+ std::dynamic_pointer_cast<EsEventListener>(fake_eventlistener));
+
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+
+ auto esplayer = EsPlusPlayer::Create();
+ esplayer->Open();
+
+ std::unique_ptr<esplusplayer_ut::AppWindow> appwindow(
+ new esplusplayer_ut::AppWindow(0, 0, 1920, 1080));
+ assert(appwindow.get());
+#if ECORE_WAYLAND_DISPLAY_TEST
+ esplayer->SetDisplay(DisplayType::kOverlay, appwindow->GetEcoreWL2Window(), 0,
+ 0, 1920, 1080);
+#else
+ Evas_Object *obj = appwindow->GetWindow().obj;
+ esplayer->SetDisplay(esplusplayer::DisplayType::kOverlay, obj);
+#endif
+
+ esplayer->RegisterListener(mock_eventlistener.get(), nullptr);
+ EXPECT_CALL(*mock_eventlistener, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener, OnPrepareDone(true, _));
+
+ auto feeding_task_fn = [this, &esplayer]() {
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+ // Feeder must be started before TrackSource::Start to prevent loss of
+ // packets.
+ feeder->Start(esplayer.get());
+ tracksource->Start();
+
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ };
+
+ esplayer->SetStream(GetVideoStream());
+ esplayer->SetStream(GetAudioStream());
+ esplayer->PrepareAsync();
+
+ fake_eventlistener->WaitAllStreamData();
+ auto feeding_task = std::thread(feeding_task_fn);
+
+ esplayer->Start();
+
+ feeding_task.join();
+
+ esplayer->Stop();
+ esplayer->Close();
+
+ feeder->Stop();
+ tracksource->Stop();
+}
+
+TEST_F(EsPlayerTest2, Seek) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ auto mock_eventlistener = std::make_shared<EsPlayerMockEventListener1>();
+ auto fake_eventlistener = std::make_shared<EsPlayerFakeEventListener1>();
+ mock_eventlistener->Bind(
+ std::dynamic_pointer_cast<EsEventListener>(fake_eventlistener));
+
+ std::unique_ptr<esplusplayer_ut::AppWindow> appwindow(
+ new esplusplayer_ut::AppWindow(0, 0, 1920, 1080));
+ assert(appwindow.get());
+ Evas_Object *obj = appwindow->GetWindow().obj;
+
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+ auto esplayer = EsPlusPlayer::Create();
+
+ esplayer->Open();
+
+ esplayer->RegisterListener(mock_eventlistener.get(), nullptr);
+ EXPECT_CALL(*mock_eventlistener, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener, OnPrepareDone(true, _));
+
+ auto feeding_task_fn = [this, &esplayer]() {
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+ // Feeder must be started before TrackSource::Start to prevent loss of
+ // packets.
+ feeder->Start(esplayer.get());
+ tracksource->Start();
+
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ };
+
+ esplayer->SetDisplay(esplusplayer::DisplayType::kOverlay, obj);
+ esplayer->SetStream(GetVideoStream());
+ esplayer->SetStream(GetAudioStream());
+ esplayer->PrepareAsync();
+
+ fake_eventlistener->WaitAllStreamData();
+ auto feeding_task = std::thread(feeding_task_fn);
+
+ esplayer->Start();
+
+ feeding_task.join();
+
+ feeder->Stop();
+ tracksource->Pause();
+ tracksource->Seek(5000);
+
+ EXPECT_CALL(*mock_eventlistener, OnReadyToSeek(_, _, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener, OnSeekDone(_));
+
+ esplayer->Seek(5000);
+
+ fake_eventlistener->WaitAllStreamData();
+ auto seek_feeding_task = std::thread(feeding_task_fn);
+
+ seek_feeding_task.join();
+
+ esplayer->Stop();
+ esplayer->Close();
+ feeder->Stop();
+ tracksource->Stop();
+}
+
+TEST_F(EsPlayerTest2, DISABLED_SetPlaybackRate) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ auto mock_eventlistener = std::make_shared<EsPlayerMockEventListener1>();
+ auto fake_eventlistener = std::make_shared<EsPlayerFakeEventListener1>();
+ mock_eventlistener->Bind(
+ std::dynamic_pointer_cast<EsEventListener>(fake_eventlistener));
+
+ std::unique_ptr<esplusplayer_ut::AppWindow> appwindow(
+ new esplusplayer_ut::AppWindow(0, 0, 1920, 1080));
+ assert(appwindow.get());
+ Evas_Object *obj = appwindow->GetWindow().obj;
+
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+ auto esplayer = EsPlusPlayer::Create();
+
+ esplayer->Open();
+
+ esplayer->RegisterListener(mock_eventlistener.get(), nullptr);
+ EXPECT_CALL(*mock_eventlistener, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener, OnPrepareDone(true, _));
+
+ auto feeding_task_fn = [this, &esplayer]() {
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+ // Feeder must be started before TrackSource::Start to prevent loss of
+ // packets.
+ feeder->Start(esplayer.get());
+ tracksource->Start();
+
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ };
+
+ esplayer->SetDisplay(esplusplayer::DisplayType::kOverlay, obj);
+ esplayer->SetStream(GetVideoStream());
+ esplayer->SetStream(GetAudioStream());
+ esplayer->PrepareAsync();
+
+ fake_eventlistener->WaitAllStreamData();
+ auto feeding_task = std::thread(feeding_task_fn);
+
+ esplayer->Start();
+
+ feeding_task.join();
+
+ feeder->Stop();
+ tracksource->Pause();
+
+ double playback_rate = 1.5;
+ uint64_t time_millisecond = 0;
+ esplayer->GetPlayingTime(&time_millisecond);
+
+ esplayer->SetPlaybackRate(playback_rate, true);
+ tracksource->Seek(time_millisecond, playback_rate);
+
+ auto seek_feeding_task = std::thread(feeding_task_fn);
+ seek_feeding_task.join();
+
+ esplayer->Stop();
+ esplayer->Close();
+ feeder->Stop();
+ tracksource->Stop();
+}
+
+TEST_F(EsPlayerTest2, A_DeactivateActivate) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ auto mock_eventlistener = std::make_shared<EsPlayerMockEventListener1>();
+ auto fake_eventlistener = std::make_shared<EsPlayerFakeEventListener1>();
+ mock_eventlistener->Bind(
+ std::dynamic_pointer_cast<EsEventListener>(fake_eventlistener));
+
+ std::unique_ptr<esplusplayer_ut::AppWindow> appwindow(
+ new esplusplayer_ut::AppWindow(0, 0, 1920, 1080));
+ assert(appwindow.get());
+ Evas_Object *obj = appwindow->GetWindow().obj;
+
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+ auto esplayer = EsPlusPlayer::Create();
+
+ esplayer->Open();
+
+ esplayer->RegisterListener(mock_eventlistener.get(), nullptr);
+ EXPECT_CALL(*mock_eventlistener, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener, OnPrepareDone(true, _));
+
+ auto feeding_task_fn = [this, &esplayer]() {
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+ // Feeder must be started before TrackSource::Start to prevent loss of
+ // packets.
+ feeder->Start(esplayer.get());
+ tracksource->Start();
+ };
+ uint64_t cur_pos_msec = 0;
+ esplayer->SetDisplay(esplusplayer::DisplayType::kOverlay, obj);
+ tracksource->SelectTrack(kTrackTypeAudio, 0, 0);
+ esplayer->SetStream(GetVideoStream());
+ esplayer->SetStream(GetAudioStream());
+ esplayer->PrepareAsync();
+
+ fake_eventlistener->WaitAllStreamData();
+ auto feeding_task = std::thread(feeding_task_fn);
+
+ esplayer->Start();
+ feeding_task.join();
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+
+ auto track_list = tracksource->GetTrackInfo();
+ Track activated_track;
+ track_util::GetActiveTrack(track_list, kTrackTypeAudio, &activated_track);
+ if (activated_track.index == 0) {
+ printf("the index[%d] is already activated", 0);
+ }
+ esplayer->Deactivate(static_cast<esplusplayer::StreamType>(kTrackTypeAudio));
+ esplayer->GetPlayingTime(&cur_pos_msec);
+
+ feeder->Flush(kTrackTypeAudio);
+ feeder->Stop();
+ tracksource->Pause();
+ tracksource->SelectTrack(kTrackTypeAudio, 1, cur_pos_msec);
+ tracksource->Seek(cur_pos_msec);
+ printf("activate tracktype : %d index : %d playingtime : %llu ms ",
+ static_cast<int>(kTrackTypeAudio), 0, cur_pos_msec);
+ track_list = tracksource->GetTrackInfo();
+ if (!track_util::GetActiveTrack(track_list, kTrackTypeAudio,
+ &activated_track)) {
+ LOG_ERROR("Can not find active track with [%d] index", 1);
+ return;
+ }
+ esplayer->SetStream(GetAudioStream2());
+ esplayer->Activate(static_cast<esplusplayer::StreamType>(kTrackTypeAudio));
+ esplayer->Seek(cur_pos_msec);
+ fake_eventlistener->WaitAllStreamData();
+ auto new_feeding_task = std::thread(feeding_task_fn);
+
+ new_feeding_task.join();
+ std::this_thread::sleep_for(std::chrono::seconds(3));
+ esplayer->Stop();
+ esplayer->Close();
+ feeder->Stop();
+ tracksource->Stop();
+}
+
+TEST_F(EsPlayerTest2, VideoPeekSeek) {
+ using ::testing::_;
+ using ::testing::AtLeast;
+
+ auto mock_eventlistener = std::make_shared<EsPlayerMockEventListener1>();
+ auto fake_eventlistener = std::make_shared<EsPlayerFakeEventListener1>();
+ mock_eventlistener->Bind(
+ std::dynamic_pointer_cast<EsEventListener>(fake_eventlistener));
+
+ std::unique_ptr<esplusplayer_ut::AppWindow> appwindow(
+ new esplusplayer_ut::AppWindow(0, 0, 1920, 1080));
+ assert(appwindow.get());
+ Evas_Object *obj = appwindow->GetWindow().obj;
+
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+ auto esplayer = EsPlusPlayer::Create();
+
+ esplayer->Open();
+
+ esplayer->RegisterListener(mock_eventlistener.get(), nullptr);
+ EXPECT_CALL(*mock_eventlistener, OnReadyToPrepare(_, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener, OnPrepareDone(true, _));
+
+ auto feeding_task_fn = [this, &esplayer]() {
+ auto feeder = GetFeeder2();
+ auto tracksource = GetTrackSource();
+ // Feeder must be started before TrackSource::Start to prevent loss of
+ // packets.
+ feeder->Start(esplayer.get());
+ tracksource->Start();
+ };
+
+ esplayer->SetDisplay(esplusplayer::DisplayType::kOverlay, obj);
+ esplayer->SetStream(GetVideoStream());
+ esplayer->SetStream(GetAudioStream());
+ esplayer->SetVideoFramePeekMode();
+ esplayer->PrepareAsync();
+
+ fake_eventlistener->WaitAllStreamData();
+ auto feeding_task = std::thread(feeding_task_fn);
+
+ esplayer->Start();
+
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+
+ feeding_task.join();
+
+ feeder->Stop();
+ tracksource->Pause();
+ tracksource->Seek(0);
+
+ EXPECT_CALL(*mock_eventlistener, OnReadyToSeek(_, _, _)).Times(AtLeast(1));
+ EXPECT_CALL(*mock_eventlistener, OnSeekDone(_));
+
+ esplayer->Pause();
+ esplayer->Seek(0);
+ fake_eventlistener->WaitAllStreamData();
+ auto seek_feeding_task = std::thread(feeding_task_fn);
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ std::cout <<" Render Video and wait 5 sec ~"<< std::endl;
+ esplayer->RenderVideoFrame();
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ std::cout <<" Resume ~"<< std::endl;
+ esplayer->Resume();
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ seek_feeding_task.join();
+
+ esplayer->Stop();
+ esplayer->Close();
+ feeder->Stop();
+ tracksource->Stop();
+}
\ No newline at end of file
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <memory>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "trackrenderer/trackrenderer.h"
+#include "ut/include/streamreader.hpp"
+
+namespace es {
+
+namespace utils {
+pp::trackrenderer::DecoderInputBufferPtr MakeBufferFromPacket(
+ const es::PacketPtr &pkt, pp::trackrenderer::TrackType type) {
+ pp::trackrenderer::DecoderInputBufferPtr buffer = nullptr;
+ if (pkt == nullptr) {
+ buffer = es::Packet::MakeEosBuffer(type);
+ } else {
+ buffer = pkt->MakeDecoderInputBuffer(type);
+ }
+ return std::move(buffer);
+}
+void SubmitDecoderInputBuffer(const pp::trackrenderer::TrackRenderer::Ptr &trackrenderer,
+ const pp::trackrenderer::DecoderInputBufferPtr &buffer) {
+ using SubmitStatus = pp::trackrenderer::SubmitStatus;
+ SubmitStatus status = SubmitStatus::kNotPrepared;
+ while (status == SubmitStatus::kNotPrepared ||
+ status == SubmitStatus::kFull || status == SubmitStatus::kHold) {
+ trackrenderer->SubmitPacket(buffer, &status);
+ }
+}
+} // namespace utils
+} // namespace es
+
+class TrackRendererMockEventListener : public pp::trackrenderer::TrackRenderer::EventListener {
+ public:
+ MOCK_METHOD1_T(OnError, void(const pp::trackrenderer::ErrorType));
+ MOCK_METHOD0_T(OnResourceConflicted, void());
+ MOCK_METHOD0_T(OnSeekDone, void());
+ MOCK_METHOD0_T(OnEos, void());
+ MOCK_METHOD4_T(OnDrmInitData, void(int *, unsigned int, unsigned char *,
+ pp::trackrenderer::TrackType));
+ MOCK_METHOD2_T(OnBufferStatus, void(const pp::trackrenderer::TrackType &,
+ const pp::trackrenderer::BufferStatus &));
+ MOCK_METHOD2_T(OnSeekData,
+ void(const pp::trackrenderer::TrackType &, const uint64_t));
+
+ void Bind(std::shared_ptr<pp::trackrenderer::TrackRenderer::EventListener>
+ &&eventlistener) {
+ using ::testing::_;
+ using ::testing::Invoke;
+ eventlistener_ = eventlistener;
+ ON_CALL(*this, OnError(_))
+ .WillByDefault(
+ Invoke(eventlistener_.get(),
+ &pp::trackrenderer::TrackRenderer::EventListener::OnError));
+ ON_CALL(*this, OnResourceConflicted())
+ .WillByDefault(Invoke(eventlistener_.get(),
+ &pp::trackrenderer::TrackRenderer::EventListener::
+ OnResourceConflicted));
+ ON_CALL(*this, OnSeekDone())
+ .WillByDefault(Invoke(
+ eventlistener_.get(),
+ &pp::trackrenderer::TrackRenderer::EventListener::OnSeekDone));
+ ON_CALL(*this, OnEos())
+ .WillByDefault(
+ Invoke(eventlistener_.get(),
+ &pp::trackrenderer::TrackRenderer::EventListener::OnEos));
+ ON_CALL(*this, OnDrmInitData(_, _, _, _))
+ .WillByDefault(Invoke(
+ eventlistener_.get(),
+ &pp::trackrenderer::TrackRenderer::EventListener::OnDrmInitData));
+ ON_CALL(*this, OnBufferStatus(_, _))
+ .WillByDefault(Invoke(
+ eventlistener_.get(),
+ &pp::trackrenderer::TrackRenderer::EventListener::OnBufferStatus));
+ ON_CALL(*this, OnSeekData(_, _))
+ .WillByDefault(Invoke(
+ eventlistener_.get(),
+ &pp::trackrenderer::TrackRenderer::EventListener::OnSeekData));
+ }
+
+ private:
+ std::shared_ptr<pp::trackrenderer::TrackRenderer::EventListener>
+ eventlistener_;
+};
+
+class EsPlayerTrackRendererTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ env_ = new Environment();
+ ESPacketDownloader::Init();
+ }
+ static void TearDownTestCase() {
+ if (env_) {
+ delete env_;
+ env_ = nullptr;
+ }
+ }
+
+ void SetUp() override { gst_init_check(nullptr, nullptr, nullptr); }
+ void TearDown() override {}
+
+ public:
+ static Environment *env_;
+};
+
+Environment *EsPlayerTrackRendererTest::env_ = nullptr;
+
+TEST_F(EsPlayerTrackRendererTest, DISABLED_DefaultPlaybackOnPushMode) {
+ class TrackRendererFakeEventListener
+ : public pp::trackrenderer::TrackRenderer::EventListener {};
+ TrackRendererFakeEventListener eventlistener;
+
+ auto v_streamreader = es::StreamReader<pp::trackrenderer::TrackType>::Create(
+ "/welcome_movie/video_00/", pp::trackrenderer::kTrackTypeVideo);
+ auto videoinfo = v_streamreader->GetMediaInfo<
+ es::VideoInfo<pp::trackrenderer::Track, pp::trackrenderer::TrackType>>();
+ auto videoextradata = v_streamreader->GetExtraData();
+ auto videotrack = videoinfo.GetTrack();
+ videotrack.codec_data = videoextradata->data;
+ videotrack.codec_data_len = videoextradata->size;
+
+ auto a_streamreader = es::StreamReader<pp::trackrenderer::TrackType>::Create(
+ "/welcome_movie/audio_01/", pp::trackrenderer::kTrackTypeAudio);
+ auto audioinfo = a_streamreader->GetMediaInfo<
+ es::AudioInfo<pp::trackrenderer::Track, pp::trackrenderer::TrackType>>();
+ auto audiotrack = audioinfo.GetTrack();
+
+ auto trackrenderer = pp::trackrenderer::TrackRenderer::Create();
+ ASSERT_TRUE(
+ trackrenderer->SetDisplay(pp::trackrenderer::DisplayType::kOverlay, env_->Window()));
+ trackrenderer->RegisterListener(&eventlistener);
+
+ auto tracks = {videotrack, audiotrack};
+ ASSERT_TRUE(trackrenderer->SetTrack(tracks));
+
+ auto streaming_task_fn = [&trackrenderer](es::StreamReader<pp::trackrenderer::TrackType>::Ptr &streamreader) {
+ while (true) {
+ auto pkt = streamreader->ReadNextPacket();
+ auto buffer =
+ es::utils::MakeBufferFromPacket(pkt, streamreader->GetTrackType());
+ es::utils::SubmitDecoderInputBuffer(trackrenderer, buffer);
+ if (pkt == nullptr) break;
+ }
+ };
+
+ auto video_task = std::thread(streaming_task_fn, std::ref(v_streamreader));
+ auto audio_task = std::thread(streaming_task_fn, std::ref(a_streamreader));
+
+ ASSERT_TRUE(trackrenderer->Prepare());
+ ASSERT_TRUE(trackrenderer->Start());
+
+ video_task.join();
+ audio_task.join();
+
+ ASSERT_TRUE(trackrenderer->Stop());
+}
+
+TEST_F(EsPlayerTrackRendererTest, DISABLED_Reuse) {
+ class TrackRendererFakeEventListener
+ : public pp::trackrenderer::TrackRenderer::EventListener {};
+ TrackRendererFakeEventListener eventlistener;
+ auto trackrenderer = pp::trackrenderer::TrackRenderer::Create();
+ trackrenderer->RegisterListener(&eventlistener); // only once
+
+ auto streaming_task_fn =
+ [&trackrenderer](
+ es::StreamReader<pp::trackrenderer::TrackType>::Ptr &streamreader) {
+ while (true) {
+ auto pkt = streamreader->ReadNextPacket();
+ auto buffer = es::utils::MakeBufferFromPacket(
+ pkt, streamreader->GetTrackType());
+ es::utils::SubmitDecoderInputBuffer(trackrenderer, buffer);
+ if (pkt == nullptr) break;
+ }
+ };
+
+ // first playback
+ {
+ auto v_streamreader =
+ es::StreamReader<pp::trackrenderer::TrackType>::Create(
+ "/welcome_movie/video_00/", pp::trackrenderer::kTrackTypeVideo);
+ auto videoinfo = v_streamreader->GetMediaInfo<es::VideoInfo<
+ pp::trackrenderer::Track, pp::trackrenderer::TrackType>>();
+ auto videoextradata = v_streamreader->GetExtraData();
+ auto videotrack = videoinfo.GetTrack();
+ videotrack.codec_data = videoextradata->data;
+ videotrack.codec_data_len = videoextradata->size;
+
+ auto a_streamreader =
+ es::StreamReader<pp::trackrenderer::TrackType>::Create(
+ "/welcome_movie/audio_01/", pp::trackrenderer::kTrackTypeAudio);
+ auto audioinfo = a_streamreader->GetMediaInfo<es::AudioInfo<
+ pp::trackrenderer::Track, pp::trackrenderer::TrackType>>();
+ auto audiotrack = audioinfo.GetTrack();
+
+ auto tracks = {videotrack, audiotrack};
+ ASSERT_TRUE(trackrenderer->SetTrack(tracks));
+
+ ASSERT_TRUE(trackrenderer->SetDisplay(
+ pp::trackrenderer::DisplayType::kOverlay, env_->Window()));
+
+ auto video_task = std::thread(streaming_task_fn, std::ref(v_streamreader));
+ auto audio_task = std::thread(streaming_task_fn, std::ref(a_streamreader));
+
+ ASSERT_TRUE(trackrenderer->Prepare());
+ ASSERT_TRUE(trackrenderer->Start());
+
+ ASSERT_TRUE(trackrenderer->SetAudioMute(true));
+ ASSERT_TRUE(
+ trackrenderer->SetDisplayMode(pp::trackrenderer::DisplayMode::kDstRoi));
+ pp::trackrenderer::Geometry roi;
+ roi.x = 100;
+ roi.y = 100;
+ roi.w = 640;
+ roi.h = 480;
+ ASSERT_TRUE(trackrenderer->SetDisplayRoi(roi));
+
+ video_task.join();
+ audio_task.join();
+
+ ASSERT_TRUE(trackrenderer->Stop());
+ }
+
+ // reuse
+ {
+ auto v_streamreader =
+ es::StreamReader<pp::trackrenderer::TrackType>::Create(
+ "/welcome_movie/video_00/", pp::trackrenderer::kTrackTypeVideo);
+ auto videoinfo = v_streamreader->GetMediaInfo<es::VideoInfo<
+ pp::trackrenderer::Track, pp::trackrenderer::TrackType>>();
+ auto videoextradata = v_streamreader->GetExtraData();
+ auto videotrack = videoinfo.GetTrack();
+ videotrack.codec_data = videoextradata->data;
+ videotrack.codec_data_len = videoextradata->size;
+
+ auto a_streamreader =
+ es::StreamReader<pp::trackrenderer::TrackType>::Create(
+ "/welcome_movie/audio_01/", pp::trackrenderer::kTrackTypeAudio);
+ auto audioinfo = a_streamreader->GetMediaInfo<es::AudioInfo<
+ pp::trackrenderer::Track, pp::trackrenderer::TrackType>>();
+ auto audiotrack = audioinfo.GetTrack();
+
+ auto tracks = {videotrack, audiotrack};
+ ASSERT_TRUE(trackrenderer->SetTrack(tracks));
+
+ ASSERT_TRUE(trackrenderer->SetDisplay(
+ pp::trackrenderer::DisplayType::kOverlay, env_->Window()));
+
+ auto video_task = std::thread(streaming_task_fn, std::ref(v_streamreader));
+ auto audio_task = std::thread(streaming_task_fn, std::ref(a_streamreader));
+
+ ASSERT_TRUE(trackrenderer->Prepare());
+ ASSERT_TRUE(trackrenderer->Start());
+
+ video_task.join();
+ audio_task.join();
+
+ ASSERT_TRUE(trackrenderer->Stop());
+ }
+}
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+int main(int argc, char *argv[]) {
+
+ //putenv((char*)"GST_DEBUG=*:2,*appsrc*:9,*omx*:9,*basesink*:9,*videosink*:9");
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::InitGoogleMock(&argc, argv);
+
+ auto ret = -1;
+ ret = RUN_ALL_TESTS();
+
+
+ return ret;
+}
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include <iostream>
+
+#include <boost/any.hpp>
+#include <boost/scope_exit.hpp> // to test "BoostScopeExit"
+
+#include "gtest/gtest.h"
+
+#include "core/utils/scope_exit.h" // to test "ScopeExit"
+#include "trackrenderer/core/pipeline.hpp" // to test "UnlinkElements"
+
+using namespace esplusplayer;
+
+TEST(MiscTest, DISABLED_BoostScopeExit) {
+ std::string input;
+ std::cout << "input string:" << std::endl;
+ std::getline(std::cin , input);
+
+ if(input == "exit_now") {
+ std::cout << "early return" << std::endl;
+ return;
+ }
+
+ BOOST_SCOPE_EXIT(&input) {
+ input = "string updated";
+ std::cout << "Boost Scopt Exit : string(" << input << ")" << std::endl;
+ } BOOST_SCOPE_EXIT_END
+
+ if(input == "1") {
+ std::cout << "input is 1" << std::endl;
+ return;
+ } else {
+ std::cout << "input is not 1" << std::endl;
+ }
+}
+
+enum class TestState {
+ kNone,
+ kGood,
+ kBad
+};
+
+static void ControlDropRate(TestState* state) {
+ std::cout << __FUNCTION__ << std::endl;
+ *state = TestState::kGood;
+ return;
+}
+
+TEST(MiscTest, DISABLED_BoostScopeExit2) {
+ TestState state = TestState::kNone;
+ BOOST_SCOPE_EXIT(&state) {
+ std::cout << static_cast<int>(state) << std::endl;
+ ASSERT_EQ(state , TestState::kGood);
+ } BOOST_SCOPE_EXIT_END
+ return ControlDropRate(&state);
+}
+
+TEST(MiscTest, DISABLED_BoostScopeExitSequence) {
+ std::string input1{"1st scopt exit requested"};
+ BOOST_SCOPE_EXIT(&input1) {
+ std::cout << input1 << std::endl;
+ } BOOST_SCOPE_EXIT_END
+
+ std::string input2{"2nd scopt exit requested"};
+ BOOST_SCOPE_EXIT(&input2) {
+ std::cout << input2 << std::endl;
+ } BOOST_SCOPE_EXIT_END
+
+ std::string input3{"3rd scopt exit requested"};
+ BOOST_SCOPE_EXIT(&input3) {
+ std::cout << input3 << std::endl;
+ } BOOST_SCOPE_EXIT_END
+}
+
+// TODO(js4716.chun) : don't use this yet...use boost_scope_exit
+TEST(MiscTest, DISABLED_ScopeExit) {
+ std::string input;
+ std::cout << "input string:" << std::endl;
+ std::getline(std::cin, input);
+
+ if (input == "exit_now") {
+ std::cout << "early return" << std::endl;
+ return;
+ }
+
+ auto x = utils::ScopeExit([&input]() {
+ std::cout << "lambda in" << std::endl;
+ input = "string updated";
+ std::cout << "Scopt Exit : string(" << input << ")" << std::endl;
+ });
+
+ if (input == "1") {
+ std::cout << "input is 1 (" << input << ")" << std::endl;
+ return;
+ } else {
+ std::cout << "input is not 1 (" << input << ")" << std::endl;
+ }
+}
+
+TEST(MiscTest, DISABLED_BoostAnyCast) {
+ const float test1 = 3.14;
+ std::cout << "test1: [" << test1 << "]" << std::endl;
+
+ //
+ // Failed Cases
+ //
+ boost::any value1 = 3.14;
+ const float* casted_val1 = boost::any_cast<float>(&value1);
+ if(!casted_val1) { // if casting failed
+ std::cout << "casted_val1: casting failed" << std::endl;
+ } else {
+ std::cout << "casted_val1: [" << *casted_val1 << "]" << std::endl;
+ }
+ try {
+ const float casted_val2 =
+ boost::any_cast<float>(value1); // Exception thrown.
+ std::cout << "casted_val2: [" << casted_val2 << "]" << std::endl;
+ } catch(...) {
+ std::cout << "casted_val2: casting failed" << std::endl;
+ }
+
+ //
+ // Success Cases
+ //
+ boost::any value2 = float{3.14};
+ const float casted_val3 = boost::any_cast<float>(value2); // Success
+ std::cout << "casted_val3: [" << casted_val3 << "]" << std::endl;
+ const float* casted_val4 = boost::any_cast<float>(&value2); // Success
+ std::cout << "casted_val4: [" << *casted_val4 << "]" << std::endl;
+}
--- /dev/null
+//
+// @ Copyright [2018] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "ut/include/streamreader.hpp"
+#include "esplusplayer/track.h"
+
+class StreamReaderTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() { ESPacketDownloader::Init(); }
+ static void TearDownTestCase() {}
+
+ void SetUp() override {}
+ void TearDown() override {}
+
+ public:
+ static Environment *env_;
+};
+
+TEST_F(StreamReaderTest, DISABLED_Create) {
+ ASSERT_NO_THROW({
+ es::StreamReader<pp::TrackType>::Create("/welcome_movie/video_00/",
+ pp::kTrackTypeVideo);
+ es::StreamReader<pp::TrackType>::Create("/welcome_movie/audio_00/",
+ pp::kTrackTypeAudio);
+ });
+}
+
+TEST_F(StreamReaderTest, DISABLED_ThrowOnCreate) {
+ ASSERT_ANY_THROW({
+ es::StreamReader<pp::TrackType>::Create("foo/bar/", pp::kTrackTypeVideo);
+ });
+}
+
+TEST_F(StreamReaderTest, DISABLED_GetVideoExtraData) {
+ auto v_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/video_00/", pp::kTrackTypeVideo);
+ ASSERT_NO_THROW({ v_streamreader->GetExtraData(); });
+}
+
+TEST_F(StreamReaderTest, DISABLED_GetAudioExtraData) {
+ auto a_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/audio_00/", pp::kTrackTypeAudio);
+ ASSERT_NO_THROW({ a_streamreader->GetExtraData(); });
+}
+
+TEST_F(StreamReaderTest, DISABLED_GetPtsFromFirstVideoPacket) {
+ auto v_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/video_00/", pp::kTrackTypeVideo);
+ auto firstpkt = v_streamreader->ReadNextPacket();
+ ASSERT_EQ(1, firstpkt->pts);
+}
+
+TEST_F(StreamReaderTest, DISABLED_GetPtsFromTwoVideoPacket) {
+ auto v_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/video_00/", pp::kTrackTypeVideo);
+ auto firstpkt = v_streamreader->ReadNextPacket();
+ ASSERT_EQ(1, firstpkt->pts);
+
+ auto secondpkt = v_streamreader->ReadNextPacket();
+ ASSERT_EQ(16666668, secondpkt->pts);
+}
+
+TEST_F(StreamReaderTest, DISABLED_GetPtsFromFirstAudioPacket) {
+ auto a_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/audio_00/", pp::kTrackTypeAudio);
+ auto firstpkt = a_streamreader->ReadNextPacket();
+ ASSERT_EQ(1, firstpkt->pts);
+}
+
+TEST_F(StreamReaderTest, DISABLED_GetPtsFromTwoAudioPacket) {
+ auto a_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/audio_00/", pp::kTrackTypeAudio);
+ auto firstpkt = a_streamreader->ReadNextPacket();
+ ASSERT_EQ(1, firstpkt->pts);
+
+ auto secondpkt = a_streamreader->ReadNextPacket();
+ ASSERT_EQ(21333334, secondpkt->pts);
+}
+
+TEST_F(StreamReaderTest, DISABLED_CheckVideoEOF) {
+ auto v_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/video_00/", pp::kTrackTypeVideo);
+ es::PacketPtr pkt = nullptr;
+ while (1) {
+ pkt = v_streamreader->ReadNextPacket();
+ if (pkt == nullptr) break;
+ ASSERT_NE(nullptr, pkt);
+ }
+ ASSERT_EQ(nullptr, pkt);
+}
+
+TEST_F(StreamReaderTest, DISABLED_CheckAudioEOF) {
+ auto a_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/audio_00/", pp::kTrackTypeAudio);
+ es::PacketPtr pkt = nullptr;
+ while (1) {
+ pkt = a_streamreader->ReadNextPacket();
+ if (pkt == nullptr) break;
+ ASSERT_NE(nullptr, pkt);
+ }
+ ASSERT_EQ(nullptr, pkt);
+}
+
+TEST_F(StreamReaderTest, DISABLED_CheckVideoInfo) {
+ auto v_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/video_00/", pp::kTrackTypeVideo);
+ auto videoinfo =
+ v_streamreader->GetMediaInfo<es::VideoInfo<pp::Track, pp::TrackType>>();
+ ASSERT_EQ("video/x-h265", videoinfo.mimetype);
+ ASSERT_EQ(3840, videoinfo.width);
+ ASSERT_EQ(2160, videoinfo.height);
+ ASSERT_EQ(3840, videoinfo.maxwidth);
+ ASSERT_EQ(2160, videoinfo.maxheight);
+ ASSERT_EQ(60, videoinfo.framerate_num);
+ ASSERT_EQ(1, videoinfo.framerate_den);
+}
+
+TEST_F(StreamReaderTest, DISABLED_CheckAudioInfo) {
+ auto a_streamreader = es::StreamReader<pp::TrackType>::Create(
+ "/welcome_movie/audio_00/", pp::kTrackTypeAudio);
+ auto audioinfo = a_streamreader->GetMediaInfo<
+ es::AudioInfo<pp::Track, pp::TrackType>>();
+ ASSERT_EQ("audio/mpeg", audioinfo.mimetype);
+ ASSERT_EQ(48000, audioinfo.samplerate);
+ ASSERT_EQ(2, audioinfo.channels);
+}
--- /dev/null
+//
+// @ Copyright [2017] <S/W Platform, Visual Display, Samsung Electronics>
+//
+
+#include "gst/gst.h"
+#include "gtest/gtest.h"
+
+#include "Ecore.h"
+#include "Elementary.h"
+#include "glib-object.h"
+
+#include "core/track_util.h"
+#include "core/trackrendereradapter.h"
+#include "core/trackrendereradapter_utils.h"
+#include "esplusplayer/track.h"
+#include "trackrenderer/core/decoderinputbuffer.h"
+#include "trackrenderer/core/track_util.h"
+#include "trackrenderer/trackrenderer_capi_utils.h"
+#include "trackrenderer_capi/iniproperty.h"
+#include "trackrenderer_capi/track.h"
+#include "trackrenderer_capi/trackrenderer_capi.h"
+#include "ut/include/appwindow.h"
+
+using namespace esplusplayer;
+
+class TrackRendererAdapterTest : public ::testing::Test {
+ public:
+ TrackRendererAdapterTest() {}
+ void SetUp() override {
+ // basic , clean stream
+ std::string url = "http://10.88.105.104/WebAPITest/HLS/Clean/normal.m3u8";
+ trackrenderer_adapter_ = TrackRendererAdapter::Create();
+ ASSERT_TRUE(trackrenderer_adapter_.get());
+ }
+ std::shared_ptr<TrackRendererAdapter> GetAdapter() {
+ return trackrenderer_adapter_;
+ }
+ std::shared_ptr<esplusplayer_ut::AppWindow> GetAppWindow() {
+ return appwindow_;
+ }
+
+ private:
+ std::shared_ptr<TrackRendererAdapter> trackrenderer_adapter_;
+ std::shared_ptr<esplusplayer_ut::AppWindow> appwindow_;
+};
+constexpr int kTrackRendererMaxStreamNumber = 3;
+
+Track SetVideoInfo_(std::string mimetype, TrackType type,
+ const char* codec_data, int w, int h, int framerate_num,
+ int framerate_den, bool activate) {
+ Track trackinfo;
+ trackinfo.mimetype = mimetype;
+ trackinfo.type = type;
+ trackinfo.codec_data = std::make_shared<char>(*codec_data);
+ trackinfo.width = w;
+ trackinfo.height = h;
+ trackinfo.framerate_num = framerate_num;
+ trackinfo.framerate_den = framerate_den;
+ trackinfo.active = activate;
+ 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(TrackRendererAdapterTest, DISABLED_Start) {
+ // auto adapter = GetAdapter();
+ // ASSERT_TRUE(adapter->Start());
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_Stop) {
+ // auto adapter = GetAdapter();
+ // ASSERT_TRUE(adapter->Stop());
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_Prepare) {
+ // auto adapter = GetAdapter();
+ // ASSERT_TRUE(adapter->Prepare());
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_Pause) {
+ // auto adapter = GetAdapter();
+ // ASSERT_TRUE(adapter->Pause());
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_Resume) {
+ // auto adapter = GetAdapter();
+ // ASSERT_TRUE(adapter->Resume());
+}
+
+std::vector<Track> MakeTrackInfo() {
+ std::vector<Track> trackvector;
+ const char* ffmpeg = "ffmpeg";
+ Track trackvideoinfo =
+ SetVideoInfo_("video/x-h264", TrackType::kTrackTypeVideo, ffmpeg, 640,
+ 352, 30, 1, true);
+ Track trackaudioinfo = SetAudioInfo_("audio/mpeg", 44100, 0, 2, 4);
+ trackvector.push_back(trackvideoinfo);
+ trackvector.push_back(trackaudioinfo);
+ return trackvector;
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetTrack) {
+ auto adapter = GetAdapter();
+ std::vector<Track> input_vector = MakeTrackInfo();
+ int size = input_vector.size();
+ if (size <= 0 || size > kTrackRendererMaxStreamNumber) return;
+
+ TrackRendererTrack trackrenderer_tracks[size];
+ int index = 0;
+ for (const auto& track : input_vector) {
+ adapter_utils::MakeTrackRendererTrack(&trackrenderer_tracks[index], track);
+ index++;
+ }
+ // ASSERT
+ //
+
+ auto output_vector =
+ trackrenderer::capi_utils::MakeTrack(trackrenderer_tracks, size);
+
+ esplusplayer::track_util::ShowTrackInfo(input_vector);
+ esplusplayer::trackrenderer::track_util::ShowTrackInfo(output_vector);
+
+ index = 0;
+ for (const auto& input : input_vector) {
+ auto output = output_vector.at(index);
+ ASSERT_EQ(input.index, output.index);
+ ASSERT_EQ(input.id, output.id);
+ ASSERT_EQ(input.mimetype, output.mimetype);
+ ASSERT_EQ(input.streamtype, output.streamtype);
+ ASSERT_EQ(input.type, output.type);
+ ASSERT_EQ(input.codec_data_len, output.codec_data_len);
+ ASSERT_EQ(0, memcmp(input.codec_data.get(), output.codec_data.get(),
+ output.codec_data_len));
+ ASSERT_EQ(input.width, output.width);
+ ASSERT_EQ(input.height, output.height);
+ ASSERT_EQ(input.maxwidth, output.maxwidth);
+ ASSERT_EQ(input.maxheight, output.maxheight);
+ ASSERT_EQ(input.framerate_num, output.framerate_num);
+ ASSERT_EQ(input.framerate_den, output.framerate_den);
+ ASSERT_EQ(input.sample_rate, output.sample_rate);
+ ASSERT_EQ(input.sample_format, output.sample_format);
+ ASSERT_EQ(input.channels, output.channels);
+ ASSERT_EQ(input.version, output.version);
+ ASSERT_EQ(input.layer, output.layer);
+ ASSERT_EQ(input.bits_per_sample, output.bits_per_sample);
+ ASSERT_EQ(input.block_align, output.block_align);
+ ASSERT_EQ(input.bitrate, output.bitrate);
+ ASSERT_EQ(input.endianness, output.endianness);
+ ASSERT_EQ(input.is_signed, output.is_signed);
+ ASSERT_EQ(input.active, output.active);
+ ASSERT_EQ(input.use_swdecoder, output.use_swdecoder);
+ ASSERT_EQ(input.language_code, output.language_code);
+ ASSERT_EQ(input.subtitle_format, output.subtitle_format);
+ index++;
+ }
+ ASSERT_TRUE(adapter->SetTrack(input_vector));
+}
+
+std::map<std::string, bool> MakeIniProperties() {
+ std::map<std::string, bool> properties;
+ std::string key = "use_new_hls_mpegts_demuxer";
+ properties[key] = true;
+ key = "use_new_dash_tiny_demuxer";
+ properties[key] = true;
+ key = "use_new_http_demuxer";
+ properties[key] = true;
+ key = "generate_dot";
+ properties[key] = true;
+ return properties;
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetIniProperty) {
+ std::map<std::string, bool> input = MakeIniProperties();
+ const int size = input.size();
+ if (size <= 0) return;
+
+ TrackRendererIniProperty trackrenderer_iniproperty[size];
+ int index = 0;
+ for (const auto& pair : input) {
+ trackrenderer_iniproperty[index].key = pair.first.c_str();
+ trackrenderer_iniproperty[index].value = pair.second;
+ index++;
+ }
+
+ std::map<std::string, bool> output =
+ trackrenderer::capi_utils::MakeIniProperty(trackrenderer_iniproperty, size);
+ ASSERT_EQ(input, output);
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_Seek) {
+ // auto adapter = GetAdapter();
+ // uint64_t time_millisecond = 30;
+ // double playback_rate = 2.5;
+ // ASSERT_TRUE(adapter->Seek(time_millisecond, playback_rate));
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_GetPlayingTime) {
+ // auto adapter = GetAdapter();
+ // uint64_t time_millisecond = 0;
+ // ASSERT_TRUE(adapter->GetPlayingTime(&time_millisecond));
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_Deactivate) {
+ // auto adapter = GetAdapter();
+ // TrackType input = TrackType::kTrackTypeVideo;
+ // TrackRendererTrackType type =
+ // adapter_utils::ConvertToTrackRendererTrackType(input); TrackType output =
+ // esplusplayer::trackrenderer::capi_utils::ConvertToTrackType(type); ASSERT_EQ(input,
+ // output); ASSERT_TRUE(adapter->Deactivate(input));
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_Activate) {
+ const char* ffmpeg = "ffmpeg";
+ Track input = SetVideoInfo_("video/x-h264", TrackType::kTrackTypeVideo,
+ ffmpeg, 640, 352, 30, 1, true);
+ TrackRendererTrack trackrenderer_track;
+
+ adapter_utils::MakeTrackRendererTrack(&trackrenderer_track, input);
+ auto output_vector = trackrenderer::capi_utils::MakeTrack(&trackrenderer_track, 1);
+
+ auto output = output_vector.front();
+
+ ASSERT_EQ(input.index, output.index);
+ ASSERT_EQ(input.id, output.id);
+ ASSERT_EQ(input.mimetype, output.mimetype);
+ ASSERT_EQ(input.streamtype, output.streamtype);
+ ASSERT_EQ(input.type, output.type);
+ ASSERT_EQ(input.codec_data_len, output.codec_data_len);
+ ASSERT_EQ(0, memcmp(input.codec_data.get(), output.codec_data.get(),
+ output.codec_data_len));
+ ASSERT_EQ(input.width, output.width);
+ ASSERT_EQ(input.height, output.height);
+ ASSERT_EQ(input.maxwidth, output.maxwidth);
+ ASSERT_EQ(input.maxheight, output.maxheight);
+ ASSERT_EQ(input.framerate_num, output.framerate_num);
+ ASSERT_EQ(input.framerate_den, output.framerate_den);
+ ASSERT_EQ(input.sample_rate, output.sample_rate);
+ ASSERT_EQ(input.sample_format, output.sample_format);
+ ASSERT_EQ(input.channels, output.channels);
+ ASSERT_EQ(input.version, output.version);
+ ASSERT_EQ(input.layer, output.layer);
+ ASSERT_EQ(input.bits_per_sample, output.bits_per_sample);
+ ASSERT_EQ(input.block_align, output.block_align);
+ ASSERT_EQ(input.bitrate, output.bitrate);
+ ASSERT_EQ(input.endianness, output.endianness);
+ ASSERT_EQ(input.is_signed, output.is_signed);
+ ASSERT_EQ(input.active, output.active);
+ ASSERT_EQ(input.use_swdecoder, output.use_swdecoder);
+ ASSERT_EQ(input.language_code, output.language_code);
+ ASSERT_EQ(input.subtitle_format, output.subtitle_format);
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SubmitPacket) {
+ auto input = DecoderInputBuffer::Create(
+ static_cast<TrackType>(kTrackTypeVideo), kInvalidTrackIndex, nullptr);
+
+ TrackRendererDecoderInputBuffer trackrenderer_decoderinputbuffer{
+ adapter_utils::ConvertToTrackRendererTrackType(input->GetType()),
+ input->GetIndex(), const_cast<GstBuffer*>(input->Get())};
+
+ auto output = trackrenderer::DecoderInputBuffer::Create(
+ trackrenderer::capi_utils::ConvertToTrackType(
+ trackrenderer_decoderinputbuffer.type),
+ trackrenderer_decoderinputbuffer.index,
+ trackrenderer_decoderinputbuffer.buffer);
+
+ ASSERT_EQ((*input).GetType(), (*output).GetType());
+ ASSERT_EQ((*input).GetIndex(), (*output).GetIndex());
+ ASSERT_EQ((*input).Get(), (*output).Get());
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetDrm) {
+ const drm::Property input;
+ TrackRendererDrmProperty trackrenderer_drm_property;
+
+ adapter_utils::MakeTrackRendererDrmProperty(&trackrenderer_drm_property,
+ input);
+
+ trackrenderer::drm::Property output;
+ trackrenderer::capi_utils::MakeDrmProperty(&output, trackrenderer_drm_property);
+ ASSERT_EQ(input.type, output.type);
+ ASSERT_EQ(input.handle, output.handle);
+ ASSERT_EQ(input.external_decryption, output.external_decryption);
+ ASSERT_EQ(input.license_acquired_cb, output.license_acquired_cb);
+ ASSERT_EQ(input.license_acquired_userdata, output.license_acquired_userdata);
+}
+TEST_F(TrackRendererAdapterTest, DISABLED_SetMatroskaColorInfo) {}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_DrmLicenseAcquiredDone) {
+ TrackType input = TrackType::kTrackTypeAudio;
+ auto type =
+ adapter_utils::ConvertToTrackRendererTrackType(
+ input);
+
+ trackrenderer::TrackType output = trackrenderer::capi_utils::ConvertToTrackType(type);
+ ASSERT_EQ(input, output);
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetDisplayMode) {
+ auto adapter = GetAdapter();
+ const DisplayMode input = DisplayMode::kOriginSize;
+ TrackRendererDisplayMode mode =
+ adapter_utils::ConvertToTrackRendererDisplayMode(input);
+ trackrenderer::DisplayMode output = trackrenderer::capi_utils::ConvertToDisplayMode(mode);
+
+ ASSERT_EQ(input, output);
+ ASSERT_TRUE(adapter->SetDisplayMode(input));
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetDisplay2) {
+ auto adapter = GetAdapter();
+ auto appwindow = GetAppWindow();
+ appwindow.reset(new esplusplayer_ut::AppWindow(0, 0, 1920, 1080));
+
+ const DisplayType input = DisplayType::kOverlay;
+
+ TrackRendererDisplayType type =
+ adapter_utils::ConvertToTrackRendererDisplayType(input);
+ trackrenderer::DisplayType output = trackrenderer::capi_utils::ConvertToDisplayType(type);
+
+ ASSERT_EQ(input, output);
+ ASSERT_TRUE(adapter->SetDisplay(input, appwindow->GetWindow().obj));
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetDisplay3) {}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetDisplayRoi) {
+ auto adapter = GetAdapter();
+ Geometry input;
+ input.x = 1;
+ input.y = 1;
+ input.w = 1;
+ input.h = 1;
+ TrackRendererGeometry geometry = {0, 0, 1920, 1080};
+ adapter_utils::MakeTrackRendererGeometry(&geometry, input);
+
+ trackrenderer::Geometry output;
+ trackrenderer::capi_utils::MakeGeometry(&output, geometry);
+ ASSERT_EQ(input.x, output.x);
+ ASSERT_EQ(input.y, output.y);
+ ASSERT_EQ(input.w, output.w);
+ ASSERT_EQ(input.h, output.h);
+ ASSERT_TRUE(adapter->SetDisplayRoi(input));
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetDisplayVisible) {
+ auto adapter = GetAdapter();
+ bool is_visible = false;
+ ASSERT_TRUE(adapter->SetDisplayVisible(is_visible));
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetAudioMute) {
+ // auto adapter = GetAdapter();
+ // bool is_mute = false;
+ // ASSERT_TRUE(adapter->SetAudioMute(is_mute));
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SetVideoStillMode) {
+ StillMode input = StillMode::kOn;
+ TrackRendererStillMode still_mode =
+ adapter_utils::ConvertToTrackRendererStillMode(input);
+
+ trackrenderer::StillMode output = trackrenderer::capi_utils::ConvertToStillMode(still_mode);
+ ASSERT_EQ(input, output); // TODO(js4716.chun) : wrong condition. fix it
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_ErrorCb) {
+ trackrenderer::ErrorType input = ErrorType::kInvalidOperation;
+ TrackRendererErrorType error_type =
+ trackrenderer::capi_utils::ConvertToTrackRendererErrorType(input);
+ ErrorType output = adapter_utils::ConvertToErrorType(error_type);
+ ASSERT_EQ(input, output); // TODO(js4716.chun) : wrong condition. fix it
+}
+
+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));
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_SubtitleDataCb) {
+ SubtitleType input_type = SubtitleType::kPicture;
+ TrackRendererSubtitleType type =
+ trackrenderer::capi_utils::ConvertToTrackRendererSubtitleType(input_type);
+ SubtitleType output_type = adapter_utils::ConvertToSubtitleType(type);
+ ASSERT_EQ(input_type, output_type);
+}
+
+TEST_F(TrackRendererAdapterTest, DISABLED_DrmInitDataCb) {
+ TrackRendererTrackType input = kTrackRendererTrackTypeSubtitle;
+ auto type = trackrenderer::capi_utils::ConvertToTrackType(input);
+ TrackRendererTrackType output =
+ trackrenderer::capi_utils::ConvertToTrackRendererTrackType(type);
+ ASSERT_EQ(input, output);
+}
+/*
+TEST_F(TrackRendererAdapterTest, DISABLED_GetDisplay){}
+TEST_F(TrackRendererAdapterTest, DISABLED_RegisterListener){}
+TEST_F(TrackRendererAdapterTest, DISABLED_ClosedCaptionDataCb_){}
+*/
--- /dev/null
+#include <Ecore.h>
+#include <Elementary.h>
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <memory>
+
+#include <unistd.h>
+#include <poll.h>
+
+#include "ut/include/utils/utility.h"
+#include "ut/include/esplusplayer/eseventlistener.hpp"
+#include "ut/include/esplusplayer/esreader.hpp"
+
+#include <math.h>
+#include <fstream>
+#include <jconfig.h>
+#include <jpeglib.h>
+#include <capi-system-info/system_info.h>
+#include <capi-system-info/system_info_key.h>
+#include <system-type/system_type_enum.h>
+#include "ivideocapture.hpp"
+#include "capi-video-capture.h"
+#include "iaudio-control.hpp"
+#include "diagnosis-audio-control.hpp"
+
+using namespace std;
+using namespace esplusplayer;
+//using namespace tc;
+using UserData = void*;
+
+namespace utils {
+using util_ptr = std::unique_ptr<Utility>;
+static util_ptr ptr = nullptr;
+
+Utility& Utility::Instance() {
+ if (ptr.get() == nullptr) ptr.reset(new Utility());
+ return *(ptr.get());
+}
+
+void Utility::Kill() { ptr.reset(); }
+
+void Utility::ThreadSleep(long ms) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+}
+
+Utility::Utility() {
+ appwindow_.reset(new esplusplayer_ut::AppWindow(0, 0, 1920, 1080));
+ audioControl = IAudioControl::getInstance();
+ audioDiagnoser = DiagnosisAudioControl::getInstance();
+}
+
+Utility::~Utility() {}
+
+const char* Utility::GetCurrentTestName(void) {
+ return ::testing::UnitTest::GetInstance()->current_test_info()->name();
+}
+
+#ifndef IS_AUDIO_PRODUCT
+
+#if 0
+esplusplayer::PlusPlayer::Ptr Utility::GetOpenedMixPlusPlayer(std::string& uri,
+ Mixer* mixer,
+ Geometry& roi) {
+ auto player = esplusplayer::PlusPlayer::Create();
+ EXPECT_TRUE(player->Open(uri.c_str()));
+ EXPECT_TRUE(player->SetDisplay(DisplayType::kMixer, mixer));
+ EXPECT_TRUE(player->SetDisplayRoi(roi));
+ return player;
+}
+
+esplusplayer::PlusPlayer::Ptr Utility::GetPreparedMixPlusPlayer(std::string& uri,
+ Mixer* mixer,
+ Geometry& roi) {
+ auto player = this->GetOpenedMixPlusPlayer(uri, mixer, roi);
+ EXPECT_TRUE(player->Prepare());
+ return player;
+}
+
+esplusplayer::PlusPlayer::Ptr Utility::GetStartedMixPlusPlayer(std::string& uri,
+ Mixer* mixer,
+ Geometry& roi) {
+ auto player = this->GetPreparedMixPlusPlayer(uri, mixer, roi);
+ EXPECT_TRUE(player->Start());
+ return player;
+}
+#endif
+
+esplusplayer_handle Utility::GetOpenedMixESPP(mixer_handle mixer,
+ Geometry& roi) {
+ esplusplayer_handle player = esplusplayer_create();
+ EXPECT_EQ(esplusplayer_open(player), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(
+ esplusplayer_set_display(player, ESPLUSPLAYER_DISPLAY_TYPE_MIXER, mixer),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_display_roi(player, roi.x, roi.y, roi.w, roi.h),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ return player;
+}
+
+esplusplayer_handle Utility::GetPreparedMixESPP(std::string& uri,
+ mixer_handle mixer,
+ Geometry& roi) {
+ esplusplayer_handle player = this->GetOpenedMixESPP(mixer, roi);
+
+ if (!PrepareESPP(player, uri)) return nullptr;
+ return player;
+}
+
+esplusplayer_handle Utility::GetStartedMixESPP(std::string& uri,
+ mixer_handle mixer,
+ Geometry& roi) {
+ esplusplayer_handle player = this->GetPreparedMixESPP(uri, mixer, roi);
+
+ if (esplusplayer_start(player) == ESPLUSPLAYER_ERROR_TYPE_NONE)
+ return player;
+ else
+ printf("esplusplayer_start failed\n");
+ return nullptr;
+}
+#endif
+
+esplusplayer_handle Utility::GetOpenedESPP(Geometry& roi) {
+ esplusplayer_handle player = esplusplayer_create();
+ EXPECT_EQ(esplusplayer_open(player), ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_display(player, ESPLUSPLAYER_DISPLAY_TYPE_OVERLAY,
+ this->GetWindow()),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(
+ esplusplayer_set_display_mode(player, ESPLUSPLAYER_DISPLAY_MODE_DST_ROI),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ EXPECT_EQ(esplusplayer_set_display_roi(player, roi.x, roi.y, roi.w, roi.h),
+ ESPLUSPLAYER_ERROR_TYPE_NONE);
+ return player;
+}
+
+esplusplayer_handle Utility::GetPreparedESPP(std::string& uri, Geometry& roi) {
+ esplusplayer_handle player = this->GetOpenedESPP(roi);
+
+ if (!PrepareESPP(player, uri)) return nullptr;
+ return player;
+}
+
+esplusplayer_handle Utility::GetStartedESPP(std::string& uri, Geometry& roi) {
+ esplusplayer_handle player = this->GetPreparedESPP(uri, roi);
+
+ if (esplusplayer_start(player) == ESPLUSPLAYER_ERROR_TYPE_NONE)
+ return player;
+ else
+ printf("esplusplayer_start failed\n");
+ return nullptr;
+}
+
+bool Utility::PrepareESPP(esplusplayer_handle player, std::string& uri,
+ EsType type) {
+ if (!player) return false;
+
+ EsStreamReader* video_reader = nullptr;
+ EsStreamReader* audio_reader = nullptr;
+
+ if (static_cast<int>(type) & EsType::kVideo) {
+ video_reader =
+ new EsStreamReader(uri + "video_00/", ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ EXPECT_TRUE(video_reader->SetStreamInfo(player));
+ }
+ if (static_cast<int>(type) & EsType::kAudio) {
+ audio_reader =
+ new EsStreamReader(uri + "audio_00/", ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+ EXPECT_TRUE(audio_reader->SetStreamInfo(player));
+ }
+ EsPlayerEventCallback* callback =
+ new EsPlayerEventCallback(player, video_reader, audio_reader);
+ callback->SetCallback();
+
+ bool ret = false;
+ if (esplusplayer_prepare_async(player) != ESPLUSPLAYER_ERROR_TYPE_NONE)
+ printf("esplusplayer_prepare_async failed\n");
+ else
+ ret = callback->WaitForPrepareDone();
+
+ delete callback;
+ if (video_reader != nullptr) delete video_reader;
+ if (audio_reader != nullptr) delete audio_reader;
+ printf("PrepareESPP done\n");
+ return ret;
+}
+
+void Utility::FeedingEsPacket(esplusplayer_handle player,
+ const esplusplayer_stream_type type,
+ const std::string& uri) {
+ EsStreamReader* reader;
+ auto feeding_task_fn = [this, &player, &reader]() {
+ esplusplayer_es_packet pkt;
+ while (true) {
+ memset(&pkt, 0, sizeof(esplusplayer_es_packet));
+ if (!reader->ReadNextPacket(pkt)) break;
+ esplusplayer_submit_packet(player, &pkt);
+ delete []pkt.buffer;
+ }
+ };
+ if (type == ESPLUSPLAYER_STREAM_TYPE_VIDEO) {
+ reader =
+ new EsStreamReader(uri + "video_00/", ESPLUSPLAYER_STREAM_TYPE_VIDEO);
+ } else {
+ reader =
+ new EsStreamReader(uri + "audio_00/", ESPLUSPLAYER_STREAM_TYPE_AUDIO);
+ }
+ auto feeding_task = std::thread(feeding_task_fn);
+ if (feeding_task.joinable()) feeding_task.join();
+}
+
+void Utility::DestroyESPP(esplusplayer_handle player) {
+ if (!player) return;
+ int ret = esplusplayer_destroy(player);
+ assert(ret == ESPLUSPLAYER_ERROR_TYPE_NONE);
+ player = nullptr;
+}
+
+evas_h Utility::GetWindow() const { return appwindow_->GetWindow().obj; }
+
+unsigned int Gcd(unsigned int u, unsigned int v) {
+ int shift = 0;
+
+ /* GCD(0,v) == v; GCD(u,0) == u, GCD(0,0) == 0 */
+
+ if (u == 0) {
+ return v;
+ }
+ if (v == 0) {
+ return u;
+ }
+
+ /* Let shift := lg K, where K is the greatest power of 2
+ dividing both u and v. */
+ for (shift = 0; ((u | v) & 1) == 0; ++shift) {
+ u >>= 1;
+ v >>= 1;
+ }
+
+ while ((u & 1) == 0) {
+ u >>= 1;
+ }
+
+ /* From here on, u is always odd. */
+ do {
+ /* remove all factors of 2 in v -- they are not common */
+ /* note: v is not zero, so while will terminate */
+ while ((v & 1) == 0) {
+ v >>= 1;
+ /* Loop X */
+ }
+ /* Now u and v are both odd. Swap if necessary so u <= v,
+ then set v = v - u (which is even). For bignums, the
+ swapping is just pointer movement, and the subtraction
+ can be done in-place. */
+ if (u > v) {
+ unsigned int t = v;
+ v = u;
+ u = t;
+ } // Swap u and v.
+ v = v - u; // Here v >= u.
+ } while (v != 0);
+
+ /* restore common factors of 2 */
+ return u << shift;
+}
+
+void ResizeCopy(unsigned int m_width, unsigned int m_height,
+ unsigned int m_rwidth, unsigned int m_rheight,
+ char* c_ptr, char* y_ptr, unsigned char* dest,
+ unsigned int color_format) {
+ unsigned int omit = 1;
+ unsigned int x = 0;
+ unsigned int y = 0;
+ unsigned char resize = 1;
+
+ if (m_width == m_rwidth) {
+ resize = 0;
+ } else {
+ omit = Gcd(m_width, m_rwidth);
+ omit = m_width / omit;
+ }
+ printf("m_width %u, m_rwidth %u, omit %u\n", m_width, m_rwidth, omit);
+
+ // long int inc = 3 * m_rwidth * m_rheight;
+ long int inc = 0;
+
+ for (y = 0; y < m_height; y++) {
+ if ((y + 1) % omit || !resize) {
+ for (x = 0; x < m_width; x += sizeof(unsigned int)) {
+
+ unsigned int Yplain =
+ *((unsigned int*)((void*)(y_ptr + y * m_width + x)));
+ unsigned int Cplain =
+ *((unsigned int*)((void*)(c_ptr + y * m_width + x)));
+
+ switch (color_format) {
+ case SECVIDEO_CAPTURE_COLOR_YUV444: // 444
+ break;
+ case SECVIDEO_CAPTURE_COLOR_YUV422: // 422
+ Cplain = *((unsigned int*)((void*)(c_ptr + y * m_width + x)));
+ break;
+ case SECVIDEO_CAPTURE_COLOR_YUV420: // 420
+ Cplain = *((unsigned int*)((void*)(c_ptr + (y / 2) * m_width + x)));
+ break;
+ default:
+ break;
+ }
+ if ((x + 4) % omit || !resize) {
+ dest[inc++] = (Yplain) & 0xFF;
+ dest[inc++] = Cplain & 0xFF;
+ dest[inc++] = (Cplain >> 8) & 0xFF;
+ }
+ if ((x + 3) % omit || !resize) {
+ dest[inc++] = (Yplain >> 8) & 0xFF;
+ dest[inc++] = (Cplain) & 0xFF;
+ dest[inc++] = (Cplain >> 8) & 0xFF;
+ }
+
+ if ((x + 2) % omit || !resize) {
+ dest[inc++] = (Yplain >> 16) & 0xFF;
+ dest[inc++] = (Cplain >> 16) & 0xFF;
+ dest[inc++] = (Cplain >> 24) & 0xFF;
+ }
+
+ if ((x + 1) % omit || !resize) {
+ dest[inc++] = (Yplain >> 24) & 0xFF;
+ dest[inc++] = (Cplain >> 16) & 0xFF;
+ dest[inc++] = (Cplain >> 24) & 0xFF;
+ }
+ }
+ }
+ }
+}
+
+int Utility::CaptureYUV(int capture_width, int capture_height,
+ char* ybuff, char* cbuff, int ysize,
+ int csize, std::string result_file_path) {
+ // screen capture
+ IVideoCapture* VCObjptr = IVideoCapture::getInstance();
+
+ IVideoCapture::InputParams input_params;
+
+ input_params.capture_w = capture_width;
+ input_params.capture_h = capture_height;
+
+ IVideoCapture::OutputParams output_params;
+ output_params.p_y_addr = ybuff;
+ output_params.p_c_addr = cbuff;
+ output_params.size_y = ysize;
+ output_params.size_c = csize;
+
+ sleep(1);
+
+ VCObjptr->getVideoPostYUV(input_params, output_params);
+
+ // write
+ FILE* fexpect = NULL;
+
+ while (output_params.p_y_addr) {
+ fexpect = fopen(result_file_path.c_str(), "wb");
+ if (fexpect == NULL) {
+ LOGE("can't open the file");
+ break;
+ }
+ fwrite(output_params.p_y_addr, 1, ysize, fexpect);
+ fclose(fexpect);
+ fexpect = NULL;
+ output_params.p_y_addr = NULL;
+ }
+
+ return 0;
+}
+///TODO:: Modify the API to return the pointer instead of writing it as an image file.
+int Utility::CaptureJPG(const int capture_width, const int capture_height,
+ char* ybuff, char* cbuff, const int ysize,
+ const int csize, std::string img_file_path) {
+ IVideoCapture* VCObjptr = IVideoCapture::getInstance();
+ int output_color_format = 0;
+
+ IVideoCapture::InputParams input_params;
+ input_params.capture_w = capture_width;
+ input_params.capture_h = capture_height;
+
+ IVideoCapture::OutputParams output_params;
+ output_params.p_y_addr = ybuff;
+ output_params.p_c_addr = cbuff;
+ output_params.size_y = ysize;
+ output_params.size_c = csize;
+
+ sleep(1);
+
+ VCObjptr->getVideoPostYUV(input_params, output_params);
+ output_color_format = static_cast<int>(output_params.color_format);
+
+ sleep(1);
+
+ unsigned char* rawImage;
+ rawImage = (unsigned char*)malloc(sizeof(unsigned char) * 3 * capture_width *
+ capture_height);
+
+ ResizeCopy(capture_width, capture_height, capture_width, capture_height,
+ cbuff, ybuff, rawImage, output_color_format);
+
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ FILE* outfile = NULL; /* target file */
+
+ JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
+ int row_stride; /* physical row width in image buffer */
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ outfile = fopen(img_file_path.c_str(), "wb");
+ if (outfile == NULL) {
+ LOGE("can't open the file");
+ if (rawImage != NULL) free(rawImage);
+ return -1;
+ }
+ jpeg_stdio_dest(&cinfo, outfile);
+
+ cinfo.image_width = capture_width;
+ cinfo.image_height = capture_height;
+ cinfo.input_components = 3; /* # of color components per pixel */
+ cinfo.in_color_space = JCS_YCbCr;
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, 70, TRUE);
+ jpeg_start_compress(&cinfo, TRUE);
+
+ row_stride = capture_width * 3; /* JSAMPLEs per row in image_buffer */
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ row_pointer[0] = &((rawImage)[cinfo.next_scanline * row_stride]);
+ (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+ if (rawImage != NULL) free(rawImage);
+
+ if (outfile == NULL) {
+ LOGE("Fail to open file");
+ return -1;
+ } else {
+ fclose(outfile);
+ }
+ return 0;
+}
+
+int Utility::CheckYUV(int x, int y) {
+
+ /* screen capture */
+ IVideoCapture* VCObjptr = IVideoCapture::getInstance();
+
+ int CAPTURE_WIDTH = 640;
+ int CAPTURE_HEIGHT = 360;
+ int CAPTURE_BUF_SIZE = CAPTURE_WIDTH * CAPTURE_HEIGHT;
+ char ybuff[CAPTURE_BUF_SIZE] = {0};
+ char cbuff[CAPTURE_BUF_SIZE] = {0};
+
+ IVideoCapture::InputParams input_params;
+ input_params.capture_w = 640;
+ input_params.capture_h = 360;
+
+ IVideoCapture::OutputParams output_params;
+ output_params.p_y_addr = ybuff;
+ output_params.p_c_addr = cbuff;
+ output_params.size_y = CAPTURE_BUF_SIZE;
+ output_params.size_c = CAPTURE_BUF_SIZE;
+
+ sleep(1);
+
+ VCObjptr->getVideoPostYUV(input_params, output_params);
+
+ /* check YUV value. As the capture size (640 x 360) is mapped with the full screen (1920 x 1080), the position should be resized. */
+ int new_X = x / 3;
+ int new_Y = y / 3;
+ int position = CAPTURE_WIDTH * (new_Y - 1) + new_X;
+
+ LOGE("Y value : %d", (int)ybuff[position]);
+ return (int)ybuff[position];
+}
+
+bool Utility::IsAudioDisconnected() {
+ TZTVAudioSource src = AUDIO_SOURCE_MAX;
+ EXPECT_EQ(audioControl->getMainOutSourceSelect(&src), 0);
+ return (src != AUDIO_MULTIMEDIA_DEC0);
+}
+
+bool Utility::IsAudioMute() {
+ long audioMute = 0;
+ EXPECT_EQ(audioDiagnoser->Diagnosis_GetBoolean(0, "main out mute", &audioMute), 0);
+ return (audioMute != 0);
+}
+
+int Utility::GetPlayingTimeForManualTestInMsec() {
+ // If you want to adjust the playingTime for the TC which use this method,
+ // sh-3.2# touch /tmp/espp_playing_time
+ // sh-3.2# echo 2000 > /tmp/espp_playing_time
+ static int playingTime = -1;
+ std::fstream myfile("/tmp/espp_playing_time", std::ios_base::in);
+ if (myfile) {
+ myfile >> playingTime;
+ return playingTime;
+ }
+ return 100; // default 100 msec
+}
+
+#define FMS_KEY_CHIPNAME "com.samsung/featureconf/product.chipset"
+bool Utility::IsChipset(const char* name) {
+ char* chipset = NULL;
+
+ system_info_get_custom_string(FMS_KEY_CHIPNAME, &chipset);
+ if (!chipset) return false;
+
+ char* substr = strstr(chipset, name);
+ bool ret = (substr ? true : false);
+ free(chipset);
+ return ret;
+}
+
+std::string Utility::getKeyboardInput(int timeoutMsec) {
+ struct pollfd pfd = { STDIN_FILENO, POLLIN, 0 };
+ std::string line = "";
+ int ret = poll(&pfd, 1, timeoutMsec);
+ if(ret == 1) {// there is something to read
+ std::getline(std::cin, line);
+ }
+ return line;
+}
+std::string Utility::Execute(std::string cmd) {
+ char buffer[128];
+ std::string result = "";
+ FILE* pipe = popen(cmd.c_str(), "r");
+ assert(pipe != nullptr);
+ while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
+ result += buffer;
+ }
+ pclose(pipe);
+ return result;
+}
+
+vector<string> Utility::Split(string in, char delimiter) {
+ vector<string> result;
+ stringstream ss(in);
+ string line;
+ while (getline(ss, line)) {
+ stringstream liness(line);
+ string token;
+ while(getline(liness, token, delimiter)) {
+ if (!token.empty()) {
+ result.push_back(token);
+ }
+ }
+ }
+ return result;
+}
+
+} // namespace utils