From 946c204191f778f236c0665e2a33cab1267c5d1b Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EB=B0=B1=EC=A2=85=EB=AF=BC/Tizen=20Platform=20Lab=28SR=29/?= =?utf8?q?=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Fri, 30 Jun 2023 13:20:32 +0900 Subject: [PATCH] initial commit code from //TIZEN/[MAIN]/[ONEPROD_Prj]/[DEV]/[MULTIMEDIA]/[Mmplayer_SRCN]/Mmplayer/esplusplayer/...@11344755 --- AUTHORS | 1 + CMakeLists.txt | 61 + LICENSE.APLv2 | 204 + config/esplusplayer.ini | 12 + docs/class_diagram/esplusplayer.plantuml | 52 + docs/coding_rule.md | 56 + docs/gtest_guide.md | 131 + .../PlantUML_Language_Reference_Guide.pdf | Bin 0 -> 2007353 bytes esplusplayer.pc.in | 10 + include/esplusplayer/appinfo.h | 51 + include/esplusplayer/attribute.h | 51 + include/esplusplayer/audioeasinginfo.h | 45 + include/esplusplayer/decodedvideopacketex.h | 73 + include/esplusplayer/drm.h | 61 + include/esplusplayer/elementary_stream.h | 343 ++ include/esplusplayer/es_eventlistener.h | 248 + include/esplusplayer/espacket.h | 188 + include/esplusplayer/esplusplayer.h | 1266 ++++ include/esplusplayer/external_drm.h | 111 + include/esplusplayer/track.h | 161 + include/esplusplayer/types/buffer.h | 82 + include/esplusplayer/types/display.h | 89 + include/esplusplayer/types/error.h | 78 + include/esplusplayer/types/event.h | 56 + include/esplusplayer/types/latency.h | 44 + include/esplusplayer/types/picturequality.h | 39 + include/esplusplayer/types/resource.h | 72 + include/esplusplayer/types/source.h | 70 + include/esplusplayer/types/stream.h | 36 + include/esplusplayer/types/streaming_message.h | 65 + include/esplusplayer/types/submitdata.h | 40 + include/esplusplayer_capi/buffer.h | 118 + include/esplusplayer_capi/display.h | 73 + include/esplusplayer_capi/drm.h | 183 + include/esplusplayer_capi/error.h | 69 + include/esplusplayer_capi/espacket.h | 114 + include/esplusplayer_capi/esplusplayer_capi.h | 3487 +++++++++++ include/esplusplayer_capi/esplusplayer_internal.h | 154 + include/esplusplayer_capi/event.h | 71 + include/esplusplayer_capi/latency.h | 56 + include/esplusplayer_capi/matroska_color.h | 170 + include/esplusplayer_capi/state.h | 51 + include/esplusplayer_capi/stream.h | 168 + include/esplusplayer_capi/submitdatatype.h | 47 + include/esplusplayer_capi/submitstatus.h | 49 + include/mixer/decodedvideoinfo.h | 55 + include/mixer/mixer.h | 274 + include/mixer/mixer_eventlistener.h | 82 + include/mixer/mixerticket.h | 180 + include/mixer/mixerticket_eventlistener.h | 89 + include/mixer_capi/display.h | 61 + include/mixer_capi/error.h | 52 + include/mixer_capi/mixer_capi.h | 304 + .../class_diagram/esplusplayer/esplusplayer.png | Bin 0 -> 45550 bytes packaging/esplusplayer.manifest | 5 + packaging/esplusplayer.spec | 248 + src/CMakeLists.txt | 5 + src/cpplint.py | 6123 ++++++++++++++++++++ src/esplusplayer/CMakeLists.txt | 73 + .../include_internal/esplayer/decoded_pkt_list.h | 304 + .../include_internal/esplayer/espacket_logger.h | 43 + .../include_internal/esplayer/esplayer.h | 344 ++ .../include_internal/esplayer/esplayer_drm.h | 20 + .../include_internal/esplayer/message.hpp | 401 ++ .../include_internal/esplayer/state_manager.hpp | 387 ++ src/esplusplayer/src/elementary_stream.cpp | 231 + src/esplusplayer/src/espacket.cpp | 37 + src/esplusplayer/src/espacket_logger.cpp | 94 + src/esplusplayer/src/esplayer.cpp | 2909 ++++++++++ src/esplusplayer/src/esplayer_drm.cpp | 87 + src/esplusplayer/src/esplusplayer.cpp | 18 + src/esplusplayer/src/esplusplayer_capi.cpp | 2338 ++++++++ src/mixer/CMakeLists.txt | 60 + src/mixer/include_internal/mixer/abs_videoframe.h | 41 + src/mixer/include_internal/mixer/defaultmixer.h | 146 + .../mixer/interfaces/accessiblebuffer.h | 19 + .../mixer/interfaces/bufferobject.h | 16 + .../mixer/interfaces/memoryallocator.h | 17 + .../mixer/interfaces/phyaddraccessor.h | 13 + .../mixer/interfaces/renderableobj_factory.h | 14 + .../mixer/interfaces/renderableobject.h | 33 + .../mixer/interfaces/videoplanecollection.h | 16 + .../mixer/interfaces/videoplanecolorfiller.h | 17 + .../mixer/interfaces/videoplanecopier.h | 13 + .../mixer/interfaces/videoplanemanipulable.h | 18 + .../mixer/interfaces/videoplanemanipulator.h | 22 + .../mixer/interfaces/videoplanescaler.h | 13 + src/mixer/include_internal/mixer/mixedframe.h | 63 + src/mixer/include_internal/mixer/renderer.h | 94 + .../include_internal/mixer/rendererwithdoublebuf.h | 154 + .../include_internal/mixer/sys/tbminterface.h | 29 + .../mixer/tizen/tizenaccessiblebufferobj.h | 19 + .../mixer/tizen/tizenbufferkeyvideoframe.h | 36 + .../include_internal/mixer/tizen/tizenbuffermgr.h | 291 + .../include_internal/mixer/tizen/tizenbufferobj.h | 72 + .../mixer/tizen/tizenbufferphyaddraccessor.h | 69 + .../mixer/tizen/tizendefaultphyaddraccessor.h | 20 + .../mixer/tizen/tizenhwbufferobj.h | 27 + .../mixer/tizen/tizenhwvideoframe.h | 32 + .../mixer/tizen/tizenrenderableobj_factory.h | 25 + .../mixer/tizen/tizensurfacevideoframe.h | 34 + .../include_internal/mixer/types/buffertype.h | 23 + .../include_internal/mixer/types/planecomponent.h | 13 + .../mixer/types/videoplanemanipinfo.h | 21 + .../mixer/types/videoplanemoveinfo.h | 17 + src/mixer/include_internal/mixer/videoplane.h | 116 + src/mixer/src/abs_videoframe.cpp | 46 + src/mixer/src/defaultmixer.cpp | 655 +++ src/mixer/src/mixedframe.cpp | 143 + src/mixer/src/mixer.cpp | 14 + src/mixer/src/mixer_capi.cpp | 206 + src/mixer/src/renderer.cpp | 252 + src/mixer/src/sys/tbminterface.cpp | 50 + src/mixer/src/tizen/tizenaccessiblebufferobj.cpp | 23 + src/mixer/src/tizen/tizenbufferkeyvideoframe.cpp | 36 + .../src/tizen/tizendefaultphyaddraccessor.cpp | 12 + src/mixer/src/tizen/tizenhwbufferobj.cpp | 29 + src/mixer/src/tizen/tizenhwvideoframe.cpp | 46 + src/mixer/src/tizen/tizenrenderableobj_factory.cpp | 16 + src/mixer/src/tizen/tizensurfacevideoframe.cpp | 45 + src/mixer/src/videoplane.cpp | 116 + src/plusplayer-core/Build/appendix.mk | 1 + src/plusplayer-core/Build/basedef.mk | 34 + src/plusplayer-core/Build/build_c.mk | 113 + src/plusplayer-core/Build/build_edc.mk | 81 + src/plusplayer-core/Build/build_po.mk | 64 + src/plusplayer-core/Build/flags.mk | 23 + src/plusplayer-core/Build/funcs.mk | 50 + src/plusplayer-core/Build/makefile | 34 + src/plusplayer-core/Build/makefile.mk | 206 + src/plusplayer-core/Build/platform.mk | 18 + src/plusplayer-core/Build/tooldef.mk | 70 + src/plusplayer-core/CMakeLists.txt | 68 + src/plusplayer-core/build_def.prop | 6 + .../core/decodedvideorawmodepacket.h | 40 + .../include_internal/core/decoderinputbuffer.h | 139 + .../core/decoderinputbuffer_listener.h | 25 + src/plusplayer-core/include_internal/core/error.h | 18 + .../include_internal/core/gst_utils.h | 26 + .../include_internal/core/gstobject_guard.h | 66 + .../include_internal/core/gstsignal_holder.h | 42 + src/plusplayer-core/include_internal/core/kpi.h | 58 + .../include_internal/core/serializer.h | 58 + .../include_internal/core/subtitle_attr_parser.h | 28 + .../include_internal/core/track_util.h | 35 + .../include_internal/core/trackrendereradapter.h | 300 + .../core/trackrendereradapter_utils.h | 118 + .../include_internal/core/utils/base64.h | 22 + .../include_internal/core/utils/caf_logger.h | 91 + .../core/utils/performance_checker.h | 48 + .../include_internal/core/utils/plusplayer_cfg.h | 17 + .../include_internal/core/utils/plusplayer_log.h | 117 + .../include_internal/core/utils/scope_exit.h | 36 + .../include_internal/core/videoframetypestrategy.h | 40 + src/plusplayer-core/project_def.prop | 58 + src/plusplayer-core/src/caf_logger.cpp | 320 + src/plusplayer-core/src/decodedvideopacketex.cpp | 24 + src/plusplayer-core/src/decoderinputbuffer.cpp | 22 + src/plusplayer-core/src/error.cpp | 161 + src/plusplayer-core/src/gst_utils.cpp | 46 + src/plusplayer-core/src/gstobject_guard.cpp | 52 + src/plusplayer-core/src/kpi.cpp | 198 + src/plusplayer-core/src/plusplayer_cfg.cpp | 24 + src/plusplayer-core/src/serializer.cpp | 49 + src/plusplayer-core/src/track_util.cpp | 147 + src/plusplayer-core/src/trackrendereradapter.cpp | 1257 ++++ .../src/trackrendereradapter_utils.cpp | 968 ++++ src/plusplayer-core/src/videoframetypestrategy.cpp | 22 + tomato/tc/TCList.dat | 1 + tomato/tc/testfarm_script.xml | 5 + tomato/tc/unit_test/ut_esplusplayer_all.xml | 9 + ut/CMakeLists.txt | 102 + ut/README.md | 131 + ut/cpplint.py | 6123 ++++++++++++++++++++ ut/include/appwindow.h | 97 + ut/include/esplusplayer/eseventlistener.hpp | 257 + ut/include/esplusplayer/esreader.hpp | 295 + ut/include/esplusplayer/tclist.h | 39 + ut/include/mixer/constant.h | 83 + ut/include/mixer/matcher.h | 105 + ut/include/mixer/mock/fakebuffer.h | 42 + ut/include/mixer/mock/mock_bufferobject.h | 37 + ut/include/mixer/mock/mock_memallocator.h | 16 + ut/include/mixer/mock/mock_phyaddraccessor.h | 16 + ut/include/mixer/mock/mock_renderableobj.h | 37 + ut/include/mixer/mock/mock_renderableobj_factory.h | 20 + ut/include/mixer/mock/mock_renderer_evtlistener.h | 16 + ut/include/mixer/mock/mock_vpcollection.h | 17 + ut/include/mixer/mock/mock_vpmanipulator.h | 19 + ut/include/mixer/mock/mock_vpscaler.h | 17 + ut/include/mixer/mock/movable.h | 47 + ut/include/mixer/mock/moveobj_wrapper.h | 30 + ut/include/streamreader.hpp | 228 + ut/include/utils/mock_videosink.hpp | 193 + ut/include/utils/utility.h | 265 + ut/src/esplusplayer/tclist.cpp | 69 + ut/src/esplusplayer/ut_basic.cpp | 1136 ++++ ut/src/esplusplayer/ut_display.cpp | 296 + ut/src/esplusplayer/ut_dual_audio.cpp | 107 + ut/src/esplusplayer/ut_inapp_multiview.cpp | 140 + ut/src/esplusplayer/ut_setstream.cpp | 51 + ut/src/mixer/constant.cpp | 51 + ut/src/mixer/matcher.cpp | 10 + ut/src/mixer/ut_espp_mixerscenario.cpp | 597 ++ ut/src/mixer/ut_mixedframe.cpp | 355 ++ ut/src/mixer/ut_mixer.cpp | 257 + ut/src/mixer/ut_mixer_capi.cpp | 232 + ut/src/mixer/ut_mixer_espp_capi.cpp | 351 ++ ut/src/mixer/ut_mixerscenario.cpp | 630 ++ ut/src/mixer/ut_mixerticket.cpp | 231 + ut/src/mixer/ut_renderer.cpp | 361 ++ ut/src/mixer/ut_tizenbuffermgr.cpp | 183 + ut/src/mixer/ut_tizenbufferobj.cpp | 129 + ut/src/mixer/ut_videoplane.cpp | 212 + ut/src/ut_cloudgame.cpp | 676 +++ ut/src/ut_espacket.cpp | 104 + ut/src/ut_esplayer.cpp | 510 ++ ut/src/ut_esplayer2.cpp | 776 +++ ut/src/ut_esplayer_trackrenderer.cpp | 266 + ut/src/ut_main.cpp | 19 + ut/src/ut_miscellaneous.cpp | 133 + ut/src/ut_streamreader.cpp | 130 + ut/src/ut_trackrendereradapter.cpp | 410 ++ ut/src/utils/utility.cpp | 561 ++ 224 files changed, 49342 insertions(+) create mode 100644 AUTHORS create mode 100644 CMakeLists.txt create mode 100644 LICENSE.APLv2 create mode 100644 config/esplusplayer.ini create mode 100644 docs/class_diagram/esplusplayer.plantuml create mode 100644 docs/coding_rule.md create mode 100644 docs/gtest_guide.md create mode 100644 docs/reference/PlantUML_Language_Reference_Guide.pdf create mode 100644 esplusplayer.pc.in create mode 100644 include/esplusplayer/appinfo.h create mode 100644 include/esplusplayer/attribute.h create mode 100644 include/esplusplayer/audioeasinginfo.h create mode 100644 include/esplusplayer/decodedvideopacketex.h create mode 100644 include/esplusplayer/drm.h create mode 100644 include/esplusplayer/elementary_stream.h create mode 100644 include/esplusplayer/es_eventlistener.h create mode 100644 include/esplusplayer/espacket.h create mode 100644 include/esplusplayer/esplusplayer.h create mode 100644 include/esplusplayer/external_drm.h create mode 100644 include/esplusplayer/track.h create mode 100644 include/esplusplayer/types/buffer.h create mode 100644 include/esplusplayer/types/display.h create mode 100644 include/esplusplayer/types/error.h create mode 100644 include/esplusplayer/types/event.h create mode 100644 include/esplusplayer/types/latency.h create mode 100644 include/esplusplayer/types/picturequality.h create mode 100644 include/esplusplayer/types/resource.h create mode 100644 include/esplusplayer/types/source.h create mode 100644 include/esplusplayer/types/stream.h create mode 100644 include/esplusplayer/types/streaming_message.h create mode 100644 include/esplusplayer/types/submitdata.h create mode 100644 include/esplusplayer_capi/buffer.h create mode 100644 include/esplusplayer_capi/display.h create mode 100644 include/esplusplayer_capi/drm.h create mode 100644 include/esplusplayer_capi/error.h create mode 100644 include/esplusplayer_capi/espacket.h create mode 100644 include/esplusplayer_capi/esplusplayer_capi.h create mode 100644 include/esplusplayer_capi/esplusplayer_internal.h create mode 100644 include/esplusplayer_capi/event.h create mode 100644 include/esplusplayer_capi/latency.h create mode 100644 include/esplusplayer_capi/matroska_color.h create mode 100644 include/esplusplayer_capi/state.h create mode 100644 include/esplusplayer_capi/stream.h create mode 100644 include/esplusplayer_capi/submitdatatype.h create mode 100644 include/esplusplayer_capi/submitstatus.h create mode 100644 include/mixer/decodedvideoinfo.h create mode 100644 include/mixer/mixer.h create mode 100644 include/mixer/mixer_eventlistener.h create mode 100644 include/mixer/mixerticket.h create mode 100644 include/mixer/mixerticket_eventlistener.h create mode 100644 include/mixer_capi/display.h create mode 100644 include/mixer_capi/error.h create mode 100644 include/mixer_capi/mixer_capi.h create mode 100644 out/docs/class_diagram/esplusplayer/esplusplayer.png create mode 100644 packaging/esplusplayer.manifest create mode 100644 packaging/esplusplayer.spec create mode 100644 src/CMakeLists.txt create mode 100644 src/cpplint.py create mode 100644 src/esplusplayer/CMakeLists.txt create mode 100644 src/esplusplayer/include_internal/esplayer/decoded_pkt_list.h create mode 100644 src/esplusplayer/include_internal/esplayer/espacket_logger.h create mode 100644 src/esplusplayer/include_internal/esplayer/esplayer.h create mode 100644 src/esplusplayer/include_internal/esplayer/esplayer_drm.h create mode 100644 src/esplusplayer/include_internal/esplayer/message.hpp create mode 100644 src/esplusplayer/include_internal/esplayer/state_manager.hpp create mode 100644 src/esplusplayer/src/elementary_stream.cpp create mode 100644 src/esplusplayer/src/espacket.cpp create mode 100644 src/esplusplayer/src/espacket_logger.cpp create mode 100644 src/esplusplayer/src/esplayer.cpp create mode 100644 src/esplusplayer/src/esplayer_drm.cpp create mode 100644 src/esplusplayer/src/esplusplayer.cpp create mode 100644 src/esplusplayer/src/esplusplayer_capi.cpp create mode 100644 src/mixer/CMakeLists.txt create mode 100644 src/mixer/include_internal/mixer/abs_videoframe.h create mode 100644 src/mixer/include_internal/mixer/defaultmixer.h create mode 100644 src/mixer/include_internal/mixer/interfaces/accessiblebuffer.h create mode 100644 src/mixer/include_internal/mixer/interfaces/bufferobject.h create mode 100644 src/mixer/include_internal/mixer/interfaces/memoryallocator.h create mode 100644 src/mixer/include_internal/mixer/interfaces/phyaddraccessor.h create mode 100644 src/mixer/include_internal/mixer/interfaces/renderableobj_factory.h create mode 100644 src/mixer/include_internal/mixer/interfaces/renderableobject.h create mode 100644 src/mixer/include_internal/mixer/interfaces/videoplanecollection.h create mode 100644 src/mixer/include_internal/mixer/interfaces/videoplanecolorfiller.h create mode 100644 src/mixer/include_internal/mixer/interfaces/videoplanecopier.h create mode 100644 src/mixer/include_internal/mixer/interfaces/videoplanemanipulable.h create mode 100644 src/mixer/include_internal/mixer/interfaces/videoplanemanipulator.h create mode 100644 src/mixer/include_internal/mixer/interfaces/videoplanescaler.h create mode 100644 src/mixer/include_internal/mixer/mixedframe.h create mode 100644 src/mixer/include_internal/mixer/renderer.h create mode 100644 src/mixer/include_internal/mixer/rendererwithdoublebuf.h create mode 100644 src/mixer/include_internal/mixer/sys/tbminterface.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizenaccessiblebufferobj.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizenbufferkeyvideoframe.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizenbuffermgr.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizenbufferobj.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizenbufferphyaddraccessor.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizendefaultphyaddraccessor.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizenhwbufferobj.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizenhwvideoframe.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizenrenderableobj_factory.h create mode 100644 src/mixer/include_internal/mixer/tizen/tizensurfacevideoframe.h create mode 100644 src/mixer/include_internal/mixer/types/buffertype.h create mode 100644 src/mixer/include_internal/mixer/types/planecomponent.h create mode 100644 src/mixer/include_internal/mixer/types/videoplanemanipinfo.h create mode 100644 src/mixer/include_internal/mixer/types/videoplanemoveinfo.h create mode 100644 src/mixer/include_internal/mixer/videoplane.h create mode 100644 src/mixer/src/abs_videoframe.cpp create mode 100644 src/mixer/src/defaultmixer.cpp create mode 100644 src/mixer/src/mixedframe.cpp create mode 100644 src/mixer/src/mixer.cpp create mode 100644 src/mixer/src/mixer_capi.cpp create mode 100644 src/mixer/src/renderer.cpp create mode 100644 src/mixer/src/sys/tbminterface.cpp create mode 100644 src/mixer/src/tizen/tizenaccessiblebufferobj.cpp create mode 100644 src/mixer/src/tizen/tizenbufferkeyvideoframe.cpp create mode 100644 src/mixer/src/tizen/tizendefaultphyaddraccessor.cpp create mode 100644 src/mixer/src/tizen/tizenhwbufferobj.cpp create mode 100644 src/mixer/src/tizen/tizenhwvideoframe.cpp create mode 100644 src/mixer/src/tizen/tizenrenderableobj_factory.cpp create mode 100644 src/mixer/src/tizen/tizensurfacevideoframe.cpp create mode 100644 src/mixer/src/videoplane.cpp create mode 100644 src/plusplayer-core/Build/appendix.mk create mode 100644 src/plusplayer-core/Build/basedef.mk create mode 100644 src/plusplayer-core/Build/build_c.mk create mode 100644 src/plusplayer-core/Build/build_edc.mk create mode 100644 src/plusplayer-core/Build/build_po.mk create mode 100644 src/plusplayer-core/Build/flags.mk create mode 100644 src/plusplayer-core/Build/funcs.mk create mode 100644 src/plusplayer-core/Build/makefile create mode 100644 src/plusplayer-core/Build/makefile.mk create mode 100644 src/plusplayer-core/Build/platform.mk create mode 100644 src/plusplayer-core/Build/tooldef.mk create mode 100644 src/plusplayer-core/CMakeLists.txt create mode 100644 src/plusplayer-core/build_def.prop create mode 100644 src/plusplayer-core/include_internal/core/decodedvideorawmodepacket.h create mode 100644 src/plusplayer-core/include_internal/core/decoderinputbuffer.h create mode 100644 src/plusplayer-core/include_internal/core/decoderinputbuffer_listener.h create mode 100644 src/plusplayer-core/include_internal/core/error.h create mode 100644 src/plusplayer-core/include_internal/core/gst_utils.h create mode 100644 src/plusplayer-core/include_internal/core/gstobject_guard.h create mode 100644 src/plusplayer-core/include_internal/core/gstsignal_holder.h create mode 100644 src/plusplayer-core/include_internal/core/kpi.h create mode 100644 src/plusplayer-core/include_internal/core/serializer.h create mode 100644 src/plusplayer-core/include_internal/core/subtitle_attr_parser.h create mode 100644 src/plusplayer-core/include_internal/core/track_util.h create mode 100644 src/plusplayer-core/include_internal/core/trackrendereradapter.h create mode 100644 src/plusplayer-core/include_internal/core/trackrendereradapter_utils.h create mode 100644 src/plusplayer-core/include_internal/core/utils/base64.h create mode 100644 src/plusplayer-core/include_internal/core/utils/caf_logger.h create mode 100644 src/plusplayer-core/include_internal/core/utils/performance_checker.h create mode 100644 src/plusplayer-core/include_internal/core/utils/plusplayer_cfg.h create mode 100644 src/plusplayer-core/include_internal/core/utils/plusplayer_log.h create mode 100644 src/plusplayer-core/include_internal/core/utils/scope_exit.h create mode 100644 src/plusplayer-core/include_internal/core/videoframetypestrategy.h create mode 100644 src/plusplayer-core/project_def.prop create mode 100644 src/plusplayer-core/src/caf_logger.cpp create mode 100644 src/plusplayer-core/src/decodedvideopacketex.cpp create mode 100644 src/plusplayer-core/src/decoderinputbuffer.cpp create mode 100644 src/plusplayer-core/src/error.cpp create mode 100644 src/plusplayer-core/src/gst_utils.cpp create mode 100644 src/plusplayer-core/src/gstobject_guard.cpp create mode 100644 src/plusplayer-core/src/kpi.cpp create mode 100644 src/plusplayer-core/src/plusplayer_cfg.cpp create mode 100644 src/plusplayer-core/src/serializer.cpp create mode 100644 src/plusplayer-core/src/track_util.cpp create mode 100644 src/plusplayer-core/src/trackrendereradapter.cpp create mode 100644 src/plusplayer-core/src/trackrendereradapter_utils.cpp create mode 100644 src/plusplayer-core/src/videoframetypestrategy.cpp create mode 100644 tomato/tc/TCList.dat create mode 100644 tomato/tc/testfarm_script.xml create mode 100644 tomato/tc/unit_test/ut_esplusplayer_all.xml create mode 100644 ut/CMakeLists.txt create mode 100644 ut/README.md create mode 100644 ut/cpplint.py create mode 100644 ut/include/appwindow.h create mode 100644 ut/include/esplusplayer/eseventlistener.hpp create mode 100644 ut/include/esplusplayer/esreader.hpp create mode 100644 ut/include/esplusplayer/tclist.h create mode 100644 ut/include/mixer/constant.h create mode 100644 ut/include/mixer/matcher.h create mode 100644 ut/include/mixer/mock/fakebuffer.h create mode 100644 ut/include/mixer/mock/mock_bufferobject.h create mode 100644 ut/include/mixer/mock/mock_memallocator.h create mode 100644 ut/include/mixer/mock/mock_phyaddraccessor.h create mode 100644 ut/include/mixer/mock/mock_renderableobj.h create mode 100644 ut/include/mixer/mock/mock_renderableobj_factory.h create mode 100644 ut/include/mixer/mock/mock_renderer_evtlistener.h create mode 100644 ut/include/mixer/mock/mock_vpcollection.h create mode 100644 ut/include/mixer/mock/mock_vpmanipulator.h create mode 100644 ut/include/mixer/mock/mock_vpscaler.h create mode 100644 ut/include/mixer/mock/movable.h create mode 100644 ut/include/mixer/mock/moveobj_wrapper.h create mode 100644 ut/include/streamreader.hpp create mode 100644 ut/include/utils/mock_videosink.hpp create mode 100644 ut/include/utils/utility.h create mode 100644 ut/src/esplusplayer/tclist.cpp create mode 100644 ut/src/esplusplayer/ut_basic.cpp create mode 100644 ut/src/esplusplayer/ut_display.cpp create mode 100644 ut/src/esplusplayer/ut_dual_audio.cpp create mode 100644 ut/src/esplusplayer/ut_inapp_multiview.cpp create mode 100644 ut/src/esplusplayer/ut_setstream.cpp create mode 100644 ut/src/mixer/constant.cpp create mode 100644 ut/src/mixer/matcher.cpp create mode 100644 ut/src/mixer/ut_espp_mixerscenario.cpp create mode 100644 ut/src/mixer/ut_mixedframe.cpp create mode 100644 ut/src/mixer/ut_mixer.cpp create mode 100644 ut/src/mixer/ut_mixer_capi.cpp create mode 100644 ut/src/mixer/ut_mixer_espp_capi.cpp create mode 100644 ut/src/mixer/ut_mixerscenario.cpp create mode 100644 ut/src/mixer/ut_mixerticket.cpp create mode 100644 ut/src/mixer/ut_renderer.cpp create mode 100644 ut/src/mixer/ut_tizenbuffermgr.cpp create mode 100644 ut/src/mixer/ut_tizenbufferobj.cpp create mode 100644 ut/src/mixer/ut_videoplane.cpp create mode 100644 ut/src/ut_cloudgame.cpp create mode 100644 ut/src/ut_espacket.cpp create mode 100644 ut/src/ut_esplayer.cpp create mode 100644 ut/src/ut_esplayer2.cpp create mode 100644 ut/src/ut_esplayer_trackrenderer.cpp create mode 100644 ut/src/ut_main.cpp create mode 100644 ut/src/ut_miscellaneous.cpp create mode 100644 ut/src/ut_streamreader.cpp create mode 100644 ut/src/ut_trackrendereradapter.cpp create mode 100644 ut/src/utils/utility.cpp diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..135c035 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,61 @@ + +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 diff --git a/LICENSE.APLv2 b/LICENSE.APLv2 new file mode 100644 index 0000000..a06208b --- /dev/null +++ b/LICENSE.APLv2 @@ -0,0 +1,204 @@ +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. + diff --git a/config/esplusplayer.ini b/config/esplusplayer.ini new file mode 100644 index 0000000..b8590ee --- /dev/null +++ b/config/esplusplayer.ini @@ -0,0 +1,12 @@ +{ + "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 +} diff --git a/docs/class_diagram/esplusplayer.plantuml b/docs/class_diagram/esplusplayer.plantuml new file mode 100644 index 0000000..91ab40d --- /dev/null +++ b/docs/class_diagram/esplusplayer.plantuml @@ -0,0 +1,52 @@ +@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 diff --git a/docs/coding_rule.md b/docs/coding_rule.md new file mode 100644 index 0000000..a7acbe9 --- /dev/null +++ b/docs/coding_rule.md @@ -0,0 +1,56 @@ +## **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. diff --git a/docs/gtest_guide.md b/docs/gtest_guide.md new file mode 100644 index 0000000..750652d --- /dev/null +++ b/docs/gtest_guide.md @@ -0,0 +1,131 @@ +**GTest guide** +=============== + For unit test for plusplayer + +--- +### Reference +- +- + +## Assertion +* ASSERT_* : 실패시 해당 테스트를 바로 종료
+* EXPECT_* : 실패하여도 테스트 계속 진행
+ +#### 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**.
+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
+ +1. test selection + * --gtest_list_tests
+ > 테스트할 항목을 보여준다. (테스트 실시 X) + * --gtest_also_run_disabled_tests
+ > DISABLED_ 로 막아둔 test case 를 일시적으로 실행 + * --gtest_filter
+ > 특정 case 들만 실행 가능
+ Ex) --gtest_filter="*.create*" : 모든 TC중에서 create 로 시작하는 모든 TC 실행.
+ +2. test Execution + * --gtest_repeat
+ > test 반복 가능. -1일 경우 무한히 반복
+ --gtest_break_on_failure와 함께 사용하면 실패할 경우 멈춤. + * --gtest_shuffle
+ > 무작위로 실행 가능 (test case 간 dependency 가 없어야 하기 때문이다)
+ gtest는 기본적으로 현재시간을 랜덤함수의 시드값으로 사용하나, --gtest_random_seed로 조절가능하다 + * --gtest_random_seed + > 1 ~ 99999까지의 값을 --gtest_shuffle에서 사용할 랜덤함수의 시드로 사용. + +--- +## Reference + * Gtest Primer(KR)
+ * Gtest Primer(EN)
+ * Unit Test Planning & How to write Unit Test Code(KR)
+ * Unit Test Planning & How to write Unit Test Code(EN)
diff --git a/docs/reference/PlantUML_Language_Reference_Guide.pdf b/docs/reference/PlantUML_Language_Reference_Guide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..838b4754a1ac22ce1404c46cb833242ad45dc981 GIT binary patch literal 2007353 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[MBwKd?SI!Y@9G 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 C1-b_vPjmYG^ip-grdWM}Ac# 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

vLuX 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

;aSfYMQ~vavXt%<(@vBdC)iK9}Yl7xZ+ZYUDR7g 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�zYugDUh6-BC-1G($stz-mDG z-ceTv!e5UozRe>#O!KZp8v_dwB(fZAUkD*~V`B&-CcVz3d&k%D3u6dgmWZU+nexR?T>ks!h-BV|O6xm$+e4yxZZ`IX zum4Di`~zHc`Oi8N%t0%j?xL4l3Af`#E-q&s+n8GJ&PhgWP`Y8AYN^_Z*3^Avf(=FT z`d65h!3RHm%l-hG{)DiOi~0Y`8|glYuB|V&nO2IlN&UN>Kq6757nL#n$Hd#7H9lN4j@J?OSbwQiPKUr>#IHe7x5kd@T8gh}SYfd~3}pbzRm}GO zbB|GjlL^8$f)laI8XxYzEZzy(IY(kU&fr~aT+$Z*Sj7FVDLRL_ z6GyfUWdFR096R)b&p)#JF>C8_jDOPg9}8;y{rZUHd;EFK1vw<_k!O6WS;EC7ew5EJ zHd%xK%=Gwa5#(oH|MLmvF$e$gm%p#{pRfNJHn$lh?*|

uk^zW;uz0962nndS}c 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

X^9z!lR4}+#hbg`go-BAYz!pNY`)rW( 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°UPtjy0i2V%_bM8^0oZkt?=-r}O%ljAb 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%0AK$)t37{UQnE+4PJl}^8IAvAHC@3ZImZ0pO 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%0A$O=u$JWnWM2 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

tY7f8W|>yDv?&KFfY?Y31jCWEO?<* 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¨~TrB_7+_ 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

Yxh&gUIWdTcqlxt*vRF zgYHmJK+!kjM()sJK;}Mfj6OPvxQ4rNOBJn7f4{tQa=$Z$s|n3^te|v}9eH3>KRBsz z;H>_%6Jqn-Z8=woRqVQ+E6V+vkFfA<%QBmPU1e7xPCV0NzJI#*da=O^+GMN8NwQ-unSSOU=Ov zte|tk7%~0(8q@l*7_zdmuChIp@o;QKDfVNDi|xF+$}`;sX0Zul-afYV{;RD?5BClZ zyyA3^xw#}EEHKkGLp(z*q{h?H5zlOF^h;kKECRTjrrp;57@o64_-}WzQtA{yAN@z0 zpb-a;`LDMpQTeay9#B7GU||`#Zd1#mh>(cAmndDI*?(+m4WBFD0=_dMe4{L%o=Jv^Ccx2>^J z&%=@&gKBFIVE2ZYALpm-Zdi~eA-vh-{yW<$eoxJ)`1i*cq0c>vi!(my2_G;i*{eF= zkEx{_=&);F(W3+K4g(d%?{2+%p_#8gJ9q`)pG5D%?`{_*qw`)}JpVaao3;PD#5 z-$q|OCBf1HXadlqc8o)uwDJ;8ZB5O0#AhrTLphe#QU0%cb8KXtvfuo!rPo)gYie3zu*!EQ0nBk0MU&wl}Vy|OS0{3M{qQgeW$ zy$LJGieP?NXA{Q71w^ryRxO0$0@|7qBhEedM!MWpW({5zvyjgI6ida^M54J6a!CuQ zaPL{W&3=B2^z1AkO8SJT1n6to-qF=sZ7fy}{Sc2`U7JHU&6yZYi-vqAnKvGqI&&x zacGDeiHq;Jn&|r_Nq?mK`R*ec-`mv3$-H|PY|1pGG32aO^{G4ywr02EbW;y7B2ta0 z&4go*widE23M*}=czR6s`kuh>6JrI*7fAOnuz`|kVK=gYaFv@X-ra^k0f>8FtbL3s zC?K#b4+kfFq$(<3PTR$50R9Q*ssWA+dbvg z`q1s@*^!06Usp$mXdaK>DLRmVU*k)u)I^&jn)h4B1vJnfG7tPGjp5HO8<3vA*u5~I`rMHiIg70?Bq&h4l_IW zRMj^XtDAJgIsJ^cFm&A_rgf%<(Vkf$5Oib3W!bMa@kohzTcvwEX?Es%rPQu!BSzh< zth(*i#!6$X+Ahvd0NaiZ1y_C(xmT`Y^`#1|mFS3fwk(7l#zP%lB?Hc%XBYddFyR#1 z@jXB8GrXS>t6LkVappp*6Ph^_A(fch7jDGbWBx$vs`Xm61ifBPg(1EvF|g5Feo4zK z$`5L32uYN`MRYV4#*Y^hmna1hr>sn%^xr=v4%P5+I7}rZ>9m{5O0m&M8FZi>gu1=YU7$eE5i#biyuSGeF9#QN8wVY5p<@PdTW1 z9*vfipXm-#q&Bne0ofI1G%pY_^{CubIvcTB8z*w zJB0h+a;d6vr1o8h^An#oQZ(KwY~8R{sz2EJ*pI)Sa>n(tZII7p760s`7-J)2_}@|< zD*CPHky8=if;B3l-+l((fd5zt^nyXxiN4QGyqbdO?*A_4XWuT)c`+fp1E_NBm1^!i zeq4M;5p9I6T!~!H!vVXGyPS6PdTy?+MaT!LFWJu3kKL~I)g1o~GH3wb;V~rDcTSjr zgIN=n1qp`beUrKS)jx@hG6#B-7}?BBdnu=Xe~b&$E%om2?fh#8yO4(wU7T*0HOwci z^kb&8BNu?&y1BU(H&b2Ek;e2B&ip-<7;z4#ZvDAxhh|riXL<}h#Ir3PS9l$v_kdom z4uY#0PVaMi=r_6uY8?lWqke_z{np@;*Bc2k#M@v_t^pehFur7Gqi{$mEln(vjSe7$ zC}h^axejs;2Y1c7{9}A`fSr?aP_TRj#R8#V&qVZ-iZFg?!7GzC+_h4}9!!m2os2*s z_+FK14*AeEgp^%E-;Zs0vT0=HEFt0_J}Yv{E9oEsEy~Qil)#v}$MmlQ2zkx9%-)W= z$O{fY5f|FleJEMMVZVBj$og=%yny<@uJ-oNk|{6upXLpQ&3E3i;9o=aKQ$u7#Y08m zh)n2f>C%T~qK>$Y%_D3$p_UjsRMt?v|L-dOr=XP*?V!4D2O{1{RP5^MwX zvj+kEcQ-M~EHD2eB;hcDdpP`I#Aj~#ZTMLY*y=;Lo2)cL3qCb_7{NZ<%&8=cT+%{|b~;=Zb5 zFvvt=aIXIPB_sKqw>POJzKT`Qa%JPv2knCd#UTPC5yQ94r>@*@YM!V%h{0K!MHZo!}j@>o$L2L#NCdX@%x z_4JF$xKSE3oCDqXvEvagE-Njx@p`S9hwxP6KT+w*6e%eyZ;n@9xt9%5Nl*}q?k7DT zj=BA}BY55*AlI0k{R94A7{HT%gL}n6z+j@vOd&T57#?^bAQ^O~)$1!+vsJrVq!CfX z*w5d5JuJzd^($1z*K`aF44%Q_s@4zi^4pJBh#g6PTG7!%8xYrMhu%Xy1bRl>&p1i| zBz&wP9~DKjg2gG+F~dL6dK*9AKN;_eC3?Wh!0@K5EIld7A@sq{CHv5k(629vwvKk5 zym?_sz597zmj$^S;cVkz@ttizBm=48YjA#!5U70I(UQPD!K_rR^I|6K`O5#s2`p>s%wz46m^xczuUK|~KU@rG&4rx4sGTMjffd9?sgcTK4 zE~q70Bs*`BSWgg&knI0Qe}bxV9XsQJcMpYN zaf*W`2vzBC@(2hV!^W}dnzfny@MSp=nBL3ItREj84GKS|jOCBH^iGhnA+wl46g8Av z7SW%c$~B3KrT=|5Q%!d1?pq9cmL-8N>yN1w7)*`foN@|(u=6|;ypE`!FCZjd7Li2{ zvwD8ybJW#bpEvge9i~k5%sL)F#Vy3>dvPyip8%0%b3hY@HLZ%D)L9j4mX>gjXxP=} zs+pCnlyQ9Ta`{kV7vJhA|fN(5e>!ZS`;%Y)Z3&ulJrqi=uzA|LFH~~E| zn2Q@W5&|~Da$_GS!h3^2vsLlrWnFD8CGH0kwJi!T;}(966MX&m5#0YleRHDT-k3xO z(eIA}Pe9Jq!2zPyhbiZVa{uuV6e-bOu;UPCwY$7yMce{1^`(JWu5|mY<#8>etXDDO z8b=_lktEqocFu>Y0^QLHZ^BBK4LOWJ%Wd`iGTbxa?Vqa7zBZRyW!G~l3_a8=ogdQ) zS*D?_grx0)>aFipeaIV{r1Q{9Pg(b~OM0uZCETq&NFD zg->nyktXR1X|JK1`h``2m8iwTEfu6UvCiBn-a7M8?K#;OVh1uLYVzk!#$LA;yepo4sXZ>(`M*t^r2mcu-0dlXW7!E!H2a}qhZ)zf#s>9SP zwj{Ee=oNFo2d>w`W=4&#ps{V-%NXS?r+4G4#F>#5i=o23a|@zUQffl)(u4(FRo@P1 z+a<5zHAry|lCiQ6u?=!?3KwE$7Y|w!Z3vCi50eu4k|v1Gc(>S%*)>=3=zh}+V)+>S z))z?h#4+qyrJ3vK)N4H@z)s$sZbo=Vr7xjNm$9fIu3>O!0#oA@T_-D+`uYngdZ!Ep zDJ~|{0PppI7vStF`SDsC5hl8Ntr@Sk2&cEetD?@Y`WxDtaHIE?Gq)JD0>9n&olmgA zTT5U$;Tm(DzWRX#*Ncc#U{}U=PPtr3s_qtmNPw#h3)|PGt=d`HV?NL$fNe>PoRf)l z)1O7eOPcBDcs?5$+{-YZYAxZS4|LV|h9cbfwR(a}2uh*lDh(aVPssD{#wpBfP4qPY8amh7TbVp< zB?fZ{k!ai2c2!z@=FICg?uk#U?`SxnMf*O!T4#XqOz?8B^y<*_O$SL*E@K( zBimu<9o=Lcd)wmwxS9Uah&5((#KbeSv#Lw?h|b^RV(4}YyiVwMZMphEexT(#)9-%X zZ=%d#v!nJx3@?0ICU^);U>od~b#tpfMqwzN4j7$v$gm-NcM*(5T4vNr65CmYC8anu z80SFxgzetf&guO@NFxI;j75dr*^}CM@*H&@7q+!d!i>(~Pi(rVik$c5SaxwXioTh8 zluqR`2g#Brwd9%D;fA3~GjKC#$;zX*aIAs!7h<_T;Q$&xQA(7PlcTC^`uWXbzwNTy zz%nRM0w&om7iM91+abXn?I7SAK!X%LB;(^jV%e^S#rpB*(;946V63GwDZw~$A(mnJ z9H%LVZln-=puImGGoL1N(a~D)E_o{LR|;g}0fACME)@98q^{J*%Qn!*>4gE&th7rK zd4pF)j31H{<4hM46kMxdEB`*ed2R9Hph$ObQFly7Rq`GaQ?|S5u!?pS_7k&e@^Y~W zAIUq7Yxce;Qx_+`7xq4LI@jP6xV`oG%5_y0POnSMy0R z4(59ZGwkHPQxm8+TDwQ+V%GMRvM?BWXC8c`3h)LU<;V#BLS<`fYm5~nS%FYh^pKg6 zu@PdLSFczN;o|*mGCjAEuoHP5z)sFaH-spxepoX6d?BEu`v@kRq9UuACYM$B>%tvs zz`6`7%nW+MfRj8bP-L?o#}pA6S*ll^|Mo2ff#XO6d!X?RhB7i6FEraBQhr6f0)V$k zs#yn$leqXV!Y_wdAVVCcut<3_JAmfJNC{Wz9)e3PPJLWC9!bi(>y|EaI15|Ag%R^4L!L^HCEz=VwKO$EVBNrh?e5I zI>J`YGm56mBA3eDq3?AnG8T2{C*$GIB>*LjbQ(Rn? zNQw)EP3NBs7tY2%87?9+*vIi7DAptJw`-;ad|bRR7g^DWAH;mno&&deAX>i&+i?( zF#NmFHc~(A-F_d)JuxU5qQ8R3+$b||8OuZFRY_iTn)CHEa+ay&+)Hgp=>wBQ4rC_d zcq+Dq*d6TcWl`d{*N$jK(S{<(xjeGvL4Z zlp8yvI(sV|&6B)AQs9@@Kbk%P^QM$7SzFg3X36?;r||LhBwJ_fFKx0p$Q2DBx+2(M zAQSO@b8UBvp4c#v1;)g%@SWGanyixX__AixCRF&tOjG?9`{*n4iz{aq;Q=2SO zEhmrVGKL-n9e*(R@_6D#V%}h{kU}}DW8ng$>c*mwDwowTc~8;O?L%{#K*c_%!Y^Ru z-#jCjYi85M9cM}k{6|ALR-iCp%UBmY{`ZZW5r2ZdmM75pi_#^F^9P?0Mf&4o;Q{~8 z1NveOi78gUT;vvsvwbz1|@0NjYRX@9VJ-(MKzh+Ex~-AHqs#N;tvBCMR?m|7-F z_l=P*h}WCJNQ%O+I|5=X8l}qfAf_{h(`;t>#t&?%I}x;m;d5+QBHLzMC#5yb*Q!&* z#l+sX{PyNO5p{CSH11mw8cTGpq><2Sx;mNdEA#XF@F|z1 zPDdmz_sWUgILYy^TKs*DPs_1YyGP#xe(f!e!}4=Eo;~p>WVCraEE@m*`>x*buSS`) z_~2B{C3I-&0@jyLQB3Z_Yv^`zJ2ksWD<#Rgh7A)CC1`y^7zkAca82Kgr> zhXbUI)-Q7#(_VSH-7u2OwtUPG#sdQ?T1%^)T%AwTnqE@3IO0SpJ%w9%d)UmK!#M=@ z2m;P)fFYGG+L){@E+}{cGCeq3jmSTiB&%DzF#acr#zhf`)W6D-h@cN zDrWOtTKp*$p{35STMXZiz<+UM8$cInBv!r4Bu_05W_>LiAuIB+TVA$8SNt&L4gP^s|(4Bdm~{8dj=$vUrVDx zv?Y{8hp@$$ulDHv3G8httQT*IdV$8XG{YVH2Q?EHFYmB`C`Csuyg`XZQkI>!sop@# z=6x{Z_ChYv$)#C`t2eeY*ydTv#WxiepyVEn6e+M|SS76&l$5Pz1d~$Vz&h=q_LJ)| zZ%sr{$B`RWC2lGsCJHJ8%mm|5T6}I|WhsmZAjD)t^lA?T@(ez~D@w-pbz8qoDQ~8X zZ{br4H?Vnk%S|JxmM&?%{Krih7loDL6eM{<5jvj~>odf2m9<0djGuTlOmO^K?&C}P zCS7mfqy_zxDlyQl<>f>aodnlWf2^TS+;EaoyNgqz2w9L}ji>im0Cb^CW%v2{8^NO(*aj*y z@b46!!wTggIjq0!65`}fyY&4pOP6NsPS5ktj%j>8aP~=5NaxR|@k3$H@NZj*alRrR^w;n$r44-LuDj{Nbx4qzjX`*eKsFntipM*5< zbt6u2)BJp4%LYt5@PMhla4q0jb}g3d>Ya{{kEd{3f3N2_?JCH>Q{j@HckeL@Eva6s zXKnIS&J?jeY_G~wc(tqMW%ho^5ztR)xmfmSsCKNe%Z*yar32gQ8iJXD{@=P!{J;PU zuK<(|X(0|IXyM%r(X~T`Rv=G>>OxQa09>$)CHMwd*6k(eTTBvm{nssKMp+o{9%b=r zQ_lI_*%z1%ol@aZ523e@@$dL5W!f*zkXmxzq#Y0_Rmz$LqnqU=CGaLbz(fY2W(enramieE_% z<$FzRosr&Sk`vg?mr&!5wvnhn5gyxxYU9BYEXT4^a58b|{H(|EP57VQ=APz&N|Kac z%NUrA#;>I4HNFWJkJTmSW9n%7UGpEet=6pK5}77Ia00>N>QyF|9BA>tsG%6!+HQbc zhg^;Y1G)N+lW-*P;^h{Oa*hcH#GV|Fl|F>}6<#wB^z=HXja6AN_oJ~Z?g^CjyT)dz zF@L}<>VNh*QD1LF&BaWv=QigvjXz79vi2}byL?OfmZTS^aYA;cf11(98{s`*Xr~;) zkvM=2&utC^x?pVLbY&Xu{<+>UM?5OkcZW7i=`tt{HuFAbXH)IhD1~IsICkr(tqxy) zND=OFuM$ET*63)S9iTCzJVc|Fcd3NDf7mcr@xZXhsIh_c1=lf1{$_^uRFp(ayz*g1 zI^Oc?#rrx;RQ&-DeIYq{LKfEi=iAg_tZZy;FdeDW^MvI|3`w{<3^VA`MBNd=M);oX z>4Ye1IB=I-tERZiXx0@#L)a)lD{`ptP0WkD{5E4dwM*#xDzwz{>owZ&7CdeTYW#Q9 z*}*qo5ztQOJ};T|a;`GUUnkEAh>a@>h|^^WZlif6o0vz0kBNy%0~d(Vk1-HMGuA>u zWsz#Its4&$enGzv*Gxy7Ik|Us(ccDTXq3a!V2iSaCo`rB+_NT)7x?FwkRe73&(J$@1+p?1X*pXDm-bbB9xY($`y zO!`zQvM%30>dyMX8iPNrvnWsG{v_2=R*9Bvy0f5>ThVQ*l+ZQT>l_CDbGR=O_S93i zK(I7T05aZD5UTO;?8D+cd%W=YR7b}i%@u@`(+=poE#>k4hME(b3Qjot_x0a-_7K!Xtq*C&v z)9sHh*X^*lDA*EFdTp^YKzkubNOc{Z_2^ZnxGUW3%TirY<9A#=b`s%ZX8ife*k=Of z(?yb<>)+mq>#pByR(JaQ@Yv#X-6BLMonTdTU0t2%C(W<}bQ!iVapZ%NU~sdQ3@9G=fl*a62ONJd1`7e-Aus>a zCHRSt(84&kiD4E8SG)ym9|~3M#`(FAJ)VMyX6G^q3CYN>k-OX@loPa%vmbJB>V}}xlG!O*l|&kRu2WfNPn*O{f@GK9MfF|G(0%4`VIY>731id zyArbaW8-Jv6N4E`1`g*BfoLdVJZzn%(J|OJZ-%s;tMLeixn4tMe*ZP>+I3X_X{lLX zQh2CFnwo)+USNwy<{6A2ayZ23AlHkr$Z7^%cXLtPhm*xegMQk67 zKt3DpEdKbg+A9>JNu3l14dU`;2RgCxK<m_GwEDby4iJyLVH*OF^MG-yY9%u-2oM=X>@m{P%D|=L_s7mv*sl@BA7{ zzLG{5^J@6A*>$7l=)NZ&96UUA9y^Ojap4pMvxNE{tWQwO+4N*OB@%3v4^*v>zx5XM zO5dKJpNHL8-$Ni72B6}6i(eZXdDJw!+mJiA+=*ORJZx>x%)|uNqVRU;z*KLkNZi&L zv21T**n2W0C4?#sY#Wkn^OlD#1jvH>W*LWnROb@Yw8uI`G-+v#!WzGkM^r{)!wRKp zloT&>)-#1OB;^V^hGSBSa0(^ty`nxKkiTKe6@vonS$BzsNX304u_pEMqvr{_sZA*_ z`sI!5(P*`Yg+EW-Ow+`g_>pFGo4*@6fq_R{F>>@d2k&_($%%=dXZS^Sj6&cX97;Ec49#5o#A^vA?7t33N}B?S*XB!@F@#aU&7zA z&m~F9`n_6k(zqf&ry)iM;)+!(CLU&Qo*k~49BMW#tRR|7cM_(bbIXc#w3T@8NyXlXm`NyGWi1+HVj6>Z4giq}Pq5nwGh;?A9 zI;D?uy*8>v4uNY+eMNFx0(~1B8$11h_v9Lv?q|Isw4UU(yJDvheoVPG%fNtM{EA~H ze;ej8*~mF`5T-+6m(_#9zKE}x-{DGq^J|72+We?HzLNUx*eJhU)#Dyp1eAE2AgJ&k z9%W{NyIKpa>qA=SVhF?BY1l`=$b9rKVVWO62)acJKo+YSyoQX=n7ZcWOOyf`lB!bBIVD90|mcWKDYEU*O!Kp;`YLn7wl7{1# ztGm~jeKR?JF+6yqwXRf}F@x!kosqG3PV;_c&eT-A_9`S?78i@PEINj4QlHIfe%Z@A zEcF{QJzQU}UPKegVcmR*3s!!R_&adOC9?Jeu{MB7Mz zx|GKtHV6+7@QjP^?*k&Qi@u*$ey(>vrtoIJk`87DCTAlaX<1pxt}TLlCg&Kx-$Uu0 zwuh^0;=Q34V%W5mB}LWoMJQ{3Ua*F<)XxPyqF2$;q1%Nkfvtd$d89ToSAD&DZZ>_? zH1Scn%RB=l4DGR1{bqZJW%&hsHB*z5;;*mv@+;KdW&Atew||`2kc;6AlsZA(JtOTh z;kBKVOqGB&Dwpa9ApUE4bAUl>b9vchiSBrWF2ty!V#MJ|#m;p>0g%!GrBlY^?eXW! zni_$+4W9ye%~Bi#+}%|y$yF1J+*!!WIlf9CckiIU>ic?uzhYTua4|Ep_ZXS(foz+j z_-k>AS(-8JBV=`cm;F>YPLXL;j?r$!Z z$@zdz0yJQN%gm;# zp(p)*E-&L5VZ2joaiXuR>%j;ecnzU@`hI;B?s2->qk8gMetViR(n{jv&AJb8b3ul| z?(|+JzJJkA4&jOnE4brxm<8cw&CRJZhTr%0?Uc-K@9WF>`~+Coq5~jdAY|CdB50Z_Gim>1K z-+j&X>jQ!RJ8IH-^pW1p%|3A}|9vB8FCH=d8%*3DrJ}I(b#e3}+UPRtOyq=(7^O@W zYq>dHs_H^^6bJDb?cj-Sy*cM=aUB}G%`4U zK7z*nHY8Q`fnV4K67ri+c^85F1 zn0{M}`Ekt@Nm~8$5ANrP1Hs`j%U3mxpBMSAva*ufdTiwDSLQHN)`3wN;>A1R*}{aL z+o3^~d7$=JUYm42CSB4wY<8LaP9LGOs9{zHI6WZpXvnPSL^xzOZrcX?R<&V`6j^Jr zf~^^F0@Bj+%4V&zvE+uBR|zaf$eMq;x{G6~A2#E?l@frpGNik$MM<(W4tf9b;Lwjn zq*HpPYFgCk4OBf(tb#^Hf?R?r+>s_hYFom7DZt2&Lq^n@)De>qe{Ld1b%M+=aqD#= zOQv5`r7t&ekj zvH|zj^_wFqXBrvEl57LdS2OLE{GGWYXmB3Oe4U+*DiHSE#rShq^$e@DB$noyvXo(# zB38>S)c)AI)=0XLx*bMHzR8+1&Jn|2>VFU;Mcl@lTvmiM=>z!VImLbKcD% zZ{ZNnyeSsb&>Mc&PaD1$C*NY|RZaY#Z~M>^Dwi}jdjWkwXK$w!IKr^e222OdIn!ON ze1<#O^^Ae$!~TgJ*6NM7hQ7ia4}j3JOZ4f$L=|kR0J$Z$iVzi%Mn$Hq4*9i&{z0>A zUo7PiwzIk$wPU@qGli!My+qqL2V8qtfop%nmlUizVK6v!$Btq+9}~nVxFK>H1d7YL zGA~p{Rq@MICb%gSzmHn?qZUZsc11kq%{1&sBgh{?Z_brad2)6 zJqgI=MtbjHK`t&zU)(Tsr4~s@j>7(}X|Mc#gW!QHiUr?p9HD@X&lk$GT%pvd&hc4aeY~x{Y{sY(|Xj@1t?A+-CxZ_yeQgNpxmP1#yNDiGQ&+Nwm!^+fJnBuyoBOBVpRm>OL&%$`U0T@i%L3Tj6VwCgXN zNB_KIu5>(f7B6|Pwbu=kTEXIGXbo=Z@=nEY(r)407Q#EC9l#6!u=R@bP1mbIBeh9} z4+&u0gq@>tKdXebUAnzEPK_S&!!41au-@nSXRCmt*3|t)+2-3@@{vMTQ`1S1+=pup z%RGvjQ7E|^hcT`EJ+aV;$2xy;f{c!^aoORi-SER~KI|)gajzn=gh6rRW!v8EYb*#^(aZJY=|JM^^J?KtWJ%vwAel(vhZAvxb$Hm&@?f zx^C#VVmpU%D%N^%1`Xz!h{+v=ILN$88#r*kf{ch?TmMheVU~l5UNo7>5F*XrK0D%6 zlKb)7T}S6tN{I(iA&L&-x0{KHs5dR{(JoROB0X_zS+`J@m{#=w6oS%MkO&E~(Xc&P z{QPuK`uqAiOTVlULt#v&4ikR?DTm(iNk_ZAiP6B6l6XM3GwIcK)u$ zmJQFXowA%F`pk~w$FC-PrdO;p>j7u?7lR7?Y4a_4a>Mvn2jOKila%!1BVdee;E8fVAOX;-PR*>IOgB z_3g>bbBJEc={*-p>^JfIs|vmmHN$A#t10_1EGDM= zJ*=(Q!1)4t>V9ER)5{9zJYBPz}`$^YT)34RAa`Z5FvP3Auk5-NGCu47P{ zU4!e6)B_LrMMXueFRemLRC~Q5=o0%MNr|QU60LUh5p6v3w7>|T#@FQ9Zk5RGlNUkz zVt$`vA2EM~4Curq8`L_hgtK>ZmwVtL5I65Lq2+(<_6-{aDu=_~>Zs7z5k$-VD&Lwe z(*5HD3N7mrM@lc7o16CvJob2k2434;LQG0rDpfy1T?KBxGQ0%%+|A?XmCf z@*vF9l2d3umf?RTUF<0pjkdLb+Rnt}U-lPXK$v+5?L(2RD1T0f(K-0P>N?arw%?iw zuzh>VihoVgiV9$c2<5xFRyKl!aRlLHNJ!U@tkSHeoPD<90LPCJpFQf?(asWRb04!Du6f^V z92f739<{zXQ?A>mpV%3|Z)$a(TG^)^-HiD<2Sv)+!qO5FyfxAX8(O5%9{{;S8)hi& zF0u0$Oo(1jh>tgDc2yc2nwnAw5nfpU%lyDZ&+3f@fQi6x^qK8-Z*Yn;3wha_;lO@X z|2Fv9Y{jF19(JG)#>M`D888yBQak5VR}a&PdHV6B-+uW?E&XCAtHbh!>BMu$i}yBw zb|OlX-O~BGzy*Um&)j?C)XD+olE0z~e5riahFV_Em!zL?|kJSy0P+o0;1yn|Q2VU4Ksr`+@l*O!dvbhI9uG;jca4$2ZM?a!#^EvYb zTNdbt5k1eKqu-f-8_%7br%9lzLI7gCFz>gP!7LXs1B=m)`&Zg0tDoFV6R{So@9lj^ z74t5^-`^jAv#_vhhWE^A%rbB+Ki}Bp`LbBo)8)jua)3=CWMPd#r@~h1pB%sul9DG( zSW!m3u*=eWU$;CH@>l@Ydf5))Mgn<6ZZk-zA#M3zP(wk{|2Nc-LjtLS_k;$7rm)AG z-PrIC0a=K7{N(qu{dwy%XP(%vNT<;JJ!2JBf3_jR9;k`b1d1dQsI8nje$H(Di9(f)acl#h>wn&=rG~uM3w#h{7mJVjP+q2b0CWcYrCS~#`Ls_EAJ$S&us^aO6vcE-#l4M|hy6nY*Tp%R?L8BjnaHcsOdvT|`ZTzvGqnXWYO0{(k$T z9>qhi>$=YCJdfjfJV#x~MjzB0M){`6@-IZ(6zZd)3NDQAs>&GDc!OD5SjFFeZ;4Mb z#s5OsnB|c$QawR~m*%^fpv=p#s%Hf{|HV2TuLyPpu z%mX=46of?w4(?7FUF}1VwmVpxL|HV#Dy(S?mJY5)3H%$gSo^lUZJv4$%7n5cQ0znS z4K$O2KUP6@_@z-f2w`b9;O*(zP**qg;&bJFrC!}MK5vwkF=k%T+#!=d9*u8cP^2AL zi<0C%n|EVrY;+WFuVKEwIg#iN=r}DD95G#--@3t7i)OwjsG7z1LN8%rQh6bERUvfy zrWNmgxDksruVOK4MRxxb^RK7l*Y$rNfAxfyGN1yGn9Xn_IPr`=*J|#|BYDc+7*}g#D&b5D>=8H%m7fC)uA>2CRAUZ0f6+ySfs-iKb|6YgMhXagI2@d+x=+<_=PaUc%3G zPsFBHP1By=+n)KmqA3ML#g97PXJ%&F$O~i^2Cn=+pZ5LuiQSp%udpC!I?cR-$6!)H zfkU&g+X!;zDs5PJ z`1ba8wEzvtRg5lH1P_xMm-&)g09Xy80N|A-mYAAGF?;?$3>Gdqp5pEjSwyrg`0|(K z!Bx3p|zY!;@dem%cJw3DTp#y{Flj zqH;4uB}YGxRdE!%-ljn_lxV|NLyR^71l4z4RwGy=O7vv_(V*;&ORM@mtcOXaYx)BF zkw)V5JTGo$I=e1DTW`KkD!oAA?GW~drjCxWqL$Q|YjS988=-gGpR?&sZ(njO+vWay zgz!b>a)e-S=@#Z9Esap(sC%C5+_W95bVZ;SIybbq0 z%;lO{Cdrj>IZzl`VyCP`nY!@aRjeRH{Vdu}J3^s^W^Eg!tF1(ZQZN4)H>do1+A-_E$!MC{cEWG=aeM*yZXDuOKsE~ zpSd04dfjikXZ-k0yqBwoM|Bl8>CDmuHB|rt}(A zJ7ZIX{~=U9sQ&+ZhK$VsqmtqfF6uw$KJPHJ^fIwo-G`g!CWn>Yx|_5H;nH7dF#~tk zbz)99`>Oad-q*|fORwGCY9^X%hcIOMo}(w+%7&&F&p)*y11C2uGZ>$^1jAUH$Cq%P zkrG*0NC+|%Y#I}Se#78!ZwQsqA7YS^Ww$DfV@J@bu&^ZdVLJ=`6Q|Z6JMbY<5;H-Y zvH!}qg{H6Q1ZPPPbFFbuqhIS=w!3K|jPG}5NB?!ZNU`?gu5Nv~_})V`^!^bYcDjo* zZb<-N8tU4Vp5TqM^fx%lrqB42a;6*MsMaBVO@xLT+S^UYE+^~xN;q6DQS#&b3x&A@ zC8CGNyo^|XDU&nQHCZwBwY5Y9#0CQr=^yuNMdy{swQ`G!iq)D)2p(xETB)-Lf9IlTqFE3~1N(!| zUtA)oYya@6&6GgfzNEwkjSbyposi;q%WRLh;MtQ0!cNO#ka8wWJ7<1ebif|1=BFDb7dL@U;QUB-rIVMR z#w;K(8_hAvENRxgV9o_7m{sHO*6Z_AjttQ7$$W5(9T-Yv2}SQK5?gvnJ3?J%_C91z zi&9+bm}A6+ZdU}!%odXv$@&zS#S;Rz377kU0JuV&!a_n`sRh%e`?O1mve`M+TkA9< z#`d$%8RXgRx@p8|EMjo5Fh>{9Blz)DRZNQ0qSJv$yS2I5alU~dN17#k)%>W6DNh1> zKU9ZGnu(rw7bhnR80{8ATic)Oop;;o`i_1%&f}&qi0r0d8cx*neVwwSP^y2TaQ_?L zXC|IM47_e`W)2;zYNQ{}$Gle+ncaWV@;@=B** zDU_+13_6(hm^?y4dOA8*Sg`Uzg=bS7#mn}D5P*j9aePBTiOwWCYEI)0dokbIquJ@{ zhV?*G8^Ui4nDcNL~JC6O)Xw|zr>Glo|lw@Syzt7*j zV=4sm+fS0s%pW9tH#RoZ#+BwW$_xgF7v!T7LxX~4S^!1x5SG6R@Dm#smq**~)zx?e zeStMa&yf3g1{Qun^qu4vo^6FVB!72?hjWdT2SCkc%ANqStv zfeFkvOf`2PmZgX@@h5N^xGIkfC3=w-m3+&tgsF#neo(@;ygf;O$i>cn#E3{%hnYtj zP9_&;T3XuaUeh;;<_-Vfhg3&Ns3>hKMaACFA32Q_rRr~fWn&85u4LT!ff#Z`m?SJs z%Gh^IJbLSkk-`@liV?z8Y5cGm-`T2 z0DLwS$_QTIO2Tl^lbylHK5gV>j1tDZvUl&&Q!Iwm;T-vr-k`2++JuLnO>x!vJ7x+e zAQR5GO_l>Gy;b?T=9G6g5@sph-qF@eDV_(m)=V6&&JGSHA3yl$=S_I<5a-iz<72oG zrZvO%g>?g|1G57T596D-20EwBGIF@=sK5v!#79n-g5;_7J844zOpPk3AxMsFxNN&V z42WXoe;>PC)+|cwqr#akPYcE3RZ(e)IdCIN@X!hUey*jmf!C9dAaq$pxPCHqKj8zyo?p3{L{1*WtLE2sKetoBNm^9f>l5IIGpXhv^)eVQYdut(r zR&K53<$LEp4}lOl$E(2o$Lu>?OS=Q((kRo%kRj8}>21qlb4_=hy)(848x>VhowKY4 zaLAi2V8q?|+P6S2;A8Q14}6>m0~GSl_xcFlIfbg7$h@0a2o$;H{pq#`|&B?WWm_-~;RZFORY^ zdQxzz9yC#i0_3$uXKy+`9-fo|!bchr$A|Mnn9C~l2+xaLfgb<#-|l9e+f?zp+l^g| zA5rgAGx_txmtXHVDRO#Zdec-psKw~V%XgM!`{^}Rg{ecf2+|~xDgGIF593H5YxGwY zVzRFoOY{3C|F)1NM8}||7x$<*l+kdXu;~E*MecTh6fu9)(XAhUtf25#o)(Y^KO8iI z7!`=IFp~ms{ZVQpaq*2(lF8>&uu))8a&mIEx^9kP>bJgnZzor4&-CfPC>E!*(unXz zo9Jlcd#)RRr;aqcD?FB!MKc0>RxUQSZ7{+#5#sv^F*~t84Z!QQ z_r>4OeSe(@Bc;p;o)KEsgvLQ+(4p9CIMPI(o^snOSXMwGDmYB=(1y6A-rn{B=2udc3cK`hUzrKxG2X*7E#^YU^sQ#c9-lX&2x)$+RQ zQ!=mQ@7&FP6F~hu1KnkTZ~4!RKN0tH#!0|Pjyl5Pp?L~-;n^PEo)caJ4w%Nxs)yvn-9$$9dS>dN8k5UCuoEO(+$h&41;{k!emRX zr=PG(Y+lEWz1)dKGRDe_E=IY-;0w!(a0=$B@gAa{`&U8%u*|g6@qVO-)52&^Zf?w2 znMPJ5jY!(be8H|kH6Mxli-jFML*B$m4!*hiU?zS7ecOXi`_vq;!#|&1P{yNBG{|xy%wcGY6`thYO&A3 z_Aco_fHH$bDT}U>BuC3^%0o$7l!3^(9HmZ4!pS3boF+Mr2%#XOh=Ph&Dp*kbI0Mo+ ztGK`Ywha+2zN4bjqMljVq&zM9;i*DNe8gD0c1_EZuPXT;X^&~{;<+?Y%A0xoC1zer zK=3{k`56oO;;0{vcCW9m0~9fyOn)oR!_DoX1PQM*mPur{$zau_Zsm^oOzVoKl8=p* z@>z{kC(8VdyHKfULPQDab0eYS=)&5me7>4|3**(z;xb6=>K@%VZIc{A-{J(G05130#5l|Q~2 zgbPHG5b%y|aJt&R^y$+hfe?9Q-|wp_M*p79&}C)?K>G88deM|!@;y=K&&ZgbRxrC= z>?PEEEQ`AR+WGfz69MGJ`M&~`|GH`#EPs-O)d>OC`HJ(9G z`9W(&LXz3`37TI-nPeYl`76vP*R&|$CV}z?U33b*kAHrG;U|?i+1)m80`)4|UY`-J zQiK~D^0DsKv@@BHZQxnFHCd8SPXM}r6&eMM1=^9ohJ9d4ONN7QOwpbhxmi>G^{zSG z7?P}z$5ND0mhwN*PTU2;szgT%D<@V8vS-oUV=M!-F@7{0xP{WADCnPP1v1Q+6#cVP zIAV?cT1Iy_R(vR%wTSze*uK+D_$Z^oCnmjeUdc)sFKv?e)eRQ!q|rx#Vx=-bNV$IFM)e#y^3U+*bW%*@ykVq3y=SJ~c_1b&DndVdpsOo8(DuIp zHbItBoVN_=^f(#t9PpY5&}eawp_JM&G`t-LRFs5n$glIyBVVl1NGzR9A+)}JU-ik_ zbe7NQPN6*Z>Pwi|0mlQ-H&{YV6YKGvddQ6Tqv>rEyj#w4=0L>T0guOC(KUrfNMI5q|;BJv^?j3_e<_>rBRonST|!6t|88Wrw^v=F6`cQDuh z7M`ybS+G+>{hfL4Cr(=mpG&vkq+SSqN}7I9(cfs<4vKs(*tRP{QDkNekwYLK`{^)O z_f9ex+m-v7S_awjhsh2CrycvOzP>(4j|upi_*Z4ZG8cHd(azX%4uOp#hF)Ooam2ZC zIVZ{s@WTA7G3ig?Fr@uZ^s$+nN`fed1LRPFU!_nIvar*Lf=a-R<{iq&re_0bvB1B) z;zVlkLv8<=DmiK%{dRZ&dN}4JvCRcaTr)SfTHUK!C>B@(b_MkpP%Hux0i6lxqmSG)Ck#X8)3VG z8j;Xw61tnim!Av0jVef2sDHqy=dFK)+=~{`Jk&UVnc3BBC-Ugg-T`Ay_g4a{pn; zJb#N|$(WKs6MJb)OFRc4lYNrHAzcmUK7}7@X%*&HxB_Ohd2>0KnHzVXK`W@gJ zdHl*8oIKkr{PL-b7;jThQWg-X)2N9^J-$@J8lzN*3e$S?g?C6{glqeqf+k1)iG!?Sy z$B5d?!TxtgvXGCD6t?SI2@;wfm`**A5~s@gd7eLYc$MQ2yuZGojk5w546e03rD(atn%Am>3`h1P8;})`o`-y_n`L_Y zFHUUlmE2v6cTjzSG6NSpT##)~R@Nmq#u2PlPR#k@RE}U2SpKx*GNM&%`UoN)W{qj6 zsT5MTNrpiqQTxfN+_>#K3{|K5tZIhI(I<6#0eJ}*x`;{D88>KQc1i7&*Y_!DBgFWT zuQ@YS?s0mNLnDS@y0FjIm8m>lSiQUIzEDz>ol{>EudZXG;zK_2)uFs{K|~*_4qaJYu?Wdqbq@7AuSfNT>jvlnT`{d z0T@FuR94Kx{v}SFMicgOU$MIGXXsK$=SJ^Q&d*A{>;8R%bmeirLQR8-JKnQVL*@=Q z+1XqT{R%)CFNRdWoFfp@Kon4BO3BQiq8 zSaLKHvAH%td+PyL8y*@eGpOy)#HfIv0(iVv(!q)__QxGzWDa@z4~cq8RK~4OF~+SB zm6)8pb@79xeiWL&W-A)Th(c@_lk)J++2=@KbOXp&K*ogm7pQJ&Yo$TqPNW2>Q}gq; z1^#@!Fh9Co4tUb+tU)6p6G)5Oziv4`OE3})zyvW7xmQgS)3PiK-)$9PL81M*_UkRu znbKd=m5hpGs@qsnS2rpg;gx_|<7yMQ6Qr9=2Nk?U&cJ@hax4G|oZx(G?U$btjHtw# z1XnuD`;1JfcN)|0Sv<*n8QFv7WFp^Bp@*vX&wHU$4ev#ZXIfZ$;D$*p&a?v7<5yd5(imjO>3m3CF8`!3#0@%eXc4m78v|v0x`7o7IC(&fuXoKu_m!<(LBLFjPb|hK9g7iNu-${cx z-S)Km62wkzTixtH6zCyxe^spPzDV!x?cMn1%~sR5R?SXJs)FRM5)L5pT@!Ef;N;+t z-XAkTF?El50bL)SGBU+`k7(+|yHEdI{4hm0ot!-$84R0G_9hSdo|n{1uO4ouukkyu zWg&Av!0;$NJMMI^s2DZzq`1Y97}3^Ns0^OmiWDoeYFaJu*4L5iygaOQ)ffJCSUERL z!I3z>7aM6DY}V)er5h;`pLpQm$%eMhS|vj3x~mxuZ_V>Hvp}&5mq80pPd$I0VmVpr zJUsA03F0fSL`ODbd=~`O1@L%cO3KS)iTR{Nkno=U?@*PVjaqmdx>Qck3oHIYX)bRz z4cw0?K@8ns7&iW^-QLqqN?CablVlsZRPew{B+_4BaTH0AgSjMxsM9kcXCOUW`D(MC zmtGXeEW94FG2YuIlxL655S@%v((*1ZFa-FDloJ8|sf|f&QiuLO*GPK2(1Z(_gfm)S zXNB8SyMy@rrakCV0j9e)j5at+pRzdD_CORtmjro zR!Wj_X|b!+aA~N=_&9;Y^&gV%_@k!^J)Ey%y?s3_-b5oRluHrJ#O~z)3!LwJ2sUV2 zc5gmM80on>|Jwn@H8I(-@DzqZieC$-aY~^UY;OTm{`Osvl|!Cj`VRw}VN;%RfM=^u zh;pOl8o>OXbOg;&!$@6S{IQ7luM-Y>qTJNyyxP+Z&aa-0^|F0FUjJ=Iox~^cL+lGM z0|7tK8>(0Dc(ZhLuPG)rosRjw2 z&;JHfLAS=nOae+cHX`o*r$m~PaG<@-@v&w31<&YPgW4@Fiow{>wBYxWRri>{X!eQ) z_&;vJs1j7D|GIs5>607yhpzLL=FP@Lh+O|6_Y$@#AWJ`NKE!$y7Zif4{SYmCk^Y1P zo2I!kA*j|dsr`9{T!j41F!?1Zsr61r%ZT2rN%?UU6x2PU2GngiGkr-bNqVbP`h=Mo z{c1)l%%e>Z3F!@wUqVD_aU#RXj{YzTTjjK{euEgw5kVz%L{8VDeP_@?KSbn|-10rGXgGM2wK&>6hqphPVjT4P6Ye%mny&A;8=*?qh+CQun8INBD zY2UBS4{nkW8qFmODrn%CSYUW8SsFSkWo7UnB?4~SD=Snxl@cfhR%rBJCOwj8UqOU0 z{@-`QUQ&QAFWNeJ-W&uZV66(-YtMT=e2~xzrSv3|e*G;Hd-d5f!cwCVog_;RC6SD7jQzvi4&ff+V>I=z`(-cP)N1 zAZ)U=@MbbC!Zf!8n3MMNB_egrXZ8uLrJUmAq*y3JHM-4s&b1@Mb+xAhxo&M4>i$@# zc#N(xQ@_ffl6_Ym(egowW8_e#?!+b;@*%(3IwG=c@L2Eg7s@|0pM5QGGZjehs!@E- z6HL81Qm>ehk(M>u9K341ePas}f5FtZc#emuAWV*GLp@M28i!?Dbhw&cAhDY`D`1diCghA zACA(MCNYzflS8H>Qh2-j*e=Z>e-$E@Vle&o8LOSGJiB7?o#cv;@DTj^*Xe;DUKcr% ze43ws`t!ijASn7@HY$N{I&tnkQ@>{s?iEtsF4uvd)+0O#JGhPD4lpMvCn@@IVWX%c zV2&kfx;20_9u6y8;`XtTNKI&3-Ie0#BC#e0$Qe+^lKv?9#CP+8pOiVGDP0r!ki*Uw+qi}X zyug?HYDtkJ?5o&!aNkBt4zFkx|3S#21&S8iR<()Tm!o?Cp#_3YvpoBNp=rJ;#-QR5 z8m>k{7x<@uNp8E*crNU$E5>|k6l?;|EqMThTKJ%p+ty~HE~OAFe|7D)Yl7dy@OSib zR!b@Ya?hG@g{JEXlvGrJj6+07n1hp!zkq`R{FSblFh+0Cczc^_1lz(4%NE=JuPJdJ z_=Gzkhc-ByOI6tqAM!>P+?_gk&zZ#OzRW&(St3#4u&wRWr#Pt8o8Vj51zz4LWmQ$l zpWkr{RvlGbsg?c~L+c3JH!RDhe7$3VE`uUNixqkMb?N){SAKlfmSfVtQM{C0>z$eY zjaMUQf>J@Rvn6V4>WRQe#kKE^LKbwVsV?|O$d>sOh}HA-JIFpdxcol=r-b@(U_uZB zcO3s8;9a);J}aruki6+i_P)hBM+kXl^BVAlrlZBE!|0GPk%a?^nu2I>BU+#W5|YUI z-|HRRQi`G{s>p25MDbe0w4|wV^C<^TgSccE%el>1HA8{CiOHb9#=sl1xVU&5OHG&d?a!g75A;NS^{1_EiJ!m( zho$R52Q3do4}mE0q93X!`?o|xqcPT&K{&2cMmmcT?C*arrvC}N5CoVhhF|I{mqp|T zR7k17p#_`5*^?;y@1`qLcxEI6qTZ;9QKzC7fj2Ru=+c9fGww2}q^K&UD}vtxE-!rYh~@z)K2{>5V%%Ptw)H zMR&yFW%|a_*<&jplI}82K;&Y%>>v7(s=&tDTmv{3sDF4K6@}^Tb_id9<9AbL@M!Z2 zo=(5kRmbs^4;!i@OF_maCXnccP&^xD(PkZG=QYUrle+A{MkK(h^gj1J4)oe;1Rm1& z_@zz%Rxv55sglWihj#`!H|Y)plI7_CGKg01@$jr{WV5J|yQPze(#DEBNeS>DdJ?Ml zq@vFiG+M7i`f)o>PlYU21oE2YpEZYz70ATGE=AKWt|<`6x{E z?;}*WR8BQ8BU>U4K>8gT9;sFGU!_in$$ytR#s8E#UcV}RG2Mw#26wcOL7zz(TbUyXz zQ+PD<#+JoBTY08qpbaDL;WRz5YWwcKXTtzrNd!8rwVj>hQO7QG0~j}?He&}mVd?=w zsA>Ctu^V$DWz|CO2$ae^)m;3~$M0APJfg$BK-iW@XhZd*sv7qxLPrZf6G?batuu`N zZJu}870a54xDjs{6oIT26n5MkeN}9g0*gwL6_zOdDJ>$yBBGjn5o&7WTE7rhz=126 zNtJg~c=ew6e%NWIZqb9|7hH7udyQZkqLdfTF2U$(AJLT`L+I)5K2;i7OyPS5`lzWx z^f4@@n!eWQ77?Hv+u|9^_urMTgKY_+dMc1#yDV4+dy1>hMv>VpPx=yGPZDUM@P&tu zOc?4i9jYrfW?dn0I6m)f!z^8&D>5yUFGFA9e2Y%2h7BU^j}E#21?wQaC>P!_Diy1i zJqnyjoq6$0tTvw9T8fPoAPfo*=7*Ary6Pi^Ys^ zy^NgIMTnq_iQ!6jBl39?{4px^cEF5<+rhUUWA}SlD+mRO{#}T4%Y3|+^)gkoQZX*R zp5WikXqTXiZ{q(6y7-dk`q8niO(g0S5&T5nNb`xjF>C#sn~4K#$j^a~VgF|6%S_Zi7m>>O0%B?KK$1$HlW;JiJ!!_)*3n@Eb2M5XvY{s8^e)cYjUho5 zWOsI!W1Ljey|bI&<}ftqdMFj9<5#t{3RnebI%CxWbVH`r^rbewef!jHv2K8~s8?J` zJ7VYUmhI;K+Ipm4aOr7q7_7?4>}j!3eGO`7>Ah#ybuNGfHrsonY49Uf3JQ$$M;w&= zP0SkdVl4*L#J8r%y|}{>$vvVtDnB^9GL>qgH!&-`-#K{QBa_H{a|}hhJX!?1?^q z&MjFWuYXQW?ck>< z2|J;qJV&UEL~=K&b%}b#tbe!t`E_4z8M3t8u_lcZPb@%0(D)GBV*D)2-O zAx|3KYyX_ExX8a@UaBH=qLzm(UoJ(Y#lwQ9kIM=!kQn5H{MKu)Co^xqbkOi3~sTjAeEt8Yp}1K#DP* zF(kt>JideIxUKsui;E;R;hSnYmX?>70cI+u*`&1Owy?HlF1Oz-Iw@5qRT@&4d2$}I zt^ep@=k7oP!rIK1lH;%?Lb`>k>6sI1CU7?VI*r>&FV) zEy_qFiATN>g*4<+vr|)HZN27`c=OOc@&rj<#UtRY@pSWfYB7EF2XB0U4VH_XUW_~s zMYRPrX)<0QXxF;MV@@FnNx>Y~xdKWFK3SBhP6LYyJ_5UbbIIIza}7NH$t(+PWg1e5 z^U4WmBaPFv{N9O2Dq+cN8^((acoozG-7=#><@ajh98`LL2e$5l*)-S*CuQL~eV- ziX9R+#ap9YIFi99h?sEF> zL$XsB!^?g@qhww}~NaJ&!)h)cbB> z3(L0`9ur3%5kC^UkU%laN1b=HI9XC1uG&qh?*~Q;3DqPyf6+RG|0;MS9jso%X68Oh zDSTujBXO_c_o23DbUdSH_En zoTs9n-V!t@z?=2Y1@1D;Tx3tFykFUeeh4#GI(MrhCRDaC7x9!*?bcNy>I<{tEnr)u zeJ#!YqU*Kg-bY@(S8ov%6BtCNGRA(6wCtn48%6}%eyqo1o`|s2_#3onuQN>VQ6dIt z^!S14Ai0A4=PJx8D%frzkqt~NKD)_{o6~7)Qr&ynj1q=gn!y9>%I^>*-27Yv#w)L2D^WuVZ5cn7D;iWQsWZ0n+@VSnUkNFZl zn$n}ReH1--!TL)SHF(7wCK_W%M5u@}ne_Vq^hYmC#E|3VmpwaxJ_2O8K+j z0IoiesM}s=M`1fGP_(9AXz1&^&vOT0q(J0(S^ye+0-&}^cKL(d`>eVsN#Zn6`^92_ z$_+0N@YaLvy~s0;W&Qo{hj4wc1X(}QkhhPI_TIx&9msr}o`z^JSrO9cZugz(%7^d6 zfQtg_KPto8*w{Gs%V*88m}A`%iVUETwAy{*)Gqt?DXby5NGm!PE0AJ)B<`{yf*+Yv zY8+Vy3(X@sD#{|v`d8%(l21?-)MnGRcz&nFI0Qq{SKjrpNN#_kCt8FfOdaRK@1Hd2g>HUqM)iW@l zRwZi){Mnnc^To0(nw!#XoS>hs`B-_?;bX!VP?5fT!{H!Mg)M2i<;26FM%n zDl77FOtweClj|X9s*kvcFjCFsm70s&Y?d4CzBjRbjT8kV37oYQeX!ic^^phkwn5Gy zK0~23X4^uGm;gK5jB9Mv1CMADEH&gQdc|!J8iC4N>ZEy)cK_Y%H)Hw}{3a2omZE1^ zSNa7U=QoN_)yI=3z!ZB2aRXLOSdx32#?P+Fv5*iG$9H^g-S=>I#*+}xmNCOvw=)Xa z%qdy%^w7B%w!9Csx%gc%I%YcKk^{U~*REskAWb+*_v<~Xb%ZoDcXwiQNs9{7Y&Dhb zB4W%?iW@zaqEV?a?2{ImpvTdED>bKM8Bs^&U>KuhXKOq4}j0 zk_K_B$5xD$tTwgAcqqItpv8>J^##Tu*ntC^^Tx}_(JwAY=n8kOjve8$sw+!z0Ni@# zwhi0^ZNWr?3eVaJ(I)Mx=YxKbTbz`u;zvJ- zzIzK?@TA6B_gx>6X7gIo21;ie}xa^+;c*lZQxlHPDj56}1cJ1sVvlcA^ za~Xo2q-Q67N)GTUb&byrraO);c5XSwSK>f&WDSir?UA)ET@z#F8{LpR8yS?0P`}M( zz!MP&B_OhFj|y5a59^s!fCy)4E>8^YVNL$aX=7d8N8b4;IFLi=I8)V)xE?k@bgIGe z>r}}SEHPmy+H1NZ3C6K1iXXuNV!bG=mpriQPNx;ru#r&c49m*AW<>9En9YQMZaVUv_9Y~2#j8XCdMhnT5CRq z%uXPcE)#A&*PCrTF}E=^GJ5u9JAJ>QwXeJ-)Z#I&xszWT{}b1(jIKAv-+>liquQu9 zhsp#Ilq#q4@l%iN%v!jZDpGgrzNAh0obJiJOS*hPv1E!wrG{mgd>m9 zEq>QKE^O`W+=VPfRJ_?o#z(iHMV*B2f;x6{8DxWy6oTA>H7svGRqW>H*9ta?@IU*m zXia4m%mT_pmw^715~-$9X<~7zmP&E1uTNgh*_VFvdwTH+abk;jek~kygNwDOol+XZ z9m?Vlj-!q^1qk7!ar6%G1l>CXZr;iE)O=lB+^1@$^vMOOX}8z1zf>Z31PT}7)>6*R zt-U>&2e@`N6cDPN<4TPi@PZ}NP1$UB>(yftMEqC#p3igfy4YmMnk(D-gh9dC=^CG; zhZ_9-8XDeFB^fcwKW8bOAuxiWmHVmL_dg!2|2}kUaOdQBNR>QMOA_HEcA3Hx^*ZSK z{`L#{LymhB6^-{`BS{On^&L=~nVA_#V*F-^;-#hF=-2Q?5oue@h4E=vdwDB(cb%V~ zLoCgLvGVzrt~Q{FI={SHj`;}O)rw{RxWyN89aq)VAe3R_Oar{Q_nt`MXxOx!W~s}w z`!L0kl9En0d);P12tnG~UYbiq-Q3AWPV6_=Bq1cYXD&{)_(;36#eTklCt8N;x`N3k z6NP)@1F#*5+_?kYGdURXwxl@6U?d{J`ztd61beloy zN&piVq^@`!zAl=Xn`3jQ#!q|s^6i^9vo$v3u%M-eAd;1sh^Vi!eX-ESoHFbh0K0Ic z2U{jI6gh2Aad0W9A6pm8P*sJs(k7ty$NqM90uRhk=uhFzdE|qt*vYq6X`rr$tgWvv zKQ|A9`PRlp14X|crYalNCp`l522acz%)GtNp#9VG%MR$COL_r6KeReg+q{@qS3o43 zWwh1k6qqNp^n5gE5fg-#+^@r~1=fe(2oz*yW7CwjaN!SHVI+XCU^aI4kW_+7dGZJB z#H(uQ3uQkU_w49cd>2G;(c4+TB3FbE`JMPjxj6*24f==VU-B_96T9-t+Mg3S6k1uj zLT%|XNB5kA5h<0Xm0s@S9M*+L^2tw4PUr>5p85UxO(`5XU3c%l4&H?%^+T{<+{(jR z<<7Z)Io~RW)bI=8;EaP4_i|qs)NK@iw>sPRfqFrLFLw3MZ$Jf&lk@uLHLy!_BJPTa zu(~f2iY0+jifa{2noAd*p!54s@MCS?gAwHC{?-t< zZPTEA!X_7=hE&T_@D)~TZ)4U=)SJ{VHeB^AcK?0uh0I8pH0|3>_==km7d+<9kz2Z=;n=l7xr}e}%(ZEBuD&$?>uH z1LkR)$3Luoi-Aadettgb&sAq)`Pap~2T!?|x+DpNcf;u3PXte8{&bOA&*+MpzZH!1 zD>+mkKTLvVL&7b9xY+jBD*Y6eHO*CtLk%S6kvt?=H(wo-VKdwE zDOWeU-)X^1w?u|By3AtB zh82fX$eV>?N_9v{kQJ+6wb=zrd)y$N^)YXIVFsNwt68bJF(#(U#b7LC=_eI@RMGyE z;(5|6?&&24s7QeUZ#+WmaT(ckF!clEE~J7sN2?_X-Nidr)Jh?*7$te|1;< z8?i;D4wYC|@TB(*Pa1F5XIDe@sAz5bp9HwiHifxRnS!(xKy`vYi;ZrIzVjI8zrMA- zmOU@Yah|W>HgyYIj-x+`#IdbSProUAfCP;Tcz^)>4s`b?&*LlwR5_bY`qbyJ{J?Vk zI(}}?0Wz#TDCifT(+#zzek`h!liBkW zWG+;9+y}9KN`Jg&Bt~bCQw*jB<;;&rJPqQ z+l8LfPu*fvVyu9EoIs6%fgz>3!64qFKBa{~r(B->Y7iL(#XO8MBzfbxhq}s3OQ&sK zYI(z;Br6N5!4?qP(z2s=dU<)xZUi{7-?tk-7`Y>?Y*zE*O4(0sENs-G^PYbWd47q@ zLmn3=wxRzkc&kF)3&o*M>P)ZYr+YkIxCA@b2~tv0AcS@8C$iMdV{&TZf?nQwt!SB7 zNA6?2z33_{8&q!)Nd-0fMn+kac4pGI?D|cW^D3~mfM-@jz4&l6(FFI_J5E}O`R3Kg zN*U+-M)IX<49uY6K;HdD7d!5P@fJK;*zR3OiCh+z=cu?A!z?y3G zDAP3Rl{KkK595pT?U-!ieSKO-l`g)+cWuICdKz*ow8(L}#T>&Jk z{JpFFf6$Ut|6MgCP4g+-c_Q=|(ViX|q1Jl4b`7&;v#wdSCWYP_hjHZf-J{5LQ*NQV zcVA4->atLJ{Niq9)hqjH?K3rHQHqpu<9MJS$^4-Lt+U(HmIs#iB#FPYw_iC|0_^C) zP*;}}aDne4SM&-yo(|qQysv)_^4Xx81sUU1_uvI>OY@nz<+}n4B zt~mGR2AT?xm7fB_4X^_=Rho}>#R~sNNZV@Mlx~d`^bRiel+#N~TyakE2f^14agDiZgXY zbE#Mdnh30xk3#FH->cZf1x;qe)je`hmeB~11!d_g2R@^uX^>Wm>Cd(Z)ea74rHc~1 zAys`L6qt;$X$KimFejH!bQkXV(-h8ic%Nmy!8v%HPbv!ipk$IAk0RjKLoPL3%yI+G zi_9sFoktejJMkr%~g?*cd)e_*xJ4QS%jy zv7@6JYd^5+gNa9_QNzx6q`)hOC*ky!vrgub)U)sKeu;C)*YMv?xnd?CX29L|prA&! zBV$l@SKlxxfgOZLaEh)&K*^Mhiv?j$X!o!ud@$<6`_DH_e@?CjvG2`l+m-Wly2zlu zrz=>~XLJDUINkH}dlEC_qD!`?`Z8>su{uQpLP8Ce1H?ZGVZilywA%M|Y~?-q(w_9` zwKYq!JMiAfc@w35eOuh` zu@-ZeS>B`Dm1k$~zGY2X%v70zeoOVP(5rO48Kcaa*Jpy!1!*69O+9n@!tN)3RTDQX zgowqz9jLa~<>8c5)SSU{+5F`gO=uYz!5n?t(=458xA`tFuZFHJQ<&A~Vghr@#OcfK zRGjXFzY^g!bCn;`YuKL(x$ltfqI13V7)z9DC8cNse+W!iZThhzqaOOW@ZU=72t7){q}R@Cnd4_F=b$#jpAlrN%|&mk(!1_lksGAmEJpo zDVth~7H>k_0El>OE^p4ypH9imy1nuJK%)9IE9bHW{gqL{Ox+h^3f!W9(+*zJm&_DC zb(!^m@b~P{Zgxa4r3i#d(43Z^op~F4>H7Wbe}iyW9!F%QNzsgR#=v(?`&7t~mg307 zw*Sfu1Ee-qB8+;x{E=@3Tq>wmh@*^+d}U-X+&MlbN+)~%GSS=X-V?1ciRedR2Q|KS z1^mK^8B+GoCjN-E{)?-5y_8{Hs6aa(KaDp<`rQX`SlNW00e%={O~G^w#-T3wnKI<^ zkI|RuPs0B{*4{EIu5DWv4Fm~p!Gb%%-3e|%fmXcY;TvY<&SCa z?Tr0eINv_DRD=CO%K7%N4|;Ooka@}cKkCPY8a)wUwE)^7o6S*qKcDW>_{+oqHYyeZ z)R+Pf=YYpG3tE(RP(HJh8KsAvotbrg43fv*G)- z*<+l~CRFzGV1lV9$a>7o;1s(%P!sc)?(g}aAf@vBd*B4E>$BO}7~0=km7-WVJ2~C3 zE0*{63&mS1L~X9IBH-0Te#Km(ViCh}e-Z4`McDqMj^Y%~-kgy!znR>3yiInvqR zQAo?K03@E+o*UiJ(lUu{pZCt|$y`)s-i?K7qBOf(Vhj;9=Ff%}RxX6bZcIcQkykai zx5Ub(3!6uYEq9nR68KaroXC2FLU#$SMUB7+?)vf& zFGKII2!L{HcMx|4WadBDCniwcW2*Y42t6h}AhZ|;73z8nni^+#+3t#Ro47J%kUG{{qv7wpE8)XTuf8tcZs89|oVsblXOI%P>?m%3Uz)%4Z|smmXC- zPJYSBj!7_-$WD-02kv-)HXArl%#P)2MM)fs38H#kw-I)H?v*|vL!e&9L zibEbE!2weL!{5Jg@$i5+A~T3jf8E&L0+eR}uAF%n<7{PN?}(E)S%cQYHc*{BeFFm~ zHv_+=0+uKd4-XH({ND63mm|L^@io7^1;JB4E=(su{m}i-tR3b`3m|a=wQ4Kd42|%J zt(8D&NxJVQ@F)OQezh55qxS|CiS$Ac(Gf5Q$#0cr)7>21xmhq;fUNU`GRUKE!R7?DKgVLU*Sf?2_Up%6DnVeoZQWYFdKAgL3_Ux%}4^M4mA4e1H!fd8N#%;fj$6ithL~#kTeDy-Q+8z zzwX6cWL&_0xU61^aB(SfdUr{VB@1eZpJAM@d)XL!t}q%wEdVGi;OZ8>&2iQ5V}BKf zrsU((anJ@(we9Y|yS=}{`6RX!SQ?lj(Jk)rZJ)2YF8kOjKv%0&HDk6N-{HCRECc^> z3%~;c;#v7@4U@mx#^^Vdo7-#GPue$uTcG@)_sQ&3_5m|8RtAmmPR3?|J}@UO)vbo} zzg7S*Rz%m<#+F^i&*gOq^`KPx-?3G`r=tHX8Xw0*>?tK8h#8|Ku~ z+roA4sp(&JP%SNGs_#ig6dN(kB?byiciYzi0)D# zas81Ol%UR=#pgB!!UCX8b>_M)2GP@pf8njQEFK1g(B2*2NK3fw-`n@M9^RLQh%K!1 zs$iSzF)gpH1vVZrj!ak)hYPhY8Jhp5K`pIRV4Trt8?ArP zyi(HEW+;ZbELXYty=^Z0+XA#2qoCv`it4bkMpB}d3u2nr=KzLEk8~LRcdxfM>6ORK z<)*wSjh2~8ELngH{n^0407M-A!o639PU&h%RW7K0-r`v(V( zn9l{}Q5CR?;N)UvRsdATFOnlN7+MZdf%_Sgr4^0)kTWbs$QmZ)Mx~bp31=YajLVB!?)D=R(Xu@j7? zimRXGgB$&qd;sV}04$W&>Wrj6d_bxF3CNm`JQFW72h%Z^s#Qlm%$Lu`6MZXR&_#g0 zFK=W&S+QGel3gOV1N!}g#V^Uh&SE){)+ZQgpBMzi!@;>)|MOYB<;f4#)D{#~WV8PG z&@K5&^ph>{eC|sPsATAP=N-TcNX@J#T#rjqhdC|SjMrM|`2=~1bjzfMsnD8!w3-Ua zNp=~pem+5^I6;>)_Ci*W;h>#%uL|fUZ<4pX?Zj(-PyNksC}(|_on6y96mAk56Z_W%P*z5 zT4?n)b)NFeF_EuZrpky@O!POJp6lS%VR6G#IcJGtjXW1j*YkP{KYxG7^X!xqSQG>7 z&EetjZLIoHwn_GR$)+*ZS`F27N0O55A7>UH%lOS`#B=!@H09HCL{<9CzFIQY6A^tqj6 z0|42xsx@Dhst{tbbc{;(r4rcTJtj_81LF$EG(TAx8;c`%3I4^$9|r?mK`lkcS34fr zhY>^bEFPOysuF@y@J(27L~5gFt$l628CJYoVmt}?6Wa+?7J2+BjsA^KU;q|CK+rKr z;|76ko9XE=`=y!3^y&14xhx0ilv?f7g2MNt-MmP=)_)>C+gp7%x6iMIK6jK74PX{c zsUYv5I6sN_N&*M*ua=8H-!Rih%QcApp*cPR2iDlg!EM{w1Ujx@PISC1DBfiqS&&~o-^Bw<90X|r7R7^MlbZmE4ii1^zjFW-$|AeK zHKmW{c8+Bf?rMH6AJUzliy_dxszU@QUv$6_WvzyB+OJ`$gFRb7x&<(Z#^dDLnoU`e zQA0Tc6r2`+M{?m(LXrOKC8gyAb+%mFmw4FIt%w5K&HpRZyQRD8+p#x=bfM7+GePu% zzHSP_-|bsnV<)GJgM*4;(#X`_ZoY&oGHU}PlI^CG6N8g_Pf!8Q)S#)dz4 z1`Cj%LFf1rUC5ld-zrYuAU3i@`70}Et0!Ce5F#cGRZ@^^#JQNu43Fu&h zU7l%K`S5_C1-(}wLFu?n1^kCVFgy1SOC%r&h^zfO7kI1zxj+^S%m3UM23f#}ddp$$ zT(bjA0DatsTER6uYX`*laN{&)`!0MSOv1MUVGJZ)K0xQMk}Y{o%wxjjX`}P@)`24# zziW8-JxE5s1p@k%o%03?ph5$DMyMDRcYD}_++nh+slc+N26^xdP=7|ND#X7*SfHht zgwvg&1;lES2TO7XKjrvk`8Ke z(3PJOy3Jz3wHp2U(4}kZ>blT=sBqtrolf`{YHMj~iW*s$ttCxA|D||D zoN2DbcBA|JoNZJzR600pYRLneZ+`WyLFLKZ3k;_Q_X3n5fT4j=4Zx%5+UF|T<$w}J z#N+9aKYG2IM`G(*vSpN%kvHN&eZrV@&-%;h4~7#=rHMHC;c^Bt53=;KylA#Bt-z~ z13>(%AIj;=07);%C6Yk3U%EUdJn~GdLh*PfoK(Q>=Ayh@M(el?n zplpBDQit|AegT}G9B;l_Au497O3;rgal!1Q#*iF=b<@ni2*NBE(s0@lyE1zu6n zM|dBD+SC-=9+37`gw1OF+Z9(S&Et|8l={llK92BdDuz`bcg zXsa2pq5XHesAVqO011npmKNhK*kYMwe+qh715F8#D7gU-il1Lrg3i1lQ2OKM(hc>1 z(7;Od+qf_1)ekDt^cM%ep)Db*yg_*eQsAviJ+dMkz2bT;;%E=OinmiN7zn~sBDRPy zditPD^L<%>|3YZRkjxp&C@rK{OQ+RUp+Fq)7JPe7xFQ>t!SQ?^0=#(ocs(%e!~Jtu z!=z)%zrAU@9m&BLC7>@kFp<|Vza^UuHg+yA0xArrAP zF%c(|E)>t>l*Y)SD!uc%bp#ZTU)b>ydxdgrAC2|j&h%rX59X(Bs`ctC^p}M209deg zymz=#zEd&-S>N(#`#{6|TNc0A%UM*n1d%S`4?79iYu-tRgv;p z9iyE7h^N%B^JlGW(Uv)JhTIlQY+V^6awtU|3lVT-Qvkl_rLCwzbE|-TMLahRs%%G>nmRI#lfqW!B1of)D7}&D)~R1Jpd@)WAzvOvp607&dG1Hu6cw2 zJ~735a#XyT{!)Gd40h?WH$7#_l@I-iKv2Lb!rUJ0Gi@%v3=iE@2iQzN$J0f9x7aXa z&+pOvT+;yfH;Ka6UqeXRY`QH%^{;J7b60ovUTM~V4l6(!5egE-ZYCteE^{mNh>aNa zPo_L^KbXVESdogj)nlG7@Lw=+o=K%4&g#kUX{w~lJqHT<^26s#{MR0QVM@FdfNMb) zSAP)($UDCQQT6`(MZ(Dz{b!Bo>9ig+QiY6n3anQ{@pwHf4$mu$x2c7 zLk|;T+ZpXW>|UQ;JDmzYF^>PTs7+|kf--}$Db?dUxv7d}!KCfhC2zSbBr0O2TQpGw z=2w({Jrx3=kv9J59r%4NAO|Yg&ObU9r$RS);Oi=m4Yu^G1TGKS5L*6D@E9d@1&NC? znZZE_jA<9g=7yIi1eWlh`a_D739~!}(<^u?ioD?i{dXzd188!(u@qe&T5<=rbYXVs zc{J9sTf2|OG13`dizOW73rW8>#^*^%`UpLZK)joZQO`|IPO%k5ukB-%WrB`^vbJP< zWkb8^wXYiOZiA7F@K&$sNKI!(EM2}N*2cs^S_?+ZIP*0{q+^u^xzVibu4o92H)@0D zstJEcwAaDNkV)Ssto^3$+Mk}r%PbGdtY@~|szjsfzxCI7R-nJqtg5xM+?I-+#=8<& zrUv8O_7@sk7;B|E>>6!$ORP49f1am5&$a9~-d7dlf{X81s@5Eql3KiMfF5X6R2$Zw z;EMkBv$C#nHRz_X_2$P9#-6+8zb@hb>tb>;{oBP9v9YwVSF+VJFk%!nayBzCQj`#e zXH<5xHDXl%pl@zu;0VvC|J)oS33)Zg$}SdSy2$lJ=89LJgX-VM67l+2 zs*XaXh(d+q^a(qO&O87jjaw$}D;w`0oUFTB{VU(v@0!-mzFn=HY45bTU4n9Fw35T) zPjT271V!RvsTq-@zvk?(sr4&fio|8DmwB#m&revE%+t@I+B|e?1OEacutu<3OnrU)yTC7)+dr63-tpd_zFc1~`6<$MNMF+^zLuAs6m# zUAn&jd1)Q6-@W`&G-ro5R9e1*zL|FMwBB*5O{!&kr*xJ1_Ho$|V=3s4t~0NpJ`ZO3 z*Wb>nm-G*^zll;U?=uv!J%-k{SHWQXDb^Z_eL||OJv5@rwY|GLzv`{H6p9rQj(v8T zpst%k!|fI@G*efT*Voi1);cw{I`aL2i=AEQ$h9>LyE^^5T113EM3k4x=T>ovWwk0# zxTKPj(36vB=sE1bgOzX4(>51-CA4Z~H*v7YhCzXJz>8@VfIJAUwUD1%vr_42)$x@f{y zJOjU58?C+O^|f=wu&QrfxZOS$D$+NUy0_PVCi=spy}*EmSU8F3R9{ok_pmYjlb{X zq>F8i%5WMd%4&0Q@jrO=!UCJ#bIdR!TU%60LjAW@$b9^3uaLZ$c|BTYSn0AQgzN{a z^Xg&?mp9WF*r-mZ7E!){rU zT#PaD{JiX5Ef&5>ji=K~c|8pt(iW%EwuCMzHWs2M=)a+0m4T_y{sriu-eObB3&+sr%KOu77n^+Y!p5@)gDfUz|GCLggFVN$A8{CZyBD zWi1b+=+7m)7x*szWJ`ytQ2|}pZKz_hr-!DU>68PAKG;442Az_l#$QmiFS)zW4)c`B zc)fPYcfyv!u%kZLw~Re%T}O?do*Eq*{`FPA8}B^4`};P+buv|o_vDoFFwrJ9?xx~q zi1{@B$7++qZuGfjbR^UhpzvG)%_9H>f&5aV@^YqB2_Ou^fOtDP3bK(8@83U7?O0xW892nombKuxh) zYXGYY7_bY*5^ov35p8zjLNq`ma&Ckuvj|MU_ zh>FO{1uf_xZP0hsBdRDV5u~7^8lYrs)^1G47A9g5sS%{%;^G>7D6KOdk2bCeNPqE&DEA`&s`wJ6b`4t?SoIBy?=A)`n<+9$lzmrm1BuIGFQT?&9|* zw@q8e#^mK%8CGM(wf`x>A)(2^?S6)Nf}>E}+i7pY&w?;qtbF2C#AIaoo;{x$rmt$v zMhi0gWx#^{u}1v7OsyLiJp^POk*RY>q+yXXG7EVmxTqjq#+B(dav!_6#205a2%pP z>bhP+gJYSWm>yKCqnqsu(}>*G1yv;n>vD2BGcsaYeX(g{hxXmvaOFdi0|UQ`i!Zri z&wLh9ROGn0nEe)ly`w19MOAXJeD#*E<3;tu9V;GQhqzd^z#EJ+cndzeJTQ05)Vj*Y zxe?%p=fIzcFB7{yG10S1^^YEr0=F|AN)$CJf?%t(rzgHK*HU`g3@$EFbBLOu;q}A> zv~qCBr`leub!c(0lU_Agti^JOOLQFkzQbmXDoTC8h9rYTh+)#x8*#&KFItDV?m}u^ zTB51#zZvN5{pvQS_nnqlH|zp;;(Fw2p>-X4zr691w%7A=QW(v}U+h)GZ~Wr3rREE* z2)7`tkF7f|w|?KHylYB^D^Kf21mK zxNWSTpI@O7)j&&6tC1&nbqS@qh88yQ9G`oVv7PrF92_c+*LV^_50P|4_s21zs`eMnZXDvZOi7qT1j{7N#}eHmw#$5 z2RhhPwc~bWXhb$bjmH!MU&^* z`4{CUA1?N@3(c>5=#)6%NJsY5vQ9~INjoO=Yt<`N)4$LCWyga-DkQ_dE#j|tsG2Zj z$dL|=2$`=z0sZ&W!cueMZQ3?$ZYzc>TmI6j_ESJFd%1pesvfWsh=bZmC`E~ZyOrfEfBIHfP)ghgp zo~@fdDVHp%GAw2>yF5Yzx34tBmL*zWDgkHQYB&>%fSyDnRsQr`CK%ersxt0u0Tl;}$6+&nu`}gk~h`4ofs>DRg&ZS}P zC9}TmhEMW5Ne^uav8i0M(dwejV*dH1ROtA#aI32iytC7j#!)(J4&*s>T~oxPhsS7o zP6h*hQExC;?40DwB{(>rM9CfFpp=z7F?}7oXZ^;xn!%Xm$bpaiYx(3`TXZ|K2sWP@ zJxBKXX}F0h3^Tb(G|qf5K2( z8z(EPQqlc7S053DD5`c5`qL+R-3BFeTf6teyMt!bPJy5BEO`K5(vM9Q>p&_DN&?eB zurtKW%poxCix&AC4&?h@I5mA*zbI~UXmn2(sp9XBOEToBaH5cLg8$otVw^3RPQ;NS z`=w~HzZB|6?fLV>aiueO%3QTyi>oWAIk)f@m^VSe4WFdzIIyZmd%SwBVIz_x{1Z=` zbBlo&&Bq zI-45augmI$(dtBr5BqlKY-^Xi$gi}@Y1YJ8&0E%7LB#;W{VRyQZ_aCZ<3b|cBX})h zEt*-E9H5r)WL@lTKNM7G_-h^9M8wO`;RQa(P-O4X2{peUfr z0m^m2+X6nthLVz@?+f9Pk^3JcB5{~Kua9$qycWrh70fYk3gT=+gZF)yfZ}oes zFanESqsex|9~$9#!j8wKWerrU@mL02U?H_2%%u9S#pl>BNj`x~7AGi99*YLYh6aAgj1KqLvu^*kJh@7(R-WkAfVMEm zB|lsNBAsiV2N{-HE~_jz1l`QIX_n}2W{Wk?!LYw zN?RsInk4vnK$%LbCVLY_$;4Cx1eWUD-gfE}~`>jK!rrFgGF#u)f zXb(gSqcb4@sy`&muRec3Nlj&e+CYV?<2R_sfcT@i1N2G2HArtXo7-UroF~67Kpo^c zq$c;%=?Q2;VbX;gK!a;LQaT#u1uxh*eBwMDn&u_%^H>=lA2)B{p~^Lgr3#TNkI=r| ziZ~s^I1oL)x^i)(Se=JO#`n$oK>^W%xaG+OK)z_noL$mwamc+uAU%KTQ2Y21417T# z&D1vwCewV_&^2xmhTKCkbmbb{6&1E2fk1$8MoKj@)JQa^Q&&<7sEFu;v%D+u(G0uS(}*={a5TCS}K{}2gxD~O1vL)9C6WeqTy+CbMPLOvQ4N>R*f%+-_WKWr_%LbBVsJ zXYnuIT~&MCoZ5Tb6;4e>rE_?RkaLkj&}?thPn)aNq}Vzijf$covFPu)rIb*!uO@{O zap%d(J_zHZ`v-*lXmvxE)B5qY0Gf(i_mY{T

SUAgC)gHYOBG!U>W!)0+PdEg7!h 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 z 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%0AwU$N({wOXzx#k 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

k%UFt`kGh1Rfedx}nl7|Wts|qD= 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¨^STStM`EkwU$uzZf7h300_#U)oD$2noHHv8&94E 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 literal 0 HcmV?d00001 diff --git a/esplusplayer.pc.in b/esplusplayer.pc.in new file mode 100644 index 0000000..8625c6f --- /dev/null +++ b/esplusplayer.pc.in @@ -0,0 +1,10 @@ + +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@ diff --git a/include/esplusplayer/appinfo.h b/include/esplusplayer/appinfo.h new file mode 100644 index 0000000..7252d80 --- /dev/null +++ b/include/esplusplayer/appinfo.h @@ -0,0 +1,51 @@ +/** + * @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 + +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 diff --git a/include/esplusplayer/attribute.h b/include/esplusplayer/attribute.h new file mode 100644 index 0000000..20bb2bd --- /dev/null +++ b/include/esplusplayer/attribute.h @@ -0,0 +1,51 @@ +/** + * @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 diff --git a/include/esplusplayer/audioeasinginfo.h b/include/esplusplayer/audioeasinginfo.h new file mode 100644 index 0000000..3f00ae9 --- /dev/null +++ b/include/esplusplayer/audioeasinginfo.h @@ -0,0 +1,45 @@ +/** + * @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 diff --git a/include/esplusplayer/decodedvideopacketex.h b/include/esplusplayer/decodedvideopacketex.h new file mode 100644 index 0000000..26f69c0 --- /dev/null +++ b/include/esplusplayer/decodedvideopacketex.h @@ -0,0 +1,73 @@ +/** + * @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 +#include +#include + +#include "tbm_surface.h" +#include "tbm_type.h" + +namespace esplusplayer { + +class DecodedVideoPacketEx : private boost::noncopyable { + public: + using Ptr = std::unique_ptr; + + 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__ diff --git a/include/esplusplayer/drm.h b/include/esplusplayer/drm.h new file mode 100644 index 0000000..ff385e4 --- /dev/null +++ b/include/esplusplayer/drm.h @@ -0,0 +1,61 @@ +/** +* @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__ diff --git a/include/esplusplayer/elementary_stream.h b/include/esplusplayer/elementary_stream.h new file mode 100644 index 0000000..5e12a46 --- /dev/null +++ b/include/esplusplayer/elementary_stream.h @@ -0,0 +1,343 @@ +/** + * @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 +#include + +#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; + /** + * @brief Create a audio stream object + * @remarks You must use this to get audio stream object + * @return audio stream object (unique_ptr) + */ + 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 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 of codec data + * @see AudioStream::SetCodecData(), AudioStream::GetCodecDataSize() + */ + std::shared_ptr 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; + /** + * @brief Create a video stream object + * @remarks You must use this to get video stream object + * @return video stream object (unique_ptr) + */ + 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 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 of codec data + * @see VideoStream::SetCodecData(), VideoStream::GetCodecDataSize() + */ + std::shared_ptr 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_ diff --git a/include/esplusplayer/es_eventlistener.h b/include/esplusplayer/es_eventlistener.h new file mode 100644 index 0000000..95a0d32 --- /dev/null +++ b/include/esplusplayer/es_eventlistener.h @@ -0,0 +1,248 @@ +/** + * @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 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 diff --git a/include/esplusplayer/espacket.h b/include/esplusplayer/espacket.h new file mode 100644 index 0000000..979fd22 --- /dev/null +++ b/include/esplusplayer/espacket.h @@ -0,0 +1,188 @@ +/** + * @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 +#include + +#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; + /** + * @brief Create a espacket object + * @remarks You must use this to get espacket object + * @return espacket object (unique_ptr) + */ + static Ptr Create(const StreamType type = StreamType::kMax, + std::shared_ptr 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 hdr10p_data = nullptr); + /** + * @brief Create an eos espacket object + * @remarks You must use this to get espacket object + * @return espacket object (unique_ptr) + */ + 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 GetBuffer() const { return buffer_; } + /** + * @brief Get the Hdr10p Metadata for the associated packet + * @return the Hdr10p Metadata to set + */ + std::shared_ptr 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 buffer, + const uint32_t buffer_size, const uint64_t pts, + const uint64_t duration, const uint32_t hdr10p_size, + std::shared_ptr hdr10p_data); + + private: + const StreamType type_ = StreamType::kMax; + std::shared_ptr 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 hdr10p_metadata_ = nullptr; + std::unique_ptr matroska_color_info_ = nullptr; +}; + +using EsPacketPtr = EsPacket::Ptr; + +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_ESPACKET_H__ \ No newline at end of file diff --git a/include/esplusplayer/esplusplayer.h b/include/esplusplayer/esplusplayer.h new file mode 100644 index 0000000..fdd3064 --- /dev/null +++ b/include/esplusplayer/esplusplayer.h @@ -0,0 +1,1266 @@ +/** + * @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 +#include +#include +#include +#include +#include +#include + +#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; + /** + * @brief Create a esplusplayer object + * @remarks You must use this to get esplusplayer object + * @return Player object (unique_ptr) + */ + 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__ diff --git a/include/esplusplayer/external_drm.h b/include/esplusplayer/external_drm.h new file mode 100644 index 0000000..082b79c --- /dev/null +++ b/include/esplusplayer/external_drm.h @@ -0,0 +1,111 @@ +/** + * @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 +#include +#include + +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 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 kid; + std::vector initialization_vector; + + MetaData sub_data = nullptr; + std::array 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__ diff --git a/include/esplusplayer/track.h b/include/esplusplayer/track.h new file mode 100644 index 0000000..05d38e6 --- /dev/null +++ b/include/esplusplayer/track.h @@ -0,0 +1,161 @@ +/** +* @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 +#include +#include +#include +#include +#include +#include + +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 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 _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::max(); + const uint32_t stop_time = std::numeric_limits::max(); + const boost::any value; + const int extsub_index = -1; +}; +using SubtitleAttrList = std::list; +using SubtitleAttrListPtr = std::unique_ptr; +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 diff --git a/include/esplusplayer/types/buffer.h b/include/esplusplayer/types/buffer.h new file mode 100644 index 0000000..93e78c1 --- /dev/null +++ b/include/esplusplayer/types/buffer.h @@ -0,0 +1,82 @@ +/** + * @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 + +#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__ diff --git a/include/esplusplayer/types/display.h b/include/esplusplayer/types/display.h new file mode 100644 index 0000000..33906a5 --- /dev/null +++ b/include/esplusplayer/types/display.h @@ -0,0 +1,89 @@ +/** + * @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__ diff --git a/include/esplusplayer/types/error.h b/include/esplusplayer/types/error.h new file mode 100644 index 0000000..0344f31 --- /dev/null +++ b/include/esplusplayer/types/error.h @@ -0,0 +1,78 @@ +/** +* @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__ diff --git a/include/esplusplayer/types/event.h b/include/esplusplayer/types/event.h new file mode 100644 index 0000000..a9012c6 --- /dev/null +++ b/include/esplusplayer/types/event.h @@ -0,0 +1,56 @@ +/** + * @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__ diff --git a/include/esplusplayer/types/latency.h b/include/esplusplayer/types/latency.h new file mode 100644 index 0000000..ac9b7a5 --- /dev/null +++ b/include/esplusplayer/types/latency.h @@ -0,0 +1,44 @@ +/** +* @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 diff --git a/include/esplusplayer/types/picturequality.h b/include/esplusplayer/types/picturequality.h new file mode 100644 index 0000000..93078e4 --- /dev/null +++ b/include/esplusplayer/types/picturequality.h @@ -0,0 +1,39 @@ +/** +* @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 diff --git a/include/esplusplayer/types/resource.h b/include/esplusplayer/types/resource.h new file mode 100644 index 0000000..7036a8d --- /dev/null +++ b/include/esplusplayer/types/resource.h @@ -0,0 +1,72 @@ +/** + * @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 diff --git a/include/esplusplayer/types/source.h b/include/esplusplayer/types/source.h new file mode 100644 index 0000000..0f6e7c3 --- /dev/null +++ b/include/esplusplayer/types/source.h @@ -0,0 +1,70 @@ +/** +* @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__ diff --git a/include/esplusplayer/types/stream.h b/include/esplusplayer/types/stream.h new file mode 100644 index 0000000..c108849 --- /dev/null +++ b/include/esplusplayer/types/stream.h @@ -0,0 +1,36 @@ +/** + * @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 diff --git a/include/esplusplayer/types/streaming_message.h b/include/esplusplayer/types/streaming_message.h new file mode 100644 index 0000000..92da102 --- /dev/null +++ b/include/esplusplayer/types/streaming_message.h @@ -0,0 +1,65 @@ +/** +* @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 diff --git a/include/esplusplayer/types/submitdata.h b/include/esplusplayer/types/submitdata.h new file mode 100644 index 0000000..db4c6cf --- /dev/null +++ b/include/esplusplayer/types/submitdata.h @@ -0,0 +1,40 @@ +/** +* @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 diff --git a/include/esplusplayer_capi/buffer.h b/include/esplusplayer_capi/buffer.h new file mode 100644 index 0000000..326aa4c --- /dev/null +++ b/include/esplusplayer_capi/buffer.h @@ -0,0 +1,118 @@ +/** + * @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 + +#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__ diff --git a/include/esplusplayer_capi/display.h b/include/esplusplayer_capi/display.h new file mode 100644 index 0000000..ff953f3 --- /dev/null +++ b/include/esplusplayer_capi/display.h @@ -0,0 +1,73 @@ +/** + * @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__ diff --git a/include/esplusplayer_capi/drm.h b/include/esplusplayer_capi/drm.h new file mode 100644 index 0000000..80cbcb3 --- /dev/null +++ b/include/esplusplayer_capi/drm.h @@ -0,0 +1,183 @@ +/** + * @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 + +/** + * @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__ diff --git a/include/esplusplayer_capi/error.h b/include/esplusplayer_capi/error.h new file mode 100644 index 0000000..791b8fd --- /dev/null +++ b/include/esplusplayer_capi/error.h @@ -0,0 +1,69 @@ +/** + * @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__ diff --git a/include/esplusplayer_capi/espacket.h b/include/esplusplayer_capi/espacket.h new file mode 100644 index 0000000..50bf18a --- /dev/null +++ b/include/esplusplayer_capi/espacket.h @@ -0,0 +1,114 @@ +/** + * @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 + +#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__ diff --git a/include/esplusplayer_capi/esplusplayer_capi.h b/include/esplusplayer_capi/esplusplayer_capi.h new file mode 100644 index 0000000..d6bc74b --- /dev/null +++ b/include/esplusplayer_capi/esplusplayer_capi.h @@ -0,0 +1,3487 @@ +/** + * @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 + +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(&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__ diff --git a/include/esplusplayer_capi/esplusplayer_internal.h b/include/esplusplayer_capi/esplusplayer_internal.h new file mode 100644 index 0000000..148e31e --- /dev/null +++ b/include/esplusplayer_capi/esplusplayer_internal.h @@ -0,0 +1,154 @@ +/** + * @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 + +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__ diff --git a/include/esplusplayer_capi/event.h b/include/esplusplayer_capi/event.h new file mode 100644 index 0000000..63a71e0 --- /dev/null +++ b/include/esplusplayer_capi/event.h @@ -0,0 +1,71 @@ +/** + * @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 + +#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__ diff --git a/include/esplusplayer_capi/latency.h b/include/esplusplayer_capi/latency.h new file mode 100644 index 0000000..b68762d --- /dev/null +++ b/include/esplusplayer_capi/latency.h @@ -0,0 +1,56 @@ +/** + * @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__ diff --git a/include/esplusplayer_capi/matroska_color.h b/include/esplusplayer_capi/matroska_color.h new file mode 100644 index 0000000..561ada5 --- /dev/null +++ b/include/esplusplayer_capi/matroska_color.h @@ -0,0 +1,170 @@ +/** + * @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 + +#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__ diff --git a/include/esplusplayer_capi/state.h b/include/esplusplayer_capi/state.h new file mode 100644 index 0000000..12a0ebc --- /dev/null +++ b/include/esplusplayer_capi/state.h @@ -0,0 +1,51 @@ +/** + * @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, /** + +#include + +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__ diff --git a/include/mixer/mixer.h b/include/mixer/mixer.h new file mode 100644 index 0000000..4d74a10 --- /dev/null +++ b/include/mixer/mixer.h @@ -0,0 +1,274 @@ +/** + * @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 +#include + +#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; + /** + * @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) + */ + 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__ diff --git a/include/mixer/mixer_eventlistener.h b/include/mixer/mixer_eventlistener.h new file mode 100644 index 0000000..83cef61 --- /dev/null +++ b/include/mixer/mixer_eventlistener.h @@ -0,0 +1,82 @@ +/** + * @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__ diff --git a/include/mixer/mixerticket.h b/include/mixer/mixerticket.h new file mode 100644 index 0000000..2de13e0 --- /dev/null +++ b/include/mixer/mixerticket.h @@ -0,0 +1,180 @@ +/** + * @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 + +#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 diff --git a/include/mixer/mixerticket_eventlistener.h b/include/mixer/mixerticket_eventlistener.h new file mode 100644 index 0000000..5c7f3d9 --- /dev/null +++ b/include/mixer/mixerticket_eventlistener.h @@ -0,0 +1,89 @@ +/** + * @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__ diff --git a/include/mixer_capi/display.h b/include/mixer_capi/display.h new file mode 100644 index 0000000..6b92342 --- /dev/null +++ b/include/mixer_capi/display.h @@ -0,0 +1,61 @@ +/** + * @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 diff --git a/include/mixer_capi/error.h b/include/mixer_capi/error.h new file mode 100644 index 0000000..f6538e6 --- /dev/null +++ b/include/mixer_capi/error.h @@ -0,0 +1,52 @@ +/** + * @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 diff --git a/include/mixer_capi/mixer_capi.h b/include/mixer_capi/mixer_capi.h new file mode 100644 index 0000000..dc4b96a --- /dev/null +++ b/include/mixer_capi/mixer_capi.h @@ -0,0 +1,304 @@ +/** + * @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 + +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__ + diff --git a/out/docs/class_diagram/esplusplayer/esplusplayer.png b/out/docs/class_diagram/esplusplayer/esplusplayer.png new file mode 100644 index 0000000000000000000000000000000000000000..3e5a9e1d6f06683954cae87ee4353c3d01b02f21 GIT binary patch literal 45550 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

LQ-05Q1PX8r3r}57ztmeX1z7GD}-7Z5{gz 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 T23K$qd7AbGJ&ind+uQ#ID@$>- literal 0 HcmV?d00001 diff --git a/packaging/esplusplayer.manifest b/packaging/esplusplayer.manifest new file mode 100644 index 0000000..50eefc9 --- /dev/null +++ b/packaging/esplusplayer.manifest @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packaging/esplusplayer.spec b/packaging/esplusplayer.spec new file mode 100644 index 0000000..445e14f --- /dev/null +++ b/packaging/esplusplayer.spec @@ -0,0 +1,248 @@ +# %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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..82d4cda --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,5 @@ +ADD_SUBDIRECTORY(plusplayer-core) +ADD_SUBDIRECTORY(esplusplayer) +IF(${PRODUCT_TYPE_AUDIO} STREQUAL "no") +ADD_SUBDIRECTORY(mixer) +ENDIF(${PRODUCT_TYPE_AUDIO} STREQUAL) diff --git a/src/cpplint.py b/src/cpplint.py new file mode 100644 index 0000000..52cb7d0 --- /dev/null +++ b/src/cpplint.py @@ -0,0 +1,6123 @@ +#!/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] ... + + 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 , 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] "') + + +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 ." 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 + # template + # template + # template + 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 , + # template 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 ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and ))?' + # 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> 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 + # 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{}> + # + # 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(... + # string Class::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 // 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(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',)), + ('', ('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', + )), + ('', ('numeric_limits',)), + ('', ('list',)), + ('', ('map', 'multimap',)), + ('', ('allocator', 'make_shared', 'make_unique', 'shared_ptr', + 'unique_ptr', 'weak_ptr')), + ('', ('queue', 'priority_queue',)), + ('', ('set', 'multiset',)), + ('', ('stack',)), + ('', ('char_traits', 'basic_string',)), + ('', ('tuple',)), + ('', ('unordered_map', 'unordered_multimap')), + ('', ('unordered_set', 'unordered_multiset')), + ('', ('pair',)), + ('', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('', ('hash_map', 'hash_multimap',)), + ('', ('hash_set', 'hash_multiset',)), + ('', ('slist',)), + ) + +_HEADERS_MAYBE_TEMPLATES = ( + ('', ('copy', 'max', 'min', 'min_element', 'sort', + 'transform', + )), + ('', ('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(..., ...), 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 . + + 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: { '': (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[''] = (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() diff --git a/src/esplusplayer/CMakeLists.txt b/src/esplusplayer/CMakeLists.txt new file mode 100644 index 0000000..bd82c70 --- /dev/null +++ b/src/esplusplayer/CMakeLists.txt @@ -0,0 +1,73 @@ +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/ +) diff --git a/src/esplusplayer/include_internal/esplayer/decoded_pkt_list.h b/src/esplusplayer/include_internal/esplayer/decoded_pkt_list.h new file mode 100644 index 0000000..a2b1a91 --- /dev/null +++ b/src/esplusplayer/include_internal/esplayer/decoded_pkt_list.h @@ -0,0 +1,304 @@ +// +// @ Copyright [2019] +// + +#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_DECODED_PACKET_LIST_H__ +#define __ESPLUSPLAYER_SRC_ESPLAYER_DECODED_PACKET_LIST_H__ + +#include + +#include +#include +#include +#include + +#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(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>; + + public: + virtual bool TryToAdd(esplusplayer_decoded_video_packet* packet) override { + if (packet == nullptr) { + LOG_ERROR("packet is nullptr"); + return false; + } + std::unique_lock 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 lk(mtx_); + list_.remove_if([&packet](const DecodedPacketPtr& cur) { + if (cur.get() == packet) return true; + return false; + }); + } + virtual void Clear() override { + std::unique_lock 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& GetList() { return list_; } + + protected: + virtual bool IsAvailableInternal() = 0; + virtual void DecodedPacketDeleter( + esplusplayer_decoded_video_packet* packet) = 0; + + protected: + std::mutex mtx_; + std::list 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; + 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 lk(mtx_); + if (is_scale_change) { + for (std::vector::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(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 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 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(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 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(iter.tbm_surf)); + } + std::vector 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(packet->surface_data)); + packet->surface_data = NULL; + } + } + } +// LCOV_EXCL_STOP + + private: + const std::uint32_t kMaxAvailableSize_ = 5; + std::vector tbm_mgr_; +}; +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_SRC_ESPLAYER_DECODED_PACKET_LIST_H__ \ No newline at end of file diff --git a/src/esplusplayer/include_internal/esplayer/espacket_logger.h b/src/esplusplayer/include_internal/esplayer/espacket_logger.h new file mode 100644 index 0000000..092ae02 --- /dev/null +++ b/src/esplusplayer/include_internal/esplayer/espacket_logger.h @@ -0,0 +1,43 @@ +// +// @ Copyright [2019] +// + +#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_ESPACEKT_LOGGER__H__ +#define __ESPLUSPLAYER_SRC_ESPLAYER_ESPACEKT_LOGGER__H__ + +#include +#include + +#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(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 diff --git a/src/esplusplayer/include_internal/esplayer/esplayer.h b/src/esplusplayer/include_internal/esplayer/esplayer.h new file mode 100644 index 0000000..3d0c76b --- /dev/null +++ b/src/esplusplayer/include_internal/esplayer/esplayer.h @@ -0,0 +1,344 @@ +// +// @ Copyright [2018] +// + +#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_ESPLAYER__H__ +#define __ESPLUSPLAYER_SRC_ESPLAYER_ESPLAYER__H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + + 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* 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_; + 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 msg_queue_; + std::future msg_handler_task_; + + std::unique_ptr trackrenderer_event_listener_{ + new TrackRendererEventListener(this)}; +#ifndef IS_AUDIO_PRODUCT + std::unique_ptr mixer_event_listener_{new MixerListener(this)}; +#endif + std::unique_ptr trackrenderer_; + std::future 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 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__ diff --git a/src/esplusplayer/include_internal/esplayer/esplayer_drm.h b/src/esplusplayer/include_internal/esplayer/esplayer_drm.h new file mode 100644 index 0000000..5d79faa --- /dev/null +++ b/src/esplusplayer/include_internal/esplayer/esplayer_drm.h @@ -0,0 +1,20 @@ +// +// @ Copyright [2018] +// + +#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; +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 diff --git a/src/esplusplayer/include_internal/esplayer/message.hpp b/src/esplusplayer/include_internal/esplayer/message.hpp new file mode 100644 index 0000000..ab2d709 --- /dev/null +++ b/src/esplusplayer/include_internal/esplayer/message.hpp @@ -0,0 +1,401 @@ +// +// @ Copyright [2018] +// + +#ifndef __ESPLUSPLAYER_SRC_ESPLAYER_MESSAGE_H__ +#define __ESPLUSPLAYER_SRC_ESPLAYER_MESSAGE_H__ + +#include +#include +#include +#include + +#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; + 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; + using Operator = std::function; + + 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; + using Operator = std::function; + + 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; + using Operator = + std::function; + + 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; + using Operator = std::function; + + 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; + using Operator = std::function; + + 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; + using Operator = std::function; + + 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; + using Operator = std::function; + + 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; + using Operator = std::function; + + 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; + using Operator = std::function; + + 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; + using Operator = std::function; + + 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; + using Operator = + std::function; + + 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; + using Operator = std::function; + + 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; + using Operator = + std::function, 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 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; + using Operator = std::function; + + 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; + using Operator = std::function; + + 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__ diff --git a/src/esplusplayer/include_internal/esplayer/state_manager.hpp b/src/esplusplayer/include_internal/esplayer/state_manager.hpp new file mode 100644 index 0000000..5c1afb2 --- /dev/null +++ b/src/esplusplayer/include_internal/esplayer/state_manager.hpp @@ -0,0 +1,387 @@ +// +// @ Copyright [2018] +// + +// +// StateMachine using boost::msm (meta state machine) +// +// * 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 +#include +#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 // back-end +#include // functors +#include // front-end +#include +#include + +#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; +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 + 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 { + // + // 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 + void on_entry(Event const&, FSM&) { + STATE_TRACE("entering: EsPlayer StateMachine"); + } + template + 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 + bool operator()(EVT const& evt, FSM&, SourceState& from, TargetState& to) { + return true; + } + }; + + struct CheckOp { + template + 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 + 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 + 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 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 lock(control_m_); + stopped_ = true; + // need to check & wait message queue is empty + msm_.stop(); + } + } + + template + bool ProcessEvent(const T& es_event) { + std::lock_guard 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 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(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(EsState::kNone); + } + return static_cast(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 state_vector_ = {EsState::kNone, // None + EsState::kIdle, // Idle + EsState::kIdle, // StreamReady + EsState::kReady, // Ready + EsState::kPlaying, // Playing + EsState::kPaused}; // Paused + msm::back::state_machine msm_; +}; // class StateManager + +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_SRC_ESPLAYER_STATE_MANAGER_H__ \ No newline at end of file diff --git a/src/esplusplayer/src/elementary_stream.cpp b/src/esplusplayer/src/elementary_stream.cpp new file mode 100644 index 0000000..517f202 --- /dev/null +++ b/src/esplusplayer/src/elementary_stream.cpp @@ -0,0 +1,231 @@ +// +// @ Copyright [2018] +// + +#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 diff --git a/src/esplusplayer/src/espacket.cpp b/src/esplusplayer/src/espacket.cpp new file mode 100644 index 0000000..e8889a8 --- /dev/null +++ b/src/esplusplayer/src/espacket.cpp @@ -0,0 +1,37 @@ +// +// @ Copyright [2018] +// +#include "esplusplayer/espacket.h" + +namespace esplusplayer { +EsPacketPtr EsPacket::Create(const StreamType type, + std::shared_ptr buffer, + const uint32_t buffer_size, const uint64_t pts, + const uint64_t duration, + const uint32_t hdr10p_size, + std::shared_ptr 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 buffer, + const uint32_t buffer_size, const uint64_t pts, + const uint64_t duration, const uint32_t hdr10p_size, + std::shared_ptr 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 diff --git a/src/esplusplayer/src/espacket_logger.cpp b/src/esplusplayer/src/espacket_logger.cpp new file mode 100644 index 0000000..9de4a6a --- /dev/null +++ b/src/esplusplayer/src/espacket_logger.cpp @@ -0,0 +1,94 @@ +// +// @ Copyright [2019] +// + +#include "esplayer/espacket_logger.h" + +#include +#include +#include +#include + +#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(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(packet->GetType()); + if (track_index >= static_cast(StreamType::kMax)) return; + + std::lock_guard 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(type); + if (track_index >= static_cast(StreamType::kMax)) return; + + std::lock_guard 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(" [%s] %s", count, (force ? ": last" : ""), + (type == StreamType::kAudio ? "AUDIO" : "VIDEO"), logmsg.c_str()); + } +} + +void EsPacketLogger::ResetLog(const StreamType type) { + std::lock_guard lk(mtx_); + + last_packet_info_[static_cast(type)].valid = false; + last_log_packet_count_[static_cast(type)] = 0; +} + +} // namespace esplusplayer \ No newline at end of file diff --git a/src/esplusplayer/src/esplayer.cpp b/src/esplusplayer/src/esplayer.cpp new file mode 100644 index 0000000..58a73c9 --- /dev/null +++ b/src/esplusplayer/src/esplayer.cpp @@ -0,0 +1,2909 @@ +// +// @ Copyright [2018] +// +// to manipulate TrackRenderer objects +// to provide optimized control of TrackRenderer + +#include "esplayer/esplayer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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& 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& 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 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(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(kLowLatencyModeEnableGameMode); + exclusive_mode |= + static_cast(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 lk(submit_mutex_); + if (state_manager_.GetState() >= EsState::kIdle) { + Stop(); + } + auto op = [this]() noexcept { + if (is_msg_task_stop_ == false) { + { + std::unique_lock 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(type))) { + return false; + } + { + std::lock_guard 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(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(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(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(time_unit_type)); +} + +// LCOV_EXCL_START +#ifndef IS_AUDIO_PRODUCT +bool EsPlayer::PrepareVideoMixingMode_(std::vector* 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(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 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 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 lock2(eos_mutex_); + eos_status_ = internal::ResetEosStatus(kTrackTypeAudio, eos_status_); + need_data_[track.type].mask |= kNeedDataMaskByPrepare; + break; + } + case kTrackTypeVideo: { + std::lock_guard 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 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(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 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 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(byte_size_); + *time_size = boost::any_cast(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 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 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 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)); + if (!trackrenderer_->SubmitPacket2(inbuffer, nullptr)) { + std::lock_guard 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(tz_handle), "packet_size", + G_TYPE_UINT, static_cast(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(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 lk(submit_mutex_); + return SubmitPacketCommon_(packet, nullptr); +} + +// LCOV_EXCL_START +PacketSubmitStatus EsPlayer::SubmitTrustZonePacket(const EsPacketPtr& packet, + uint32_t tz_handle) { + std::lock_guard 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(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 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(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(size); + break; + case BufferOption::kBufferVideoMinByteThreshold: + src_queue_size_.kMinByteThresholdOfVideoSrcQueue = + static_cast(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(size); + break; + case BufferOption::kBufferVideoMinTimeThreshold: + src_queue_size_.kMinTimeThresholdOfVideoSrcQueue = + static_cast(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(low_latency_mode_), + static_cast(mode)); + return false; + } + low_latency_mode_ |= static_cast(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(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(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(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(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(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(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(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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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(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 diff --git a/src/esplusplayer/src/esplayer_drm.cpp b/src/esplusplayer/src/esplayer_drm.cpp new file mode 100644 index 0000000..72de295 --- /dev/null +++ b/src/esplusplayer/src/esplayer_drm.cpp @@ -0,0 +1,87 @@ +// +// @ Copyright [2018] +// + +#include "esplayer/esplayer_drm.h" + +#include +#include +#include +#include + +#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(0); // box_size : (lazy) + s.Put(true); // secure + s.Put(drm_info.handle); // handle + s.Put(0); // session_id + s.Put(0); // psshinfo_size + auto psa_size_offset = s.Put(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(drm_info.use_out_buffer); // bUseOutBuf + s.Put(drm_info.kid.size()); // uKIDLen + s.Put(drm_info.kid); // pKID + s.Put(0); // uDataLen + s.Put(0); // uOutBufLen + s.Put(drm_info.initialization_vector.size()); // uIVLen + s.Put(drm_info.initialization_vector); // pIV + + // box.pPSAParam->pSubData + drm::DrmbEsFragmentedMp4Data *sub_data = + reinterpret_cast(drm_info.sub_data); + if (sub_data != nullptr && sub_data->sub_sample_info_vector.size() != 0) { + uint32_t subdata_size = static_cast( + sizeof(uint32_t) + sub_data->sub_sample_info_vector.size() * + sizeof(drm::DrmbEsSubSampleInfo)); + s.Put(subdata_size); // subData_size + const size_t sub_sample_count = sub_data->sub_sample_info_vector.size(); + s.Put(sub_sample_count); // uSubSampleCount + for (const auto &value : sub_data->sub_sample_info_vector) { + s.Put(value.bytes_of_clear_data); // uBytesOfClearData + s.Put(value.bytes_of_encrypted_data); // uBytesOfEncryptedData + } + } else { + s.Put(0); // subData_size + } + + // box.pPSAParam + s.Put( + reinterpret_cast(drm_info.split_offsets.data()), + sizeof(int) * drm_info.split_offsets.max_size()); // split_offsets + s.Put(drm_info.use_pattern); // use_pattern + s.Put(drm_info.crypt_byte_block); // crypt_byte_block + s.Put(drm_info.skip_byte_block); // skip_byte_block + + // box + auto reconfigure_pssh_offset = s.Put(0); // reconfigure_pssh + + const auto total_size = s.GetSize(); + const auto psa_size = reconfigure_pssh_offset - psa_size_offset; + + std::unique_ptr> + 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(raw_data, total_size); // box_size + Serializer::Put(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 diff --git a/src/esplusplayer/src/esplusplayer.cpp b/src/esplusplayer/src/esplusplayer.cpp new file mode 100644 index 0000000..51fdbc4 --- /dev/null +++ b/src/esplusplayer/src/esplusplayer.cpp @@ -0,0 +1,18 @@ +// +// @ Copyright [2018] +// + +#include "esplusplayer/esplusplayer.h" + +#include "core/utils/plusplayer_log.h" +#include "esplayer/esplayer.h" + +namespace esplusplayer { + +std::unique_ptr EsPlusPlayer::Create() { + auto instance = std::unique_ptr(new EsPlayer); + LOG_INFO("Create Es Player [%p]", instance.get()); + return instance; +} + +} // namespace esplusplayer diff --git a/src/esplusplayer/src/esplusplayer_capi.cpp b/src/esplusplayer/src/esplusplayer_capi.cpp new file mode 100644 index 0000000..b53e1d2 --- /dev/null +++ b/src/esplusplayer/src/esplusplayer_capi.cpp @@ -0,0 +1,2338 @@ +#include "esplusplayer_capi/esplusplayer_capi.h" + +#include "esplusplayer/esplusplayer.h" + +using esplusplayer::EsPlusPlayer; +using esplusplayer::Geometry; + +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 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(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 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(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(type), + // static_cast(status)); + + if (this->buffer_status_cb_) + this->buffer_status_cb_(static_cast(type), + static_cast(status), + buffer_status_cb_userdata_); + if (this->buffer_byte_status_cb_) + this->buffer_byte_status_cb_( + static_cast(type), + static_cast(status), byte_size, + buffer_byte_status_cb_userdata_); + if (this->buffer_time_status_cb_) + this->buffer_time_status_cb_( + static_cast(type), + static_cast(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(type)); + if (this->ready_to_prepare_cb_) + this->ready_to_prepare_cb_(static_cast(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 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(type), + offset, ready_to_seek_cb_userdata_); + } + + void SetDecodedPacketManager( + std::shared_ptr& 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(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 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(event)); + esplusplayer_event_msg event_msg; + event_msg.data = const_cast(msg_data.data.c_str()); + event_msg.len = msg_data.len; + if (this->event_cb_) + this->event_cb_(static_cast(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(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(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(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(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(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 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 player; + std::unique_ptr listener{new listener_bridge()}; + std::shared_ptr 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(pp); + return priv ? priv->player.get() : nullptr; +} + +inline listener_bridge* listener_cast_(esplusplayer_handle pp) { + auto priv = static_cast(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 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 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 buffer = nullptr; + std::shared_ptr hdr10p_metadata = nullptr; + if (from->buffer_size != 0 && from->buffer) { + buffer = std::shared_ptr(new char[from->buffer_size], + std::default_delete()); + memcpy(buffer.get(), from->buffer, from->buffer_size); + } + if (from->hdr10p_metadata_size != 0 && from->hdr10p_metadata) { + hdr10p_metadata = std::shared_ptr( + new char[from->hdr10p_metadata_size], std::default_delete()); + memcpy(hdr10p_metadata.get(), from->hdr10p_metadata, + from->hdr10p_metadata_size); + } + auto espacket = EsPacket::Create(static_cast(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>; +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( + 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(from->algorithm); + drm_info->format = + static_cast(from->format); + drm_info->phase = + static_cast(from->phase); + + // kid + if (from->kid && from->kid_length > 0) { + drm_info->kid = std::move( + std::vector(from->kid, from->kid + from->kid_length)); + } + + // initialization_vector + if (from->iv && from->iv_length > 0) { + drm_info->initialization_vector = std::move( + std::vector(from->iv, from->iv + from->iv_length)); + } + + // sub_data + auto* from_sub_data = + reinterpret_cast(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( + 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(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 codec_data = nullptr; + + if (from->codec_data_length != 0) { + codec_data = std::shared_ptr(new char[from->codec_data_length], + std::default_delete()); + memcpy(codec_data.get(), from->codec_data, from->codec_data_length); + } + + stream->SetCodecData(codec_data, from->codec_data_length); + stream->SetMimeType( + static_cast(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(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 codec_data = nullptr; + + if (from->codec_data_length != 0) { + codec_data = std::shared_ptr(new char[from->codec_data_length], + std::default_delete()); + memcpy(codec_data.get(), from->codec_data, from->codec_data_length); + } + + stream->SetCodecData(codec_data, from->codec_data_length); + stream->SetMimeType( + static_cast(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(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(handle)->video_stream_; + if (vstream.is_open()) { + vstream.close(); + LOG_DEBUG("Close video_stream_"); + } + std::ofstream& astream = + static_cast(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(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(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(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(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(type), ticket)); + } +#endif + return convert_return_type_( + cast_(handle)->SetDisplay(static_cast(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(type), window); + + return convert_return_type_(cast_(handle)->SetDisplay( + static_cast(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(type), subsurface); + + return convert_return_type_(cast_(handle)->SetDisplaySubsurface( + static_cast(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(type), surface_id); + + return convert_return_type_(cast_(handle)->SetDisplay( + static_cast(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(mode)); + + return convert_return_type_( + cast_(handle)->SetDisplayMode(static_cast(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(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(rotation)); + return convert_return_type_( + cast_(handle)->SetDisplayRotate(static_cast(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(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(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(cast_(handle)->GetState()); + // LOG_INFO_P(cast_(handle), "state : %d", static_cast(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(handle)->audio_stream_ + : static_cast(handle)->video_stream_); + + if (ostream.is_open() == false) return; + + uint64_t tmp = packet->pts * 1000000; + ostream.write(reinterpret_cast(&tmp), sizeof(uint64_t)); + tmp = packet->duration * 1000000; + ostream.write(reinterpret_cast(&tmp), sizeof(uint64_t)); + std::uint64_t size = (std::uint64_t)packet->buffer_size; + ostream.write(reinterpret_cast(&size), sizeof(size)); + ostream.write(reinterpret_cast(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(status)); + } + return static_cast(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(status)); + } + return static_cast(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(status)); + } + return static_cast(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(type)))); + return static_cast(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(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(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(&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(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(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(&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 CreateDecodedPacketManager( + esplusplayer_handle handle, + esplusplayer_decoded_video_frame_buffer_type type) { + std::shared_ptr mgr = nullptr; + if (type == ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_COPY) + mgr = std::make_shared(); + else if (type == ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_REFERENCE) + mgr = std::make_shared(); + else if (type == ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_SCALE) + mgr = std::make_shared(); + else if (type == ESPLUSPLAYER_DECODED_VIDEO_FRAME_BUFFER_TYPE_MANUAL_COPY) + mgr = std::make_shared( + [handle](esplusplayer_decoded_video_packet* pkt) { + esplusplayer::DecodedVideoPacket _pkt; + _pkt.pts = pkt->pts; + _pkt.duration = pkt->duration; + _pkt.surface_data = static_cast(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(type)); + + auto priv = static_cast(handle); + priv->decoded_pkt_mgr = ::CreateDecodedPacketManager(handle, type); + priv->listener->SetDecodedPacketManager(priv->decoded_pkt_mgr); + + auto ret = cast_(handle)->SetVideoFrameBufferType( + static_cast(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(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(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(option), size); + cast_(handle)->SetBufferSize(static_cast(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(_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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(policy)); + + auto ret = cast_(handle)->SetResourceAllocatePolicy( + static_cast(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(type)); + + auto ret = + cast_(handle)->SetVideoScanType(static_cast(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(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(type)); + auto ret = + cast_(handle)->SetTimeUnitType(static_cast(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(rotation)); + return convert_return_type_( + cast_(handle)->SetVideoStreamRotationInfo(static_cast(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(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((level))); + + return convert_return_type_(cast_(handle)->SetSimpleMixOutBufferLevel( + static_cast(level))); +} diff --git a/src/mixer/CMakeLists.txt b/src/mixer/CMakeLists.txt new file mode 100644 index 0000000..85c8dee --- /dev/null +++ b/src/mixer/CMakeLists.txt @@ -0,0 +1,60 @@ +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/ +) diff --git a/src/mixer/include_internal/mixer/abs_videoframe.h b/src/mixer/include_internal/mixer/abs_videoframe.h new file mode 100644 index 0000000..c9c9186 --- /dev/null +++ b/src/mixer/include_internal/mixer/abs_videoframe.h @@ -0,0 +1,41 @@ +#ifndef __ESPLUSPLAYER_ABSTRACT_MIXER_VIDEO_FRAME_H__ +#define __ESPLUSPLAYER_ABSTRACT_MIXER_VIDEO_FRAME_H__ + +#include + +#include "mixer/interfaces/videoplanecollection.h" +#include "mixer/interfaces/videoplanemanipulable.h" + +namespace esplusplayer { + +class AbstractVideoFrame : public VideoPlaneCollection { + public: + using VideoPlaneManipulablePtr = std::unique_ptr; + + public: + explicit AbstractVideoFrame() = default; + virtual ~AbstractVideoFrame() = default; + + public: + virtual const std::vector 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 planes_; +}; +} // namespace esplusplayer + +#endif \ No newline at end of file diff --git a/src/mixer/include_internal/mixer/defaultmixer.h b/src/mixer/include_internal/mixer/defaultmixer.h new file mode 100644 index 0000000..679165e --- /dev/null +++ b/src/mixer/include_internal/mixer/defaultmixer.h @@ -0,0 +1,146 @@ +// +// @ Copyright [2020] +// + +#ifndef __ESPLUSPLAYER_SRC_PLAYER_DEFAULTMIXER__H__ +#define __ESPLUSPLAYER_SRC_PLAYER_DEFAULTMIXER__H__ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; + + 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 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_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 diff --git a/src/mixer/include_internal/mixer/interfaces/accessiblebuffer.h b/src/mixer/include_internal/mixer/interfaces/accessiblebuffer.h new file mode 100644 index 0000000..8859ec8 --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/accessiblebuffer.h @@ -0,0 +1,19 @@ +#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_ACCESSIBLE_BUFFER_H__ +#define __ESPLUSPLAYER_MIXER_INTERFACES_ACCESSIBLE_BUFFER_H__ + +#include + +#include "mixer/interfaces/bufferobject.h" +#include "mixer/interfaces/phyaddraccessor.h" + +namespace esplusplayer { +struct AccessibleBuffer { + using PhyAddrAccessorPtr = std::unique_ptr; + + 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 diff --git a/src/mixer/include_internal/mixer/interfaces/bufferobject.h b/src/mixer/include_internal/mixer/interfaces/bufferobject.h new file mode 100644 index 0000000..f10daf3 --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/bufferobject.h @@ -0,0 +1,16 @@ +#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 diff --git a/src/mixer/include_internal/mixer/interfaces/memoryallocator.h b/src/mixer/include_internal/mixer/interfaces/memoryallocator.h new file mode 100644 index 0000000..13c42c9 --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/memoryallocator.h @@ -0,0 +1,17 @@ +#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_MEMORY_OPERATOR_H__ +#define __ESPLUSPLAYER_MIXER_INTERFACES_MEMORY_OPERATOR_H__ + +#include +#include + +#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 diff --git a/src/mixer/include_internal/mixer/interfaces/phyaddraccessor.h b/src/mixer/include_internal/mixer/interfaces/phyaddraccessor.h new file mode 100644 index 0000000..ae4a085 --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/phyaddraccessor.h @@ -0,0 +1,13 @@ +#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 diff --git a/src/mixer/include_internal/mixer/interfaces/renderableobj_factory.h b/src/mixer/include_internal/mixer/interfaces/renderableobj_factory.h new file mode 100644 index 0000000..bc2a5a4 --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/renderableobj_factory.h @@ -0,0 +1,14 @@ +#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 diff --git a/src/mixer/include_internal/mixer/interfaces/renderableobject.h b/src/mixer/include_internal/mixer/interfaces/renderableobject.h new file mode 100644 index 0000000..5f64ac0 --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/renderableobject.h @@ -0,0 +1,33 @@ +#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_RENDERABLE_OBJECT_H__ +#define __ESPLUSPLAYER_MIXER_INTERFACES_RENDERABLE_OBJECT_H__ + +#include +#include +#include + +#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; + 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& 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 diff --git a/src/mixer/include_internal/mixer/interfaces/videoplanecollection.h b/src/mixer/include_internal/mixer/interfaces/videoplanecollection.h new file mode 100644 index 0000000..f1269ba --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/videoplanecollection.h @@ -0,0 +1,16 @@ +#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLLECTION_H__ +#define __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLLECTION_H__ + +#include + +#include "mixer/types/videoplanemanipinfo.h" + +namespace esplusplayer { +struct VideoPlaneCollection { + virtual ~VideoPlaneCollection() = default; + virtual const std::vector GetVideoPlaneManipInfo() + const = 0; +}; +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLLECTION_H__ \ No newline at end of file diff --git a/src/mixer/include_internal/mixer/interfaces/videoplanecolorfiller.h b/src/mixer/include_internal/mixer/interfaces/videoplanecolorfiller.h new file mode 100644 index 0000000..42db37c --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/videoplanecolorfiller.h @@ -0,0 +1,17 @@ +#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLOR_FILLER_H__ +#define __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_COLOR_FILLER_H__ + +#include + +#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 diff --git a/src/mixer/include_internal/mixer/interfaces/videoplanecopier.h b/src/mixer/include_internal/mixer/interfaces/videoplanecopier.h new file mode 100644 index 0000000..36f92d0 --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/videoplanecopier.h @@ -0,0 +1,13 @@ +#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 diff --git a/src/mixer/include_internal/mixer/interfaces/videoplanemanipulable.h b/src/mixer/include_internal/mixer/interfaces/videoplanemanipulable.h new file mode 100644 index 0000000..9a4205a --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/videoplanemanipulable.h @@ -0,0 +1,18 @@ +#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__ diff --git a/src/mixer/include_internal/mixer/interfaces/videoplanemanipulator.h b/src/mixer/include_internal/mixer/interfaces/videoplanemanipulator.h new file mode 100644 index 0000000..e605ca6 --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/videoplanemanipulator.h @@ -0,0 +1,22 @@ +#ifndef __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_MANIPULATOR_H__ +#define __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_MANIPULATOR_H__ + +#include + +#include "mixer/types/planecomponent.h" +#include "mixer/types/videoplanemanipinfo.h" + +namespace esplusplayer { + +template +struct VideoPlaneManipulatorWithType { + virtual bool Do(const T&, const VideoPlaneManipulableInfo&) const = 0; +}; + +using VideoPlaneManipulator = + VideoPlaneManipulatorWithType; + +using VideoPlaneColorManipulator = VideoPlaneManipulatorWithType; +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_MIXER_INTERFACES_VIDEO_PLANE_MANIPULATOR_H__ \ No newline at end of file diff --git a/src/mixer/include_internal/mixer/interfaces/videoplanescaler.h b/src/mixer/include_internal/mixer/interfaces/videoplanescaler.h new file mode 100644 index 0000000..6b24256 --- /dev/null +++ b/src/mixer/include_internal/mixer/interfaces/videoplanescaler.h @@ -0,0 +1,13 @@ +#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 diff --git a/src/mixer/include_internal/mixer/mixedframe.h b/src/mixer/include_internal/mixer/mixedframe.h new file mode 100644 index 0000000..f4ef018 --- /dev/null +++ b/src/mixer/include_internal/mixer/mixedframe.h @@ -0,0 +1,63 @@ +#ifndef __ESPLUSPLAYER_MIXER_MIXED_FRAME_H__ +#define __ESPLUSPLAYER_MIXER_MIXED_FRAME_H__ + +#include +#include + +#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; + using BufferObjectPtr = std::unique_ptr; + + 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 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& 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 diff --git a/src/mixer/include_internal/mixer/renderer.h b/src/mixer/include_internal/mixer/renderer.h new file mode 100644 index 0000000..703caaa --- /dev/null +++ b/src/mixer/include_internal/mixer/renderer.h @@ -0,0 +1,94 @@ +#ifndef __ESPLUSPLAYER_MIXER_RENDERER_H__ +#define __ESPLUSPLAYER_MIXER_RENDERER_H__ + +#include +#include +#include +#include +#include +#include + +#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; + + 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& 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& planes, + const Geometry& geom); + virtual bool MuteInternal_(const VideoPlaneColorManipulator* const filler, + const Geometry& geom); + virtual bool MoveInternal_( + const VideoPlaneColorManipulator* const filler, + const std::vector& 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 diff --git a/src/mixer/include_internal/mixer/rendererwithdoublebuf.h b/src/mixer/include_internal/mixer/rendererwithdoublebuf.h new file mode 100644 index 0000000..c3925c8 --- /dev/null +++ b/src/mixer/include_internal/mixer/rendererwithdoublebuf.h @@ -0,0 +1,154 @@ +#ifndef __ESPLUSPLAYER_MIXER_RENDERER_WITH_DOUBLE_BUFFERING_H__ +#define __ESPLUSPLAYER_MIXER_RENDERER_WITH_DOUBLE_BUFFERING_H__ + +#include +#include +#include + +#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( + 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 lk(rendering_mtx_); + if (IsValid() == false) return false; + // LOG_INFO("[PERF] OnRenderingBefore_ Lock [%llu]ms", + // std::chrono::duration_cast( + // 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::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 lk(rendering_mtx_); + frame_.reset(nullptr); + frame_ = MixedFramePtr(dynamic_cast( + mf_factory.CreateRenderableObject(width, height))); + } + return Renderer::ChangeResolutionInternal_(mf_factory, width, height); + } + + virtual bool RenderInternal_( + const VideoPlaneManipulator* const scaler, + const std::vector& planes, + const Geometry& geom) override { + if (scaler == nullptr) return false; + auto before = std::chrono::system_clock::now(); + std::unique_lock 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::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 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& moving_planes) override { + if (filler == nullptr) return false; + + AcquireRenderingLock_(); + BOOST_SCOPE_EXIT(this_) { this_->ReleaseRenderingLock_(); } + BOOST_SCOPE_EXIT_END + + std::unique_lock lk(rendering_mtx_); + if (IsValid() == false) return false; + + auto* mixedframe = dynamic_cast(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__ diff --git a/src/mixer/include_internal/mixer/sys/tbminterface.h b/src/mixer/include_internal/mixer/sys/tbminterface.h new file mode 100644 index 0000000..fd86f3a --- /dev/null +++ b/src/mixer/include_internal/mixer/sys/tbminterface.h @@ -0,0 +1,29 @@ +#ifndef __ESPLUSPLAYER_MIXER_SYS_TBM_INTERFACE_H__ +#define __ESPLUSPLAYER_MIXER_SYS_TBM_INTERFACE_H__ + +#include + +#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 diff --git a/src/mixer/include_internal/mixer/tizen/tizenaccessiblebufferobj.h b/src/mixer/include_internal/mixer/tizen/tizenaccessiblebufferobj.h new file mode 100644 index 0000000..3dbe884 --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizenaccessiblebufferobj.h @@ -0,0 +1,19 @@ +#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 diff --git a/src/mixer/include_internal/mixer/tizen/tizenbufferkeyvideoframe.h b/src/mixer/include_internal/mixer/tizen/tizenbufferkeyvideoframe.h new file mode 100644 index 0000000..d06faa2 --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizenbufferkeyvideoframe.h @@ -0,0 +1,36 @@ +#ifndef __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_KEY_VIDEO_SURFACE_H__ +#define __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_KEY_VIDEO_SURFACE_H__ + +#include + +#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; + + 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 diff --git a/src/mixer/include_internal/mixer/tizen/tizenbuffermgr.h b/src/mixer/include_internal/mixer/tizen/tizenbuffermgr.h new file mode 100644 index 0000000..f12d832 --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizenbuffermgr.h @@ -0,0 +1,291 @@ +#ifndef __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_MANAGER_H__ +#define __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_MANAGER_H__ + +#include + +#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 +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; + +/****************************************************************************** + * Body + */ +template +BufferManagerWithType::BufferManagerWithType() + : scaler_(this), copier_(this), color_filler_(this) { + bufmgr_ = ::kGlobalInstance.bufmgr; +} + +template +BufferObject* BufferManagerWithType::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 +const VideoPlaneManipulator* const +BufferManagerWithType::GetScaleManipulator() const { + return &scaler_; +} + +template +const VideoPlaneManipulator* const +BufferManagerWithType::GetCopyManipulator() const { + return &copier_; +} + +template +const VideoPlaneColorManipulator* const +BufferManagerWithType::GetColorFillManipulator() const { + return &color_filler_; +} + +template +bool BufferManagerWithType::IsValid() const { + if (bufmgr_ == nullptr) return false; + return true; +} + +template +BufferObject* BufferManagerWithType::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 +bool BufferManagerWithType::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 +bool BufferManagerWithType::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 +bool BufferManagerWithType::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 +BufferManagerWithType::Scaler::Scaler( + const BufferManagerWithType* const bufmgr) + : bufmgr_(bufmgr) {} + +template +bool BufferManagerWithType::Scaler::Do( + const VideoPlaneManipulableInfo& src, + const VideoPlaneManipulableInfo& dest) const { + return bufmgr_->Scale_(src, dest); +} + +/****************************************************************************** + * Body for copier + */ +template +BufferManagerWithType::Copier::Copier( + const BufferManagerWithType* const bufmgr) + : bufmgr_(bufmgr) {} + +template +bool BufferManagerWithType::Copier::Do( + const VideoPlaneManipulableInfo& src, + const VideoPlaneManipulableInfo& dest) const { + return bufmgr_->Copy_(src, dest); +} + +/****************************************************************************** + * Body for color filler + */ +template +BufferManagerWithType::ColorFiller::ColorFiller( + const BufferManagerWithType* const bufmgr) + : bufmgr_(bufmgr) {} + +template +bool BufferManagerWithType::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 diff --git a/src/mixer/include_internal/mixer/tizen/tizenbufferobj.h b/src/mixer/include_internal/mixer/tizen/tizenbufferobj.h new file mode 100644 index 0000000..b8601fd --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizenbufferobj.h @@ -0,0 +1,72 @@ +#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 +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; + +/****************************************************************************** + * Body + */ +template +BufferObjectWithType::BufferObjectWithType(BufferDefaultType buffer) { + if (buffer == nullptr) return; + buffer_ = TBM::BoRef(buffer); +} + +template +BufferObjectWithType::~BufferObjectWithType() { + if (buffer_) TBM::BoUnRef(buffer_); +} + +template +bool BufferObjectWithType::IsValid() const { + if (buffer_ == nullptr) return false; + return true; +} + +template +BufferHandleType BufferObjectWithType::GetBufferHandle() const { + if (IsValid() == false) return kInvalidBufferHandle; + auto handle = TBM::BoGetHandle(buffer_, TBM_DEVICE_2D); + return handle.u32; +} + +template +BufferKeyType BufferObjectWithType::Export() const { + if (IsValid() == false) return kInvalidBufferKey; + return TBM::BoExport(buffer_); +} + +template +std::uint32_t BufferObjectWithType::GetSize() const { + if (IsValid() == false) return kInvalidBufferSize; + return TBM::BoSize(buffer_); +} + +template +const BufferDefaultType& BufferObjectWithType::GetBuffer_() const { + return buffer_; +} +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_MIXER_TIZEN_TIZEN_BUFFER_OBJECT_H__ \ No newline at end of file diff --git a/src/mixer/include_internal/mixer/tizen/tizenbufferphyaddraccessor.h b/src/mixer/include_internal/mixer/tizen/tizenbufferphyaddraccessor.h new file mode 100644 index 0000000..bc0a0ea --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizenbufferphyaddraccessor.h @@ -0,0 +1,69 @@ +#ifndef __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_PHYSICAL_ADDRESS_ACCESOR_H__ +#define __ESPLUSPLAYER_MIXER_TIZEN_BUFFER_PHYSICAL_ADDRESS_ACCESOR_H__ + +#include + +#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 diff --git a/src/mixer/include_internal/mixer/tizen/tizendefaultphyaddraccessor.h b/src/mixer/include_internal/mixer/tizen/tizendefaultphyaddraccessor.h new file mode 100644 index 0000000..3b184fb --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizendefaultphyaddraccessor.h @@ -0,0 +1,20 @@ +#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 diff --git a/src/mixer/include_internal/mixer/tizen/tizenhwbufferobj.h b/src/mixer/include_internal/mixer/tizen/tizenhwbufferobj.h new file mode 100644 index 0000000..1c18a34 --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizenhwbufferobj.h @@ -0,0 +1,27 @@ +#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 diff --git a/src/mixer/include_internal/mixer/tizen/tizenhwvideoframe.h b/src/mixer/include_internal/mixer/tizen/tizenhwvideoframe.h new file mode 100644 index 0000000..12f2b9a --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizenhwvideoframe.h @@ -0,0 +1,32 @@ +#ifndef __ESPLUSPLAYER_MIXER_TIZEN_HW_VIDEO_FRAME_H__ +#define __ESPLUSPLAYER_MIXER_TIZEN_HW_VIDEO_FRAME_H__ + +#include + +#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; + + 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 diff --git a/src/mixer/include_internal/mixer/tizen/tizenrenderableobj_factory.h b/src/mixer/include_internal/mixer/tizen/tizenrenderableobj_factory.h new file mode 100644 index 0000000..b8de701 --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizenrenderableobj_factory.h @@ -0,0 +1,25 @@ +#ifndef __ESPLUSPLAYER_MIXER_TIZEN_RENDERABLE_OBJECT_FACTORY_H__ +#define __ESPLUSPLAYER_MIXER_TIZEN_RENDERABLE_OBJECT_FACTORY_H__ + +#include + +#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 diff --git a/src/mixer/include_internal/mixer/tizen/tizensurfacevideoframe.h b/src/mixer/include_internal/mixer/tizen/tizensurfacevideoframe.h new file mode 100644 index 0000000..b4fc5a1 --- /dev/null +++ b/src/mixer/include_internal/mixer/tizen/tizensurfacevideoframe.h @@ -0,0 +1,34 @@ +#ifndef __ESPLUSPLAYER_MIXER_TIZEN_SURFACE_VIDEO_FRAME_H__ +#define __ESPLUSPLAYER_MIXER_TIZEN_SURFACE_VIDEO_FRAME_H__ + +#include + +#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; + + 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 diff --git a/src/mixer/include_internal/mixer/types/buffertype.h b/src/mixer/include_internal/mixer/types/buffertype.h new file mode 100644 index 0000000..43d56d0 --- /dev/null +++ b/src/mixer/include_internal/mixer/types/buffertype.h @@ -0,0 +1,23 @@ +#ifndef __ESPLUSPLAYER_MIXER_TYPES_BUFFER_TYPE_H__ +#define __ESPLUSPLAYER_MIXER_TYPES_BUFFER_TYPE_H__ + +#include + +#include + +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 diff --git a/src/mixer/include_internal/mixer/types/planecomponent.h b/src/mixer/include_internal/mixer/types/planecomponent.h new file mode 100644 index 0000000..1db1536 --- /dev/null +++ b/src/mixer/include_internal/mixer/types/planecomponent.h @@ -0,0 +1,13 @@ +#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 diff --git a/src/mixer/include_internal/mixer/types/videoplanemanipinfo.h b/src/mixer/include_internal/mixer/types/videoplanemanipinfo.h new file mode 100644 index 0000000..12ebaa4 --- /dev/null +++ b/src/mixer/include_internal/mixer/types/videoplanemanipinfo.h @@ -0,0 +1,21 @@ +#ifndef __ESPLUSPLAYER_MIXER_TYPES_VIDEO_PLANE_MAINIPULABLE_INFO_H__ +#define __ESPLUSPLAYER_MIXER_TYPES_VIDEO_PLANE_MAINIPULABLE_INFO_H__ + +#include + +#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 diff --git a/src/mixer/include_internal/mixer/types/videoplanemoveinfo.h b/src/mixer/include_internal/mixer/types/videoplanemoveinfo.h new file mode 100644 index 0000000..02a7d98 --- /dev/null +++ b/src/mixer/include_internal/mixer/types/videoplanemoveinfo.h @@ -0,0 +1,17 @@ +#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 diff --git a/src/mixer/include_internal/mixer/videoplane.h b/src/mixer/include_internal/mixer/videoplane.h new file mode 100644 index 0000000..45a1376 --- /dev/null +++ b/src/mixer/include_internal/mixer/videoplane.h @@ -0,0 +1,116 @@ +#ifndef __ESPLUSPLAYER_MIXER_VIDEO_PLANE_H__ +#define __ESPLUSPLAYER_MIXER_VIDEO_PLANE_H__ + +#include +#include + +#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; + + 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; + + 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; + using BufferObjectWeakPtr = std::weak_ptr; + + 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; + using BufferObjectWeakPtr = std::weak_ptr; + + 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 diff --git a/src/mixer/src/abs_videoframe.cpp b/src/mixer/src/abs_videoframe.cpp new file mode 100644 index 0000000..d29e7b8 --- /dev/null +++ b/src/mixer/src/abs_videoframe.cpp @@ -0,0 +1,46 @@ +#include "mixer/abs_videoframe.h" + +namespace esplusplayer { + +const std::vector +AbstractVideoFrame::GetVideoPlaneManipInfo() const { + if (IsValid() == false) return {}; + std::vector 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 diff --git a/src/mixer/src/defaultmixer.cpp b/src/mixer/src/defaultmixer.cpp new file mode 100644 index 0000000..b1864eb --- /dev/null +++ b/src/mixer/src/defaultmixer.cpp @@ -0,0 +1,655 @@ +#include "mixer/defaultmixer.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#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 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 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 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 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 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& 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 lock(ticket_lock_); + std::vector 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 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 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 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(userdata); + if (!mixer) return; + mixer->listener_->OnResourceConflicted(); +} +void DefaultMixer::ErrorCb_(const TrackRendererErrorType error_code, + UserData userdata) { + auto mixer = static_cast(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(ResourceType::kHwMain); + type < static_cast(ResourceType::kNdecoder); type++) { + Resource resource; + resource.category = ResourceCategory::kVideoDecoder; + resource.type = static_cast(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 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(*type)); + return true; + } + } + LOG_ERROR("no available resource type"); + return false; +} + +bool DefaultMixer::Ticket::IsAudioFocusHandler() { + std::lock_guard lock(handler_->ticket_lock_); + return handler_->enable_audio_focus_setting_; +} + +bool DefaultMixer::Ticket::IsRscAllocHandler() { + std::lock_guard lock(handler_->ticket_lock_); + return handler_->resource_allocation_mode_ != RscAllocMode::kDisable; +} + +bool DefaultMixer::Ticket::Alloc(const ResourceCategory& category, + const ResourceType& type) { + std::lock_guard 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( + 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(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 diff --git a/src/mixer/src/mixedframe.cpp b/src/mixer/src/mixedframe.cpp new file mode 100644 index 0000000..de60607 --- /dev/null +++ b/src/mixer/src/mixedframe.cpp @@ -0,0 +1,143 @@ +#include "mixer/mixedframe.h" + +#include + +#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(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(ptr->GetAddress()), (char)0x00, y_size); + std::memset(static_cast(ptr->GetAddress()) + y_size, (char)0x80, + uv_size); + } + LOG_DEBUG("MixedFrame UV memset done"); + } +} + +const std::vector +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& 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 diff --git a/src/mixer/src/mixer.cpp b/src/mixer/src/mixer.cpp new file mode 100644 index 0000000..eb26816 --- /dev/null +++ b/src/mixer/src/mixer.cpp @@ -0,0 +1,14 @@ +#include "mixer/mixer.h" + +#include "core/utils/plusplayer_log.h" +#include "mixer/defaultmixer.h" + +namespace esplusplayer { + +std::unique_ptr Mixer::Create() { + auto instance = std::unique_ptr(new DefaultMixer); + LOG_INFO("Create Mixer [%p]", instance.get()); + return instance; +} + +} // namespace esplusplayer diff --git a/src/mixer/src/mixer_capi.cpp b/src/mixer/src/mixer_capi.cpp new file mode 100644 index 0000000..3c5dd62 --- /dev/null +++ b/src/mixer/src/mixer_capi.cpp @@ -0,0 +1,206 @@ +#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; + std::unique_ptr 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(mixer); + return priv->mixer.get(); +} + +inline listener_bridge* listener_cast_(mixer_handle pp) { + auto priv = static_cast(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(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(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(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(mode)); + return convert_return_type_(cast_(handle)->SetDisplayMode( + static_cast(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(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); +} diff --git a/src/mixer/src/renderer.cpp b/src/mixer/src/renderer.cpp new file mode 100644 index 0000000..72e3865 --- /dev/null +++ b/src/mixer/src/renderer.cpp @@ -0,0 +1,252 @@ +#include "mixer/renderer.h" +#include +#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 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 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(resolution_info_.width) && + height == static_cast(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 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(resolution_info_.framerate_num) && + framerate_den == + static_cast(resolution_info_.framerate_den)) + return false; + std::unique_lock 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& planes, + const Geometry& geom) { + if (scaler == nullptr) return false; + const auto before = system_clock::now(); + std::unique_lock lk(rendering_mtx_); + if (IsValid() == false) return false; + LOG_INFO("[PERF] RenderInternal_ Lock [%" PRId64"]ms", + std::chrono::duration_cast( + system_clock::now() - before) + .count()); + const auto ret = frame_->Render(scaler, planes, geom); + LOG_INFO("[PERF] Scale [%" PRId64"]ms", + std::chrono::duration_cast( + 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 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& 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& moving_planes) { + if (filler == nullptr) return false; + std::unique_lock 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(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 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(system_clock::now() - before_1).count()); + jitter += duration_cast(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(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(system_clock::now() - before_2 - + milliseconds(next_in_ms)); + // LOG_DEBUG( + // "[PERF] before_2 ~ now : [%lld]ms / next_in_ms : [%llu]ms", + // duration_cast(system_clock::now() - before_2).count(), + // next_in_ms); + } + std::unique_lock 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 diff --git a/src/mixer/src/sys/tbminterface.cpp b/src/mixer/src/sys/tbminterface.cpp new file mode 100644 index 0000000..56582f2 --- /dev/null +++ b/src/mixer/src/sys/tbminterface.cpp @@ -0,0 +1,50 @@ +#include "mixer/sys/tbminterface.h" + +#include +#include + +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 diff --git a/src/mixer/src/tizen/tizenaccessiblebufferobj.cpp b/src/mixer/src/tizen/tizenaccessiblebufferobj.cpp new file mode 100644 index 0000000..d1025f6 --- /dev/null +++ b/src/mixer/src/tizen/tizenaccessiblebufferobj.cpp @@ -0,0 +1,23 @@ +#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 diff --git a/src/mixer/src/tizen/tizenbufferkeyvideoframe.cpp b/src/mixer/src/tizen/tizenbufferkeyvideoframe.cpp new file mode 100644 index 0000000..12e184a --- /dev/null +++ b/src/mixer/src/tizen/tizenbufferkeyvideoframe.cpp @@ -0,0 +1,36 @@ +#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 diff --git a/src/mixer/src/tizen/tizendefaultphyaddraccessor.cpp b/src/mixer/src/tizen/tizendefaultphyaddraccessor.cpp new file mode 100644 index 0000000..3a5010f --- /dev/null +++ b/src/mixer/src/tizen/tizendefaultphyaddraccessor.cpp @@ -0,0 +1,12 @@ +#include "mixer/tizen/tizendefaultphyaddraccessor.h" + +namespace esplusplayer { + +TizenDefaultPhyAddrAccessor::TizenDefaultPhyAddrAccessor(std::uint32_t viraddr) + : viraddr_(viraddr) {} + +BufferPhysicalAddrType TizenDefaultPhyAddrAccessor::GetAddress() { + return reinterpret_cast(viraddr_); +} + +} // namespace esplusplayer \ No newline at end of file diff --git a/src/mixer/src/tizen/tizenhwbufferobj.cpp b/src/mixer/src/tizen/tizenhwbufferobj.cpp new file mode 100644 index 0000000..2729c3d --- /dev/null +++ b/src/mixer/src/tizen/tizenhwbufferobj.cpp @@ -0,0 +1,29 @@ +#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(info_.phyaddr); +} + +BufferKeyType TizenHWBufferObject::Export() const { return kInvalidBufferKey; } + +std::uint32_t TizenHWBufferObject::GetSize() const { + return height_ * info_.linesize; +} +// LCOV_EXCL_STOP +} // namespace esplusplayer diff --git a/src/mixer/src/tizen/tizenhwvideoframe.cpp b/src/mixer/src/tizen/tizenhwvideoframe.cpp new file mode 100644 index 0000000..5e21e1d --- /dev/null +++ b/src/mixer/src/tizen/tizenhwvideoframe.cpp @@ -0,0 +1,46 @@ +#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 diff --git a/src/mixer/src/tizen/tizenrenderableobj_factory.cpp b/src/mixer/src/tizen/tizenrenderableobj_factory.cpp new file mode 100644 index 0000000..f33ee62 --- /dev/null +++ b/src/mixer/src/tizen/tizenrenderableobj_factory.cpp @@ -0,0 +1,16 @@ +#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 diff --git a/src/mixer/src/tizen/tizensurfacevideoframe.cpp b/src/mixer/src/tizen/tizensurfacevideoframe.cpp new file mode 100644 index 0000000..f1fcefc --- /dev/null +++ b/src/mixer/src/tizen/tizensurfacevideoframe.cpp @@ -0,0 +1,45 @@ +#include "mixer/tizen/tizensurfacevideoframe.h" + +#include +#include + +#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 diff --git a/src/mixer/src/videoplane.cpp b/src/mixer/src/videoplane.cpp new file mode 100644 index 0000000..e0a144a --- /dev/null +++ b/src/mixer/src/videoplane.cpp @@ -0,0 +1,116 @@ +#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 diff --git a/src/plusplayer-core/Build/appendix.mk b/src/plusplayer-core/Build/appendix.mk new file mode 100644 index 0000000..fad5ae4 --- /dev/null +++ b/src/plusplayer-core/Build/appendix.mk @@ -0,0 +1 @@ +# Appendix diff --git a/src/plusplayer-core/Build/basedef.mk b/src/plusplayer-core/Build/basedef.mk new file mode 100644 index 0000000..a762983 --- /dev/null +++ b/src/plusplayer-core/Build/basedef.mk @@ -0,0 +1,34 @@ +# 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 diff --git a/src/plusplayer-core/Build/build_c.mk b/src/plusplayer-core/Build/build_c.mk new file mode 100644 index 0000000..5fffeea --- /dev/null +++ b/src/plusplayer-core/Build/build_c.mk @@ -0,0 +1,113 @@ +# 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 diff --git a/src/plusplayer-core/Build/build_edc.mk b/src/plusplayer-core/Build/build_edc.mk new file mode 100644 index 0000000..6f85fdd --- /dev/null +++ b/src/plusplayer-core/Build/build_edc.mk @@ -0,0 +1,81 @@ +# 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 diff --git a/src/plusplayer-core/Build/build_po.mk b/src/plusplayer-core/Build/build_po.mk new file mode 100644 index 0000000..d88d71a --- /dev/null +++ b/src/plusplayer-core/Build/build_po.mk @@ -0,0 +1,64 @@ +# 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 diff --git a/src/plusplayer-core/Build/flags.mk b/src/plusplayer-core/Build/flags.mk new file mode 100644 index 0000000..967a0cc --- /dev/null +++ b/src/plusplayer-core/Build/flags.mk @@ -0,0 +1,23 @@ +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 = diff --git a/src/plusplayer-core/Build/funcs.mk b/src/plusplayer-core/Build/funcs.mk new file mode 100644 index 0000000..3ba778b --- /dev/null +++ b/src/plusplayer-core/Build/funcs.mk @@ -0,0 +1,50 @@ + +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) diff --git a/src/plusplayer-core/Build/makefile b/src/plusplayer-core/Build/makefile new file mode 100644 index 0000000..117b240 --- /dev/null +++ b/src/plusplayer-core/Build/makefile @@ -0,0 +1,34 @@ +# +# Usege : make -f /Build/makefile -C +# + +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 diff --git a/src/plusplayer-core/Build/makefile.mk b/src/plusplayer-core/Build/makefile.mk new file mode 100644 index 0000000..4b93f37 --- /dev/null +++ b/src/plusplayer-core/Build/makefile.mk @@ -0,0 +1,206 @@ +# +# Usege : make -f /Build/makefile -C +# + +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) diff --git a/src/plusplayer-core/Build/platform.mk b/src/plusplayer-core/Build/platform.mk new file mode 100644 index 0000000..3895d90 --- /dev/null +++ b/src/plusplayer-core/Build/platform.mk @@ -0,0 +1,18 @@ +# 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" diff --git a/src/plusplayer-core/Build/tooldef.mk b/src/plusplayer-core/Build/tooldef.mk new file mode 100644 index 0000000..c62243c --- /dev/null +++ b/src/plusplayer-core/Build/tooldef.mk @@ -0,0 +1,70 @@ +# 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 + diff --git a/src/plusplayer-core/CMakeLists.txt b/src/plusplayer-core/CMakeLists.txt new file mode 100644 index 0000000..a130592 --- /dev/null +++ b/src/plusplayer-core/CMakeLists.txt @@ -0,0 +1,68 @@ +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/ +) diff --git a/src/plusplayer-core/build_def.prop b/src/plusplayer-core/build_def.prop new file mode 100644 index 0000000..a9244e0 --- /dev/null +++ b/src/plusplayer-core/build_def.prop @@ -0,0 +1,6 @@ + +# 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 diff --git a/src/plusplayer-core/include_internal/core/decodedvideorawmodepacket.h b/src/plusplayer-core/include_internal/core/decodedvideorawmodepacket.h new file mode 100644 index 0000000..4093b86 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/decodedvideorawmodepacket.h @@ -0,0 +1,40 @@ +// +// @ Copyright [2020] +// + +#ifndef __ESPLUSPLAYER_SRC_CORE_DECODED_RAW_MODE_PACKET_H__ +#define __ESPLUSPLAYER_SRC_CORE_DECODED_RAW_MODE_PACKET_H__ + +#include + +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 diff --git a/src/plusplayer-core/include_internal/core/decoderinputbuffer.h b/src/plusplayer-core/include_internal/core/decoderinputbuffer.h new file mode 100644 index 0000000..aa145ff --- /dev/null +++ b/src/plusplayer-core/include_internal/core/decoderinputbuffer.h @@ -0,0 +1,139 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_CORE_DECODERINPUTBUFFER_H__ +#define __ESPLUSPLAYER_SRC_CORE_DECODERINPUTBUFFER_H__ + +#include +#include +#include +#include + +#include "gst/gst.h" +// temporary until drmdecrypt platform interfaces are added into rootstrap +#ifndef PLUPLAYER_DOWNLOADABLE_APP_TVPLUS +#include +#endif + +#include "esplusplayer/track.h" + +namespace esplusplayer { + +class DecoderInputBuffer : private boost::noncopyable { + public: + using Ptr = std::unique_ptr; + + 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(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& queue); + +} // namespace decoderinputbuffer_util + +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_SRC_CORE_DECODERINPUTBUFFER_H__ diff --git a/src/plusplayer-core/include_internal/core/decoderinputbuffer_listener.h b/src/plusplayer-core/include_internal/core/decoderinputbuffer_listener.h new file mode 100644 index 0000000..2f4a685 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/decoderinputbuffer_listener.h @@ -0,0 +1,25 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_TRACKSOURCE_DECODERINPUTBUFFER_LISTENER_H__ +#define __ESPLUSPLAYER_SRC_TRACKSOURCE_DECODERINPUTBUFFER_LISTENER_H__ + +#include + +#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__ diff --git a/src/plusplayer-core/include_internal/core/error.h b/src/plusplayer-core/include_internal/core/error.h new file mode 100644 index 0000000..819df31 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/error.h @@ -0,0 +1,18 @@ +// +// @ Copyright [2017] +// + +#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__ diff --git a/src/plusplayer-core/include_internal/core/gst_utils.h b/src/plusplayer-core/include_internal/core/gst_utils.h new file mode 100644 index 0000000..4665212 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/gst_utils.h @@ -0,0 +1,26 @@ +// +// @ Copyright [2017] +// + +#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 diff --git a/src/plusplayer-core/include_internal/core/gstobject_guard.h b/src/plusplayer-core/include_internal/core/gstobject_guard.h new file mode 100644 index 0000000..a3837d1 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/gstobject_guard.h @@ -0,0 +1,66 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_CORE_GSTOBJECT_GUARD_H__ +#define __ESPLUSPLAYER_SRC_CORE_GSTOBJECT_GUARD_H__ + +#include +#include +#include "gst/gst.h" + +namespace esplusplayer { + +// +// +// 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 +using GstGuardPtr = std::unique_ptr>; +// +// 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 +GstGuardPtr make_guard(T* obj) { + return GstGuardPtr(obj, [](T* _obj) { CustomDeleter(_obj); }); +} + +} // namespace gstguard + +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_SRC_CORE_GSTOBJECT_GUARD_H__ diff --git a/src/plusplayer-core/include_internal/core/gstsignal_holder.h b/src/plusplayer-core/include_internal/core/gstsignal_holder.h new file mode 100644 index 0000000..03ece10 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/gstsignal_holder.h @@ -0,0 +1,42 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_CORE_GSTSIGNAL_HOLDER_H__ +#define __ESPLUSPLAYER_SRC_CORE_GSTSIGNAL_HOLDER_H__ + +#include +#include +#include +#include + +#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> signal_list_; +}; + +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_SRC_CORE_GSTSIGNAL_HOLDER_H__ diff --git a/src/plusplayer-core/include_internal/core/kpi.h b/src/plusplayer-core/include_internal/core/kpi.h new file mode 100644 index 0000000..28bc826 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/kpi.h @@ -0,0 +1,58 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_CORE_KPI_H__ +#define __ESPLUSPLAYER_SRC_CORE_KPI_H__ + +#include + +#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 diff --git a/src/plusplayer-core/include_internal/core/serializer.h b/src/plusplayer-core/include_internal/core/serializer.h new file mode 100644 index 0000000..1ed8cf4 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/serializer.h @@ -0,0 +1,58 @@ +// +// @ Copyright [2018] +// + +#ifndef __ESPLUSPLAYER_SRC_CORE_SERIALIZER_H__ +#define __ESPLUSPLAYER_SRC_CORE_SERIALIZER_H__ + +#include +#include +#include +#include + +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 + Offset Put(const T data) { + static_assert( + !std::is_pointer::value || !std::is_same::value, + "this type can't be serialized"); + Offset offset = static_cast(size_); + constexpr size_t size = sizeof(T); + const Byte *data_bytes = reinterpret_cast(&data); + Put_(data_bytes, size); + return offset; + } + Offset Put(const std::vector &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 + static void Put(Byte *bytes, const T value) { + constexpr size_t size = sizeof(T); + std::memcpy(bytes, reinterpret_cast(&value), size); + } + + private: + void Put_(const Byte *data_bytes, const size_t size); + + private: + std::basic_stringbuf buf_; + size_t size_; +}; +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_SRC_CORE_SERIALIZER_H__ diff --git a/src/plusplayer-core/include_internal/core/subtitle_attr_parser.h b/src/plusplayer-core/include_internal/core/subtitle_attr_parser.h new file mode 100644 index 0000000..de65701 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/subtitle_attr_parser.h @@ -0,0 +1,28 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_CORE_SUBTITLE_ATTR_PARSER_H__ +#define __ESPLUSPLAYER_SRC_CORE_SUBTITLE_ATTR_PARSER_H__ + +#include + +#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 diff --git a/src/plusplayer-core/include_internal/core/track_util.h b/src/plusplayer-core/include_internal/core/track_util.h new file mode 100644 index 0000000..7fa5744 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/track_util.h @@ -0,0 +1,35 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_CORE_TRACK_UTIL_H__ +#define __ESPLUSPLAYER_SRC_CORE_TRACK_UTIL_H__ + +#include + +#include "gst/gst.h" + +#include "esplusplayer/track.h" + +namespace esplusplayer { + +namespace track_util { + +bool GetActiveTrack(const std::vector& track_list, TrackType type, + Track* track); +bool GetActiveTrackList(const std::vector& tracklist, + std::vector& active_track); + +void ShowTrackInfo(const std::vector& 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__ diff --git a/src/plusplayer-core/include_internal/core/trackrendereradapter.h b/src/plusplayer-core/include_internal/core/trackrendereradapter.h new file mode 100644 index 0000000..da843ac --- /dev/null +++ b/src/plusplayer-core/include_internal/core/trackrendereradapter.h @@ -0,0 +1,300 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_PLAYER_TRACKRENDERERADAPTER_H__ +#define __ESPLUSPLAYER_SRC_PLAYER_TRACKRENDERERADAPTER_H__ + +#include +#include + +#include +#include +#include + +#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; + static Ptr Create(); + + ~TrackRendererAdapter(); + + bool Start(); + bool Stop(); + bool Prepare(); + bool Pause(); + bool Resume(); + bool SetTrack(const std::vector& trackinfo); + void SetIniProperty(const std::map& 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__ diff --git a/src/plusplayer-core/include_internal/core/trackrendereradapter_utils.h b/src/plusplayer-core/include_internal/core/trackrendereradapter_utils.h new file mode 100644 index 0000000..7e800bc --- /dev/null +++ b/src/plusplayer-core/include_internal/core/trackrendereradapter_utils.h @@ -0,0 +1,118 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_PLAYER_TRACKRENDERERADAPTER_UTILS_H__ +#define __ESPLUSPLAYER_PLAYER_TRACKRENDERERADAPTER_UTILS_H__ + +#include + +#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__ diff --git a/src/plusplayer-core/include_internal/core/utils/base64.h b/src/plusplayer-core/include_internal/core/utils/base64.h new file mode 100644 index 0000000..762fab4 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/utils/base64.h @@ -0,0 +1,22 @@ +// +// @ Copyright [2020] +// + +#ifndef __ESPLUSPLAYER_SRC_BASE64_H__ +#define __ESPLUSPLAYER_SRC_BASE64_H__ + +#include +#include +#include +#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__ diff --git a/src/plusplayer-core/include_internal/core/utils/caf_logger.h b/src/plusplayer-core/include_internal/core/utils/caf_logger.h new file mode 100644 index 0000000..f3c6805 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/utils/caf_logger.h @@ -0,0 +1,91 @@ +#ifndef _AVPLAY_CAF_LOGGER_H__ +#define _AVPLAY_CAF_LOGGER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 context_aware_; + + std::mutex object_lock_; + bool connected_to_dbus_; + bool msg_thread_stopped_; + std::queue msg_queue_; + std::mutex msg_task_mutex_; + std::condition_variable msg_task_cv_; + std::future msg_handler_task_; + std::string app_id_; + int unique_number; + std::queue 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&& context_aware); + ~CafLogger(); + }; + +} //plusplayer + +#endif //_AVPLAY_CAF_LOGGER_H__ diff --git a/src/plusplayer-core/include_internal/core/utils/performance_checker.h b/src/plusplayer-core/include_internal/core/utils/performance_checker.h new file mode 100644 index 0000000..9e6f0d7 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/utils/performance_checker.h @@ -0,0 +1,48 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_UTILS_PERFORMANCE_CHECKER_H__ +#define __ESPLUSPLAYER_SRC_UTILS_PERFORMANCE_CHECKER_H__ + +#include +#include + +#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(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__ diff --git a/src/plusplayer-core/include_internal/core/utils/plusplayer_cfg.h b/src/plusplayer-core/include_internal/core/utils/plusplayer_cfg.h new file mode 100644 index 0000000..df588d2 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/utils/plusplayer_cfg.h @@ -0,0 +1,17 @@ +// +// @ Copyright [2017] +// + +#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 diff --git a/src/plusplayer-core/include_internal/core/utils/plusplayer_log.h b/src/plusplayer-core/include_internal/core/utils/plusplayer_log.h new file mode 100644 index 0000000..f9a1285 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/utils/plusplayer_log.h @@ -0,0 +1,117 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_UTILS_PLUSPLAYER_LOG_H__ +#define __ESPLUSPLAYER_SRC_UTILS_PLUSPLAYER_LOG_H__ + +#include +#include + +#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__ diff --git a/src/plusplayer-core/include_internal/core/utils/scope_exit.h b/src/plusplayer-core/include_internal/core/utils/scope_exit.h new file mode 100644 index 0000000..ab784d1 --- /dev/null +++ b/src/plusplayer-core/include_internal/core/utils/scope_exit.h @@ -0,0 +1,36 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_SRC_UTILS_SCOPE_EXIT_H__ +#define __ESPLUSPLAYER_SRC_UTILS_SCOPE_EXIT_H__ + +namespace esplusplayer { + +namespace utils { + +template +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 +AtScopeExit ScopeExit(const T& func) { + return AtScopeExit(func); +} + +} // namespace utils + +} // namespace esplusplayer + +#endif // __ESPLUSPLAYER_SRC_UTILS_SCOPE_EXIT_H__ diff --git a/src/plusplayer-core/include_internal/core/videoframetypestrategy.h b/src/plusplayer-core/include_internal/core/videoframetypestrategy.h new file mode 100644 index 0000000..34327af --- /dev/null +++ b/src/plusplayer-core/include_internal/core/videoframetypestrategy.h @@ -0,0 +1,40 @@ +// +// @ Copyright [2020] +// + +#ifndef __ESPLUSPLAYER_SRC_CORE_VIDEO_FRAME_TYPE_STRATEGY_H__ +#define __ESPLUSPLAYER_SRC_CORE_VIDEO_FRAME_TYPE_STRATEGY_H__ + +#include + +#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; + +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 diff --git a/src/plusplayer-core/project_def.prop b/src/plusplayer-core/project_def.prop new file mode 100644 index 0000000..51cf809 --- /dev/null +++ b/src/plusplayer-core/project_def.prop @@ -0,0 +1,58 @@ + +# 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 = + diff --git a/src/plusplayer-core/src/caf_logger.cpp b/src/plusplayer-core/src/caf_logger.cpp new file mode 100644 index 0000000..5b4e5cb --- /dev/null +++ b/src/plusplayer-core/src/caf_logger.cpp @@ -0,0 +1,320 @@ +#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 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 guard(object_lock_); + + if(context_aware_ == NULL) { + context_aware_ = std::make_shared(); + } + + 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 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 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 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 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 guard(object_lock_); + if(msg_handler_task_.valid()) { + CafEventData event; + event.event_type = event_type; + event.event_data = data; + std::unique_lock 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 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 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 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&& context_aware) +{ + LOG_ENTER; + context_aware_ = context_aware; + LOG_LEAVE; +} +// LCOV_EXCL_STOP + +} //plusplayer diff --git a/src/plusplayer-core/src/decodedvideopacketex.cpp b/src/plusplayer-core/src/decodedvideopacketex.cpp new file mode 100644 index 0000000..81c0dfc --- /dev/null +++ b/src/plusplayer-core/src/decodedvideopacketex.cpp @@ -0,0 +1,24 @@ +// +// @ Copyright [2020] +// + +#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 diff --git a/src/plusplayer-core/src/decoderinputbuffer.cpp b/src/plusplayer-core/src/decoderinputbuffer.cpp new file mode 100644 index 0000000..42b64be --- /dev/null +++ b/src/plusplayer-core/src/decoderinputbuffer.cpp @@ -0,0 +1,22 @@ +// +// @ Copyright [2017] +// + +#include "core/decoderinputbuffer.h" + +namespace esplusplayer { + +namespace decoderinputbuffer_util { + +// LCOV_EXCL_START +bool FlushQueue(std::queue& queue) { + while (!queue.empty()) { + queue.pop(); + } + return true; +} +// LCOV_EXCL_STOP + +} // namespace decoderinputbuffer_util + +} // namespace esplusplayer diff --git a/src/plusplayer-core/src/error.cpp b/src/plusplayer-core/src/error.cpp new file mode 100644 index 0000000..1e101a1 --- /dev/null +++ b/src/plusplayer-core/src/error.cpp @@ -0,0 +1,161 @@ +// +// @ Copyright [2017] +// + +#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 diff --git a/src/plusplayer-core/src/gst_utils.cpp b/src/plusplayer-core/src/gst_utils.cpp new file mode 100644 index 0000000..942f66c --- /dev/null +++ b/src/plusplayer-core/src/gst_utils.cpp @@ -0,0 +1,46 @@ +// +// @ Copyright [2017] +// +#include "core/gst_utils.h" + +#include +#include + +#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(gstparam1.c_str()); + std::string gstparam2 = root.get("gstparam2", "").asString(); + argv[argc++] = const_cast(gstparam2.c_str()); + std::string gstparam3 = root.get("gstparam3", "").asString(); + argv[argc++] = const_cast(gstparam3.c_str()); + std::string gstparam4 = root.get("gstparam4", "").asString(); + argv[argc++] = const_cast(gstparam4.c_str()); + std::string gstparam5 = root.get("gstparam5", "").asString(); + argv[argc++] = const_cast(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 diff --git a/src/plusplayer-core/src/gstobject_guard.cpp b/src/plusplayer-core/src/gstobject_guard.cpp new file mode 100644 index 0000000..7a21c33 --- /dev/null +++ b/src/plusplayer-core/src/gstobject_guard.cpp @@ -0,0 +1,52 @@ +// +// @ Copyright [2017] +// +#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 diff --git a/src/plusplayer-core/src/kpi.cpp b/src/plusplayer-core/src/kpi.cpp new file mode 100644 index 0000000..ba8d44b --- /dev/null +++ b/src/plusplayer-core/src/kpi.cpp @@ -0,0 +1,198 @@ +// +// @ Copyright [2017] +// + +#include "core/kpi.h" + +#include + +#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 diff --git a/src/plusplayer-core/src/plusplayer_cfg.cpp b/src/plusplayer-core/src/plusplayer_cfg.cpp new file mode 100644 index 0000000..48f0759 --- /dev/null +++ b/src/plusplayer-core/src/plusplayer_cfg.cpp @@ -0,0 +1,24 @@ +// +// @ Copyright [2017] +// + +#include + +#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 diff --git a/src/plusplayer-core/src/serializer.cpp b/src/plusplayer-core/src/serializer.cpp new file mode 100644 index 0000000..490ae3a --- /dev/null +++ b/src/plusplayer-core/src/serializer.cpp @@ -0,0 +1,49 @@ +// +// @ Copyright [2018] +// + +#include "core/serializer.h" +// LCOV_EXCL_START +namespace esplusplayer { +using Offset = Serializer::Offset; +using Byte = Serializer::Byte; + +Offset Serializer::Put(const std::vector &vector) { + Offset offset = static_cast(size_); + if (vector.size() == 0) return offset; + const size_t size = vector.size(); + const Byte *data_bytes = reinterpret_cast(vector.data()); + Put_(data_bytes, size); + return offset; +} + +Offset Serializer::Put(const std::string &data) { + Offset offset = static_cast(size_); + if (data.length() == 0) return offset; + const size_t size = data.length(); + const Byte *data_bytes = reinterpret_cast(data.c_str()); + Put_(data_bytes, size); + return offset; +} + +Offset Serializer::Put(const Byte *data, size_t size) { + Offset offset = static_cast(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 diff --git a/src/plusplayer-core/src/track_util.cpp b/src/plusplayer-core/src/track_util.cpp new file mode 100644 index 0000000..799fc82 --- /dev/null +++ b/src/plusplayer-core/src/track_util.cpp @@ -0,0 +1,147 @@ +// +// @ Copyright [2017] +// + +#include "core/track_util.h" + +#include +#include +#include + + +#include "core/utils/plusplayer_log.h" + +namespace esplusplayer { + +namespace track_util { + +bool GetActiveTrack(const std::vector& 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& tracklist, + std::vector& 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& trackinfo) { + std::vector 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 data(new char[codec_data_info.size], + std::default_delete()); + 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 diff --git a/src/plusplayer-core/src/trackrendereradapter.cpp b/src/plusplayer-core/src/trackrendereradapter.cpp new file mode 100644 index 0000000..caccdd1 --- /dev/null +++ b/src/plusplayer-core/src/trackrendereradapter.cpp @@ -0,0 +1,1257 @@ +// +// @ Copyright [2017] +// + +#include "core/trackrendereradapter.h" + +#include + +#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& 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& 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(const_cast(data->Get()))}; +#else + TrackRendererDecoderInputBuffer decoderinputbuffer{ + adapter_utils::ConvertToTrackRendererTrackType(data->GetType()), + data->GetIndex(), const_cast(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(const_cast(data->Get()))}; +#else + TrackRendererDecoderInputBuffer decoderinputbuffer{ + adapter_utils::ConvertToTrackRendererTrackType(data->GetType()), + data->GetIndex(), const_cast(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(const_cast(data->Get()))}; +#else + TrackRendererDecoderInputBuffer decoderinputbuffer{ + adapter_utils::ConvertToTrackRendererTrackType(data->GetType()), + data->GetIndex(), const_cast(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 + 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 + 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(attr), info.name.c_str()); + switch (info.value_type) { + case adapter_utils::ValueType::kInt32: { + std::int32_t _value = boost::any_cast(value); + trackrenderer_set_attribute(handle_, info.name.c_str(), _value, + nullptr); + } break; + case adapter_utils::ValueType::kUInt32: { + std::uint32_t _value = boost::any_cast(value); + trackrenderer_set_attribute(handle_, info.name.c_str(), _value, + nullptr); + } break; + case adapter_utils::ValueType::kInt64: { + std::int64_t _value = boost::any_cast(value); + trackrenderer_set_attribute(handle_, info.name.c_str(), _value, + nullptr); + } break; + case adapter_utils::ValueType::kUInt64: { + std::uint64_t _value = boost::any_cast(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(value); + trackrenderer_set_attribute(handle_, info.name.c_str(), _value, + nullptr); + } break; + case adapter_utils::ValueType::kUInt64: { + std::uint64_t _value = boost::any_cast(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(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(userdata); + if (!adapter) return; + adapter->eventlistener_->OnErrorMsg( + adapter_utils::ConvertToErrorType(error_code), error_msg); +} + +void TrackRendererAdapter::ResourceConflictCb_(UserData userdata) { + auto adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnResourceConflicted(); +} + +void TrackRendererAdapter::SeekDoneCb_(UserData userdata) { + auto adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnSeekDone(); +} + +void TrackRendererAdapter::FlushDoneCb_(UserData userdata) { + auto adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnFlushDone(); +} +// LCOV_EXCL_STOP + +void TrackRendererAdapter::EosCb_(UserData userdata) { + auto adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnEos(); +} + +void TrackRendererAdapter::EventCb_(const TrackRendererEventType event_type, + const TrackrendererEventMsg msg_data, + UserData userdata) { + auto adapter = static_cast(userdata); + if (!adapter) return; + EventMsg event_msg; + event_msg.data = msg_data.data; + event_msg.len = msg_data.len; + adapter->eventlistener_->OnEvent(static_cast(event_type), + event_msg); +} + +void TrackRendererAdapter::FirstDecodingDoneCb_(UserData userdata) { + auto adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnFirstDecodingDone(); +} + +// LCOV_EXCL_START +void TrackRendererAdapter::SubtitleRawDataCb_( + TrackRendererDecoderInputBuffer* buf, const TrackRendererSubtitleType type, + UserData userdata) { + auto adapter = static_cast(userdata); + if (!adapter || !buf) return; + +#ifdef TRACKRENDERER_GST_DEPENDENCY_REMOVAL + auto buffer = DecoderInputBuffer::Create( + adapter_utils::ConvertToTrackType(buf->type), buf->index, + static_cast(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(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(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(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(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(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(userdata); + if (!adapter) return; + adapter->eventlistener_->OnMediaPacketGetTbmBufPtr(ptr, is_scale_change); +} + +void TrackRendererAdapter::MediaPacketVideoDecodedCb_( + const TrackRendererDecodedVideoPacket* packet, UserData userdata) { + auto adapter = static_cast(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(userdata); + if (!adapter) return; + DecodedVideoRawModePacket _packet; + _packet.type = static_cast(type); + _packet.pts = packet->pts; + _packet.width = packet->width; + _packet.height = packet->height; + _packet.data = + *static_cast(packet->internal_data); + adapter->eventlistener_->OnMediaPacketVideoRawDecoded(_packet); +} + +void TrackRendererAdapter::VideoDecoderUnderrunCb_(UserData userdata) { + auto* adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnVideoDecoderUnderrun(); +} + +void TrackRendererAdapter::VideoLatencyStatusCb_( + const TrackRendererLatencyStatus video_latency_status, UserData userdata) { + auto* adapter = static_cast(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(userdata); + if (!adapter) return; + adapter->eventlistener_->OnAudioLatencyStatus( + adapter_utils::ConvertToLatencyStatus(audio_latency_status)); +} + +void TrackRendererAdapter::VideoHighLatencyCb_(UserData userdata) { + auto* adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnVideoHighLatency(); +} +// LCOV_EXCL_START + +void TrackRendererAdapter::AudioHighLatencyCb_(UserData userdata) { + auto* adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnAudioHighLatency(); +} + +void TrackRendererAdapter::MultiviewStartVideoCb_(UserData userdata) { + auto adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnMultiviewStartVideo(); +} + +void TrackRendererAdapter::MultiviewStopVideoCb_(UserData userdata) { + auto adapter = static_cast(userdata); + if (!adapter) return; + adapter->eventlistener_->OnMultiviewStopVideo(); +} +// LCOV_EXCL_STOP + +void TrackRendererAdapter::VideoFrameDroppedCb_(const uint64_t count, + UserData userdata) { + auto* adapter = static_cast(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(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(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 diff --git a/src/plusplayer-core/src/trackrendereradapter_utils.cpp b/src/plusplayer-core/src/trackrendereradapter_utils.cpp new file mode 100644 index 0000000..62d7ee9 --- /dev/null +++ b/src/plusplayer-core/src/trackrendereradapter_utils.cpp @@ -0,0 +1,968 @@ +// +// @ Copyright [2017] +// + +#include "core/trackrendereradapter_utils.h" + +#include + +#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(drm_property.handle); + trackrenderer_drm_property->external_decryption = + static_cast(drm_property.external_decryption); + trackrenderer_drm_property->license_acquired_cb = + static_cast(drm_property.license_acquired_cb); + trackrenderer_drm_property->license_acquired_userdata = + static_cast(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(app_info.id.c_str()); + app_attr->version = const_cast(app_info.version.c_str()); + app_attr->type = const_cast(app_info.type.c_str()); +} + +void MakeTrackRendererAppInfoEx(TrackRendererAppInfoEx* app_attr, + const PlayerAppInfoEx& app_info) { + app_attr->id = const_cast(app_info.id.c_str()); + app_attr->version = const_cast(app_info.version.c_str()); + app_attr->type = const_cast(app_info.type.c_str()); + app_attr->runtitle = const_cast(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(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(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 diff --git a/src/plusplayer-core/src/videoframetypestrategy.cpp b/src/plusplayer-core/src/videoframetypestrategy.cpp new file mode 100644 index 0000000..3fa179c --- /dev/null +++ b/src/plusplayer-core/src/videoframetypestrategy.cpp @@ -0,0 +1,22 @@ +#include "core/videoframetypestrategy.h" + +#include +#include + +#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 diff --git a/tomato/tc/TCList.dat b/tomato/tc/TCList.dat new file mode 100644 index 0000000..568f446 --- /dev/null +++ b/tomato/tc/TCList.dat @@ -0,0 +1 @@ +unit_test/ut_esplusplayer_all.xml \ No newline at end of file diff --git a/tomato/tc/testfarm_script.xml b/tomato/tc/testfarm_script.xml new file mode 100644 index 0000000..517b656 --- /dev/null +++ b/tomato/tc/testfarm_script.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tomato/tc/unit_test/ut_esplusplayer_all.xml b/tomato/tc/unit_test/ut_esplusplayer_all.xml new file mode 100644 index 0000000..afcb07f --- /dev/null +++ b/tomato/tc/unit_test/ut_esplusplayer_all.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ut/CMakeLists.txt b/ut/CMakeLists.txt new file mode 100644 index 0000000..fda48c4 --- /dev/null +++ b/ut/CMakeLists.txt @@ -0,0 +1,102 @@ +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 +) diff --git a/ut/README.md b/ut/README.md new file mode 100644 index 0000000..750652d --- /dev/null +++ b/ut/README.md @@ -0,0 +1,131 @@ +**GTest guide** +=============== + For unit test for plusplayer + +--- +### Reference +- +- + +## Assertion +* ASSERT_* : 실패시 해당 테스트를 바로 종료
+* EXPECT_* : 실패하여도 테스트 계속 진행
+ +#### 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**.
+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
+ +1. test selection + * --gtest_list_tests
+ > 테스트할 항목을 보여준다. (테스트 실시 X) + * --gtest_also_run_disabled_tests
+ > DISABLED_ 로 막아둔 test case 를 일시적으로 실행 + * --gtest_filter
+ > 특정 case 들만 실행 가능
+ Ex) --gtest_filter="*.create*" : 모든 TC중에서 create 로 시작하는 모든 TC 실행.
+ +2. test Execution + * --gtest_repeat
+ > test 반복 가능. -1일 경우 무한히 반복
+ --gtest_break_on_failure와 함께 사용하면 실패할 경우 멈춤. + * --gtest_shuffle
+ > 무작위로 실행 가능 (test case 간 dependency 가 없어야 하기 때문이다)
+ gtest는 기본적으로 현재시간을 랜덤함수의 시드값으로 사용하나, --gtest_random_seed로 조절가능하다 + * --gtest_random_seed + > 1 ~ 99999까지의 값을 --gtest_shuffle에서 사용할 랜덤함수의 시드로 사용. + +--- +## Reference + * Gtest Primer(KR)
+ * Gtest Primer(EN)
+ * Unit Test Planning & How to write Unit Test Code(KR)
+ * Unit Test Planning & How to write Unit Test Code(EN)
diff --git a/ut/cpplint.py b/ut/cpplint.py new file mode 100644 index 0000000..52cb7d0 --- /dev/null +++ b/ut/cpplint.py @@ -0,0 +1,6123 @@ +#!/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] ... + + 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 , 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] "') + + +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 ." 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 + # template + # template + # template + 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 , + # template 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 ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and ))?' + # 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> 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 + # 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{}> + # + # 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(... + # string Class::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 // 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(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',)), + ('', ('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', + )), + ('', ('numeric_limits',)), + ('', ('list',)), + ('', ('map', 'multimap',)), + ('', ('allocator', 'make_shared', 'make_unique', 'shared_ptr', + 'unique_ptr', 'weak_ptr')), + ('', ('queue', 'priority_queue',)), + ('', ('set', 'multiset',)), + ('', ('stack',)), + ('', ('char_traits', 'basic_string',)), + ('', ('tuple',)), + ('', ('unordered_map', 'unordered_multimap')), + ('', ('unordered_set', 'unordered_multiset')), + ('', ('pair',)), + ('', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('', ('hash_map', 'hash_multimap',)), + ('', ('hash_set', 'hash_multiset',)), + ('', ('slist',)), + ) + +_HEADERS_MAYBE_TEMPLATES = ( + ('', ('copy', 'max', 'min', 'min_element', 'sort', + 'transform', + )), + ('', ('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(..., ...), 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 . + + 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: { '': (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[''] = (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() diff --git a/ut/include/appwindow.h b/ut/include/appwindow.h new file mode 100644 index 0000000..9cbfdef --- /dev/null +++ b/ut/include/appwindow.h @@ -0,0 +1,97 @@ +// +// @ Copyright [2017] +// + +#ifndef __ESPLUSPLAYER_UT_INCLUDE_APPWINDOW_H__ +#define __ESPLUSPLAYER_UT_INCLUDE_APPWINDOW_H__ + +#define ECORE_WAYLAND_DISPLAY_TEST 1 + +#include +#include +#include + +#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__ diff --git a/ut/include/esplusplayer/eseventlistener.hpp b/ut/include/esplusplayer/eseventlistener.hpp new file mode 100644 index 0000000..d6eaac8 --- /dev/null +++ b/ut/include/esplusplayer/eseventlistener.hpp @@ -0,0 +1,257 @@ +// +// @ Copyright [2018] +// + +#ifndef __ESPLUSPLAYER_UT_INCLUDE_ES_EVENT_LISTENER_H__ +#define __ESPLUSPLAYER_UT_INCLUDE_ES_EVENT_LISTENER_H__ + +#include +#include +#include +#include +#include + +#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(userdata); + LOG_ENTER; + if (type == ESPLUSPLAYER_EVENT_REQUESTED_FIRST_RENDER_FRAME) { + std::unique_lock 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(userdata); + LOG_ENTER; + std::unique_lock 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 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(userdata); + LOG_ENTER; + StartDataFeedingTask(cb, ESPLUSPLAYER_STREAM_TYPE_AUDIO); + StartDataFeedingTask(cb, ESPLUSPLAYER_STREAM_TYPE_VIDEO); + std::unique_lock 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(userdata); + LOG_ENTER; + StartDataFeedingTask(cb, type); + } + static void OnReadyToSeek(const esplusplayer_stream_type type, + const uint64_t offset, void* userdata) { + EsPlayerEventCallback* cb = static_cast(userdata); + LOG_ENTER; + std::unique_lock 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 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 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 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 lk(eos_m_); + eos_cv_.wait_for(lk, std::chrono::minutes(1), + [this]() -> bool { return eos_; }); + LOG_LEAVE; + lk.unlock(); + } + + void WaitForEos(std::function abort_func) { + LOG_ENTER; + std::unique_lock 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 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 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 diff --git a/ut/include/esplusplayer/esreader.hpp b/ut/include/esplusplayer/esreader.hpp new file mode 100644 index 0000000..4adf3b9 --- /dev/null +++ b/ut/include/esplusplayer/esreader.hpp @@ -0,0 +1,295 @@ +// +// @ Copyright [2018] +// + +#ifndef __ESPLUSPLAYER_UT_INCLUDE_ES_READER_H__ +#define __ESPLUSPLAYER_UT_INCLUDE_ES_READER_H__ + + +#include +#include +#include +#include + +#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(&pkt.pts), sizeof(pkt.pts)); + pkt.pts = pkt.pts / 1000000 + last_pts_before_repeat; // ns -> ms + last_pts = pkt.pts; + stream_.read(reinterpret_cast(&pkt.duration), sizeof(pkt.duration)); + last_duration = pkt.duration = pkt.duration / 1000000; // ns -> ms + stream_.read(reinterpret_cast(&size), sizeof(size)); + pkt.buffer_size = static_cast(size); + if (pkt.buffer_size == 0) return false; + pkt.buffer = new char[pkt.buffer_size]; + stream_.read(reinterpret_cast(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(&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(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(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 diff --git a/ut/include/esplusplayer/tclist.h b/ut/include/esplusplayer/tclist.h new file mode 100644 index 0000000..283562b --- /dev/null +++ b/ut/include/esplusplayer/tclist.h @@ -0,0 +1,39 @@ +// +// @ Copyright [2019] +// + +#ifndef __ESPLUSPLAYER_UT_INCLUDE_ESPLUSPLAYER_TCLIST_H__ +#define __ESPLUSPLAYER_UT_INCLUDE_ESPLUSPLAYER_TCLIST_H__ + +#include +#include + +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 tc_list = { + es_h264_aac, + //es_hevc_ac3, + es_vp9_opus, + }; + static std::vector 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 diff --git a/ut/include/mixer/constant.h b/ut/include/mixer/constant.h new file mode 100644 index 0000000..af81c39 --- /dev/null +++ b/ut/include/mixer/constant.h @@ -0,0 +1,83 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_CONSTANT_H__ +#define __ESPLUSPLAYER_MIXER_UT_CONSTANT_H__ + +#include + +#include +#include + +#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 diff --git a/ut/include/mixer/matcher.h b/ut/include/mixer/matcher.h new file mode 100644 index 0000000..4767b38 --- /dev/null +++ b/ut/include/mixer/matcher.h @@ -0,0 +1,105 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_MATCHER_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_MATCHER_H__ + +#include +#include + +#include +#include +#include + +#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 { + 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(info.component) << " vs " + << static_cast(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 IsSameVideoPlaneManipulableInfo( + const VideoPlaneManipulableInfo& comparer); +} // namespace esplusplayer_ut + +using ::testing::PrintToString; + +template +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 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 diff --git a/ut/include/mixer/mock/fakebuffer.h b/ut/include/mixer/mock/fakebuffer.h new file mode 100644 index 0000000..59f752d --- /dev/null +++ b/ut/include/mixer/mock/fakebuffer.h @@ -0,0 +1,42 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_FAKE_BUFFER_H__ +#define __ESPLUSPLAYER_MIXER_UT_FAKE_BUFFER_H__ + +#include + +#include +#include + +using ::testing::MakePolymorphicAction; +using ::testing::PolymorphicAction; + +namespace esplusplayer_ut { +struct FakeBuffer { + FakeBuffer(const std::uint32_t& size) + : ptr(std::unique_ptr(new char[size])), size(size) {} + std::unique_ptr ptr = nullptr; + const std::uint32_t size = 0; +}; +using FakeBufferPtr = std::unique_ptr; + +class CreateFakeBufferAction { + public: + explicit CreateFakeBufferAction(FakeBufferPtr& buffer) : buffer_(buffer) {} + template + 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 CreateFakeBuffer( + FakeBufferPtr& buffer) { + return MakePolymorphicAction(CreateFakeBufferAction(buffer)); +} + +} // namespace esplusplayer_ut + +#endif // __ESPLUSPLAYER_MIXER_UT_FAKE_BUFFER_H__ \ No newline at end of file diff --git a/ut/include/mixer/mock/mock_bufferobject.h b/ut/include/mixer/mock/mock_bufferobject.h new file mode 100644 index 0000000..102b3bf --- /dev/null +++ b/ut/include/mixer/mock/mock_bufferobject.h @@ -0,0 +1,37 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_BUFFER_OBJECT_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_BUFFER_OBJECT_H__ + +#include + +#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()); + MOCK_CONST_METHOD0(GetWritableAddress_, Mover()); + 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__ diff --git a/ut/include/mixer/mock/mock_memallocator.h b/ut/include/mixer/mock/mock_memallocator.h new file mode 100644 index 0000000..ebe8fc0 --- /dev/null +++ b/ut/include/mixer/mock/mock_memallocator.h @@ -0,0 +1,16 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_MEMORY_ALLOCATOR_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_MEMORY_ALLOCATOR_H__ + +#include + +#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__ diff --git a/ut/include/mixer/mock/mock_phyaddraccessor.h b/ut/include/mixer/mock/mock_phyaddraccessor.h new file mode 100644 index 0000000..b9ab923 --- /dev/null +++ b/ut/include/mixer/mock/mock_phyaddraccessor.h @@ -0,0 +1,16 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_PHYSICAL_ADDRESS_ACCESSOR_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_PHYSICAL_ADDRESS_ACCESSOR_H__ + +#include + +#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 diff --git a/ut/include/mixer/mock/mock_renderableobj.h b/ut/include/mixer/mock/mock_renderableobj.h new file mode 100644 index 0000000..2776e7e --- /dev/null +++ b/ut/include/mixer/mock/mock_renderableobj.h @@ -0,0 +1,37 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_H__ + +#include + +#include +#include +#include + +#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()); + 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&, + 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 diff --git a/ut/include/mixer/mock/mock_renderableobj_factory.h b/ut/include/mixer/mock/mock_renderableobj_factory.h new file mode 100644 index 0000000..edb15dd --- /dev/null +++ b/ut/include/mixer/mock/mock_renderableobj_factory.h @@ -0,0 +1,20 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_FACTORY_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERABLE_OBJECT_FACTORY_H__ + +#include + +#include + +#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 diff --git a/ut/include/mixer/mock/mock_renderer_evtlistener.h b/ut/include/mixer/mock/mock_renderer_evtlistener.h new file mode 100644 index 0000000..94f201a --- /dev/null +++ b/ut/include/mixer/mock/mock_renderer_evtlistener.h @@ -0,0 +1,16 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERER_EVENT_LISTENER_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_RENDERER_EVENT_LISTENER_H__ + +#include + +#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 diff --git a/ut/include/mixer/mock/mock_vpcollection.h b/ut/include/mixer/mock/mock_vpcollection.h new file mode 100644 index 0000000..9657351 --- /dev/null +++ b/ut/include/mixer/mock/mock_vpcollection.h @@ -0,0 +1,17 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEOPLANECOLLECTION_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEOPLANECOLLECTION_H__ + +#include + +#include "mixer/constant.h" +#include "mixer/interfaces/videoplanecollection.h" + +namespace esplusplayer_ut { +class MockVideoPlaneCollection : public VideoPlaneCollection { + public: + MOCK_CONST_METHOD0(GetVideoPlaneManipInfo, + const std::vector()); +}; +} // namespace esplusplayer_ut + +#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEOPLANECOLLECTION_H__ \ No newline at end of file diff --git a/ut/include/mixer/mock/mock_vpmanipulator.h b/ut/include/mixer/mock/mock_vpmanipulator.h new file mode 100644 index 0000000..3f24fc4 --- /dev/null +++ b/ut/include/mixer/mock/mock_vpmanipulator.h @@ -0,0 +1,19 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEO_PLANE_MANIPULATOR_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_VIDEO_PLANE_MANIPULATOR_H__ + +#include + +#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 diff --git a/ut/include/mixer/mock/mock_vpscaler.h b/ut/include/mixer/mock/mock_vpscaler.h new file mode 100644 index 0000000..3cee7dd --- /dev/null +++ b/ut/include/mixer/mock/mock_vpscaler.h @@ -0,0 +1,17 @@ +#ifndef __ESPLUSPLAYER_MIXER_MOCK_VIDEO_PLANE_SCALER_H__ +#define __ESPLUSPLAYER_MIXER_MOCK_VIDEO_PLANE_SCALER_H__ + +#include + +#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 diff --git a/ut/include/mixer/mock/movable.h b/ut/include/mixer/mock/movable.h new file mode 100644 index 0000000..70e77a7 --- /dev/null +++ b/ut/include/mixer/mock/movable.h @@ -0,0 +1,47 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_MOVABLE_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_MOVABLE_H__ + +#include + +namespace esplusplayer_ut { + +template +class Mover { + public: + Mover(T&& object) : object(std::move(object)), valid(true) {} + + Mover(const Mover& other) + : object(const_cast(other.object)), valid(true) { + assert(other.valid); + other.valid = false; + } + + Mover& operator=(const Mover& other) { + assert(other.valid); + object = const_cast(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 +inline Mover Movable(T&& object) { + return Mover(std::move(object)); +} +} // namespace esplusplayer_ut + +#endif // __ESPLUSPLAYER_MIXER_UT_MOCK_MOVABLE_H__ diff --git a/ut/include/mixer/mock/moveobj_wrapper.h b/ut/include/mixer/mock/moveobj_wrapper.h new file mode 100644 index 0000000..860b7fd --- /dev/null +++ b/ut/include/mixer/mock/moveobj_wrapper.h @@ -0,0 +1,30 @@ +#ifndef __ESPLUSPLAYER_MIXER_UT_MOCK_MOVE_OBJECT_WRAPPER_H__ +#define __ESPLUSPLAYER_MIXER_UT_MOCK_MOVE_OBJECT_WRAPPER_H__ + +#include + +namespace esplusplayer_ut { +template +class MoveObjectWrapper { + public: + explicit MoveObjectWrapper(T* obj) : obj_(obj) {} + virtual ~MoveObjectWrapper() { + if (moved_ == false) { + delete obj_; + std::cout << "CALLED dtor" << std::endl; + } + } + MoveObjectWrapper& 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 diff --git a/ut/include/streamreader.hpp b/ut/include/streamreader.hpp new file mode 100644 index 0000000..9f15b34 --- /dev/null +++ b/ut/include/streamreader.hpp @@ -0,0 +1,228 @@ +// +// @ Copyright [2018] +// + +#ifndef __ESPLUSPLAYER_UT_INCLUDE_ESCOMMON_H__ +#define __ESPLUSPLAYER_UT_INCLUDE_ESCOMMON_H__ + +#include +#include +#include +#include +#include +#include +#include + +#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(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 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 data; + + pp::EsPacketPtr MakeEsPacket(pp::StreamType type) { + return std::move( + pp::EsPacket::Create(type, data, static_cast(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 data; +}; +using PacketPtr = std::shared_ptr; +using CodecExtraDataPtr = std::shared_ptr; + +template +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 +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 +class StreamReader { + public: + using Ptr = std::shared_ptr>; + 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(&extradata->size), + sizeof extradata->size); + using DataPtr = std::shared_ptr; + extradata->data = DataPtr(new char[extradata->size]); + stream.read(reinterpret_cast(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(&pkt->pts), sizeof pkt->pts); + stream_.read(reinterpret_cast(&pkt->duration), + sizeof pkt->duration); + stream_.read(reinterpret_cast(&pkt->size), sizeof pkt->size); + using DataPtr = std::unique_ptr; + pkt->data = DataPtr(new char[pkt->size]); + stream_.read(reinterpret_cast(pkt->data.get()), pkt->size); + return pkt; + } + + template + 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 diff --git a/ut/include/utils/mock_videosink.hpp b/ut/include/utils/mock_videosink.hpp new file mode 100644 index 0000000..4cf6515 --- /dev/null +++ b/ut/include/utils/mock_videosink.hpp @@ -0,0 +1,193 @@ +// +// @ Copyright [2019] +// +#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&& 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 org_video_sink_; + Watcher 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 org_video_sink = + std::unique_ptr(org_builder->buildNewVideoSinkHandle()); + new_video_sink->Bind(std::move(org_video_sink)); + return new_video_sink; + } + static std::shared_ptr 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(IVideoSink::builder); + mock_builder = + std::make_shared(); + 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(); + } + + MockVideoSinkBuilder(){}; + ~MockVideoSinkBuilder(){}; + static std::shared_ptr org_builder; + static std::shared_ptr mock_builder; + static NiceMock* new_video_sink; +}; +std::shared_ptr MockVideoSinkBuilder::org_builder = nullptr; +std::shared_ptr MockVideoSinkBuilder::mock_builder = nullptr; +NiceMock* MockVideoSinkBuilder::new_video_sink = nullptr; + +} // namespace utils + +#endif // __ESPLUSPLAYER_UT_INCLUDE_MOCK_VIDEOSINK_H__ diff --git a/ut/include/utils/utility.h b/ut/include/utils/utility.h new file mode 100644 index 0000000..14882c6 --- /dev/null +++ b/ut/include/utils/utility.h @@ -0,0 +1,265 @@ +// +// @ Copyright [2019] +// +#ifndef __ESPLUSPLAYER_UT_INCLUDE_PLUSPLAYER_UTILITY_H__ +#define __ESPLUSPLAYER_UT_INCLUDE_PLUSPLAYER_UTILITY_H__ + +#include + +#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 +#include +#include + +#include +#include +#include +#include +#include + +#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 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 + 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 + 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 + 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 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 +class Watcher { + public: + Watcher() = default; + ~Watcher() { + this->SendSignalToWait(); + std::unique_lock(this->destructor_mutex_); + } + + explicit Watcher(T&& t) : value_(t) { LOGD("object : 0x%p", this); } + Watcher(Watcher&& t) : value_(std::move(std::forward(t))) { + LOGD("object&& : 0x%p", this); + } + + std::future 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 locker(this->mutex_); + value_ = rhs; + this->SendSignalToWait(); + // LOGD("operator=(const T& rhs)"); + return *this; + } + + Watcher& operator=(const Watcher& rhs) { + std::unique_lock 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& w, T expected, int timeout_ms) { + std::unique_lock(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 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__ diff --git a/ut/src/esplusplayer/tclist.cpp b/ut/src/esplusplayer/tclist.cpp new file mode 100644 index 0000000..621c592 --- /dev/null +++ b/ut/src/esplusplayer/tclist.cpp @@ -0,0 +1,69 @@ +#include "esplusplayer/tclist.h" + +#include +#include +#include +#include + +#include "core/utils/plusplayer_log.h" + +static std::unique_ptr 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; +} diff --git a/ut/src/esplusplayer/ut_basic.cpp b/ut/src/esplusplayer/ut_basic.cpp new file mode 100644 index 0000000..bd11604 --- /dev/null +++ b/ut/src/esplusplayer/ut_basic.cpp @@ -0,0 +1,1136 @@ + +// +// @ Copyright [2017] +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 { + 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(&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)); diff --git a/ut/src/esplusplayer/ut_display.cpp b/ut/src/esplusplayer/ut_display.cpp new file mode 100644 index 0000000..53b4df5 --- /dev/null +++ b/ut/src/esplusplayer/ut_display.cpp @@ -0,0 +1,296 @@ + +// +// @ Copyright [2017] +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 tc_list = { + es_h264_aac, + //es_hevc_ac3, + es_vp9_opus, + }; +} + +class EsDisplayTest : public ::testing::TestWithParam { + 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)); diff --git a/ut/src/esplusplayer/ut_dual_audio.cpp b/ut/src/esplusplayer/ut_dual_audio.cpp new file mode 100644 index 0000000..59e1a86 --- /dev/null +++ b/ut/src/esplusplayer/ut_dual_audio.cpp @@ -0,0 +1,107 @@ + +#include + +#include +#include + +#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); +} diff --git a/ut/src/esplusplayer/ut_inapp_multiview.cpp b/ut/src/esplusplayer/ut_inapp_multiview.cpp new file mode 100644 index 0000000..4229fa3 --- /dev/null +++ b/ut/src/esplusplayer/ut_inapp_multiview.cpp @@ -0,0 +1,140 @@ +// +// @ Copyright [2017] +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 { + 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)); diff --git a/ut/src/esplusplayer/ut_setstream.cpp b/ut/src/esplusplayer/ut_setstream.cpp new file mode 100644 index 0000000..1af3624 --- /dev/null +++ b/ut/src/esplusplayer/ut_setstream.cpp @@ -0,0 +1,51 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include + +#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 diff --git a/ut/src/mixer/constant.cpp b/ut/src/mixer/constant.cpp new file mode 100644 index 0000000..82f190b --- /dev/null +++ b/ut/src/mixer/constant.cpp @@ -0,0 +1,51 @@ +#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(&_kDefaultBuffer); + +BufferUnionHandleType kDefaultMappedHandle = {.u32 = kDefaultBufferHandle}; + +} // namespace esplusplayer_ut \ No newline at end of file diff --git a/ut/src/mixer/matcher.cpp b/ut/src/mixer/matcher.cpp new file mode 100644 index 0000000..492f909 --- /dev/null +++ b/ut/src/mixer/matcher.cpp @@ -0,0 +1,10 @@ +#include "mixer/matcher.h" + +namespace esplusplayer_ut { + +Matcher IsSameVideoPlaneManipulableInfo( + const VideoPlaneManipulableInfo& comparer) { + return MakeMatcher(new IsSameVideoPlaneManipulableInfoMatcher(comparer)); +} + +} // namespace esplusplayer_ut \ No newline at end of file diff --git a/ut/src/mixer/ut_espp_mixerscenario.cpp b/ut/src/mixer/ut_espp_mixerscenario.cpp new file mode 100644 index 0000000..39e73d7 --- /dev/null +++ b/ut/src/mixer/ut_espp_mixerscenario.cpp @@ -0,0 +1,597 @@ +// +// @ Copyright [2020] +// +#include + +#include + +#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 diff --git a/ut/src/mixer/ut_mixedframe.cpp b/ut/src/mixer/ut_mixedframe.cpp new file mode 100644 index 0000000..00576ab --- /dev/null +++ b/ut/src/mixer/ut_mixedframe.cpp @@ -0,0 +1,355 @@ +#include +#include + +#include +#include + +#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 r_phyaddraccessor_{ + new MockPhyAddrAccessor()}; + MoveObjectWrapper w_phyaddraccessor_{ + new MockPhyAddrAccessor()}; + MoveObjectWrapper 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; + 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 diff --git a/ut/src/mixer/ut_mixer.cpp b/ut/src/mixer/ut_mixer.cpp new file mode 100644 index 0000000..562debc --- /dev/null +++ b/ut/src/mixer/ut_mixer.cpp @@ -0,0 +1,257 @@ + +// +// @ Copyright [2020] +// +#include + +#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::Create(); + ASSERT_NE(mixer, nullptr); + mixer.reset(); +} + +TEST(MixerTest, MixerTicketCreate) { + MixerMockPlayer p1; + std::unique_ptr 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::Create(); + EXPECT_TRUE(mixer->SetRscAllocMode(RscAllocMode::kDisable)); + mixer.reset(); +} + +TEST(MixerTest, MixerDisableAutoRscAlloc_2) { + std::unique_ptr 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::Create(); + EXPECT_TRUE(mixer->DisableAudioFocusSetting()); + mixer.reset(); +} + +TEST(MixerTest, MixerDisableAudioFocusSetting_2) { + std::unique_ptr 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::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::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::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::Create(); + EXPECT_TRUE(mixer->SetDisplay(DisplayType::kOverlay, + Utility::Instance().GetWindow())); + mixer.reset(); +} + +TEST(MixerTest, MixerCommit) { + std::unique_ptr 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::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::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::Create(); + MixerMockListener* listener = new MixerMockListener; + EXPECT_TRUE(mixer->RegisterListener(listener)); + mixer.reset(); + delete listener; +} + +TEST(MixerTest, MixerStart) { + std::unique_ptr 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::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 diff --git a/ut/src/mixer/ut_mixer_capi.cpp b/ut/src/mixer/ut_mixer_capi.cpp new file mode 100644 index 0000000..14a19dc --- /dev/null +++ b/ut/src/mixer/ut_mixer_capi.cpp @@ -0,0 +1,232 @@ + + +// +// @ Copyright [2020] +// +#include + +#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); +} + diff --git a/ut/src/mixer/ut_mixer_espp_capi.cpp b/ut/src/mixer/ut_mixer_espp_capi.cpp new file mode 100644 index 0000000..a14c0e6 --- /dev/null +++ b/ut/src/mixer/ut_mixer_espp_capi.cpp @@ -0,0 +1,351 @@ +// +// @ Copyright [2020] +// +#include + +#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); +} + diff --git a/ut/src/mixer/ut_mixerscenario.cpp b/ut/src/mixer/ut_mixerscenario.cpp new file mode 100644 index 0000000..fef9481 --- /dev/null +++ b/ut/src/mixer/ut_mixerscenario.cpp @@ -0,0 +1,630 @@ +// +// @ Copyright [2020] +// +#include + +#include + +#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_; + 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 user_data = std::make_shared(); + shared_ptr mock_eventlistener = + std::make_shared(); + shared_ptr fake_eventlistener = + std::make_shared(); + mock_eventlistener->Bind( + std::dynamic_pointer_cast(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 user_data1 = std::make_shared(); + shared_ptr mock_eventlistener1 = + std::make_shared(); + shared_ptr fake_eventlistener1 = + std::make_shared(); + mock_eventlistener1->Bind( + std::dynamic_pointer_cast(fake_eventlistener1)); + + shared_ptr user_data2 = std::make_shared(); + shared_ptr mock_eventlistener2 = + std::make_shared(); + shared_ptr fake_eventlistener2 = + std::make_shared(); + mock_eventlistener2->Bind( + std::dynamic_pointer_cast(fake_eventlistener2)); + + shared_ptr user_data3 = std::make_shared(); + shared_ptr mock_eventlistener3 = + std::make_shared(); + shared_ptr fake_eventlistener3 = + std::make_shared(); + mock_eventlistener3->Bind( + std::dynamic_pointer_cast(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 diff --git a/ut/src/mixer/ut_mixerticket.cpp b/ut/src/mixer/ut_mixerticket.cpp new file mode 100644 index 0000000..c1eebc6 --- /dev/null +++ b/ut/src/mixer/ut_mixerticket.cpp @@ -0,0 +1,231 @@ +// +// @ Copyright [2020] +// +#include + +#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::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::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::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::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::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::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::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::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::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::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::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 diff --git a/ut/src/mixer/ut_renderer.cpp b/ut/src/mixer/ut_renderer.cpp new file mode 100644 index 0000000..1b64ed5 --- /dev/null +++ b/ut/src/mixer/ut_renderer.cpp @@ -0,0 +1,361 @@ +#include +#include + +#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(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_; + 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( + {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( + {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(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_; + 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 diff --git a/ut/src/mixer/ut_tizenbuffermgr.cpp b/ut/src/mixer/ut_tizenbuffermgr.cpp new file mode 100644 index 0000000..46e3089 --- /dev/null +++ b/ut/src/mixer/ut_tizenbuffermgr.cpp @@ -0,0 +1,183 @@ +#include +#include + +#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; + +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 diff --git a/ut/src/mixer/ut_tizenbufferobj.cpp b/ut/src/mixer/ut_tizenbufferobj.cpp new file mode 100644 index 0000000..d62683b --- /dev/null +++ b/ut/src/mixer/ut_tizenbufferobj.cpp @@ -0,0 +1,129 @@ +#include +#include + +#include + +#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; + +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( + 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 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 diff --git a/ut/src/mixer/ut_videoplane.cpp b/ut/src/mixer/ut_videoplane.cpp new file mode 100644 index 0000000..3e7df68 --- /dev/null +++ b/ut/src/mixer/ut_videoplane.cpp @@ -0,0 +1,212 @@ +#include +#include + +#include + +#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 +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 bufferobj_{new MockBufferObject()}; + std::unique_ptr y_plane_{new PlaneType( + BufferObjectPtr(MoveBufferObject()), kDefaultWidth, kDefaultHeight)}; +}; + +class YComponentVideoPlaneTest + : public ::testing::Test, + public DefaultVideoPlaneOptionWithBufferObject { + 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 { + 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(*bufferobj_.get()); + } + + private: + std::shared_ptr bufferobj_{new MockBufferObject()}; + std::unique_ptr y_plane_{ + new YComponentVideoPlaneWithSharedMemory(bufferobj_, kDefaultWidth, + kDefaultHeight)}; + std::unique_ptr 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 diff --git a/ut/src/ut_cloudgame.cpp b/ut/src/ut_cloudgame.cpp new file mode 100644 index 0000000..c5aaf55 --- /dev/null +++ b/ut/src/ut_cloudgame.cpp @@ -0,0 +1,676 @@ + +// +// @ Copyright [2017] +// +#include +#include +#include +#include + +#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 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 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> + 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 mock_video_sink_builder_; + static int last_game_mode; +}; +std::shared_ptr + 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 { + 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> { + public: + EsCloudGameTestForGameModes() {} + ~EsCloudGameTestForGameModes() {} + + static void SetUpTestCase() { SetUpTestCaseBase(); } + + static void TearDownTestCase() { TearDownTestCaseBase(); } + + virtual void SetUp() override { + auto param = GetParam(); + clip_op_mode_ = std::get(param); + espp_low_latency_mode_ = std::get(param); + avoc_game_mode_ = std::get(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 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 { + 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{ + "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 targetThreads = { + "video_appsrc", "audio_appsrc", "OmxAudioThread", "omxaudiodec", + "omx_outbuffer", "omx_inbuffer", "omx_poll", "render_thread", +}; + +bool VerifyBoostingResult(vector& target_threads, string policy, + string prio) { + string cmd = "AffinityReplacer --atp | grep "; + cmd += to_string((int)getpid()); + + string cmd_result = Utility::Execute(cmd); + vector 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 diff --git a/ut/src/ut_espacket.cpp b/ut/src/ut_espacket.cpp new file mode 100644 index 0000000..13b6c78 --- /dev/null +++ b/ut/src/ut_espacket.cpp @@ -0,0 +1,104 @@ +// +// @ Copyright [2018] +// + +#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)); +} diff --git a/ut/src/ut_esplayer.cpp b/ut/src/ut_esplayer.cpp new file mode 100644 index 0000000..a1f914a --- /dev/null +++ b/ut/src/ut_esplayer.cpp @@ -0,0 +1,510 @@ +// +// @ Copyright [2018] +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 &&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 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(); + fake_eventlistener_ = std::make_shared(this); + mock_eventlistener_->Bind( + std::dynamic_pointer_cast(fake_eventlistener_)); + + player_->RegisterListener(mock_eventlistener_.get(), nullptr); + + v_streamreader_ = es::StreamReader::Create( + "/welcome_movie/video_00/", pp::kTrackTypeVideo); + a_streamreader_ = es::StreamReader::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>(); + 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>(); + 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 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::Ptr &streamreader, bool is_tzdata) { + while (true) { + auto pkt = streamreader->ReadNextPacket(); + auto buffer = es::utils::MakeEsPacketFromPacket( + pkt, static_cast(streamreader->GetTrackType())); + if (is_tzdata) + SubmitTzEsPacket(buffer); + else + SubmitEsPacket(buffer); + if (pkt == nullptr) break; + } + }; + auto v_streaming_task_fn = [this]( + es::StreamReader::Ptr &streamreader, bool is_tzdata, + bool has_color_info) { + while (true) { + auto pkt = streamreader->ReadNextPacket(); + auto buffer = es::utils::MakeEsPacketFromPacket( + pkt, static_cast(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 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 submit_stopped_; + std::shared_ptr player_; + std::shared_ptr mock_eventlistener_; + std::shared_ptr fake_eventlistener_; + es::StreamReader::Ptr v_streamreader_; + es::StreamReader::Ptr a_streamreader_; + std::future video_task_; + std::future 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; +} diff --git a/ut/src/ut_esplayer2.cpp b/ut/src/ut_esplayer2.cpp new file mode 100644 index 0000000..d03b2fd --- /dev/null +++ b/ut/src/ut_esplayer2.cpp @@ -0,0 +1,776 @@ +// +// @ Copyright [2017] +// + +#include + +#include +#include + +#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 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(inbuffer->GetType()); + GstBuffer *gstbuffer = static_cast(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 data(new char[info.size], + std::default_delete()); + 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 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(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 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(type)); + Push_(std::move(inbuffer)); + } + LOG_LEAVE; + return true; + } + + bool Flush(TrackType type) { + LOG_ENTER; + if (type >= kTrackTypeMax) { + return false; + } + std::lock_guard 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(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 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 inbufferqueue; + std::future 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 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 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 &&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 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 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 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 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 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 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 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 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 GetFeeder2() { return feeder_; } + std::shared_ptr GetTrackSource() { return tracksource_; } + + private: + std::shared_ptr feeder_; + std::shared_ptr 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(); + auto fake_eventlistener = std::make_shared(); + mock_eventlistener->Bind( + std::dynamic_pointer_cast(fake_eventlistener)); + + auto feeder = GetFeeder2(); + auto tracksource = GetTrackSource(); + + auto esplayer = EsPlusPlayer::Create(); + esplayer->Open(); + + std::unique_ptr 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(); + auto fake_eventlistener = std::make_shared(); + mock_eventlistener->Bind( + std::dynamic_pointer_cast(fake_eventlistener)); + + std::unique_ptr 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(); + auto fake_eventlistener = std::make_shared(); + mock_eventlistener->Bind( + std::dynamic_pointer_cast(fake_eventlistener)); + + std::unique_ptr 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(); + auto fake_eventlistener = std::make_shared(); + mock_eventlistener->Bind( + std::dynamic_pointer_cast(fake_eventlistener)); + + std::unique_ptr 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(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(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(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(); + auto fake_eventlistener = std::make_shared(); + mock_eventlistener->Bind( + std::dynamic_pointer_cast(fake_eventlistener)); + + std::unique_ptr 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 diff --git a/ut/src/ut_esplayer_trackrenderer.cpp b/ut/src/ut_esplayer_trackrenderer.cpp new file mode 100644 index 0000000..6c91b2c --- /dev/null +++ b/ut/src/ut_esplayer_trackrenderer.cpp @@ -0,0 +1,266 @@ +// +// @ Copyright [2018] +// + +#include +#include + +#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 + &&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 + 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::Create( + "/welcome_movie/video_00/", pp::trackrenderer::kTrackTypeVideo); + auto videoinfo = v_streamreader->GetMediaInfo< + es::VideoInfo>(); + 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::Create( + "/welcome_movie/audio_01/", pp::trackrenderer::kTrackTypeAudio); + auto audioinfo = a_streamreader->GetMediaInfo< + es::AudioInfo>(); + 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::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::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::Create( + "/welcome_movie/video_00/", pp::trackrenderer::kTrackTypeVideo); + auto videoinfo = v_streamreader->GetMediaInfo>(); + 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::Create( + "/welcome_movie/audio_01/", pp::trackrenderer::kTrackTypeAudio); + auto audioinfo = a_streamreader->GetMediaInfo>(); + 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::Create( + "/welcome_movie/video_00/", pp::trackrenderer::kTrackTypeVideo); + auto videoinfo = v_streamreader->GetMediaInfo>(); + 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::Create( + "/welcome_movie/audio_01/", pp::trackrenderer::kTrackTypeAudio); + auto audioinfo = a_streamreader->GetMediaInfo>(); + 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()); + } +} diff --git a/ut/src/ut_main.cpp b/ut/src/ut_main.cpp new file mode 100644 index 0000000..f24eadd --- /dev/null +++ b/ut/src/ut_main.cpp @@ -0,0 +1,19 @@ +// +// @ Copyright [2017] +// + +#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; +} diff --git a/ut/src/ut_miscellaneous.cpp b/ut/src/ut_miscellaneous.cpp new file mode 100644 index 0000000..a570246 --- /dev/null +++ b/ut/src/ut_miscellaneous.cpp @@ -0,0 +1,133 @@ +// +// @ Copyright [2017] +// + +#include + +#include +#include // 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(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(&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(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(value2); // Success + std::cout << "casted_val3: [" << casted_val3 << "]" << std::endl; + const float* casted_val4 = boost::any_cast(&value2); // Success + std::cout << "casted_val4: [" << *casted_val4 << "]" << std::endl; +} diff --git a/ut/src/ut_streamreader.cpp b/ut/src/ut_streamreader.cpp new file mode 100644 index 0000000..1a51eea --- /dev/null +++ b/ut/src/ut_streamreader.cpp @@ -0,0 +1,130 @@ +// +// @ Copyright [2018] +// + +#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::Create("/welcome_movie/video_00/", + pp::kTrackTypeVideo); + es::StreamReader::Create("/welcome_movie/audio_00/", + pp::kTrackTypeAudio); + }); +} + +TEST_F(StreamReaderTest, DISABLED_ThrowOnCreate) { + ASSERT_ANY_THROW({ + es::StreamReader::Create("foo/bar/", pp::kTrackTypeVideo); + }); +} + +TEST_F(StreamReaderTest, DISABLED_GetVideoExtraData) { + auto v_streamreader = es::StreamReader::Create( + "/welcome_movie/video_00/", pp::kTrackTypeVideo); + ASSERT_NO_THROW({ v_streamreader->GetExtraData(); }); +} + +TEST_F(StreamReaderTest, DISABLED_GetAudioExtraData) { + auto a_streamreader = es::StreamReader::Create( + "/welcome_movie/audio_00/", pp::kTrackTypeAudio); + ASSERT_NO_THROW({ a_streamreader->GetExtraData(); }); +} + +TEST_F(StreamReaderTest, DISABLED_GetPtsFromFirstVideoPacket) { + auto v_streamreader = es::StreamReader::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::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::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::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::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::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::Create( + "/welcome_movie/video_00/", pp::kTrackTypeVideo); + auto videoinfo = + v_streamreader->GetMediaInfo>(); + 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::Create( + "/welcome_movie/audio_00/", pp::kTrackTypeAudio); + auto audioinfo = a_streamreader->GetMediaInfo< + es::AudioInfo>(); + ASSERT_EQ("audio/mpeg", audioinfo.mimetype); + ASSERT_EQ(48000, audioinfo.samplerate); + ASSERT_EQ(2, audioinfo.channels); +} diff --git a/ut/src/ut_trackrendereradapter.cpp b/ut/src/ut_trackrendereradapter.cpp new file mode 100644 index 0000000..b0ef019 --- /dev/null +++ b/ut/src/ut_trackrendereradapter.cpp @@ -0,0 +1,410 @@ +// +// @ Copyright [2017] +// + +#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 GetAdapter() { + return trackrenderer_adapter_; + } + std::shared_ptr GetAppWindow() { + return appwindow_; + } + + private: + std::shared_ptr trackrenderer_adapter_; + std::shared_ptr 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(*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 MakeTrackInfo() { + std::vector 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 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 MakeIniProperties() { + std::map 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 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 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(kTrackTypeVideo), kInvalidTrackIndex, nullptr); + + TrackRendererDecoderInputBuffer trackrenderer_decoderinputbuffer{ + adapter_utils::ConvertToTrackRendererTrackType(input->GetType()), + input->GetIndex(), const_cast(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 +bool IsSameValue(const boost::any& v1, const boost::any& v2) { + if (v1.type() != v2.type()) return false; + return (boost::any_cast(v1) == boost::any_cast(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_){} +*/ diff --git a/ut/src/utils/utility.cpp b/ut/src/utils/utility.cpp new file mode 100644 index 0000000..81fd62e --- /dev/null +++ b/ut/src/utils/utility.cpp @@ -0,0 +1,561 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ut/include/utils/utility.h" +#include "ut/include/esplusplayer/eseventlistener.hpp" +#include "ut/include/esplusplayer/esreader.hpp" + +#include +#include +#include +#include +#include +#include +#include +#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; +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(type) & EsType::kVideo) { + video_reader = + new EsStreamReader(uri + "video_00/", ESPLUSPLAYER_STREAM_TYPE_VIDEO); + EXPECT_TRUE(video_reader->SetStreamInfo(player)); + } + if (static_cast(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(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 Utility::Split(string in, char delimiter) { + vector 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 -- 2.7.4

IMo3{?b{`zj7Yo;fNpnur<- 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%0DZGc| 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%0AFTEs?jW2!@;KNzN612GDn~JN?T} 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%0AtgprGm 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 C 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