[0.0.1] add mtpr initial code 40/280540/5 accepted/tizen/unified/20220901.020252 accepted/tizen/unified/20220902.055029 submit/tizen/20220901.015435
authorEunhye Choi <eunhae1.choi@samsung.com>
Wed, 31 Aug 2022 09:35:50 +0000 (18:35 +0900)
committerEunhye Choi <eunhae1.choi@samsung.com>
Thu, 1 Sep 2022 01:37:37 +0000 (10:37 +0900)
Change-Id: I2bc2cd29d9a0e8cc708fe9d3e0415757db3944b8

66 files changed:
CMakeLists.txt [new file with mode: 0644]
LICENSE.APLv2 [new file with mode: 0644]
NOTICE [new file with mode: 0644]
capi-media-transporter.pc.in [new file with mode: 0644]
doc/media_transporter_doc.h [new file with mode: 0644]
include/MediaSourceBinAudioTest.h [new file with mode: 0644]
include/MediaSourceBinBase.h [new file with mode: 0644]
include/MediaSourceBinCamera.h [new file with mode: 0644]
include/MediaSourceBinFactory.h [new file with mode: 0644]
include/MediaSourceBinMic.h [new file with mode: 0644]
include/MediaSourceBinVideoTest.h [new file with mode: 0644]
include/MediaTransporter.h [new file with mode: 0644]
include/MediaTransporterBase.h [new file with mode: 0644]
include/MediaTransporterCallback.h [new file with mode: 0644]
include/MediaTransporterDisplay.h [new file with mode: 0644]
include/MediaTransporterException.h [new file with mode: 0644]
include/MediaTransporterFactory.h [new file with mode: 0644]
include/MediaTransporterGst.h [new file with mode: 0644]
include/MediaTransporterLog.h [new file with mode: 0644]
include/MediaTransporterObserver.h [new file with mode: 0644]
include/MediaTransporterParseIni.h [new file with mode: 0644]
include/MediaTransporterReceiver.h [new file with mode: 0644]
include/MediaTransporterReceiverRist.h [new file with mode: 0644]
include/MediaTransporterReceiverSrt.h [new file with mode: 0644]
include/MediaTransporterResource.h [new file with mode: 0644]
include/MediaTransporterSender.h [new file with mode: 0644]
include/MediaTransporterSenderRist.h [new file with mode: 0644]
include/MediaTransporterSenderRtsp.h [new file with mode: 0644]
include/MediaTransporterSenderSrt.h [new file with mode: 0644]
include/MediaTransporterSenderToServerRtsp.h [new file with mode: 0644]
include/MediaTransporterUtil.h [new file with mode: 0644]
include/mtpr.h [new file with mode: 0644]
include/mtpr_internal.h [new file with mode: 0644]
packaging/capi-media-transporter.manifest [new file with mode: 0644]
packaging/capi-media-transporter.spec [new file with mode: 0644]
src/MediaSourceBinAudioTest.cpp [new file with mode: 0644]
src/MediaSourceBinBase.cpp [new file with mode: 0644]
src/MediaSourceBinCamera.cpp [new file with mode: 0644]
src/MediaSourceBinFactory.cpp [new file with mode: 0644]
src/MediaSourceBinMic.cpp [new file with mode: 0644]
src/MediaSourceBinVideoTest.cpp [new file with mode: 0644]
src/MediaTransporter.cpp [new file with mode: 0644]
src/MediaTransporterBase.cpp [new file with mode: 0644]
src/MediaTransporterCallback.cpp [new file with mode: 0644]
src/MediaTransporterDisplay.cpp [new file with mode: 0644]
src/MediaTransporterFactory.cpp [new file with mode: 0644]
src/MediaTransporterGst.cpp [new file with mode: 0644]
src/MediaTransporterObserver.cpp [new file with mode: 0644]
src/MediaTransporterParseIni.cpp [new file with mode: 0644]
src/MediaTransporterReceiver.cpp [new file with mode: 0644]
src/MediaTransporterReceiverRist.cpp [new file with mode: 0644]
src/MediaTransporterReceiverSrt.cpp [new file with mode: 0644]
src/MediaTransporterResource.cpp [new file with mode: 0644]
src/MediaTransporterSender.cpp [new file with mode: 0644]
src/MediaTransporterSenderRist.cpp [new file with mode: 0644]
src/MediaTransporterSenderRtsp.cpp [new file with mode: 0644]
src/MediaTransporterSenderSrt.cpp [new file with mode: 0644]
src/MediaTransporterSenderToServerRtsp.cpp [new file with mode: 0644]
src/MediaTransporterUtil.cpp [new file with mode: 0644]
test/CMakeLists.txt [new file with mode: 0644]
test/mtpr_rtsp_test.c [new file with mode: 0644]
test/mtpr_test.c [new file with mode: 0644]
unittest/CMakeLists.txt [new file with mode: 0644]
unittest/testbase.hpp [new file with mode: 0644]
unittest/ut_main.cpp [new file with mode: 0644]
unittest/ut_srt_sender.cpp [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3c611cb
--- /dev/null
@@ -0,0 +1,118 @@
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+SET(fw_name "capi-media-transporter")
+
+PROJECT(${fw_name})
+
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+SET(INC_DIR include)
+INCLUDE_DIRECTORIES(${INC_DIR})
+
+SET(dependents "dlog glib-2.0 gstreamer-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 \
+                json-glib-1.0 iniparser mm-common capi-media-tool mm-display-interface \
+                cynara-client libsmack capi-system-info bundle capi-media-sound-manager \
+                gstreamer-rtsp-server-1.0 libpulse")
+
+IF(NOT TIZEN_PROFILE_TV)
+    SET(dependents "${dependents} mm-resource-manager")
+ELSE()
+    ADD_DEFINITIONS("-DTIZEN_TV")
+ENDIF()
+
+SET(pc_dependents "capi-base-common capi-media-sound-manager capi-media-tool bundle")
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(${fw_name} REQUIRED ${dependents})
+FOREACH(flag ${${fw_name}_CFLAGS})
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -Wall -Wshadow -Wsign-compare -Wmissing-field-initializers -Wcast-function-type -Werror")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -std=c++17")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+
+IF("${ARCH}" STREQUAL "arm")
+    ADD_DEFINITIONS("-DTARGET")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}")
+
+AUX_SOURCE_DIRECTORY (src MAIN_SRC)
+
+LIST (APPEND SOURCES
+     ${MAIN_SRC}
+)
+
+ADD_LIBRARY(${fw_name} SHARED ${SOURCES})
+
+TARGET_LINK_LIBRARIES(${fw_name} ${${fw_name}_LDFLAGS})
+
+SET_TARGET_PROPERTIES(${fw_name}
+     PROPERTIES
+     VERSION ${FULLVER}
+     SOVERSION ${MAJORVER}
+     CLEAN_DIRECT_OUTPUT 1
+)
+
+INSTALL(TARGETS ${fw_name} DESTINATION ${LIB_INSTALL_DIR})
+INSTALL(
+        DIRECTORY ${INC_DIR}/ DESTINATION include/media
+        FILES_MATCHING
+        PATTERN "mtpr_*.h" EXCLUDE
+        PATTERN "${INC_DIR}/*.h"
+)
+
+
+SET(PC_NAME ${fw_name})
+SET(PC_REQUIRED ${pc_dependents})
+SET(PC_LDFLAGS -l${fw_name})
+
+CONFIGURE_FILE(
+    ${fw_name}.pc.in
+    ${CMAKE_CURRENT_SOURCE_DIR}/${fw_name}.pc
+    @ONLY
+)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${fw_name}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+ADD_SUBDIRECTORY(test)
+
+OPTION(MTPR_UNITTEST "Build mtpr unittest code" OFF)
+IF(MTPR_UNITTEST)
+ADD_SUBDIRECTORY(unittest)
+ENDIF(MTPR_UNITTEST)
+
+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)
+
diff --git a/LICENSE.APLv2 b/LICENSE.APLv2
new file mode 100644 (file)
index 0000000..7d4506d
--- /dev/null
@@ -0,0 +1,204 @@
+Copyright (c) 2000 - 2022 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/NOTICE b/NOTICE
new file mode 100644 (file)
index 0000000..ccdad52
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,3 @@
+Copyright (c) Samsung Electronics Co., Ltd. All rights reserved.
+Except as noted, this software is licensed under Apache License, Version 2.
+Please, see the LICENSE file for Apache License terms and conditions.
diff --git a/capi-media-transporter.pc.in b/capi-media-transporter.pc.in
new file mode 100644 (file)
index 0000000..8cb4d53
--- /dev/null
@@ -0,0 +1,14 @@
+# Package Information for pkg-config
+
+prefix=@PREFIX@
+exec_prefix=/usr
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@/media
+
+Name: @PC_NAME@
+Description: @PACKAGE_DESCRIPTION@
+Version: @VERSION@
+Requires: @PC_REQUIRED@
+Libs: -L${libdir} @PC_LDFLAGS@
+Cflags: -I${includedir}
+
diff --git a/doc/media_transporter_doc.h b/doc/media_transporter_doc.h
new file mode 100644 (file)
index 0000000..0a1776b
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_DOC_H__
+#define __TIZEN_MEDIA_TRANSPORTER_DOC_H__
+
+
+/**
+ * @file media_transporter_doc.h
+ * @brief This file contains high level documentation of the MediaTransporter API.
+ */
+
+/**
+ * @ingroup CAPI_MEDIA_FRAMEWORK
+ * @defgroup CAPI_MEDIA_TRANSPORTER_MODULE MediaTransporter
+ * @brief The @ref CAPI_MEDIA_TRANSPORTER_MODULE API provides functions for real-time audio/video communication between peers. It supports various protocols as a native API based on GStreamer multimedia framework.
+ *
+ * @section CAPI_MEDIA_TRANSPORTER_MODULE_HEADER Required Header
+ *   \#include <mtpr.h>
+ *
+ * @section CAPI_MEDIA_TRANSPORTER_OVERVIEW Overview
+ * The MediaTransporter API provides functions to communicate with the peer using multimedia sources and generic data.
+ * The multimedia sources include audio/video stream from microphone, camera.
+ * The generic data includes string or byte data.\n
+ * This API set allows you to:
+ *  - add/remove the media source
+ *  - create/destroy data channel and send/receive data via the channel
+ *  - start/stop the state of the handle
+ *  - get notified about various changes via callbacks
+ *
+ * @subsection CAPI_MEDIA_TRANSPORTER_LIFE_CYCLE_STATE_TRANSITIONS State Transitions
+ * <div><table class="doxtable">
+ * <tr>
+ *    <th><b>FUNCTION</b></th>
+ *    <th><b>PRE-STATE</b></th>
+ *    <th><b>POST-STATE</b></th>
+ *    <th><b>SYNC TYPE</b></th>
+ * </tr>
+ * <tr>
+ *    <td> mtpr_create() </td>
+ *    <td> NONE </td>
+ *    <td> IDLE </td>
+ *    <td> SYNC </td>
+ * </tr>
+ * <tr>
+ *    <td> mtpr_destroy() </td>
+ *    <td> IDLE/PLAYING </td>
+ *    <td> NONE </td>
+ *    <td> SYNC </td>
+ * </tr>
+ * <tr>
+ *    <td> mtpr_start() </td>
+ *    <td> IDLE </td>
+ *    <td> PLAYING </td>
+ *    <td> ASYNC </td>
+ * </tr>
+ * <tr>
+ *    <td> mtpr_stop() </td>
+ *    <td> PLAYING </td>
+ *    <td> IDLE </td>
+ *    <td> ASYNC </td>
+ * </tr>
+ * </table></div>
+ *
+ * @subsection CAPI_MEDIA_TRANSPORTER_LIFE_CYCLE_CALLBACK_OPERATIONS Callback(Event) Operations
+ * The callback mechanism is used to notify the application about significant MediaTransporter events.
+ * <div><table class="doxtable">
+ *     <tr>
+ *        <th><b>REGISTER</b></th>
+ *        <th><b>UNREGISTER</b></th>
+ *        <th><b>CALLBACK</b></th>
+ *        <th><b>DESCRIPTION</b></th>
+ *     </tr>
+ *     <tr>
+ *        <td>mtpr_set_error_cb()</td>
+ *        <td>mtpr_unset_error_cb()</td>
+ *        <td>mtpr_error_cb()</td>
+ *        <td>This callback is used to notify that an error has occurred</td>
+ *     </tr>
+ * </table></div>
+ *
+ * @section CAPI_MEDIA_TRANSPORTER_MODULE_FEATURE Related Features
+ * This API is related with the following features:\n
+ *  - %http://tizen.org/feature/network.wifi\n
+ *  - %http://tizen.org/feature/network.telephony\n
+ *  - %http://tizen.org/feature/network.ethernet\n
+ *
+ * It is recommended to design feature related codes in your application for reliability.\n
+ * You can check if a device supports the related features for this API by using @ref CAPI_SYSTEM_SYSTEM_INFO_MODULE, thereby controlling the procedure of your application.\n
+ * To ensure your application is only running on the device with specific features, please define the features in your manifest file using the manifest editor in the SDK.\n
+ * More details on featuring your application can be found from <a href="https://docs.tizen.org/application/tizen-studio/native-tools/manifest-text-editor#feature-element"><b>Feature Element</b>.</a>
+ *
+ */
+
+/**
+ * @ingroup CAPI_MEDIA_TRANSPORTER_MODULE
+ * @defgroup CAPI_MEDIA_TRANSPORTER_SENDER_MODULE Media Transporter Sender
+ * @brief The @ref CAPI_MEDIA_TRANSPORTER_SENDER_MODULE API provides functions to manage media sources to communicate with the peer.
+ * @section CAPI_MEDIA_TRANSPORTER_SENDER_MODULE_HEADER Required Header
+ *    \#include <mtpr.h>
+ *
+ * @section CAPI_MEDIA_TRANSPORTER_SENDER_MODULE_OVERVIEW Overview
+ * The MediaTransporter Sender API allows you to:
+ * - add/remove the media source (camera, mic, videotest, audiotest)
+ * - set/get the media source params (video resolution, video framerate, etc)
+ *
+ * @section CAPI_MEDIA_TRANSPORTER_SENDER_MODULE_FEATURE Related Features
+ * This API is related with the following features:\n
+ *  - %http://tizen.org/feature/microphone\n
+ *  - %http://tizen.org/feature/camera
+ *
+ * It is recommended to design feature related codes in your application for reliability.\n
+ * You can check if a device supports the related features for this API by using @ref CAPI_SYSTEM_SYSTEM_INFO_MODULE, thereby controlling the procedure of your application.\n
+ * To ensure your application is only running on the device with specific features, please define the features in your manifest file using the manifest editor in the SDK.\n
+ * More details on featuring your application can be found from <a href="https://docs.tizen.org/application/tizen-studio/native-tools/manifest-text-editor#feature-element"><b>Feature Element</b>.</a>
+ *
+*/
+
+/**
+ * @ingroup CAPI_MEDIA_TRANSPORTER_MODULE
+ * @defgroup CAPI_MEDIA_TRANSPORTER_RECEIVER_MODULE Media Transporter Receiver
+ * @brief The @ref CAPI_MEDIA_TRANSPORTER_RECEIVER_MODULE API provides functions to export received audio or video stream.
+ * @section CAPI_MEDIA_TRANSPORTER_RECEIVER_MODULE_HEADER Required Header
+ *    \#include <mtpr.h>
+ *
+ * @section CAPI_MEDIA_TRANSPORTER_RECEIVER_MODULE_OVERVIEW Overview
+ * The MediaTransporter Receiver API allows you to:
+ * - set media track added callback to get notified about detected track information
+ * - set media data callback to get received audio and video data from peer
+ *
+*/
+
+#endif /* __TIZEN_MEDIA_TRANSPORTER_DOC_H__ */
diff --git a/include/MediaSourceBinAudioTest.h b/include/MediaSourceBinAudioTest.h
new file mode 100644 (file)
index 0000000..11245a1
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_SOURCE_BIN_AUDIO_TEST_H__
+#define __TIZEN_MEDIA_SOURCE_BIN_AUDIO_TEST_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <map>
+#include <mutex>
+#include <glib.h>
+#include <iniparser.h>
+#include <mm_resource_manager.h>
+#include "MediaSourceBinBase.h"
+#include "MediaTransporterGst.h"
+
+namespace tizen_media_transporter {
+
+#define RESOURCE_TYPE_MAX MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_ENCODER + 1
+const std::string DEFAULT_ELEMENT_AUDIO_TEST = "audiotestsrc";
+
+class MediaSourceBinAudioTest : public MediaSourceBinBase
+{
+public:
+       MediaSourceBinAudioTest(bundle* params);
+       ~MediaSourceBinAudioTest() = default;
+
+       MediaSourceBinInfo generate() override;
+
+private:
+       struct audioInfo {
+               int channel = -1;
+               int rate = -1;
+               std::string format = "";
+       };
+
+       struct encodingInfo {
+               int bitrate = 0;
+       };
+
+       void parseSourceParam(bundle* params);
+       GstElement* createMicSource();
+       void setEncoderParam(gst::GstElements& elements);
+       void setSourceParam(gst::GstElements& elements);
+       void replaceCapsWithAudioInfo(GstElement* element);
+       void replaceEncCapsWithAudioInfo(GstElement* element);
+       audioInfo _audioInfo;
+       encodingInfo _encInfo;
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_SOURCE_BIN_AUDIO_TEST_H__
diff --git a/include/MediaSourceBinBase.h b/include/MediaSourceBinBase.h
new file mode 100644 (file)
index 0000000..c1e17f7
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_SOURCE_BIN_BASE_H__
+#define __TIZEN_MEDIA_SOURCE_BIN_BASE_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+#include <mutex>
+#include <glib.h>
+#include <gst/gst.h>
+#include <iniparser.h>
+#include <bundle.h>
+#include <mm_resource_manager.h>
+#include "MediaTransporterParseIni.h"
+#include <tuple>
+
+namespace tizen_media_transporter {
+
+using MediaSourceBinInfo = std::tuple<mtprSourceType, GstBin*, ResourceSet>;
+
+const std::string ELEMENT_NAME_SRC_CAPSFILTER = "srcCapsfilter";
+const std::string ELEMENT_NAME_ENCODE_CAPSFILTER = "encCapsfilter";
+
+class IMediaSourceBin
+{
+public:
+       virtual ~IMediaSourceBin() {}
+       virtual MediaSourceBinInfo generate() = 0;
+};
+
+class MediaSourceBinBase : public IMediaSourceBin
+{
+public:
+       MediaSourceBinBase() = default;
+       ~MediaSourceBinBase() = default;
+
+protected:
+       std::vector<GstElement*> createVideoRestOfElements(const MtprMediaSourceIni& ini);
+       std::vector<GstElement*> createAudioRestOfElements(const MtprMediaSourceIni& ini);
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_SOURCE_BIN_BASE_H__
diff --git a/include/MediaSourceBinCamera.h b/include/MediaSourceBinCamera.h
new file mode 100644 (file)
index 0000000..927ad3a
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_SOURCE_BIN_CAMERA_H__
+#define __TIZEN_MEDIA_SOURCE_BIN_CAMERA_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <map>
+#include <mutex>
+#include <glib.h>
+#include <iniparser.h>
+#include <mm_resource_manager.h>
+#include "MediaSourceBinBase.h"
+#include "MediaTransporterGst.h"
+
+namespace tizen_media_transporter {
+
+const std::string DEFAULT_ELEMENT_CAMERASRC = "v4l2src";
+
+class MediaSourceBinCamera : public MediaSourceBinBase
+{
+public:
+       MediaSourceBinCamera(bundle* params);
+       ~MediaSourceBinCamera() = default;
+
+       MediaSourceBinInfo generate() override;
+
+private:
+       struct videoInfo {
+               int width = -1;
+               int height = -1;
+               int frameRate = -1;
+       };
+
+       struct encodingInfo {
+               int bitrate = 0;
+       };
+
+       void parseSourceParam(bundle* params);
+       GstElement* createCameraSource();
+       void setEncoderParam(gst::GstElements& elements);
+       void setSourceParam(gst::GstElements& elements);
+       void replaceCapsWithVideoInfo(GstElement* element);
+       void replaceEncCapsWithVideoInfo(GstElement* element);
+       videoInfo _videoInfo;
+       encodingInfo _encInfo;
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_SOURCE_BIN_CAMERA_H__
diff --git a/include/MediaSourceBinFactory.h b/include/MediaSourceBinFactory.h
new file mode 100644 (file)
index 0000000..e90d67c
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_SOURCE_BIN_FACTORY_H__
+#define __TIZEN_MEDIA_SOURCE_BIN_FACTORY_H__
+
+#ifdef __cplusplus
+
+#include "MediaSourceBinBase.h"
+#include <string>
+#include <map>
+#include <bundle.h>
+
+namespace tizen_media_transporter {
+
+class MediaSourceBinFactory
+{
+public:
+    static IMediaSourceBin* create(mtprSourceType type, bundle* param_list);
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_SOURCE_BIN_FACTORY_H__
diff --git a/include/MediaSourceBinMic.h b/include/MediaSourceBinMic.h
new file mode 100644 (file)
index 0000000..f65a4c3
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_SOURCE_BIN_MIC_H__
+#define __TIZEN_MEDIA_SOURCE_BIN_MIC_H__
+
+#ifdef __cplusplus
+
+
+#include <string>
+#include <map>
+#include <mutex>
+#include <glib.h>
+#include <iniparser.h>
+#include <mm_resource_manager.h>
+#include <sound_manager.h>
+
+#include "MediaTransporter.h"
+#include "MediaSourceBinBase.h"
+#include "MediaTransporterGst.h"
+
+namespace tizen_media_transporter {
+
+const std::string DEFAULT_ELEMENT_MICSRC = "pulsesrc";
+
+class MediaSourceBinMic : public MediaSourceBinBase
+{
+public:
+       MediaSourceBinMic(bundle* params);
+       ~MediaSourceBinMic() = default;
+
+       MediaSourceBinInfo generate() override;
+       void setSoundStreamInfo(sound_stream_info_h streamInfo);
+
+private:
+       struct audioInfo {
+               int channel = -1;
+               int rate = -1;
+               std::string format = "";
+       };
+
+       struct encodingInfo {
+               int bitrate = 0;
+       };
+
+       void parseSourceParam(bundle* params);
+       GstElement* createMicSource();
+       void setEncoderParam(gst::GstElements& elements);
+       void setSourceParam(gst::GstElements& elements);
+       void replaceCapsWithAudioInfo(GstElement* element);
+       void replaceEncCapsWithAudioInfo(GstElement* element);
+       audioInfo _audioInfo;
+       encodingInfo _encInfo;
+       std::string _streamInfo = "";
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_SOURCE_BIN_CAMERA_H__
diff --git a/include/MediaSourceBinVideoTest.h b/include/MediaSourceBinVideoTest.h
new file mode 100644 (file)
index 0000000..0de1cbc
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_SOURCE_BIN_VIDEO_TEST_SRC_H__
+#define __TIZEN_MEDIA_SOURCE_BIN_VIDEO_TEST_SRC_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <map>
+#include <mutex>
+#include <glib.h>
+#include <iniparser.h>
+#include <mm_resource_manager.h>
+#include "MediaSourceBinBase.h"
+#include "MediaTransporterGst.h"
+
+namespace tizen_media_transporter {
+
+const std::string DEFAULT_ELEMENT_VIDEO_TEST = "videotestsrc";
+
+class MediaSourceBinVideoTest : public MediaSourceBinBase
+{
+public:
+       MediaSourceBinVideoTest(bundle* params);
+       ~MediaSourceBinVideoTest() = default;
+
+       MediaSourceBinInfo generate() override;
+
+private:
+       struct videoInfo {
+               int width = -1;
+               int height = -1;
+               int frameRate = -1;
+       };
+
+       struct encodingInfo {
+               int bitrate = 0;
+       };
+
+       void parseSourceParam(bundle* params);
+       GstElement* createVideoTestSource();
+       void setEncoderParam(gst::GstElements& elements);
+       void setSourceParam(gst::GstElements& elements);
+       void replaceCapsWithVideoInfo(GstElement* element);
+       void replaceEncCapsWithVideoInfo(GstElement* element);
+       videoInfo _videoInfo;
+       encodingInfo _encInfo;
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_SOURCE_BIN_VIDEO_TEST_SRC_H__
diff --git a/include/MediaTransporter.h b/include/MediaTransporter.h
new file mode 100644 (file)
index 0000000..202ef28
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __TIZEN_MEDIA_TRANSPORTER_CPP_H__
+#define __TIZEN_MEDIA_TRANSPORTER_CPP_H__
+
+#ifdef __cplusplus
+
+#include "mtpr.h"
+#include "mtpr_internal.h"
+
+#include <set>
+#include <mm_resource_manager.h>
+
+namespace tizen_media_transporter {
+
+using ResourceSet = std::set<mm_resource_manager_res_type_e>;
+
+using mtprConnectionType = mtpr_connection_type_e;
+using mtprSourceType = mtpr_source_type_e;
+using mtprMediaType = mtpr_media_type_e;
+using mtprState = mtpr_state_e;
+using mtprTrackAddedCallback = mtpr_track_added_cb;
+using mtprNoMoreTrackCallback = mtpr_no_more_track_cb;
+using mtprPacketCallback = mtpr_encoded_frame_cb;
+using mtprErrorCallback = mtpr_error_cb;
+using mtprError = mtpr_error_e;
+
+using mtprDisplayType = mtpr_display_type_e;
+using mtprDisplayMode = mtpr_display_mode_e;
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_CPP_H__
diff --git a/include/MediaTransporterBase.h b/include/MediaTransporterBase.h
new file mode 100644 (file)
index 0000000..b29483d
--- /dev/null
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_BASE_H__
+#define __TIZEN_MEDIA_TRANSPORTER_BASE_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <map>
+#include <set>
+#include <mutex>
+#include <glib.h>
+#include <iniparser.h>
+#include <mm_resource_manager.h>
+#include <gst/gst.h>
+#include <bundle.h>
+#include "MediaTransporterParseIni.h"
+#include "MediaTransporterCallback.h"
+#include "MediaTransporterObserver.h"
+#include "MediaTransporterResource.h"
+
+namespace tizen_media_transporter {
+
+struct mtprGstreamer {
+       GstElement *pipeline;
+       GstBus *bus;
+       guint bus_watcher;
+       GList *signals; // mtprSignal
+};
+
+class MediaTransporterBase : public IObserver
+{
+public:
+       MediaTransporterBase() = default;
+       virtual ~MediaTransporterBase() = default;
+
+       void create();
+       ResourceSet build();
+       void destroy();
+
+       void start();
+       void stop();
+
+       mtprState state();
+
+       void setErrorCallback(void* handle, mtprErrorCallback callback, void* userData);
+       void unsetErrorCallback();
+
+       virtual void setSenderAddress(std::string address);
+       virtual std::string getSenderAddress();
+
+       virtual void setReceiverAddress(std::string address);
+       virtual std::string getReceiverAddress();
+
+       virtual mtprConnectionType type() = 0;
+
+       virtual void setConnection(std::string name, std::string value) = 0;
+       virtual void setConnection(bundle* params) = 0;
+       virtual void getConnection(bundle* params) = 0;
+
+       void setResourceManager(std::shared_ptr<MediaTransporterResource> resourceManager);
+       void changed() override; // resource interrupted
+
+protected:
+       mtprGstreamer _gst {};
+       std::unique_ptr<IInvokable> _errorCallback;
+       std::shared_ptr<MediaTransporterResource> _resourceManager;
+
+private:
+       void makePipeline();
+       void stopInternal();
+
+       virtual ResourceSet buildPipeline() = 0;
+       virtual void startPipeline() = 0;
+       virtual void stopPipeline() = 0;
+
+       static gboolean __busWatchCb(GstBus *bus, GstMessage *message, gpointer user_data);
+
+       std::mutex _mutex;
+       mtprState _state { MTPR_STATE_IDLE };
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_BASE_H__
diff --git a/include/MediaTransporterCallback.h b/include/MediaTransporterCallback.h
new file mode 100644 (file)
index 0000000..24d4ee9
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+* Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* 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.
+*/
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_CALLBACK_H__
+#define __TIZEN_MEDIA_TRANSPORTER_CALLBACK_H__
+
+#include <stdio.h>
+#include <dlog.h>
+
+#include <map>
+#include <variant>
+#include <memory>
+
+#include <media_packet.h>
+#include "MediaTransporter.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+namespace tizen_media_transporter {
+
+using VariantData = std::variant<mtprError, media_packet_h, unsigned int, mtprMediaType>;
+
+class IInvokable
+{
+public:
+       virtual ~IInvokable() = default;
+
+       virtual void invoke() = 0;
+       virtual void invoke(VariantData data) = 0;
+       virtual void invoke(VariantData data1, VariantData data2) = 0;
+};
+
+class AbstractCallback : public IInvokable
+{
+public:
+       AbstractCallback(void* handle, void* userdata) :
+               _handle(handle), _userdata(userdata) {}
+       virtual ~AbstractCallback() = default;
+
+       void invoke() override {}
+       void invoke(VariantData data) override {}
+       void invoke(VariantData data1, VariantData data2) override {}
+
+protected:
+       void* _handle;
+       void* _userdata;
+};
+
+class TrackAddedCallback : public AbstractCallback
+{
+public:
+       TrackAddedCallback(void* handle, mtprTrackAddedCallback cb, void* userdata);
+       virtual ~TrackAddedCallback() = default;
+
+       void invoke(VariantData data1, VariantData data2) override;
+
+private:
+       mtprTrackAddedCallback _callback;
+};
+
+class NoMoreTrackCallback : public AbstractCallback
+{
+public:
+       NoMoreTrackCallback(void* handle, mtprNoMoreTrackCallback cb, void* userdata);
+       virtual ~NoMoreTrackCallback() = default;
+
+       void invoke() override;
+
+private:
+       mtprNoMoreTrackCallback _callback;
+};
+
+class PacketCallback : public AbstractCallback
+{
+public:
+       PacketCallback(void* handle, mtprMediaType type, mtprPacketCallback cb, void* userdata);
+       virtual ~PacketCallback() = default;
+
+       void invoke(VariantData data1, VariantData data2) override;
+
+private:
+       mtprMediaType _type;
+       mtprPacketCallback _callback;
+};
+
+class ErrorCallback : public AbstractCallback
+{
+public:
+       ErrorCallback(void* handle, mtprErrorCallback cb, void* userdata);
+       virtual ~ErrorCallback() = default;
+
+       void invoke(VariantData data) override;
+
+private:
+       mtprErrorCallback _callback;
+};
+
+} // namespace
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIA_TRANSPORTER_CALLBACK_H__ */
diff --git a/include/MediaTransporterDisplay.h b/include/MediaTransporterDisplay.h
new file mode 100644 (file)
index 0000000..9eff6ca
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+* Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* 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.
+*/
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_DISPLAY_H__
+#define __TIZEN_MEDIA_TRANSPORTER_DISPLAY_H__
+
+#include <stdio.h>
+#include <mutex>
+#include <mm_display_interface.h>
+#include <gst/gst.h>
+
+#include "MediaTransporter.h"
+#include "MediaTransporterObserver.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+namespace tizen_media_transporter {
+
+class MediaTransporterDisplay
+{
+public:
+       MediaTransporterDisplay(mtprDisplayType type, void *surface);
+       ~MediaTransporterDisplay();
+
+       GstElement* videoSink();
+
+       mtprDisplayType getType() { return _type; };
+
+       void setVisible(bool visible);
+       bool getVisible() { return _visible; };
+
+       void setMode(mtprDisplayMode mode);
+       mtprDisplayMode getMode() { return _mode; };
+
+private:
+       void applyVisibleProperty();
+       void applyModeProperty();
+
+       mtprDisplayType _type;
+       void* _surface;
+
+       bool _visible { true };
+       mtprDisplayMode _mode { MTPR_DISPLAY_MODE_LETTER_BOX };
+
+       std::mutex _mutex;
+       mm_display_interface_h _mmDisplay { nullptr };
+
+       GstElement* _sinkElement { nullptr };
+};
+
+} // namespace tizen_media_transporter
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIA_TRANSPORTER_DISPLAY_H__ */
diff --git a/include/MediaTransporterException.h b/include/MediaTransporterException.h
new file mode 100644 (file)
index 0000000..1408b8e
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_EXCEPTION_H__
+#define __TIZEN_MEDIA_TRANSPORTER_EXCEPTION_H__
+
+#include <exception>
+#include <string>
+
+#include <MediaTransporter.h>
+
+namespace tizen_media_transporter {
+
+class MediaTransporterException : public std::exception
+{
+public:
+       MediaTransporterException(int error, std::string msg) :
+               _error(error), _msg(msg) {}
+       ~MediaTransporterException() final = default;
+
+       const char* what() const noexcept override { return _msg.c_str(); }
+       int error() const { return _error; }
+
+private:
+       int _error;
+       std::string _msg;
+};
+
+}; // tizen_media_transporter
+
+#endif // __TIZEN_MEDIA_TRANSPORTER_EXCEPTION_H__
diff --git a/include/MediaTransporterFactory.h b/include/MediaTransporterFactory.h
new file mode 100644 (file)
index 0000000..df0825e
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_FACTORY_H__
+#define __TIZEN_MEDIA_TRANSPORTER_FACTORY_H__
+
+#ifdef __cplusplus
+
+#include "MediaTransporterBase.h"
+#include <string>
+#include <map>
+
+namespace tizen_media_transporter {
+
+class MediaTransporterFactory
+{
+public:
+    static MediaTransporterBase* create(mtprConnectionType type);
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_FACTORY_H__
diff --git a/include/MediaTransporterGst.h b/include/MediaTransporterGst.h
new file mode 100644 (file)
index 0000000..79acaa4
--- /dev/null
@@ -0,0 +1,129 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_GST_H__
+#define __TIZEN_MEDIA_TRANSPORTER_GST_H__
+
+#ifdef __cplusplus
+#include <glib.h>
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include <vector>
+
+namespace tizen_media_transporter {
+namespace gst {
+
+const std::string DEFAULT_ELEMENT_FAKESINK      = "fakesink";
+const std::string DEFAULT_ELEMENT_AUDIOCONVERT  = "audioconvert";
+const std::string DEFAULT_ELEMENT_AUDIORESAMPLE = "audioresample";
+const std::string DEFAULT_ELEMENT_VIDEOCONVERT  = "videoconvert";
+const std::string DEFAULT_ELEMENT_CAPSFILTER    = "capsfilter";
+const std::string DEFAULT_ELEMENT_TSMUX         = "mpegtsmux";
+const std::string DEFAULT_ELEMENT_TSDEMUX       = "tsdemux";
+const std::string DEFAULT_ELEMENT_H264_PARSER   = "h264parse";
+const std::string DEFAULT_ELEMENT_MPEG4_PARSER  = "mpeg4videoparse";
+const std::string DEFAULT_ELEMENT_APPSRC        = "appsrc";
+const std::string DEFAULT_ELEMENT_QUEUE         = "queue";
+
+
+const std::string DEFAULT_ELEMENT_RISTSRC    = "ristsrc";
+const std::string DEFAULT_ELEMENT_RISTSINK   = "ristsink";
+const std::string DEFAULT_ELEMENT_SRTSRC     = "srtsrc";
+const std::string DEFAULT_ELEMENT_SRTSINK    = "srtsink";
+const std::string DEFAULT_ELEMENT_RTSPSINK   = "rtspclientsink";
+
+const std::string DEFAULT_VIDEO_SINK_ELEMENT = "tizenwlsink";
+const std::string DEFAULT_AUDIO_SINK_ELEMENT = "pulsesink";
+
+const std::string MEDIA_TYPE_AUDIO_RAW    = "audio/x-raw";
+const std::string MEDIA_TYPE_AUDIO_MULAW  = "audio/x-mulaw";
+const std::string MEDIA_TYPE_AUDIO_ALAW   = "audio/x-alaw";
+const std::string MEDIA_TYPE_AUDIO_OPUS   = "audio/x-opus";
+const std::string MEDIA_TYPE_AUDIO_VORBIS = "audio/x-vorbis";
+const std::string MEDIA_TYPE_AUDIO_AAC    = "audio/mpeg";
+const std::string MEDIA_TYPE_VIDEO_RAW    = "video/x-raw";
+const std::string MEDIA_TYPE_VIDEO_VP8    = "video/x-vp8";
+const std::string MEDIA_TYPE_VIDEO_VP9    = "video/x-vp9";
+const std::string MEDIA_TYPE_VIDEO_THEORA = "video/x-theora";
+const std::string MEDIA_TYPE_VIDEO_H264   = "video/x-h264";
+const std::string MEDIA_TYPE_VIDEO_H265   = "video/x-h265";
+const std::string MEDIA_TYPE_VIDEO_JPEG   = "image/jpeg";
+const std::string MEDIA_TYPE_VIDEO_MPEG   = "video/mpeg";
+
+const std::string DEFAULT_DOT_FILE_NAME_PREFIX = "mtpr";
+
+struct mtprSignal {
+       GObject* obj;
+       gulong signal_id;
+};
+
+struct elementInfo {
+       const gchar* klass_name;
+       GstCaps* srcCaps;
+       GstCaps* sinkCaps;
+       std::vector<std::string> excludedElements;
+};
+
+using GstElements = std::vector<GstElement*>;
+
+void _gstInit();
+
+void _clearElements(GstElements& elements);
+void _removeElement(GstElements& elements, GstElement* removeElement);
+GstElement*_createElement(std::string factory_name, std::string name ="");
+void _connectAndAppendSignal(GList** signals, GObject* obj, const char* sig_name, GCallback cb, gpointer user_data);
+void _disconnectSignal(gpointer data);
+bool _addNoTargetGhostpad(GstBin* bin, GstPad** new_pad, bool is_src);
+bool _setGhostpadTarget(GstPad* ghost_pad, GstElement* target_element, bool is_src);
+unsigned int _getUnoccupiedSourceId(GHashTable* slots);
+
+void _setElementProperties(GstElement* element, std::vector<std::string> key_value_pairs);
+void _applyStreamInfo(GstElement* element, std::string streamInfo);
+void _addElementsToBin(GstBin* bin, GstElements elements);
+void _removeElementsFromBin(GstBin* bin, GstElements& elements);
+GstElement* _findEncoderElement(GstElements& elements);
+bool _containHwEncoderElement(GstElements& elements);
+GstElement* _findElementByName(GstElements& elements, const std::string& nameToFind);
+
+void _linkElements(GstElements& elements);
+void _syncElementsStateWithParent(GstElements& elements);
+
+std::string _getMimeTypeFromPad(GstPad* pad);
+
+void _generateDot(GstElement* pipeline, std::string name);
+void _dumpPipelineState(GstElement* pipeline);
+void _setPipelineState(GstElement* pipeline, GstState state, int timeout);
+
+GstAudioFormat _getAudioFormatFromString(std::string format);
+std::string _getVideoMediaType(std::string codec_name);
+std::string _getAudioMediaType(std::string codec_name);
+GstCaps* _getCapsFromEncodedVideoMediaType(std::string media_type, int width, int height);
+GstCaps* _getCapsFromEncodedAudioMediaType(std::string media_type, int channels, int samplerate);
+GstCaps* _makeCapsForCapsfilter(GstPad *pad);
+GstElement* _createElementFromRegistry(std::string klassName, GstCaps* sinkCaps, GstCaps* srcCaps, const std::vector<std::string>& excludedElements);
+
+void _setPipelineState(GstElement* pipeline, GstState state, int timeoutSec);
+
+void _destroyElementFromParent(GstElement* element);
+
+void _printCaps(GstCaps* caps, std::string prefix);
+
+}; // gst
+
+} // tizen_media_transporter
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_GST_H__
diff --git a/include/MediaTransporterLog.h b/include/MediaTransporterLog.h
new file mode 100644 (file)
index 0000000..19a8103
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_LOG_H__
+#define __TIZEN_MEDIA_TRANSPORTER_LOG_H__
+
+#include <stdio.h>
+#include <dlog.h>
+#include "MediaTransporterParseIni.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "TIZEN_N_MTPR"
+
+#define FONT_COLOR_RESET    "\033[0m"
+#define FONT_COLOR_RED      "\033[31m"
+#define FONT_COLOR_GREEN    "\033[32m"
+#define FONT_COLOR_YELLOW   "\033[33m"
+#define FONT_COLOR_BLUE     "\033[34m"
+#define FONT_COLOR_PURPLE   "\033[35m"
+#define FONT_COLOR_CYAN     "\033[36m"
+#define FONT_COLOR_GRAY     "\033[37m"
+
+#define SECURE_LOG_DEBUG(fmt, arg...) \
+do { \
+       SECURE_LOGD(FONT_COLOR_RESET fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define SECURE_LOG_INFO(fmt, arg...) \
+do { \
+       SECURE_LOGI(FONT_COLOR_GREEN fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define SECURE_LOG_WARNING(fmt, arg...) \
+do { \
+       SECURE_LOGW(FONT_COLOR_YELLOW fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define SECURE_LOG_ERROR(fmt, arg...) \
+do { \
+       SECURE_LOGE(FONT_COLOR_RED fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define LOG_VERBOSE(fmt, arg...) \
+do { \
+       if (MediaTransporterIni::get().general().verboseLog) \
+               LOGD(FONT_COLOR_RESET fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define LOG_DEBUG(fmt, arg...) \
+do { \
+       LOGD(FONT_COLOR_RESET fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define LOG_INFO(fmt, arg...) \
+do { \
+       LOGI(FONT_COLOR_GREEN fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define LOG_WARNING(fmt, arg...) \
+do { \
+       LOGW(FONT_COLOR_YELLOW fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define LOG_ERROR(fmt, arg...) \
+do { \
+       LOGE(FONT_COLOR_RED fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define LOG_ERROR_IF_REACHED(fmt, arg...) \
+do { \
+       LOGE(FONT_COLOR_RED "should not be reached here." fmt FONT_COLOR_RESET, ##arg); \
+} while (0)
+
+#define LOG_DEBUG_ENTER() \
+do { \
+       LOGD(FONT_COLOR_PURPLE "<Enter>" FONT_COLOR_RESET); \
+} while (0)
+
+#define LOG_DEBUG_LEAVE() \
+do { \
+       LOGD(FONT_COLOR_PURPLE "<Leave>" FONT_COLOR_RESET); \
+} while (0)
+
+#define LOG_WARNING_IF_CALLBACK_EXISTS(x_callback) \
+do { \
+       if (x_callback.callback) \
+               LOG_WARNING("previous callback[%p] user_data[%p]", x_callback.callback, x_callback.user_data); \
+} while (0)
+
+#define RET_IF(expr, fmt, arg...) \
+do { \
+       if ((expr)) { \
+               LOG_ERROR(fmt , ##arg); \
+               return; \
+       } \
+} while (0)
+
+#define RET_VAL_IF(expr, val, fmt, arg...) \
+do { \
+       if ((expr)) { \
+               LOG_ERROR(fmt , ##arg); \
+               return (val);\
+       } \
+} while (0)
+
+#define RET_ERR_IF_INVALID_INSTANCE(arg)   \
+       RET_VAL_IF(!(arg), MTPR_ERROR_INVALID_PARAMETER, "mtpr is null")
+
+#define RET_ERR_IF_NULL_ARG(arg)      \
+       RET_VAL_IF(!(arg), MTPR_ERROR_INVALID_PARAMETER, "arg is null")
+
+#define RET_ERR_IF_INVALID_RANGE_ARG(arg, min, max)      \
+do {   \
+       RET_VAL_IF(((arg) < (min)), MTPR_ERROR_INVALID_PARAMETER, "arg is less than min"); \
+       RET_VAL_IF(((arg) > (max)), MTPR_ERROR_INVALID_PARAMETER, "arg is over max"); \
+} while (0)
+
+#define MTPR_FENTER();    LOG_DEBUG("<ENTER>");
+#define MTPR_FLEAVE();    LOG_DEBUG("<LEAVE>");
+
+#define SAFE_STR(str)               (str) ? str : "null"
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIA_TRANSPORTER_LOG_H__ */
diff --git a/include/MediaTransporterObserver.h b/include/MediaTransporterObserver.h
new file mode 100644 (file)
index 0000000..f90ce2c
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+* Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* 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.
+*/
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_OBSERVER_H__
+#define __TIZEN_MEDIA_TRANSPORTER_OBSERVER_H__
+
+#include <stdio.h>
+#include <dlog.h>
+#include <vector>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+namespace tizen_media_transporter {
+
+class IObserver
+{
+public:
+    virtual ~IObserver() = default;
+    virtual void changed() = 0;
+};
+
+class IObservable
+{
+public:
+       virtual ~IObservable() {}
+
+       virtual void subscribe(IObserver *observer) = 0;
+       virtual void unsubscribe(IObserver *observer) = 0;
+       virtual void notify() = 0;
+};
+
+class ObservableBase : public IObservable
+{
+public:
+       ObservableBase() = default;
+       ~ObservableBase() override = default;
+
+       void subscribe(IObserver *observer);
+       void unsubscribe(IObserver *observer);
+       void notify();
+
+private:
+       std::vector<IObserver*> _observers;
+};
+
+} // namespace
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIA_TRANSPORTER_OBSERVER_H__ */
+
diff --git a/include/MediaTransporterParseIni.h b/include/MediaTransporterParseIni.h
new file mode 100644 (file)
index 0000000..744cc46
--- /dev/null
@@ -0,0 +1,194 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_PARSE_INI_H__
+#define __TIZEN_MEDIA_TRANSPORTER_PARSE_INI_H__
+
+#ifdef __cplusplus
+
+#include <string>
+#include <vector>
+#include <iniparser.h>
+#include "mtpr.h"
+
+namespace tizen_media_transporter {
+
+#define MTPR_INI_PATH                      SYSCONFDIR"/multimedia/mmfw_media_transporter.ini"
+
+/* categories */
+#define INI_CATEGORY_GENERAL              "general"
+#define INI_CATEGORY_CONNECTION           "connection"
+#define INI_CATEGORY_MEDIA_SOURCE         "media source"
+#define INI_CATEGORY_SOURCE_CAMERA        "source camera"
+#define INI_CATEGORY_SOURCE_MIC           "source mic"
+#define INI_CATEGORY_SOURCE_AUDIOTEST     "source audiotest"
+#define INI_CATEGORY_SOURCE_VIDEOTEST     "source videotest"
+#define INI_CATEGORY_RENDERING_SINK       "rendering sink"
+
+/* items for general */
+#define INI_ITEM_DOT_GENERATE             "generate dot"
+#define INI_ITEM_DOT_PATH                 "dot path"
+#define INI_ITEM_VERBOSE_LOG              "verbose log"
+#define INI_ITEM_GST_ARGS                 "gstreamer arguments"
+#define INI_ITEM_GST_EXCLUDED_ELEMENTS    "gstreamer excluded elements"
+#define INI_ITEM_GST_STATE_CHANGE_TIMEOUT "timeout"
+
+#define DEFAULT_GENERATE_DOT              true
+#define DEFAULT_DOT_PATH                  "/tmp/"
+#define DEFAULT_VERBOSE_LOG               false
+#define DEFAULT_STATE_CHANGE_TIMEOUT      10
+
+/* items for connection */
+#define INI_ITEM_RTSP_SERVER_IP           "rtsp server ip"
+#define INI_ITEM_RTSP_SERVER_PORT         "rtsp server port"
+#define INI_ITEM_RTSP_SERVER_MOUNT_POINT  "rtsp server mount point"
+
+#define DEFAULT_RTSP_SERVER_IP            "127.0.0.1"
+#define DEFAULT_RTSP_SERVER_PORT          "8554"
+#define DEFAULT_RTSP_SERVER_MOUNT_POINT   "/mtpr_server/tmp"
+
+/* items for media source */
+#define INI_ITEM_SOURCE_ELEMENT            "source element"
+#define INI_ITEM_SOURCE_ELEMENT_PROPERTIES "source element properties"
+#define INI_ITEM_VIDEO_RAW_FORMAT          "video raw format"
+#define INI_ITEM_VIDEO_WIDTH               "video width"
+#define INI_ITEM_VIDEO_HEIGHT              "video height"
+#define INI_ITEM_VIDEO_FRAMERATE           "video framerate"
+#define INI_ITEM_VIDEO_DRC_SUPPORT         "video drc support"             /* source element supports dynamic resolution change */
+#define INI_ITEM_VIDEO_ENCODED_FMT_SUPPORT "video encoded format support"  /* source element supports encoded format */
+#define INI_ITEM_VIDEO_CODEC               "video codec"
+#define INI_ITEM_VIDEO_HW_ENCODER_ELEMENT  "video hw encoder element"
+#define INI_ITEM_AUDIO_RAW_FORMAT          "audio raw format"
+#define INI_ITEM_AUDIO_SAMPLERATE          "audio samplerate"
+#define INI_ITEM_AUDIO_CHANNELS            "audio channels"
+#define INI_ITEM_AUDIO_CODEC               "audio codec"
+#define INI_ITEM_AUDIO_HW_ENCODER_ELEMENT  "audio hw encoder element"
+
+#define DEFAULT_VIDEO_RAW_FORMAT          "I420"
+#define DEFAULT_VIDEO_WIDTH               320
+#define DEFAULT_VIDEO_HEIGHT              240
+#define DEFAULT_VIDEO_FRAMERATE           30
+#define DEFAULT_VIDEO_DRC_SUPPORT         false
+#define DEFAULT_VIDEO_ENCODED_FMT_SUPPORT false
+#define DEFAULT_AUDIO_RAW_FORMAT          "F32LE"
+#define DEFAULT_AUDIO_SAMPLERATE          8000
+#define DEFAULT_AUDIO_CHANNELS            1
+
+#define DEFAULT_VIDEO_CODEC               "mpeg"
+#define DEFAULT_AUDIO_CODEC               "aac"
+
+/* items for rendering sink */
+#define INI_ITEM_AUDIO_SINK_ELEMENT        "audio sink element"
+#define INI_ITEM_VIDEO_SINK_ELEMENT        "video sink element"
+#define INI_ITEM_AUDIO_HW_DECODER_ELEMENTS "audio hw decoder elements"
+#define INI_ITEM_VIDEO_HW_DECODER_ELEMENTS "video hw decoder elements"
+
+struct MtprGeneralIni {
+       bool generateDot;
+       std::string dotPath;
+       bool verboseLog;
+       std::vector<std::string> gstArgs;
+       std::vector<std::string> gstExcludedElements;
+       int timeout;
+};
+
+struct MtprRtspConnectionIni {
+       std::string rtsp_ip;
+       std::string rtsp_port;
+       std::string rtsp_mount_point;
+};
+
+struct MtprRenderingSinkIni {
+       std::string audioSinkElement;
+       std::string videoSinkElement;
+       std::vector<std::string> audioHWDecoderElements;
+       std::vector<std::string> videoHWDecoderElements;
+       bool v_overlay_resource_required;
+};
+
+struct MtprMediaSourceIni {
+       std::string sourceElement;
+       std::vector<std::string> sourceElementProperties;
+       /* video source pipeline */
+       std::string videoRawFormat;
+       int videoWidth;
+       int videoHeight;
+       int videoFramerate;
+       std::string videoCodec;
+       std::string videoHWEncoderElement;
+       bool videoDRCSupport;
+       bool videoEncodedFMTSupport;
+       /* audio source pipeline */
+       std::string audioRawFormat;
+       int audioSamplerate;
+       int audioChannels;
+       std::string audioCodec;
+       std::string audioHWEncoderElement;
+};
+
+struct MtprIni {
+       MtprGeneralIni general;
+       MtprMediaSourceIni mediaSourceDefault;
+       MtprRenderingSinkIni renderingSink;
+       std::vector<MtprMediaSourceIni> mediaSources;
+};
+
+class IReadable
+{
+public:
+       virtual ~IReadable() = default;
+
+       virtual const MtprGeneralIni& general() = 0;
+       virtual const MtprMediaSourceIni& mediaSource(int source = -1) = 0;
+       virtual const MtprRenderingSinkIni& renderingSink() = 0;
+};
+
+class MediaTransporterIni : public IReadable
+{
+public:
+       static MediaTransporterIni& get();
+
+       MediaTransporterIni(MediaTransporterIni const&) = delete;
+       MediaTransporterIni(MediaTransporterIni&&) = delete;
+       MediaTransporterIni& operator=(MediaTransporterIni const&) = delete;
+       MediaTransporterIni& operator=(MediaTransporterIni &&) = delete;
+
+       const MtprGeneralIni& general() override;
+       const MtprMediaSourceIni& mediaSource(int type = -1) override;
+       const MtprRenderingSinkIni& renderingSink() override;
+
+private:
+       MediaTransporterIni();
+
+       void load();
+       void dumpIni();
+
+       void dumpItemsOfGeneral();
+       void dumpItemsOfRenderingSink();
+       void dumpItemsOfMediaSource(MtprMediaSourceIni& source);
+
+       void applyGeneralSetting(dictionary* dict);
+       void applyRenderingSink(dictionary* dict);
+       void applyMediaSourceDefault(dictionary* dict, std::string category);
+       void applyMediaSource(dictionary* dict, MtprMediaSourceIni& source, std::string category);
+
+       MtprIni _ini;
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_PARSE_INI_H__
diff --git a/include/MediaTransporterReceiver.h b/include/MediaTransporterReceiver.h
new file mode 100644 (file)
index 0000000..6713755
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_RECEIVER_H__
+#define __TIZEN_MEDIA_TRANSPORTER_RECEIVER_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <map>
+#include <climits>
+#include <gst/gst.h>
+#include <media_format.h>
+#include <sound_manager_internal.h>
+#include "MediaTransporterBase.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterCallback.h"
+#include "MediaTransporterDisplay.h"
+
+namespace tizen_media_transporter {
+
+#define DEFAULT_QUEUE_MAX_SIZE_BUFFERS 500
+#define DEFAULT_QUEUE_MAX_SIZE_BYTES   1024 * 1024 * 10
+#define DEFAULT_QUEUE_MAX_SIZE_TIME    5 * GST_SECOND
+
+class MediaTransporterReceiver : public MediaTransporterBase
+{
+public:
+       MediaTransporterReceiver() = default;
+       virtual ~MediaTransporterReceiver() = default;
+
+       void setTrackAddedCallback(void* handle, mtprTrackAddedCallback callback, void* userData);
+       void unsetTrackAddedCallback();
+
+       void setNoMoreTrackCallback(void* handle, mtprNoMoreTrackCallback callback, void* userData);
+       void unsetNoMoreTrackCallback();
+
+       void setVideoPacketCallback(void* handle, mtprPacketCallback callback, void* userData);
+       void setAudioPacketCallback(void* handle, mtprPacketCallback callback, void* userData);
+       void unsetVideoPacketCallback();
+       void unsetAudioPacketCallback();
+
+       void setDisplay(std::shared_ptr<MediaTransporterDisplay> display);
+
+       void setSoundStreamInfo(sound_stream_info_h streamInfo);
+
+protected:
+       std::unique_ptr<IInvokable> _trackAddedCallback;
+       std::unique_ptr<IInvokable> _noMoreTrackCallback;
+
+       struct mtprCallback {
+               std::unique_ptr<IInvokable> _callback;
+               media_format_h mediaFormat { nullptr }; // FIXME : this should be A/V separated!!!
+       };
+       mtprCallback _videoCallback {};
+       mtprCallback _audioCallback {};
+
+       std::shared_ptr<MediaTransporterDisplay> _display;
+       std::string _streamInfo;
+
+       int _buildForwardingElements(GstElement* demux, GstPad* pad, GCallback callback);
+       void _buildForwardingSink(gst::GstElements& elements, GstPad* pad, GCallback callback);
+
+       int _buildAudioRenderingElements(GstElement* demux, GstPad* pad);
+       int _buildVideoRenderingElements(GstElement* demux, GstPad* pad);
+
+       static void _streamAddedCallback(GstPad* pad, gpointer data);
+       static void _noMoreStreamCallback(gpointer data);
+       static void _encodedAudioStreamCallback(GstElement* object, GstBuffer* buffer, GstPad* pad, gpointer data);
+       static void _encodedVideoStreamCallback(GstElement* object, GstBuffer* buffer, GstPad* pad, gpointer data);
+
+private:
+       static media_packet_h _makeMediaPacket(GstBuffer* buffer, GstPad* pad, media_format_h* format);
+       void _buildAudioRenderingSink(gst::GstElements& elements);
+       void _buildVideoRenderingSink(gst::GstElements& elements);
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_RECEIVER_H__
+
diff --git a/include/MediaTransporterReceiverRist.h b/include/MediaTransporterReceiverRist.h
new file mode 100644 (file)
index 0000000..54dff45
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_RECEIVER_RIST_H__
+#define __TIZEN_MEDIA_TRANSPORTER_RECEIVER_RIST_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <map>
+#include <climits>
+#include <gst/gst.h>
+#include "MediaTransporterBase.h"
+#include "MediaTransporterReceiver.h"
+
+namespace tizen_media_transporter {
+
+class MediaTransporterReceiverRist : public MediaTransporterReceiver
+{
+public:
+       MediaTransporterReceiverRist() = default;
+       ~MediaTransporterReceiverRist() = default;
+
+       ResourceSet buildPipeline() override;
+       void startPipeline() override;
+       void stopPipeline() override;
+
+       void setConnection(std::string name, std::string value) override {}
+       void setConnection(bundle* params) override {}
+       void getConnection(bundle* params) override {}
+
+       void setReceiverAddress(std::string address) override;
+       std::string getReceiverAddress() override { return _receiverAddress; }
+
+       mtprConnectionType type() override { return MTPR_CONNECTION_TYPE_RIST_RECEIVER; }
+
+private:
+       static void _demuxPadAddedCallback(GstElement *demux, GstPad *new_pad, gpointer userData);
+       static void _demuxNoMorePadsCallback(GstElement *demux, gpointer userData);
+
+       std::string _receiverAddress;
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_RECEIVER_RIST_H__
diff --git a/include/MediaTransporterReceiverSrt.h b/include/MediaTransporterReceiverSrt.h
new file mode 100644 (file)
index 0000000..64626cd
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_RECEIVER_SRT_H__
+#define __TIZEN_MEDIA_TRANSPORTER_RECEIVER_SRT_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <map>
+#include <climits>
+#include <gst/gst.h>
+#include "MediaTransporterBase.h"
+#include "MediaTransporterReceiver.h"
+
+namespace tizen_media_transporter {
+
+class MediaTransporterReceiverSrt : public MediaTransporterReceiver
+{
+public:
+       MediaTransporterReceiverSrt() = default;
+       ~MediaTransporterReceiverSrt() = default;
+
+       ResourceSet buildPipeline() override;
+       void startPipeline() override;
+       void stopPipeline() override;
+
+       void setConnection(std::string name, std::string value) override {}
+       void setConnection(bundle* params) override {}
+       void getConnection(bundle* params) override {}
+
+       void setSenderAddress(std::string address) override;
+       std::string getSenderAddress() override { return _senderAddress; }
+
+       mtprConnectionType type() override { return MTPR_CONNECTION_TYPE_SRT_RECEIVER; }
+
+private:
+       static void _demuxPadAddedCallback(GstElement *demux, GstPad *new_pad, gpointer userData);
+       static void _demuxNoMorePadsCallback(GstElement *demux, gpointer userData);
+
+       std::string _senderAddress;
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_RECEIVER_SRT_H__
diff --git a/include/MediaTransporterResource.h b/include/MediaTransporterResource.h
new file mode 100644 (file)
index 0000000..8237dd3
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+* Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* 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.
+*/
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_RESOURCE_H__
+#define __TIZEN_MEDIA_TRANSPORTER_RESOURCE_H__
+
+#include <mutex>
+#include <map>
+
+#include <mm_resource_manager.h>
+
+#include "MediaTransporter.h"
+#include "MediaTransporterObserver.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+namespace tizen_media_transporter {
+
+class MediaTransporterResource : public ObservableBase
+{
+public:
+       MediaTransporterResource() { create(); }
+       ~MediaTransporterResource() override { destroy(); }
+
+       int acquire(ResourceSet resourceSet);
+       int acquire(mm_resource_manager_res_type_e type);
+       int releaseAll();
+
+       static int _resourceReleaseCallback(mm_resource_manager_h mgr, mm_resource_manager_res_h res, void *user_data);
+
+private:
+       int create();
+       int destroy();
+       int acquireInternal(mm_resource_manager_res_type_e type);
+
+       bool resourceReleased(mm_resource_manager_res_h res);
+       mm_resource_manager_h _mgr { nullptr };
+       std::map<mm_resource_manager_res_type_e, mm_resource_manager_res_h> _resources;
+       bool _onReleaseCallback { false };
+
+       std::mutex _mutex;
+};
+
+} // namespace
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIA_TRANSPORTER_RESOURCE_H__ */
+
diff --git a/include/MediaTransporterSender.h b/include/MediaTransporterSender.h
new file mode 100644 (file)
index 0000000..fd351fd
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_SENDER_H__
+#define __TIZEN_MEDIA_TRANSPORTER_SENDER_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <map>
+#include <climits>
+#include <gst/gst.h>
+#include "MediaTransporterBase.h"
+#include "MediaSourceBinBase.h"
+#include <memory>
+
+namespace tizen_media_transporter {
+
+class MediaTransporterSender : public MediaTransporterBase
+{
+public:
+       MediaTransporterSender() = default;
+       virtual ~MediaTransporterSender();
+
+       int addMediaSource(IMediaSourceBin* sourceBin);
+       void removeMediaSource(int id);
+       IMediaSourceBin* getMediaSource(int id);
+
+protected:
+       std::map<int, std::unique_ptr<IMediaSourceBin>> _mediaSources;
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_SENDER_H__
diff --git a/include/MediaTransporterSenderRist.h b/include/MediaTransporterSenderRist.h
new file mode 100644 (file)
index 0000000..68994a7
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_SENDER_RIST_H__
+#define __TIZEN_MEDIA_TRANSPORTER_SENDER_RIST_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <climits>
+#include <gst/gst.h>
+#include "MediaTransporterSender.h"
+
+namespace tizen_media_transporter {
+
+#define RESOURCE_TYPE_MAX MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_ENCODER + 1
+
+class MediaTransporterSenderRist : public MediaTransporterSender
+{
+public:
+       MediaTransporterSenderRist() = default;
+       ~MediaTransporterSenderRist() = default;
+
+       ResourceSet buildPipeline() override;
+       void startPipeline() override;
+       void stopPipeline() override;
+
+       void setConnection(std::string name, std::string value) override;
+       void setConnection(bundle* params) override;
+       void getConnection(bundle* params) override;
+
+       void setReceiverAddress(std::string address) override;
+       std::string getReceiverAddress() override { return _receiverAddress; }
+
+       mtprConnectionType type() override { return MTPR_CONNECTION_TYPE_RIST_SENDER; }
+
+private:
+       struct MtprConnectionParamRist {
+               std::string bondingAddress;
+               double maxRtcpBandwidth { -1 };
+               unsigned int minRtcpInterval { UINT_MAX };
+               unsigned int senderBuffer { UINT_MAX };
+       };
+       MtprConnectionParamRist _connectionParam;
+
+       std::string _receiverAddress;
+
+       GstElement* _ristSink { nullptr };
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_SENDER_RIST_H__
diff --git a/include/MediaTransporterSenderRtsp.h b/include/MediaTransporterSenderRtsp.h
new file mode 100644 (file)
index 0000000..772db4b
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_SENDER_RTSP_H__
+#define __TIZEN_MEDIA_TRANSPORTER_SENDER_RTSP_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <climits>
+#include <gst/gst.h>
+#include "MediaTransporterSender.h"
+
+namespace tizen_media_transporter {
+
+class MediaTransporterSenderRtsp : public MediaTransporterSender
+{
+public:
+       MediaTransporterSenderRtsp() = default;
+       ~MediaTransporterSenderRtsp() = default;
+
+       ResourceSet buildPipeline() override;
+       void startPipeline() override;
+       void stopPipeline() override;
+
+       void setConnection(std::string name, std::string value) override {}
+       void setConnection(bundle* params) override {}
+       void getConnection(bundle* params) override {}
+
+       void setSenderAddress(std::string address) override;
+       std::string getSenderAddress() override { return _senderAddress; }
+
+       mtprConnectionType type() override { return MTPR_CONNECTION_TYPE_RTSP_SENDER; }
+
+private:
+       void startRtspServer();
+       void stopRtspServer();
+
+       std::string _senderAddress; // rtsp server ip, port + mount path
+
+       std::string _rtspMountPoint;
+       void* _rtspServer { nullptr };
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_SENDER_RTSP_H__
diff --git a/include/MediaTransporterSenderSrt.h b/include/MediaTransporterSenderSrt.h
new file mode 100644 (file)
index 0000000..1c5d96e
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_SENDER_SRT_H__
+#define __TIZEN_MEDIA_TRANSPORTER_SENDER_SRT_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <climits>
+#include <gio/gio.h>
+#include <gst/gst.h>
+#include "MediaTransporterSender.h"
+
+namespace tizen_media_transporter {
+
+class MediaTransporterSenderSrt : public MediaTransporterSender
+{
+public:
+       MediaTransporterSenderSrt() = default;
+       ~MediaTransporterSenderSrt() = default;
+
+       ResourceSet buildPipeline() override;
+       void startPipeline() override;
+       void stopPipeline() override;
+
+       void setConnection(std::string name, std::string value) override;
+       void setConnection(bundle* params) override;
+       void getConnection(bundle* params) override;
+
+       void setSenderAddress(std::string address) override;
+       std::string getSenderAddress() override { return _senderAddress; }
+
+       mtprConnectionType type() override { return MTPR_CONNECTION_TYPE_SRT_SENDER; }
+
+private:
+       bool _isValidKeyLength(int val);
+
+       enum connectionMode {
+               MTPR_CONNECTION_SRT_MODE_NONE = 0,    /**< not connected, default type */
+               MTPR_CONNECTION_SRT_MODE_CALLER,      /**< The mode to send the connection request like a client (default) */
+               MTPR_CONNECTION_SRT_MODE_LISTENER,    /**< The mode to wait for being connected by peer caller */
+               MTPR_CONNECTION_SRT_MODE_RENDEZVOUS,  /**< The mode to support one-to-one only connection */
+       };
+
+       enum connectionKeyLength {
+               MTPR_CONNECTION_SRT_NO_KEY = 0,       /**< no encryption, default value */
+               MTPR_CONNECTION_SRT_KEY_LEN_0 = 0,    /**< no encryption */
+               MTPR_CONNECTION_SRT_KEY_LEN_16 = 16,  /**< 16 bytes (128-bit) length */
+               MTPR_CONNECTION_SRT_KEY_LEN_24 = 24,  /**< 24 bytes (192-bit) length */
+               MTPR_CONNECTION_SRT_KEY_LEN_32 = 32,  /**< 32 bytes (256-bit) length */
+       };
+
+       struct MtprConnectionParamSrt {
+               connectionMode mode = MTPR_CONNECTION_SRT_MODE_NONE;
+               std::string streamId;
+               std::string passPhrase;
+               connectionKeyLength pbKeyLen = MTPR_CONNECTION_SRT_NO_KEY;
+       };
+
+       MtprConnectionParamSrt _connectionParam;
+
+       std::string _senderAddress;
+
+       GstElement* _srtSink { nullptr };
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_SENDER_SRT_H__
diff --git a/include/MediaTransporterSenderToServerRtsp.h b/include/MediaTransporterSenderToServerRtsp.h
new file mode 100644 (file)
index 0000000..b6b2bd0
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_SENDER_TO_SERVER_RTSP_H__
+#define __TIZEN_MEDIA_TRANSPORTER_SENDER_TO_SERVER_RTSP_H__
+
+#ifdef __cplusplus
+
+
+#include "MediaTransporter.h"
+#include <string>
+#include <climits>
+#include <gst/gst.h>
+#include "MediaTransporterSender.h"
+
+namespace tizen_media_transporter {
+
+class MediaTransporterSenderToServerRtsp : public MediaTransporterSender
+{
+public:
+       MediaTransporterSenderToServerRtsp() = default;
+       ~MediaTransporterSenderToServerRtsp() = default;
+
+       ResourceSet buildPipeline() override;
+       void startPipeline() override;
+       void stopPipeline() override;
+
+       void setConnection(std::string name, std::string value) override {}
+       void setConnection(bundle* params) override {}
+       void getConnection(bundle* params) override {}
+
+       void setReceiverAddress(std::string address) override;
+       std::string getReceiverAddress() override { return _receiverAddress; }
+
+       mtprConnectionType type() override { return MTPR_CONNECTION_TYPE_RTSP_SENDER_TO_SERVER; }
+
+private:
+       std::string _receiverAddress; // rtsp server address to upload data
+       GstElement* _rtspSink { nullptr };
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_SENDER_TO_SERVER_RTSP_H__
diff --git a/include/MediaTransporterUtil.h b/include/MediaTransporterUtil.h
new file mode 100644 (file)
index 0000000..f59b4b3
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_UTIL_H__
+#define __TIZEN_MEDIA_TRANSPORTER_UTIL_H__
+
+#ifdef __cplusplus
+
+#include <string>
+
+namespace tizen_media_transporter {
+namespace util {
+
+const std::string MTPR_FEATURE_NETWORK_WIFI = "http://tizen.org/feature/network.wifi";
+const std::string MTPR_FEATURE_NETWORK_TELE = "http://tizen.org/feature/network.telephony";
+const std::string MTPR_FEATURE_NETWORK_ETH = "http://tizen.org/feature/network.ethernet";
+const std::string MTPR_FEATURE_CAMERA = "http://tizen.org/feature/camera";
+const std::string MTPR_FEATURE_MICROPHONE = "http://tizen.org/feature/microphone";
+
+const std::string MTPR_PRIVILEGE_INTERNET = "http://tizen.org/privilege/internet";
+const std::string MTPR_PRIVILEGE_CAMERA = "http://tizen.org/privilege/camera";
+const std::string MTPR_PRIVILEGE_RECORDER = "http://tizen.org/privilege/recorder";
+
+void throw_if_not_privileged(const std::string& privilege);
+void throw_if_feature_not_supported(const std::string& feature);
+void throw_if_network_features_not_supported();
+
+} // util
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_TRANSPORTER_UTIL_H__
diff --git a/include/mtpr.h b/include/mtpr.h
new file mode 100644 (file)
index 0000000..9d3dd91
--- /dev/null
@@ -0,0 +1,803 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_H__
+#define __TIZEN_MEDIA_TRANSPORTER_H__
+
+#include <tizen.h>
+#include <bundle.h>
+#include <media_format.h>
+#include <media_packet.h>
+#include <sound_manager.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+* @file media_transporter.h
+* @brief This file contains the Media Transporter API.
+*/
+
+/* TEMP Define ***********************************************/
+/* FIXME: this definition have to be applied
+          at core/api/common pkg, include/tizen_error.h file */
+#define TIZEN_ERROR_MEDIA_TRANSPORTER -0x01A30000
+/*************************************************************/
+
+/**
+* @addtogroup CAPI_MEDIA_TRANSPORTER_MODULE
+* @{
+*/
+
+/**
+ * @brief Media Transporter handle type.
+ * @since_tizen 7.0
+ */
+typedef void *mtpr_h;
+
+/**
+ * @brief Enumeration for Media Transporter connection type.
+ * @since_tizen 7.0
+ */
+typedef enum {
+       MTPR_CONNECTION_TYPE_RIST_SENDER,
+       MTPR_CONNECTION_TYPE_RIST_RECEIVER,
+       MTPR_CONNECTION_TYPE_SRT_SENDER,
+       MTPR_CONNECTION_TYPE_SRT_RECEIVER,
+       MTPR_CONNECTION_TYPE_RTSP_SENDER,
+       MTPR_CONNECTION_TYPE_RTSP_SENDER_TO_SERVER
+} mtpr_connection_type_e;
+
+/**
+ * @brief Enumeration for Media Transporter error.
+ * @since_tizen 7.0
+ */
+typedef enum {
+       MTPR_ERROR_NONE = TIZEN_ERROR_NONE,                                /**< Successful */
+       MTPR_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED,              /**< Not supported */
+       MTPR_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED,      /**< Permission denied */
+       MTPR_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER,      /**< Invalid parameter */
+       MTPR_ERROR_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION,      /**< Invalid operation */
+       MTPR_ERROR_INVALID_STATE = TIZEN_ERROR_MEDIA_TRANSPORTER | 0x01,           /**< Invalid state */
+       MTPR_ERROR_CONNECTION_FAILED = TIZEN_ERROR_MEDIA_TRANSPORTER | 0x02,       /**< Connection failed */
+       MTPR_ERROR_RESOURCE_FAILED = TIZEN_ERROR_MEDIA_TRANSPORTER | 0x03,         /**< Resource failed */
+       MTPR_ERROR_RESOURCE_CONFLICT = TIZEN_ERROR_MEDIA_TRANSPORTER | 0x04,       /**< Resource conflict */
+} mtpr_error_e;
+
+/**
+ * @brief Enumeration for Media Transporter state.
+ * @since_tizen 7.0
+ */
+typedef enum {
+       MTPR_STATE_IDLE,        /**<  Created but not prepared */
+//     MTPR_STATE_READY,       /**<  Ready to start */
+       MTPR_STATE_PLAYING,     /**<  Started */
+       MTPR_STATE_PAUSED,      /**<  Paused while starting */
+} mtpr_state_e;
+
+/**
+ * @brief Enumeration for Media Transporter media type.
+ * @since_tizen 7.0
+ */
+typedef enum {
+       MTPR_MEDIA_TYPE_AUDIO,   /**< Audio */
+       MTPR_MEDIA_TYPE_VIDEO,   /**< Video */
+} mtpr_media_type_e;
+
+/**
+ * @brief Enumeration for Media Transporter source type.
+ * @since_tizen 7.0
+ */
+typedef enum {
+       MTPR_SOURCE_TYPE_CAMERA,       /**< Camera preview */
+       MTPR_SOURCE_TYPE_MIC,          /**< Audio from microphone */
+       MTPR_SOURCE_TYPE_VIDEOTEST,    /**< Video test */
+       MTPR_SOURCE_TYPE_AUDIOTEST,    /**< Audio test */
+} mtpr_source_type_e;
+
+/**
+ * @brief Definition for mode parameter of SRT.
+ * @details The connection mode of SRT.\n
+            0: not connected.\n
+            1: caller mode to send the connection request like a client. (default)\n
+            2: listener mode to wait for being connected by peer caller.\n
+            3: rendezvous mode to support one-to-one only connection.
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_SRT_MODE "mode"
+
+/**
+ * @brief Definition for streamid parameter of SRT.
+ * @details The streamid for the SRT access control.
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_SRT_STREAMID "streamid"
+
+/**
+ * @brief Definition for password parameter of SRT.
+ * @details The password for the encrypted transmission of SRT.
+ * @since_tizen 7.0
+ * @remarks This is write only parameter.
+ * @see mtpr_set_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_SRT_PASSPHRASE "passphrase"
+
+/**
+ * @brief Definition for crypto key length of SRT.
+ * @details The crypto key length in bytes of SRT.\n
+            0: no encryption. (default)\n
+            16: 16 bytes (128-bit) length.\n
+            24: 24 bytes (192-bit) length.\n
+            32: 32 bytes (256-bit) length.
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_SRT_PBKEYLEN "pbkeylen"
+
+/**
+ * @brief Definition for bonding addresses of RIST
+ * @details Bonding address to send packets to (IPv4 or IPv6).\n
+            Comma (,) separated list of <address>:<port> to send to
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_RIST_BONDING_ADDRESS "bonding-address"
+
+/**
+ * @brief Definition for max rtcp bandwidth fraction of RIST
+ * @details The maximum bandwidth used for RTCP as a fraction of RTP bandwidth of RIST
+            Range: 0 ~ 0.05
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_RIST_MAX_RTCP_BANDWIDTH "max-rtcp-bandwidth"
+
+/**
+ * @brief Definition for min rtcp interval for of RIST
+ * @details The minimum interval (in ms) between two regular successive RTCP packets.\n
+            Range: 0 ~ 100 (ms)
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_RIST_MIN_RTCP_INTERVAL "min-rtcp-interval"
+
+/**
+ * @brief Definition for size of retransmission queue for RIST
+ * @details Size of the retransmission queue (in ms).
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_RIST_SENDER_BUFFER "sender-buffer"
+
+/**
+ * @brief Definition for IP to configure RTSP server
+ * @details IP address where the RTSP server will listen on.
+ * @remarks The default value is localhost (0.0.0.0 / 127.0.0.1).
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_RTSP_SERVER_IP "server-ip"
+
+/**
+ * @brief Definition for port of RTSP server
+ * @details String containing port number bewteen 1 and 65535\n
+            where the RTSP server will listen on.
+ * @remarks The default value is '8554'.
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_RTSP_SERVER_PORT "server-port"
+
+/**
+ * @brief Definition for mount point of RTSP server
+ * @remarks The default value is '/mtpr_server/tmp'.
+ * @since_tizen 7.0
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ */
+#define MTPR_CONNECTION_PARAM_RTSP_SERVER_MOUNT_POINT "mount-point"
+
+/**
+ * @brief Definition for video source width
+ * @remarks The default value 320
+ * @since_tizen 7.0
+ * @see mtpr_add_media_source()
+ */
+#define MTPR_SOURCE_PARAM_VIDEO_WIDTH "video-width"
+
+/**
+ * @brief Definition for video source height
+ * @remarks The default value 240
+ * @since_tizen 7.0
+ * @see mtpr_add_media_source()
+ */
+#define MTPR_SOURCE_PARAM_VIDEO_HEIGHT "video-height"
+
+/**
+ * @brief Definition for video source framerate
+ * @remarks The default value 30
+ * @since_tizen 7.0
+ * @see mtpr_add_media_source()
+ */
+#define MTPR_SOURCE_PARAM_VIDEO_FRAMERATE "video-framerate"
+
+/**
+ * @brief Definition for audio source channel
+ * @remarks The default value 1
+ * @since_tizen 7.0
+ * @see mtpr_add_media_source()
+ */
+#define MTPR_SOURCE_PARAM_AUDIO_CHANNEL "audio-channel"
+
+/**
+ * @brief Definition for audio source samplerate
+ * @remarks The default value 48000
+ * @since_tizen 7.0
+ * @see mtpr_add_media_source()
+ */
+#define MTPR_SOURCE_PARAM_AUDIO_RATE "audio-rate"
+
+/**
+ * @brief Definition for audio source format
+ * @remarks The default value F32LE
+ * @since_tizen 7.0
+ * @see mtpr_add_media_source()
+ */
+#define MTPR_SOURCE_PARAM_AUDIO_FORMAT "audio-format"
+
+/**
+ * @brief Definition for bitrate for encoder
+ * @details encoding bitrate (kbits/s)\n
+            If not set encoding parm,\n
+            bitrate is controlled by the encoder according to width and height.
+ * @since_tizen 7.0
+ * @see mtpr_add_media_source()
+ */
+#define MTPR_ENCODING_PARAM_BITRATE "target-bitrate"
+
+
+/**
+ * @brief Called when an error occurs.
+ * @details The following error codes can be received:\n
+ *          #MTPR_ERROR_INVALID_OPERATION\n
+ *          #MTPR_ERROR_CONNECTION_FAILED\n
+ *          #MTPR_ERROR_RESOURCE_FAILED\n
+ *          #MTPR_ERROR_RESOURCE_CONFLICT
+ * @since_tizen 7.0
+ * @remarks The @a mtpr is the same object for which the callback was set.\n
+ *          The @a mtpr should not be released.
+ * @param[in] mtpr       Media Transporter handle
+ * @param[in] error      The error code
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see mtpr_set_error_cb()
+ * @see mtpr_unset_error_cb()
+ */
+typedef void (*mtpr_error_cb)(mtpr_h mtpr, mtpr_error_e error, void *user_data);
+
+/**
+ * @brief Called when a new track is added to the receiver.
+ * @since_tizen 7.0
+ * @remarks The @a mtpr is the same object for which the callback was set.\n
+ *          The @a mtpr should not be released.
+ * @param[in] mtpr       Media Transporter handle
+ * @param[in] type       The media type
+ * @param[in] track_id   The track id
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see mtpr_set_track_added_cb()
+ * @see mtpr_unset_track_added_cb()
+ */
+typedef void (*mtpr_track_added_cb)(mtpr_h mtpr, mtpr_media_type_e type, unsigned int track_id, void *user_data);
+
+/**
+ * @brief Called when all the track is parsed and prerolled.
+ * @since_tizen 7.0
+ * @remarks The @a mtpr is the same object for which the callback was set.\n
+ *          The @a mtpr should not be released.\n
+ * @param[in] mtpr       Media Transporter handle
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see mtpr_set_no_more_track_cb()
+ * @see mtpr_unset_no_more_track_cb()
+ */
+typedef void (*mtpr_no_more_track_cb)(mtpr_h mtpr, void *user_data);
+
+/**
+ * @brief Called when each audio or video frame is ready to be rendered via the Media Transporter pipeline after the negotiation.
+ * @since_tizen 7.0
+ * @remarks The @a mtpr is the same object for which the callback was set.\n
+ *          The @a mtpr should not be released.\n
+ *          Use media_packet_get_buffer_data_ptr() with @a packet to get the Gstreamer buffer pointer.\n
+ *          The @a packet should be released using media_packet_destroy().
+ * @param[in] mtpr       Media Transporter handle
+ * @param[in] type       The media type
+ * @param[in] track_id   The track id
+ * @param[in] packet     The media packet which has a frame data
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see mtpr_set_audio_packet_cb()
+ * @see mtpr_unset_audio_packet_cb()
+ * @see mtpr_set_video_packet_cb()
+ * @see mtpr_unset_video_packet_cb()
+ * @see media_packet_get_buffer_data_ptr()
+ */
+typedef void (*mtpr_encoded_frame_cb)(mtpr_h mtpr, mtpr_media_type_e type, unsigned int track_id, media_packet_h packet, void *user_data);
+
+/**
+ * @brief Creates an instance of Media Transporter.
+ * @since_tizen 7.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/internet
+ * @remarks A signaling channel not addressed in this API should be established to send SDP or ICE candidate messages to each other.\n
+ *          The @a mtpr should be released using mtpr_destroy().
+ * @param[in] type        The connection type
+ * @param[out] mtpr       Media Transporter handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE Successful
+ * @retval #MTPR_ERROR_NOT_SUPPORTED Not supported
+ * @retval #MTPR_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @post @a mtpr state will be #MTPR_STATE_IDLE.
+ * @see mtpr_destroy()
+ */
+int mtpr_create(mtpr_connection_type_e type, mtpr_h *mtpr);
+
+/**
+ * @brief Destroys the Media Transporter.
+ * @since_tizen 7.0
+ * @param[in] mtpr    Media Transporter handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_create()
+ */
+int mtpr_destroy(mtpr_h mtpr);
+
+/**
+ * @brief Starts the Media Transporter.
+ * @since_tizen 7.0
+ * @param[in] mtpr    Media Transporter handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @retval #MTPR_ERROR_RESOURCE_FAILED Resource failed
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @post @a mtpr state will be #MTPR_STATE_PLAYING. // FIXME
+ * @see mtpr_create()
+ * @see mtpr_stop()
+ */
+int mtpr_start(mtpr_h mtpr);
+
+/**
+ * @brief Stops the Media Transporter.
+ * @since_tizen 7.0
+ * @param[in] mtpr    Media Transporter handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @retval #MTPR_ERROR_RESOURCE_FAILED Resource failed
+ * @pre @a mtpr state must be set to #MTPR_STATE_PLAYING. // FIXME
+ * @post @a mtpr state will be #MTPR_STATE_IDLE.
+ * @see mtpr_create()
+ * @see mtpr_start()
+ */
+int mtpr_stop(mtpr_h mtpr);
+
+/**
+ * @brief Gets the Media Transporter state.
+ * @since_tizen 7.0
+ * @param[in]  mtpr     Media Transporter handle
+ * @param[out] state    Media Transporter state
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int mtpr_get_state(mtpr_h mtpr, mtpr_state_e *state);
+
+/**
+ * @brief Gets the connection type.
+ * @since_tizen 7.0
+ * @remarks The default value is #MTPR_CONNECTION_TYPE_RIST.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[out] type       The connection type
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ */
+int mtpr_get_connection_type(mtpr_h mtpr, mtpr_connection_type_e *type);
+
+/**
+ * @brief Sets the sender address
+ * @since_tizen 7.0
+ * @remarks It should set when mtpr connection type is \n
+ *                                     (#MTPR_CONNECTION_TYPE_SRT_SENDER, #MTPR_CONNECTION_TYPE_SRT_RECEIVER, #MTPR_CONNECTION_TYPE_RTSP_SENDER)
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] address     The connection address
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see mtpr_get_connection_address()
+ */
+int mtpr_set_sender_address(mtpr_h mtpr, const char *address);
+
+/**
+ * @brief Gets the sender address
+ * @since_tizen 7.0
+ * @remarks The @a address should be released using free().
+ * @param[in] mtpr        Media Transporter handle
+ * @param[out] address    The sender address
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @see mtpr_set_sender_address()
+ */
+int mtpr_get_sender_address(mtpr_h mtpr, char **address);
+
+/**
+ * @brief Sets the receiver address
+ * @since_tizen 7.0
+ * @remarks It should set when mtpr connection type is \n
+ *                                     (#MTPR_CONNECTION_TYPE_RIST_SENDER, #MTPR_CONNECTION_TYPE_RIST_RECEIVER, #MTPR_CONNECTION_TYPE_RTSP_SENDER_TO_SERVER
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] address     The connection address
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see mtpr_get_receiver_address()
+ */
+int mtpr_set_receiver_address(mtpr_h mtpr, const char *address);
+
+/**
+ * @brief Gets the receiver address
+ * @since_tizen 7.0
+ * @remarks The @a address should be released using free().
+ * @param[in] mtpr        Media Transporter handle
+ * @param[out] address    The receiver address
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @see mtpr_set_receiver_address()
+ */
+int mtpr_get_receiver_address(mtpr_h mtpr, char **address);
+
+/**
+ * @brief Sets connection parameter list.
+ * @details The parameters are key and value pairs. \n
+            The key is defined in this header file and the value data type must be string.
+ * @since_tizen  7.0
+ * @param[in] mtpr         Media Transporter handle
+ * @param[in] param_name   connection parameter nam
+ * @param[in] param_value  connection parameter value
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see mtpr_set_connection_params()
+ * @see mtpr_get_connection_params()
+ * @see #bundle
+ */
+int mtpr_set_connection_param(mtpr_h mtpr, const char *param_name, const char *param_value);
+
+/**
+ * @brief Sets connection parameter list.
+ * @details Many connection parameters can be set at one time all together by using bundle. \n
+            The parameters are key and value pairs. \n
+            The key is defined in this header file and the value data type must be string.
+ * @since_tizen  7.0
+ * @param[in] mtpr         Media Transporter handle
+ * @param[in] param_list   Key value array of connection parameters
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see mtpr_get_connection_params()
+ * @see #bundle
+ */
+int mtpr_set_connection_params(mtpr_h mtpr, bundle *param_list);
+
+/**
+ * @brief Gets connection parameter list.
+ * @since_tizen  7.0
+ * @remarks The @a param_list should be released using bundle_free().\n
+ * @param[in]  mtpr        Media Transporter handle
+ * @param[out] param_list  Key value array of connection parameters
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_set_connection_params()
+ * @see #bundle
+ */
+int mtpr_get_connection_params(mtpr_h mtpr, bundle **param_list);
+
+/**
+ * @brief Adds a media source.
+ * @since_tizen 7.0
+ * @remarks The camera privilege(%http://tizen.org/privilege/camera) should be added if @a type is #MTPR_MEDIA_SOURCE_TYPE_CAMERA.\n
+ *          The recorder privilege(%http://tizen.org/privilege/recorder) should be added if @a type is #MTPR_MEDIA_SOURCE_TYPE_MIC.
+ * @param[in]  mtpr       Media Transporter handle
+ * @param[in]  mtpr       The media source type to be added
+ * @param[in]  param_list Key value array of connection/encoding parameters
+ * @param[out] source_id  The media source id
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_NOT_SUPPORTED Not supported
+ * @retval #MTPR_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see mtpr_remove_media_source()
+ * @see mtpr_media_source_set_video_resolution()
+ * @see mtpr_media_source_get_video_resolution()
+ * @see mtpr_media_source_set_video_framerate()
+ * @see mtpr_media_source_get_video_framerate()
+ */
+int mtpr_add_media_source(mtpr_h mtpr, mtpr_source_type_e type, bundle* param_list, unsigned int *source_id);
+
+/**
+ * @brief Removes the media source.
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] source_id   The media source id to be removed
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre Add media source to @a mtpr to get @a source_id by calling mtpr_add_media_source().
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see mtpr_add_media_source()
+ */
+int mtpr_remove_media_source(mtpr_h mtpr, unsigned int source_id);
+
+/**
+ * @brief Sets the mic source's sound manager stream information.
+ * @details If @a source_id is not a media source of #MTPR_SOURCE_TYPE_MIC, this function will return #MTPR_ERROR_INVALID_PARAMETER.
+ * @since_tizen 7.0
+ * @remarks You can set sound stream information including audio routing.\n
+ *          The following sound stream types can be used to create the @a stream_info :\n
+ *          #SOUND_STREAM_TYPE_MEDIA\n
+ *          #SOUND_STREAM_TYPE_VOICE_RECOGNITION\n
+ *          #SOUND_STREAM_TYPE_VOIP\n
+ *          #SOUND_STREAM_TYPE_MEDIA_EXTERNAL_ONLY\n
+ *          For more details, please refer to @ref CAPI_MEDIA_SOUND_MANAGER_MODULE.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] source_id   The mic source id
+ * @param[in] stream_info The sound stream information
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see #sound_stream_info_h
+ * @see sound_manager_create_stream_information()
+ * @see sound_manager_destroy_stream_information()
+ */
+int mtpr_mic_source_set_sound_stream_info(mtpr_h mtpr, unsigned int source_id, sound_stream_info_h stream_info);
+
+/**
+ * @brief Sets a callback function to be invoked when an asynchronous operation error occurs.
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] callback    Callback function pointer
+ * @param[in] user_data   The user data to be passed to the callback function
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @post mtpr_error_cb() will be invoked.
+ * @see mtpr_unset_error_cb()
+ * @see mtpr_error_cb()
+ */
+int mtpr_set_error_cb(mtpr_h mtpr, mtpr_error_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the error callback function.
+ * @since_tizen 7.0
+ * @param[in] mtpr       Media Transporter handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_set_error_cb()
+ */
+int mtpr_unset_error_cb(mtpr_h mtpr);
+
+/**
+ * @brief Sets a track added callback function to be invoked when a new track is added to the receiver.
+ * @since_tizen 7.0
+ * @remarks The registered callback will be invoked in an internal thread of the media transporter.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] callback    Callback function pointer
+ * @param[in] user_data   The user data to be passed to the callback function
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @post mtpr_track_added_cb() will be invoked.
+ * @see mtpr_unset_track_added_cb()
+ * @see mtpr_track_added_cb()
+ */
+int mtpr_set_track_added_cb(mtpr_h mtpr, mtpr_track_added_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the track added callback function.
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_set_track_added_cb()
+ */
+int mtpr_unset_track_added_cb(mtpr_h mtpr);
+
+/**
+ * @brief Sets a no more track callback function to be invoked when all the tracks are added to the receiver.
+ * @since_tizen 7.0
+ * @remarks The registered callback will be invoked in an internal thread of the media transporter.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] callback    Callback function pointer
+ * @param[in] user_data   The user data to be passed to the callback function
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @post mtpr_no_more_track_cb() will be invoked.
+ * @see mtpr_unset_no_more_track_cb()
+ * @see mtpr_no_more_track_cb()
+ */
+int mtpr_set_no_more_track_cb(mtpr_h mtpr, mtpr_no_more_track_cb callback, void* user_data);
+
+/**
+ * @brief Unsets the no more track callback function.
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_set_no_more_track_cb()
+ */
+int mtpr_unset_no_more_track_cb(mtpr_h mtpr);
+
+/**
+ * @brief Sets an encoded audio frame callback function to be invoked when each audio frame is ready to be rendered.
+ * @since_tizen 7.0
+ * @remarks If @a callback is set, audio data from the remote peer will be forwarded to @a callback without being rendered by itself.\n
+ *          The registered callback will be invoked in an internal thread of the media transporter.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] callback    Callback function pointer
+ * @param[in] user_data   The user data to be passed to the callback function
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @post mtpr_encoded_frame_cb() will be invoked.
+ * @see mtpr_unset_audio_packet_cb()
+ * @see mtpr_encoded_frame_cb()
+ */
+int mtpr_set_audio_packet_cb(mtpr_h mtpr, mtpr_encoded_frame_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the encoded audio frame callback function.
+ * @since_tizen 7.0
+ * @param[in] mtpr      Media Transporter handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see mtpr_set_audio_packet_cb()
+ */
+int mtpr_unset_audio_packet_cb(mtpr_h mtpr);
+
+/**
+ * @brief Sets an encoded video frame callback function to be invoked when each video frame is ready to be rendered.
+ * @since_tizen 7.0
+ * @remarks If @a callback is set, video data from the remote peer will be forwarded to @a callback without being rendered by itself.\n
+ *          The registered callback will be invoked in an internal thread of the media transporter.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] callback    Callback function pointer
+ * @param[in] user_data   The user data to be passed to the callback function
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @post mtpr_encoded_frame_cb() will be invoked.
+ * @see mtpr_unset_video_packet_cb()
+ * @see mtpr_encoded_frame_cb()
+ */
+int mtpr_set_video_packet_cb(mtpr_h mtpr, mtpr_encoded_frame_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the encoded video frame callback function.
+ * @since_tizen 7.0
+ * @param[in] mtpr      Media Transporter handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see mtpr_set_video_packet_cb()
+ */
+int mtpr_unset_video_packet_cb(mtpr_h mtpr);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIA_TRANSPORTER_H__ */
+
diff --git a/include/mtpr_internal.h b/include/mtpr_internal.h
new file mode 100644 (file)
index 0000000..4ce0612
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __TIZEN_MEDIA_TRANSPORTER_INTERNAL_H__
+#define __TIZEN_MEDIA_TRANSPORTER_INTERNAL_H__
+
+#include <mtpr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+* @file mtpr_internal.h
+* @brief This file contains the Media Transporter internal API.
+*/
+
+/**
+ * @brief Media Transporter display handle type.
+ * @since_tizen 7.0
+ * @remarks The pointer of this handle can be obtained from EFL UI API.\n
+ *          For example, in case of #MTPR_DISPLAY_TYPE_OVERLAY, elm_win_add() can be used to get the window handle.\n
+ *          In case of #MTPR_DISPLAY_TYPE_EVAS, evas_object_image_add() can be used to get the renderable image handle.\n
+ *          For more details, please refer to https://docs.tizen.org/application/native/guides/ui/efl/.
+ */
+typedef void *mtpr_display_h;
+
+
+/**
+ * @brief Enumeration for Media Transporter display type.
+ * @since_tizen 7.0
+ */
+typedef enum {
+       MTPR_DISPLAY_TYPE_OVERLAY,      /**< Overlay */
+       MTPR_DISPLAY_TYPE_EVAS,         /**< Evas image object */
+} mtpr_display_type_e;
+
+/**
+ * @brief Enumeration for Media Transporter display mode.
+ * @since_tizen 7.0
+ */
+typedef enum {
+       MTPR_DISPLAY_MODE_LETTER_BOX,   /**< Letter box */
+       MTPR_DISPLAY_MODE_ORIGIN_SIZE,  /**< Origin size */
+       MTPR_DISPLAY_MODE_FULL,         /**< Full screen */
+} mtpr_display_mode_e;
+
+
+/**
+ * @brief Sets a display to the video track to be rendered.
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] type        The display type
+ * @param[in] display     The display handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_set_display_mode()
+ * @see mtpr_get_display_mode()
+ * @see mtpr_set_display_visible()
+ * @see mtpr_get_display_visible()
+ */
+int mtpr_set_display(mtpr_h mtpr, mtpr_display_type_e type, mtpr_display_h display);
+
+/**
+ * @brief Sets the display mode of the video track.
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] mode        The display mode
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_get_display_mode()
+ */
+int mtpr_set_display_mode(mtpr_h mtpr, mtpr_display_mode_e mode);
+
+/**
+ * @brief Gets the display mode of the video track.
+ * @since_tizen 7.0
+ * @remarks The default value is #MTPR_DISPLAY_MODE_LETTER_BOX.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[out] mode       The display mode
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_set_display_mode()
+ */
+int mtpr_get_display_mode(mtpr_h mtpr, mtpr_display_mode_e *mode);
+
+/**
+ * @brief Sets the display visibleness of the video track.
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] visible     The display visibleness
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_get_display_visible()
+ */
+int mtpr_set_display_visible(mtpr_h mtpr, bool visible);
+
+/**
+ * @brief Gets the display visibleness of the video track.
+ * @since_tizen 7.0
+ * @remarks The default value is @c true.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[out] visible    The display visibleness
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_set_display_visible()
+ */
+int mtpr_get_display_visible(mtpr_h mtpr, bool *visible);
+
+
+/**
+ * @brief Sets a video loopback to render the video frames of the media source.
+ * @details The following media source types are available for this function:\n
+ *          #MTPR_SOURCE_TYPE_CAMERA\n
+ *          #MTPR_SOURCE_TYPE_VIDEOTEST
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] source_id   The video source id
+ * @param[in] type        The display type
+ * @param[in] display     The display handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @pre Add media source to @a mtpr to get @a source_id by calling mtpr_add_media_source().
+ * @see mtpr_source_unset_video_loopback()
+ */
+int mtpr_source_set_video_loopback(mtpr_h mtpr, unsigned int source_id, mtpr_display_type_e type, mtpr_display_h display);
+
+/**
+ * @brief Unsets the video loopback.
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] source_id   The video source id
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mtpr_source_set_video_loopback()
+ */
+int mtpr_source_unset_video_loopback(mtpr_h mtpr, unsigned int source_id);
+
+/**
+ * @brief Sets a sound manager stream information to be rendered.
+ * @since_tizen 7.0
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] stream_info The sound stream information
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ */
+int mtpr_set_sound_stream_info(mtpr_h mtpr, sound_stream_info_h stream_info);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIA_TRANSPORTER_INTERNAL_H__ */
diff --git a/packaging/capi-media-transporter.manifest b/packaging/capi-media-transporter.manifest
new file mode 100644 (file)
index 0000000..50f645f
--- /dev/null
@@ -0,0 +1,8 @@
+<manifest>
+       <request>
+               <domain name="_" />
+       </request>
+       <assign>
+               <filesystem path="/usr/bin/mtpr_test" label="_" exec_label="none" />
+       </assign>
+</manifest>
diff --git a/packaging/capi-media-transporter.spec b/packaging/capi-media-transporter.spec
new file mode 100644 (file)
index 0000000..5299603
--- /dev/null
@@ -0,0 +1,135 @@
+Name:       capi-media-transporter
+Summary:    A Media Transporter library in Tizen Native API
+Version:    0.0.1
+Release:    0
+Group:      Multimedia/API
+License:    Apache-2.0
+URL:        http://source.tizen.org
+Source0:    %{name}-%{version}.tar.gz
+Source1001: capi-media-transporter.manifest
+BuildRequires:  cmake
+BuildRequires:  pkgconfig(dlog)
+BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(capi-base-common)
+BuildRequires:  pkgconfig(capi-media-tool)
+BuildRequires:  pkgconfig(gstreamer-1.0)
+BuildRequires:  pkgconfig(gstreamer-video-1.0)
+BuildRequires:  pkgconfig(gstreamer-audio-1.0)
+BuildRequires:  pkgconfig(gstreamer-rtsp-server-1.0)
+BuildRequires:  pkgconfig(libpulse)
+BuildRequires:  pkgconfig(appcore-efl)
+BuildRequires:  pkgconfig(elementary)
+BuildRequires:  pkgconfig(json-glib-1.0)
+BuildRequires:  pkgconfig(iniparser)
+BuildRequires:  pkgconfig(bundle)
+BuildRequires:  pkgconfig(mm-display-interface)
+BuildRequires:  pkgconfig(mm-common)
+BuildRequires:  pkgconfig(cynara-client)
+BuildRequires:  pkgconfig(libsmack)
+BuildRequires:  pkgconfig(capi-system-info)
+BuildRequires:  pkgconfig(capi-media-sound-manager)
+BuildRequires:  pkgconfig(mm-display-interface)
+BuildRequires:  pkgconfig(esplusplayer)
+BuildRequires:  pkgconfig(mm-resource-manager)
+%if 0%{?gtests:1}
+BuildRequires:  pkgconfig(gmock)
+%endif
+
+%description
+A Media Transporter library in Tizen Native API.
+
+%package devel
+Summary:    Multimedia Media Transporter Library in Tizen Native API (Development)
+Group:      Multimedia/Development
+Requires:   %{name} = %{version}-%{release}
+
+%description devel
+Media Transporter Library in Tizen Native API (DEV).
+
+%package tool
+Summary:  Tizen Media Transporter API testsuite
+Requires: %{name} = %{version}-%{release}
+
+%description tool
+Tizen Media Transporter API testsuite.
+
+%if 0%{?gcov:1}
+%package gcov
+Summary: Line Coverage files
+Group: Development/Multimedia
+
+%description gcov
+Collection of files related to line coverage using gcov.
+%endif
+
+%prep
+%setup -q
+cp %{SOURCE1001} .
+
+%build
+export CFLAGS+=" -DSYSCONFDIR=\\\"%{_hal_sysconfdir}\\\""
+export CXXFLAGS+=" -DSYSCONFDIR=\\\"%{_hal_sysconfdir}\\\""
+
+%if 0%{?gcov:1}
+export CFLAGS+=" -fprofile-arcs -ftest-coverage"
+export CXXFLAGS+=" -fprofile-arcs -ftest-coverage"
+export LDFLAGS+=" -lgcov"
+%endif
+
+MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
+%cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DFULLVER=%{version} -DMAJORVER=${MAJORVER} \
+%if "%{tizen_profile_name}" == "tv"
+-DTIZEN_PROFILE_TV=on \
+%else
+-DTIZEN_PROFILE_TV=off \
+%endif
+%if 0%{?gtests:1}
+-DMTPR_UNITTEST=ON
+%endif
+
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}%{_bindir}
+mkdir -p %{buildroot}%{_hal_datadir}
+cp test/mtpr_test %{buildroot}%{_bindir}
+cp test/mtpr_rtsp_test %{buildroot}%{_bindir}
+
+%make_install
+mkdir -p %{buildroot}%{_hal_sysconfdir}/multimedia
+
+%if 0%{?gcov:1}
+builddir=$(basename $PWD)
+gcno_obj_dir=%{buildroot}%{_datadir}/gcov/obj/%{name}/"$builddir"
+mkdir -p "$gcno_obj_dir"
+find . -name '*.gcno' -exec cp --parents '{}' "$gcno_obj_dir" ';'
+%endif
+
+%post
+/sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%manifest %{name}.manifest
+%{_libdir}/lib%{name}.so.*
+%license LICENSE.APLv2
+
+%files devel
+%{_includedir}/media/*.h
+%{_libdir}/pkgconfig/*.pc
+%{_libdir}/lib%{name}.so
+
+%files tool
+%defattr(-,root,root,-)
+%{_bindir}/mtpr_test
+%{_bindir}/mtpr_rtsp_test
+%if 0%{?gtests:1}
+%{_bindir}/mtpr_ut
+%endif
+
+%if 0%{?gcov:1}
+%files gcov
+%{_datadir}/gcov/obj/*
+%endif
diff --git a/src/MediaSourceBinAudioTest.cpp b/src/MediaSourceBinAudioTest.cpp
new file mode 100644 (file)
index 0000000..145aad0
--- /dev/null
@@ -0,0 +1,197 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaSourceBinAudioTest.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterParseIni.h"
+
+#include <algorithm>
+
+using namespace std;
+using namespace tizen_media_transporter;
+
+MediaSourceBinAudioTest::MediaSourceBinAudioTest(bundle* params)
+{
+       // privilege check
+       parseSourceParam(params);
+}
+
+void MediaSourceBinAudioTest::parseSourceParam(bundle* params)
+{
+       int written_count = 0;
+       char* string_val = NULL;
+
+       RET_IF(!params, "input param is NULL");
+
+       /* Will be move to mic/audiotest bin */
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_AUDIO_CHANNEL, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _audioInfo.channel = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param audio channel %u", _audioInfo.channel);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_AUDIO_RATE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _audioInfo.rate = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param audio rate %u", _audioInfo.rate);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_AUDIO_FORMAT, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _audioInfo.format = string_val;
+               LOG_DEBUG("Source param audio format %s", _audioInfo.format.c_str());
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_ENCODING_PARAM_BITRATE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _encInfo.bitrate = (guint)strtoul(string_val, NULL, 10);
+               LOG_DEBUG("Encoder target bitrate %u", _encInfo.bitrate);
+               written_count++;
+       }
+
+       if (written_count == 0)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to parse Source Param");
+}
+
+GstElement* MediaSourceBinAudioTest::createMicSource()
+{
+       GstElement* audioSrc = nullptr;
+
+       auto& ini = MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_AUDIOTEST));
+       if (ini.sourceElement.empty()) {
+               audioSrc = gst::_createElement(DEFAULT_ELEMENT_AUDIO_TEST, "audioSrc");
+       } else {
+               audioSrc = gst::_createElement(ini.sourceElement, "audioSrc");
+       }
+       gst::_setElementProperties(audioSrc, ini.sourceElementProperties);
+
+       return audioSrc;
+}
+
+void MediaSourceBinAudioTest::setEncoderParam(gst::GstElements& elements)
+{
+       GstElement* encoder = gst::_findEncoderElement(elements);
+       if (!encoder)
+               return;
+
+       if (_encInfo.bitrate) {
+               if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(encoder)), "bitrate"))
+                       g_object_set(G_OBJECT(encoder), "bitrate", _encInfo.bitrate * 1000, NULL);
+               else
+                       LOG_WARNING("%s element doesn't contain bitrate property", GST_ELEMENT_NAME(encoder));
+       }
+}
+
+void MediaSourceBinAudioTest::replaceCapsWithAudioInfo(GstElement* element)
+{
+       GstCaps* oldCaps;
+       auto& ini = MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_AUDIOTEST));
+       GstAudioFormat format;
+       if (!_audioInfo.format.empty())
+               format = gst::_getAudioFormatFromString(_audioInfo.format);
+       else
+               format = gst::_getAudioFormatFromString(ini.audioRawFormat);
+
+       RET_IF(format == GST_AUDIO_FORMAT_UNKNOWN, "not supported raw format");
+
+       GstAudioInfo info;
+       int rate = _audioInfo.rate != -1 ? _audioInfo.rate : ini.audioSamplerate;
+       int channel = _audioInfo.channel != -1 ? _audioInfo.channel : ini.audioChannels;
+       gst_audio_info_set_format(&info, format, rate, channel, NULL);
+       GstCaps* newCaps = gst_audio_info_to_caps(&info);
+
+       g_object_get(G_OBJECT(element), "caps", &oldCaps, NULL);
+       if (!gst_caps_is_equal(oldCaps, newCaps))
+               g_object_set(G_OBJECT(element), "caps", newCaps, NULL);
+
+       gst_caps_unref(newCaps);
+}
+
+void MediaSourceBinAudioTest::replaceEncCapsWithAudioInfo(GstElement* element)
+{
+       GstCaps* oldCaps;
+
+       g_object_get(G_OBJECT(element), "caps", &oldCaps, NULL);
+       RET_IF(!oldCaps, "capsfilter has no caps");
+
+       GstCaps* newCaps = gst_caps_copy(oldCaps);
+
+       if (_audioInfo.channel != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _audioInfo.channel);
+               gst_caps_set_value(newCaps, "channels", &value);
+       }
+
+       if (_audioInfo.rate != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _audioInfo.rate);
+               gst_caps_set_value(newCaps, "rate", &value);
+       }
+
+       if (!gst_caps_is_equal(oldCaps, newCaps))
+               g_object_set(G_OBJECT(element), "caps", newCaps, NULL);
+
+       gst_caps_unref(newCaps);
+}
+
+void MediaSourceBinAudioTest::setSourceParam(gst::GstElements& elements)
+{
+       GstElement* srcCapsfilter = gst::_findElementByName(elements, ELEMENT_NAME_SRC_CAPSFILTER);
+       if (srcCapsfilter)
+               replaceCapsWithAudioInfo(srcCapsfilter);
+
+       GstElement* encCapsfilter = gst::_findElementByName(elements, ELEMENT_NAME_ENCODE_CAPSFILTER);
+       if (encCapsfilter)
+               replaceEncCapsWithAudioInfo(encCapsfilter);
+}
+
+MediaSourceBinInfo MediaSourceBinAudioTest::generate()
+{
+       gst::GstElements elements;
+
+       try {
+               elements.push_back(createMicSource());
+               elements.push_back(gst::_createElement("volume"));
+
+               auto created = createAudioRestOfElements(MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_AUDIOTEST)));
+               std::copy(created.begin(), created.end(), std::back_inserter(elements));
+
+               setSourceParam(elements);
+               setEncoderParam(elements);
+       } catch (const MediaTransporterException& e) {
+               gst::_clearElements(elements);
+               throw;
+       }
+
+       GstBin* bin = GST_BIN(gst_bin_new("auditestbin"));
+
+       try {
+               gst::_addElementsToBin(bin, elements);
+               gst::_linkElements(elements);
+       } catch (const MediaTransporterException& e) {
+               // FIXME: handle error nicely here
+               if (std::string { e.what() } == "Failed to link elements")
+                       gst::_removeElementsFromBin(bin, elements);
+
+               g_object_unref(bin);
+
+               throw;
+       }
+
+       return std::make_tuple(MTPR_SOURCE_TYPE_AUDIOTEST, bin, ResourceSet());
+}
diff --git a/src/MediaSourceBinBase.cpp b/src/MediaSourceBinBase.cpp
new file mode 100644 (file)
index 0000000..9bd3f13
--- /dev/null
@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaSourceBinBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterParseIni.h"
+#include <sstream>
+#include <algorithm>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "iniparser.h"
+#ifdef __cplusplus
+}
+#endif
+
+using namespace tizen_media_transporter;
+
+static GstCaps *__makeVideoDefaultRawCaps(const MtprMediaSourceIni& ini)
+{
+       return gst_caps_new_simple(gst::MEDIA_TYPE_VIDEO_RAW.c_str(),
+                                                       "format", G_TYPE_STRING, ini.videoRawFormat.c_str(),
+                                                       "width", G_TYPE_INT, ini.videoWidth,
+                                                       "height", G_TYPE_INT, ini.videoHeight,
+                                                       NULL);
+}
+
+static GstCaps *__makeAudioDefaultRawCaps(const MtprMediaSourceIni& ini)
+{
+       GstAudioFormat format = gst::_getAudioFormatFromString(ini.audioRawFormat);
+       RET_VAL_IF(format == GST_AUDIO_FORMAT_UNKNOWN, NULL, "not supported raw format");
+
+       GstAudioInfo info;
+       gst_audio_info_set_format(&info, format, ini.audioSamplerate, ini.audioChannels, NULL);
+       return gst_audio_info_to_caps(&info);
+}
+
+static GstCaps *__makeVideoDefaultEncodedCaps(const MtprMediaSourceIni& ini)
+{
+       return gst::_getCapsFromEncodedVideoMediaType(gst::_getVideoMediaType(ini.videoCodec),
+                                                                                               ini.videoWidth, ini.videoHeight);
+}
+
+static GstCaps *__makeAudioDefaultEncodedCaps(const MtprMediaSourceIni& ini)
+{
+       return gst::_getCapsFromEncodedAudioMediaType(gst::_getAudioMediaType(ini.audioCodec),
+                                                                                               ini.audioChannels, ini.audioSamplerate);
+}
+
+static GstElement * _prepareVideoEncoder(const MtprMediaSourceIni& ini)
+{
+       if (!ini.videoHWEncoderElement.empty())
+               return gst::_createElement(ini.videoHWEncoderElement);
+
+       LOG_DEBUG("create sw encoder based on caps information");
+
+       return gst::_createElementFromRegistry("Codec/Encoder/Video",
+                                                                               __makeVideoDefaultRawCaps(ini),
+                                                                               __makeVideoDefaultEncodedCaps(ini),
+                                                                               MediaTransporterIni::get().general().gstExcludedElements);
+}
+
+static GstElement * _prepareAudioEncoder(const MtprMediaSourceIni& ini)
+{
+       if (!ini.audioHWEncoderElement.empty())
+               return gst::_createElement(ini.audioHWEncoderElement);
+
+       LOG_DEBUG("create sw encoder based on caps information");
+       return gst::_createElementFromRegistry("Codec/Encoder/Audio",
+                                                                               __makeAudioDefaultRawCaps(ini),
+                                                                               __makeAudioDefaultEncodedCaps(ini),
+                                                                               MediaTransporterIni::get().general().gstExcludedElements);
+}
+
+std::vector<GstElement*> MediaSourceBinBase::createVideoRestOfElements(const MtprMediaSourceIni& ini)
+{
+       std::vector<GstElement*>  elements;
+
+       // FIXME: make this simple
+
+       try {
+               // capsfilter
+               GstElement *capsfilter = gst::_createElement("capsfilter", ELEMENT_NAME_SRC_CAPSFILTER);
+
+               GstCaps *sinkCaps = NULL;
+               if ((sinkCaps = __makeVideoDefaultRawCaps(ini))) {
+                       gst::_printCaps(sinkCaps, ELEMENT_NAME_SRC_CAPSFILTER);
+                       g_object_set(G_OBJECT(capsfilter), "caps", sinkCaps, NULL);
+                       // source->render[idx].appsrc_caps = gst_caps_copy(sinkCaps);
+                       gst_caps_unref(sinkCaps);
+               }
+               elements.push_back(capsfilter);
+
+               // video convert
+               elements.push_back(gst::_createElement("videoconvert"));
+
+               // encoder
+               GstElement *encoder = _prepareVideoEncoder(ini);
+               elements.push_back(encoder);
+
+               // parser from encoder
+               std::string factory_name = GST_OBJECT_NAME(gst_element_get_factory(encoder));
+               GstElement *parser = nullptr;
+
+               if (factory_name.find("h264") != std::string::npos)
+                       parser = gst::_createElement("h264parse");
+               else if (factory_name.find("mpeg4") != std::string::npos)
+                       parser = gst::_createElement("mpeg4videoparse");
+
+               if (parser) {
+                       g_object_set(G_OBJECT(parser), "config-interval", -1, NULL);
+                       elements.push_back(parser);
+               }
+
+               // capsfilter2
+               GstElement *capsfilter2 = gst::_createElement("capsfilter", ELEMENT_NAME_ENCODE_CAPSFILTER);
+
+               sinkCaps = __makeVideoDefaultEncodedCaps(ini);
+               if (sinkCaps) {
+                       gst::_printCaps(sinkCaps, ELEMENT_NAME_ENCODE_CAPSFILTER);
+                       g_object_set(G_OBJECT(capsfilter2), "caps", sinkCaps, NULL);
+                       gst_caps_unref(sinkCaps);
+               }
+
+               elements.push_back(capsfilter2);
+
+               // queue
+               GstElement *queue = gst::_createElement("queue", "srcQueue");
+               g_object_set(G_OBJECT(queue), "max-size-buffers", 1, NULL);
+               elements.push_back(queue);
+       } catch (const MediaTransporterException& e) {
+               gst::_clearElements(elements);
+               throw;
+       }
+
+       return elements;
+}
+
+gst::GstElements MediaSourceBinBase::createAudioRestOfElements(const MtprMediaSourceIni& ini)
+{
+       gst::GstElements elements;
+
+       // FIXME: make this simple
+
+       try {
+               // capsfilter
+               GstElement* capsfilter = gst::_createElement("capsfilter", ELEMENT_NAME_SRC_CAPSFILTER);
+
+               GstCaps* sinkCaps = NULL;
+               if ((sinkCaps = __makeAudioDefaultRawCaps(ini))) {
+                       g_object_set(G_OBJECT(capsfilter), "caps", sinkCaps, NULL);
+                       // source->render[idx].appsrc_caps = gst_caps_copy(sinkCaps);
+                       gst_caps_unref(sinkCaps);
+               }
+               elements.push_back(capsfilter);
+
+               // audio convert
+               elements.push_back(gst::_createElement("audioconvert"));
+
+               // encoder
+               GstElement *encoder = _prepareAudioEncoder(ini);
+               elements.push_back(encoder);
+
+               // capsfilter2
+               GstElement *capsfilter2 = gst::_createElement("capsfilter", ELEMENT_NAME_ENCODE_CAPSFILTER);
+
+               sinkCaps = __makeAudioDefaultEncodedCaps(ini);
+               if (sinkCaps) {
+                       g_object_set(G_OBJECT(capsfilter2), "caps", sinkCaps, NULL);
+                       gst_caps_unref(sinkCaps);
+               }
+
+               elements.push_back(capsfilter2);
+
+               // queue
+               GstElement *queue = gst::_createElement("queue", "srcQueue");
+               g_object_set(G_OBJECT(queue), "max-size-buffers", 1, NULL);
+               elements.push_back(queue);
+       } catch (const MediaTransporterException& e) {
+               gst::_clearElements(elements);
+               throw;
+       }
+
+       return elements;
+}
diff --git a/src/MediaSourceBinCamera.cpp b/src/MediaSourceBinCamera.cpp
new file mode 100644 (file)
index 0000000..1143a5b
--- /dev/null
@@ -0,0 +1,213 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaSourceBinCamera.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterParseIni.h"
+
+#include <algorithm>
+
+using namespace std;
+using namespace tizen_media_transporter;
+
+MediaSourceBinCamera::MediaSourceBinCamera(bundle* params)
+{
+       // privilege check
+       parseSourceParam(params);
+}
+
+void MediaSourceBinCamera::parseSourceParam(bundle* params)
+{
+       int written_count = 0;
+       char* string_val = NULL;
+
+       RET_IF(!params, "input param is NULL");
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_VIDEO_WIDTH, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _videoInfo.width = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param video width %u", _videoInfo.width);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_VIDEO_HEIGHT, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _videoInfo.height = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param video height %u", _videoInfo.height);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_VIDEO_FRAMERATE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _videoInfo.frameRate = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param video framerate %u", _videoInfo.frameRate);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_ENCODING_PARAM_BITRATE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _encInfo.bitrate = (guint)strtoul(string_val, NULL, 10);
+               LOG_DEBUG("Encoder target bitrate %u", _encInfo.bitrate);
+               written_count++;
+       }
+
+       if (written_count == 0)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to parse Source Param");
+}
+
+GstElement* MediaSourceBinCamera::createCameraSource()
+{
+       GstElement* camerasrc = nullptr;
+
+       auto& ini = MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_CAMERA));
+       if (ini.sourceElement.empty()) {
+               camerasrc = gst::_createElement(DEFAULT_ELEMENT_CAMERASRC, "videoSrc");
+       } else {
+               camerasrc = gst::_createElement(ini.sourceElement, "videoSrc");
+       }
+       gst::_setElementProperties(camerasrc, ini.sourceElementProperties);
+       if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(camerasrc)), "empty-buffer-timeout"))
+               g_object_set(G_OBJECT(camerasrc), "empty-buffer-timeout", 0, NULL);
+
+       return camerasrc;
+}
+
+void MediaSourceBinCamera::setEncoderParam(gst::GstElements& elements)
+{
+       GstElement* encoder = gst::_findEncoderElement(elements);
+       if (!encoder)
+               return;
+
+       if (_encInfo.bitrate) {
+               if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(encoder)), "bitrate"))
+                       g_object_set(G_OBJECT(encoder), "bitrate", _encInfo.bitrate * 1000, NULL);
+               else
+                       LOG_WARNING("%s element doesn't contain bitrate property", GST_ELEMENT_NAME(encoder));
+       }
+}
+
+void MediaSourceBinCamera::replaceCapsWithVideoInfo(GstElement* element)
+{
+       GstCaps* oldCaps;
+       g_object_get(G_OBJECT(element), "caps", &oldCaps, NULL);
+       RET_IF(!oldCaps, "capsfilter has no caps");
+
+       GstCaps* newCaps = gst_caps_copy(oldCaps);
+
+       if (_videoInfo.width != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _videoInfo.width);
+               gst_caps_set_value(newCaps, "width", &value);
+       }
+
+       if (_videoInfo.height != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _videoInfo.height);
+               gst_caps_set_value(newCaps, "height", &value);
+       }
+
+       if (_videoInfo.frameRate != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, GST_TYPE_FRACTION);
+               gst_value_set_fraction(&value, _videoInfo.frameRate, 1);
+               gst_caps_set_value(newCaps, "framerate", &value);
+       }
+
+       if (!gst_caps_is_equal(oldCaps, newCaps)) {
+         gst::_printCaps(newCaps, "newCaps");
+               g_object_set(G_OBJECT(element), "caps", newCaps, NULL);
+       }
+
+       gst_caps_unref(newCaps);
+}
+
+void MediaSourceBinCamera::replaceEncCapsWithVideoInfo(GstElement* element)
+{
+       GstCaps* oldCaps;
+       g_object_get(G_OBJECT(element), "caps", &oldCaps, NULL);
+       RET_IF(!oldCaps, "capsfilter has no caps");
+
+       GstCaps* newCaps = gst_caps_copy(oldCaps);
+
+       if (_videoInfo.width != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _videoInfo.width);
+               gst_caps_set_value(newCaps, "width", &value);
+       }
+
+       if (_videoInfo.height != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _videoInfo.height);
+               gst_caps_set_value(newCaps, "height", &value);
+       }
+
+       if (!gst_caps_is_equal(oldCaps, newCaps)) {
+         gst::_printCaps(newCaps, "Change newCaps");
+               g_object_set(G_OBJECT(element), "caps", newCaps, NULL);
+       }
+
+       gst_caps_unref(newCaps);
+}
+
+void MediaSourceBinCamera::setSourceParam(gst::GstElements& elements)
+{
+       GstElement* srcCapsfilter = gst::_findElementByName(elements, ELEMENT_NAME_SRC_CAPSFILTER);
+       if (srcCapsfilter)
+               replaceCapsWithVideoInfo(srcCapsfilter);
+
+       GstElement* encCapsfilter = gst::_findElementByName(elements, ELEMENT_NAME_ENCODE_CAPSFILTER);
+       if (encCapsfilter)
+               replaceEncCapsWithVideoInfo(encCapsfilter);
+}
+
+MediaSourceBinInfo MediaSourceBinCamera::generate()
+{
+       gst::GstElements elements;
+
+       try {
+               elements.push_back(createCameraSource());
+
+               auto created = createVideoRestOfElements(MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_CAMERA))); // FIXME
+               std::copy(created.begin(), created.end(), std::back_inserter(elements));
+
+               setSourceParam(elements);
+               setEncoderParam(elements);
+       } catch (const MediaTransporterException& e) {
+               gst::_clearElements(elements);
+               throw;
+       }
+
+       GstBin* bin = GST_BIN(gst_bin_new("camerabin"));
+
+       try {
+               gst::_addElementsToBin(bin, elements);
+               gst::_linkElements(elements);
+       } catch (const MediaTransporterException& e) {
+               // FIXME: handle error nicely here
+               if (std::string { e.what() } == "Failed to link elements")
+                       gst::_removeElementsFromBin(bin, elements);
+
+               g_object_unref(bin);
+               throw;
+       }
+
+       ResourceSet resourceRequired = { MM_RESOURCE_MANAGER_RES_TYPE_CAMERA };
+       if (gst::_containHwEncoderElement(elements))
+               resourceRequired.insert(MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_ENCODER);
+
+       return std::make_tuple(MTPR_SOURCE_TYPE_CAMERA, bin, resourceRequired);
+}
diff --git a/src/MediaSourceBinFactory.cpp b/src/MediaSourceBinFactory.cpp
new file mode 100644 (file)
index 0000000..d51b8e6
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaSourceBinFactory.h"
+#include "MediaSourceBinCamera.h"
+#include "MediaSourceBinMic.h"
+#include "MediaSourceBinVideoTest.h"
+#include "MediaSourceBinAudioTest.h"
+
+using namespace tizen_media_transporter;
+
+IMediaSourceBin* MediaSourceBinFactory::create(mtprSourceType type, bundle* params)
+{
+       switch (type)
+       {
+       case MTPR_SOURCE_TYPE_CAMERA:
+               return static_cast<IMediaSourceBin*>(new MediaSourceBinCamera(params));
+       case MTPR_SOURCE_TYPE_MIC:
+               return static_cast<IMediaSourceBin*>(new MediaSourceBinMic(params));
+       case MTPR_SOURCE_TYPE_VIDEOTEST:
+               return static_cast<IMediaSourceBin*>(new MediaSourceBinVideoTest(params));
+       case MTPR_SOURCE_TYPE_AUDIOTEST:
+               return static_cast<IMediaSourceBin*>(new MediaSourceBinAudioTest(params));
+       default:
+               return NULL;
+       }
+}
diff --git a/src/MediaSourceBinMic.cpp b/src/MediaSourceBinMic.cpp
new file mode 100644 (file)
index 0000000..45bfa91
--- /dev/null
@@ -0,0 +1,242 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaSourceBinMic.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterParseIni.h"
+
+#include <sound_manager_internal.h>
+#include <pulse/proplist.h>
+#include <algorithm>
+#include <sstream>
+
+using namespace std;
+using namespace tizen_media_transporter;
+
+MediaSourceBinMic::MediaSourceBinMic(bundle* params)
+{
+       // privilege check
+       parseSourceParam(params);
+}
+
+void MediaSourceBinMic::parseSourceParam(bundle* params)
+{
+       int written_count = 0;
+       char* string_val = NULL;
+
+       RET_IF(!params, "input param is NULL");
+
+       /* Will be move to mic/audiotest bin */
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_AUDIO_CHANNEL, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _audioInfo.channel = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param audio channel %u", _audioInfo.channel);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_AUDIO_RATE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _audioInfo.rate = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param audio rate %u", _audioInfo.rate);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_AUDIO_FORMAT, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _audioInfo.format = string_val;
+               LOG_DEBUG("Source param audio format %s", _audioInfo.format.c_str());
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_ENCODING_PARAM_BITRATE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _encInfo.bitrate = (guint)strtoul(string_val, NULL, 10);
+               LOG_DEBUG("Encoder target bitrate %u", _encInfo.bitrate);
+               written_count++;
+       }
+
+       if (written_count == 0)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to parse Source Param");
+}
+
+GstElement* MediaSourceBinMic::createMicSource()
+{
+       GstElement* audioSrc = nullptr;
+
+       auto& ini = MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_MIC));
+       if (ini.sourceElement.empty()) {
+               audioSrc = gst::_createElement(DEFAULT_ELEMENT_MICSRC, "audioSrc");
+       } else {
+               audioSrc = gst::_createElement(ini.sourceElement, "audioSrc");
+       }
+       gst::_setElementProperties(audioSrc, ini.sourceElementProperties);
+
+       return audioSrc;
+}
+
+void MediaSourceBinMic::setEncoderParam(gst::GstElements& elements)
+{
+       GstElement* encoder = gst::_findEncoderElement(elements);
+       if (!encoder)
+               return;
+
+       if (_encInfo.bitrate) {
+               if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(encoder)), "bitrate"))
+                       g_object_set(G_OBJECT(encoder), "bitrate", _encInfo.bitrate * 1000, NULL);
+               else
+                       LOG_WARNING("%s element doesn't contain bitrate property", GST_ELEMENT_NAME(encoder));
+       }
+}
+
+void MediaSourceBinMic::replaceCapsWithAudioInfo(GstElement* element)
+{
+       GstCaps* oldCaps;
+       auto& ini = MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_MIC));
+       GstAudioFormat format;
+       if (!_audioInfo.format.empty())
+               format = gst::_getAudioFormatFromString(_audioInfo.format);
+       else
+               format = gst::_getAudioFormatFromString(ini.audioRawFormat);
+
+       RET_IF(format == GST_AUDIO_FORMAT_UNKNOWN, "not supported raw format");
+
+       GstAudioInfo info;
+       int rate = _audioInfo.rate != -1 ? _audioInfo.rate : ini.audioSamplerate;
+       int channel = _audioInfo.channel != -1 ? _audioInfo.channel : ini.audioChannels;
+       gst_audio_info_set_format(&info, format, rate, channel, NULL);
+       GstCaps* newCaps = gst_audio_info_to_caps(&info);
+
+       g_object_get(G_OBJECT(element), "caps", &oldCaps, NULL);
+       if (!gst_caps_is_equal(oldCaps, newCaps))
+               g_object_set(G_OBJECT(element), "caps", newCaps, NULL);
+
+       gst_caps_unref(newCaps);
+}
+
+void MediaSourceBinMic::replaceEncCapsWithAudioInfo(GstElement* element)
+{
+       GstCaps* oldCaps;
+
+       g_object_get(G_OBJECT(element), "caps", &oldCaps, NULL);
+       RET_IF(!oldCaps, "capsfilter has no caps");
+
+       GstCaps* newCaps = gst_caps_copy(oldCaps);
+
+       if (_audioInfo.channel != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _audioInfo.channel);
+               gst_caps_set_value(newCaps, "channels", &value);
+       }
+
+       if (_audioInfo.rate != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _audioInfo.rate);
+               gst_caps_set_value(newCaps, "rate", &value);
+       }
+
+       if (!gst_caps_is_equal(oldCaps, newCaps))
+               g_object_set(G_OBJECT(element), "caps", newCaps, NULL);
+
+       gst_caps_unref(newCaps);
+}
+
+void MediaSourceBinMic::setSourceParam(gst::GstElements& elements)
+{
+       GstElement* srcCapsfilter = gst::_findElementByName(elements, ELEMENT_NAME_SRC_CAPSFILTER);
+       if (srcCapsfilter)
+               replaceCapsWithAudioInfo(srcCapsfilter);
+
+       GstElement* encCapsfilter = gst::_findElementByName(elements, ELEMENT_NAME_ENCODE_CAPSFILTER);
+       if (encCapsfilter)
+               replaceEncCapsWithAudioInfo(encCapsfilter);
+}
+
+MediaSourceBinInfo MediaSourceBinMic::generate()
+{
+       gst::GstElements elements;
+
+       try {
+               GstElement* micSrcElement = createMicSource();
+               gst::_applyStreamInfo(micSrcElement, _streamInfo);
+               elements.push_back(micSrcElement);
+               elements.push_back(gst::_createElement("volume"));
+
+               auto created = createAudioRestOfElements(MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_MIC)));
+               std::copy(created.begin(), created.end(), std::back_inserter(elements));
+
+               setSourceParam(elements);
+               setEncoderParam(elements);
+       } catch (const MediaTransporterException& e) {
+               gst::_clearElements(elements);
+               throw;
+       }
+
+       GstBin* bin = GST_BIN(gst_bin_new("micbin"));
+
+       try {
+               gst::_addElementsToBin(bin, elements);
+               gst::_linkElements(elements);
+       } catch (const MediaTransporterException& e) {
+               // FIXME: handle error nicely here
+               if (std::string { e.what() } == "Failed to link elements")
+                       gst::_removeElementsFromBin(bin, elements);
+
+               g_object_unref(bin);
+
+               throw;
+       }
+
+       return std::make_tuple(MTPR_SOURCE_TYPE_MIC, bin, ResourceSet());
+}
+
+void MediaSourceBinMic::setSoundStreamInfo(sound_stream_info_h streamInfo)
+{
+       char *stream_type;
+       int stream_index;
+       int aec_ref_device_id = 0;
+       // bool available = false;
+
+       RET_IF(!streamInfo, "streamInfo is NULL");
+
+       sound_manager_get_type_from_stream_information(streamInfo, &stream_type);
+       sound_manager_get_index_from_stream_information(streamInfo, &stream_index);
+       sound_manager_get_echo_cancel_reference_device(streamInfo, &aec_ref_device_id);
+
+       /* FIXEME: need to add NATIVE_API_MEDIATRANSPORTER in sound-manager */
+       // ret = sound_manager_is_available_stream_information(stream_info, NATIVE_API_MEDIATRANSPORTER, &available);
+       // if (ret != SOUND_MANAGER_ERROR_NONE) {
+       //      LOG_ERROR("failed to sound_manager_is_available_stream_information()");
+       //      return MTPR_ERROR_INVALID_OPERATION;
+       // }
+
+       // if (!available) {
+       //      LOG_ERROR("this stream info[%p, type:%s, index:%d] is not allowed to this framework", stream_info, stream_type, stream_index);
+       //      return MTPR_ERROR_INVALID_PARAMETER;
+       // }
+
+       RET_IF(!stream_type, "stream_type is NULL");
+
+       LOG_INFO("stream_info[%p, type:%s, index:%d, aec_ref_device_id:%d]",
+               streamInfo, stream_type, stream_index, aec_ref_device_id);
+
+       ostringstream stringStream;
+       stringStream << "props," << PA_PROP_MEDIA_ROLE << "=" <<
+               stream_type << ", " << PA_PROP_MEDIA_PARENT_ID << "=" << stream_index;
+       if (aec_ref_device_id != SOUND_MANAGER_STREAM_NO_REFERENCE_DEVICE)
+               stringStream << ", " << PA_PROP_MEDIA_ECHO_CANCEL_METHOD << "default, "
+                << PA_PROP_MEDIA_ECHO_CANCEL_REFERENCE_DEVICE << aec_ref_device_id;
+
+       _streamInfo = stringStream.str();
+}
diff --git a/src/MediaSourceBinVideoTest.cpp b/src/MediaSourceBinVideoTest.cpp
new file mode 100644 (file)
index 0000000..972775c
--- /dev/null
@@ -0,0 +1,211 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaSourceBinVideoTest.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterParseIni.h"
+
+#include <algorithm>
+
+using namespace std;
+using namespace tizen_media_transporter;
+
+MediaSourceBinVideoTest::MediaSourceBinVideoTest(bundle* params)
+{
+       // privilege check
+       parseSourceParam(params);
+}
+
+void MediaSourceBinVideoTest::parseSourceParam(bundle* params)
+{
+       int written_count = 0;
+       char* string_val = NULL;
+
+       RET_IF(!params, "input param is NULL");
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_VIDEO_WIDTH, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _videoInfo.width = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param video width %u", _videoInfo.width);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_VIDEO_HEIGHT, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _videoInfo.height = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param video height %u", _videoInfo.height);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_SOURCE_PARAM_VIDEO_FRAMERATE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _videoInfo.frameRate = static_cast<int>(strtoul(string_val, NULL, 10));
+               LOG_DEBUG("Source param video framerate %u", _videoInfo.frameRate);
+               written_count++;
+       }
+
+       if (bundle_get_str(params, MTPR_ENCODING_PARAM_BITRATE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _encInfo.bitrate = (guint)strtoul(string_val, NULL, 10);
+               LOG_DEBUG("Encoder target bitrate %u", _encInfo.bitrate);
+               written_count++;
+       }
+
+       if (written_count == 0)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to parse Source Param");
+}
+
+GstElement* MediaSourceBinVideoTest::createVideoTestSource()
+{
+       GstElement* videotestsrc = nullptr;
+
+       auto& ini = MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_VIDEOTEST));
+       if (ini.sourceElement.empty()) {
+               videotestsrc = gst::_createElement(DEFAULT_ELEMENT_VIDEO_TEST, "videoSrc");
+       } else {
+               videotestsrc = gst::_createElement(ini.sourceElement, "videoSrc");
+       }
+       gst::_setElementProperties(videotestsrc, ini.sourceElementProperties);
+
+       return videotestsrc;
+}
+
+void MediaSourceBinVideoTest::setEncoderParam(gst::GstElements& elements)
+{
+       GstElement* encoder = gst::_findEncoderElement(elements);
+       if (!encoder)
+               return;
+
+       if (_encInfo.bitrate) {
+               if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(encoder)), "bitrate"))
+                       g_object_set(G_OBJECT(encoder), "bitrate", _encInfo.bitrate * 1000, NULL);
+               else
+                       LOG_WARNING("%s element doesn't contain bitrate property", GST_ELEMENT_NAME(encoder));
+       }
+}
+
+void MediaSourceBinVideoTest::replaceCapsWithVideoInfo(GstElement* element)
+{
+       GstCaps* oldCaps;
+       g_object_get(G_OBJECT(element), "caps", &oldCaps, NULL);
+       RET_IF(!oldCaps, "capsfilter has no caps");
+
+       GstCaps* newCaps = gst_caps_copy(oldCaps);
+
+       if (_videoInfo.width != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _videoInfo.width);
+               gst_caps_set_value(newCaps, "width", &value);
+       }
+
+       if (_videoInfo.height != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _videoInfo.height);
+               gst_caps_set_value(newCaps, "height", &value);
+       }
+
+       if (_videoInfo.frameRate != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, GST_TYPE_FRACTION);
+               gst_value_set_fraction(&value, _videoInfo.frameRate, 1);
+               gst_caps_set_value(newCaps, "framerate", &value);
+       }
+
+       if (!gst_caps_is_equal(oldCaps, newCaps)) {
+         gst::_printCaps(newCaps, "Change newCaps");
+               g_object_set(G_OBJECT(element), "caps", newCaps, NULL);
+       }
+
+       gst_caps_unref(newCaps);
+}
+
+void MediaSourceBinVideoTest::replaceEncCapsWithVideoInfo(GstElement* element)
+{
+       GstCaps* oldCaps;
+       g_object_get(G_OBJECT(element), "caps", &oldCaps, NULL);
+       RET_IF(!oldCaps, "capsfilter has no caps");
+
+       GstCaps* newCaps = gst_caps_copy(oldCaps);
+
+       if (_videoInfo.width != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _videoInfo.width);
+               gst_caps_set_value(newCaps, "width", &value);
+       }
+
+       if (_videoInfo.height != -1) {
+               GValue value = G_VALUE_INIT;
+               g_value_init(&value, G_TYPE_INT);
+               g_value_set_int(&value, _videoInfo.height);
+               gst_caps_set_value(newCaps, "height", &value);
+       }
+
+       if (!gst_caps_is_equal(oldCaps, newCaps)) {
+         gst::_printCaps(newCaps, "Change newCaps");
+               g_object_set(G_OBJECT(element), "caps", newCaps, NULL);
+       }
+
+       gst_caps_unref(newCaps);
+}
+
+void MediaSourceBinVideoTest::setSourceParam(gst::GstElements& elements)
+{
+       GstElement* srcCapsfilter = gst::_findElementByName(elements, ELEMENT_NAME_SRC_CAPSFILTER);
+       if (srcCapsfilter)
+               replaceCapsWithVideoInfo(srcCapsfilter);
+
+       GstElement* encCapsfilter = gst::_findElementByName(elements, ELEMENT_NAME_ENCODE_CAPSFILTER);
+       if (encCapsfilter)
+               replaceEncCapsWithVideoInfo(encCapsfilter);
+}
+
+MediaSourceBinInfo MediaSourceBinVideoTest::generate()
+{
+       gst::GstElements elements;
+
+       try {
+               elements.push_back(createVideoTestSource());
+
+               auto created = createVideoRestOfElements(MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_VIDEOTEST))); // FIXME
+               std::copy(created.begin(), created.end(), std::back_inserter(elements));
+
+               setSourceParam(elements);
+               setEncoderParam(elements);
+       } catch (const MediaTransporterException& e) {
+               gst::_clearElements(elements);
+               throw;
+       }
+
+       GstBin* bin = GST_BIN(gst_bin_new("videotestbin"));
+
+       try {
+               gst::_addElementsToBin(bin, elements);
+               gst::_linkElements(elements);
+       } catch (const MediaTransporterException& e) {
+               // FIXME: handle error nicely here
+               if (std::string { e.what() } == "Failed to link elements")
+                       gst::_removeElementsFromBin(bin, elements);
+
+               g_object_unref(bin);
+               throw;
+       }
+
+       ResourceSet resourceRequired;
+       if (gst::_containHwEncoderElement(elements))
+               resourceRequired.insert(MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_ENCODER);
+
+       return std::make_tuple(MTPR_SOURCE_TYPE_VIDEOTEST, bin, resourceRequired);
+}
diff --git a/src/MediaTransporter.cpp b/src/MediaTransporter.cpp
new file mode 100644 (file)
index 0000000..123c820
--- /dev/null
@@ -0,0 +1,624 @@
+
+#include "MediaTransporter.h"
+
+#include "MediaTransporterLog.h"
+#include "MediaTransporterFactory.h"
+#include "MediaSourceBinFactory.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterSender.h"
+#include "MediaTransporterReceiver.h"
+#include "MediaSourceBinMic.h"
+
+#include <cassert>
+#include <memory>
+
+using namespace tizen_media_transporter;
+
+typedef struct {
+       std::unique_ptr<MediaTransporterBase> base;
+       std::shared_ptr<MediaTransporterResource> resourceManager;
+       std::shared_ptr<MediaTransporterDisplay> display;
+} media_transporter_s;
+
+int mtpr_create(mtpr_connection_type_e type, mtpr_h* mtpr)
+{
+       auto handle = new media_transporter_s;
+       RET_VAL_IF(!handle, MTPR_ERROR_INVALID_OPERATION, "Failed to allocate handle!!!");
+
+       try {
+               handle->base = std::unique_ptr<MediaTransporterBase>(
+                                                       MediaTransporterFactory::create(static_cast<mtprConnectionType>(type)));
+               RET_ERR_IF_INVALID_INSTANCE(handle->base);
+
+               handle->base->create();
+               LOG_INFO("base instance %p created", handle->base.get());
+
+               handle->resourceManager = std::make_shared<MediaTransporterResource>();
+               handle->base->setResourceManager(handle->resourceManager);
+
+               *mtpr = handle;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to create!!! : %s", e.what());
+               delete handle;
+               return e.error();
+       }
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_destroy(mtpr_h mtpr)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               handle->base->destroy();
+               delete handle;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to destroy!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_start(mtpr_h mtpr)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+       assert(handle->resourceManager);
+
+       try {
+               ResourceSet rs = handle->base->build();
+               handle->resourceManager->acquire(rs);
+               handle->base->start();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to start!!! : %s", e.what());
+               handle->resourceManager->releaseAll();
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_stop(mtpr_h mtpr)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               handle->base->stop();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to stop!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_get_state(mtpr_h mtpr, mtpr_state_e *state)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               *state = handle->base->state();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to stop!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_get_connection_type(mtpr_h mtpr, mtpr_connection_type_e *type)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               *type = handle->base->type();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to create!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_sender_address(mtpr_h mtpr, const char *address)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               handle->base->setSenderAddress(address);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set sender address!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_get_sender_address(mtpr_h mtpr, char **address)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+       RET_ERR_IF_NULL_ARG(address);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               std::string senderAddress = handle->base->getSenderAddress();
+               if (senderAddress.empty()) {
+                       *address = NULL;
+                       return MTPR_ERROR_INVALID_OPERATION;
+               }
+               *address = strdup(senderAddress.c_str());
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set sender address!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_receiver_address(mtpr_h mtpr, const char *address)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               handle->base->setReceiverAddress(address);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set sender address!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_get_receiver_address(mtpr_h mtpr, char **address)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+       RET_ERR_IF_NULL_ARG(address);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               std::string receiverAddress = handle->base->getReceiverAddress();
+               if (receiverAddress.empty()) {
+                       *address = NULL;
+                       return MTPR_ERROR_INVALID_OPERATION;
+               }
+               *address = strdup(receiverAddress.c_str());
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set sender address!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_connection_param(mtpr_h mtpr, const char *param_name, const char *param_value)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               handle->base->setConnection(param_name, param_value);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set connection param!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_connection_params(mtpr_h mtpr, bundle *param_list)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               handle->base->setConnection(param_list);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set connection params!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_get_connection_params(mtpr_h mtpr, bundle **param_list)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+       bundle *params = NULL;
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               params = bundle_create();
+               RET_VAL_IF(!params, MTPR_ERROR_INVALID_OPERATION, "created param_list is NULL!!!");
+               handle->base->getConnection(params);
+               *param_list = params;
+       } catch (const MediaTransporterException& e) {
+               if (params)
+                       bundle_free(params);
+               LOG_ERROR("Failed to get connection params!!! : %s", e.what());
+               return e.error();
+       }
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_add_media_source(mtpr_h mtpr, mtpr_source_type_e type, bundle* param_list, unsigned int *source_id)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+       RET_VAL_IF(!source_id, MTPR_ERROR_INVALID_PARAMETER, "invalid id ptr");
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterSender* sender = dynamic_cast<MediaTransporterSender*>(handle->base.get());
+       RET_VAL_IF(!sender, MTPR_ERROR_INVALID_OPERATION, "only sender support this api!!!");
+
+       try {
+               *source_id = sender->addMediaSource(MediaSourceBinFactory::create(type, param_list));
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to add media source!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_remove_media_source(mtpr_h mtpr, unsigned int source_id)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterSender* sender = dynamic_cast<MediaTransporterSender*>(handle->base.get());
+       RET_VAL_IF(!sender, MTPR_ERROR_INVALID_OPERATION, "only sender support this api!!!");
+
+       try {
+               sender->removeMediaSource(static_cast<int>(source_id));
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to remove media source!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_mic_source_set_sound_stream_info(mtpr_h mtpr, unsigned int source_id, sound_stream_info_h stream_info)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       try {
+               MediaTransporterSender* sender = dynamic_cast<MediaTransporterSender*>(handle->base.get());
+               RET_VAL_IF(!sender, MTPR_ERROR_INVALID_OPERATION, "only sender support this api!!!");
+
+               MediaSourceBinMic* micSourceBin = dynamic_cast<MediaSourceBinMic*>(sender->getMediaSource(source_id));
+               RET_VAL_IF(!micSourceBin, MTPR_ERROR_INVALID_PARAMETER, "Invalid source id");
+
+               micSourceBin->setSoundStreamInfo(stream_info);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to mic set sound stream info!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_error_cb(mtpr_h mtpr, mtpr_error_cb callback, void *user_data)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               handle->base->setErrorCallback(mtpr, callback, user_data);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to setErrorCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_unset_error_cb(mtpr_h mtpr)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       try {
+               auto handle = static_cast<media_transporter_s*>(mtpr);
+               assert(handle->base);
+               handle->base->unsetErrorCallback();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to unsetErrorCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_track_added_cb(mtpr_h mtpr, mtpr_track_added_cb callback, void* user_data)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               receiver->setTrackAddedCallback(mtpr, callback, user_data);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to setNoMoreTrackCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_unset_track_added_cb(mtpr_h mtpr)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               receiver->unsetTrackAddedCallback();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to unsetNoMoreTrackCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_no_more_track_cb(mtpr_h mtpr, mtpr_no_more_track_cb callback, void* user_data)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               receiver->setNoMoreTrackCallback(mtpr, callback, user_data);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to setNoMoreTrackCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_unset_no_more_track_cb(mtpr_h mtpr)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               receiver->unsetNoMoreTrackCallback();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to unsetNoMoreTrackCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_audio_packet_cb(mtpr_h mtpr, mtpr_encoded_frame_cb callback, void* user_data)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               receiver->setAudioPacketCallback(mtpr, callback, user_data);//__internal_audio_packet_cb, static_cast<void*>(handle));
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to setAudioPacketCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_unset_audio_packet_cb(mtpr_h mtpr)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               receiver->unsetAudioPacketCallback();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to unsetAudioPacketCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_video_packet_cb(mtpr_h mtpr, mtpr_encoded_frame_cb callback, void* user_data)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               receiver->setVideoPacketCallback(mtpr, callback, user_data);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to setVideoPacketCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_unset_video_packet_cb(mtpr_h mtpr)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               receiver->unsetVideoPacketCallback();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to unsetVideoPacketCallback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_display(mtpr_h mtpr, mtpr_display_type_e type, mtpr_display_h display)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               handle->display = std::make_shared<MediaTransporterDisplay>(type, display);
+               receiver->setDisplay(handle->display);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to mtpr_set_display!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_display_mode(mtpr_h mtpr, mtpr_display_mode_e mode)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       RET_VAL_IF(!handle->display, MTPR_ERROR_INVALID_OPERATION, "display is not set yet!!!");
+
+       try {
+               handle->display->setMode(mode);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set modeq!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_get_display_mode(mtpr_h mtpr, mtpr_display_mode_e *mode)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       RET_VAL_IF(!handle->display, MTPR_ERROR_INVALID_OPERATION, "display is not set yet!!!");
+
+       try {
+               *mode = handle->display->getMode();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to get Mode!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_display_visible(mtpr_h mtpr, bool visible)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       RET_VAL_IF(!handle->display, MTPR_ERROR_INVALID_OPERATION, "display is not set yet!!!");
+
+       try {
+               handle->display->setVisible(visible);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set visible!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_get_display_visible(mtpr_h mtpr, bool *visible)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       RET_VAL_IF(!handle->display, MTPR_ERROR_INVALID_OPERATION, "display is not set yet!!!");
+
+       try {
+               *visible = handle->display->getVisible();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to get visible!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_set_sound_stream_info(mtpr_h mtpr, sound_stream_info_h stream_info)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       MediaTransporterReceiver* receiver = dynamic_cast<MediaTransporterReceiver*>(handle->base.get());
+       RET_VAL_IF(!receiver, MTPR_ERROR_INVALID_OPERATION, "only receiver support this api!!!");
+
+       try {
+               receiver->setSoundStreamInfo(stream_info);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to setSoundStreamInfo!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
diff --git a/src/MediaTransporterBase.cpp b/src/MediaTransporterBase.cpp
new file mode 100644 (file)
index 0000000..681a805
--- /dev/null
@@ -0,0 +1,244 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaTransporterBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterGst.h"
+
+using namespace tizen_media_transporter;
+
+gboolean MediaTransporterBase::__busWatchCb(GstBus *bus, GstMessage *message, gpointer user_data)
+{
+       auto base = static_cast<MediaTransporterBase*>(user_data);
+       RET_VAL_IF(!base, FALSE, "base is NULL");
+
+       GError* err = NULL;
+       GstState gst_state_old = GST_STATE_VOID_PENDING;
+       GstState gst_state_new = GST_STATE_VOID_PENDING;
+       GstState gst_state_pending = GST_STATE_VOID_PENDING;
+
+       RET_VAL_IF(!base->_gst.pipeline, FALSE, "pipeline is NULL");
+
+       if (!message) {
+               LOG_DEBUG("message is null");
+               return TRUE;
+       }
+
+       switch (GST_MESSAGE_TYPE(message)) {
+       case GST_MESSAGE_ERROR: {
+               mtpr_error_e error = MTPR_ERROR_INVALID_OPERATION;
+               gst_message_parse_error(message, &err, NULL);
+
+               LOG_ERROR("Error[from %s]: message[%s], code[%d]",
+                       GST_OBJECT_NAME(GST_OBJECT_CAST(GST_ELEMENT(GST_MESSAGE_SRC(message)))), err->message, err->code);
+
+               // FIXME : convert err -> error
+               if (base->_errorCallback)
+                       base->_errorCallback->invoke(VariantData{error});
+
+               g_error_free(err);
+               break;
+       }
+
+       case GST_MESSAGE_STATE_CHANGED:
+               if (GST_MESSAGE_SRC(message) != GST_OBJECT(base->_gst.pipeline))
+                       return TRUE;
+
+               gst_message_parse_state_changed(message, &gst_state_old, &gst_state_new, &gst_state_pending);
+               LOG_INFO("GST_MESSAGE_STATE_CHANGED : Old[GST_STATE_%s] New[GST_STATE_%s] Pending[GST_STATE_%s]",
+                       gst_element_state_get_name(gst_state_old),
+                       gst_element_state_get_name(gst_state_new),
+                       gst_element_state_get_name(gst_state_pending));
+               break;
+
+       case GST_MESSAGE_ASYNC_DONE:
+               if (GST_MESSAGE_SRC(message) != GST_OBJECT(base->_gst.pipeline))
+                       return TRUE;
+
+               LOG_INFO("GST_MESSAGE_ASYNC_DONE");
+               break;
+
+       case GST_MESSAGE_EOS:
+               LOG_INFO("GST_MESSAGE_EOS end-of-stream");
+               break;
+
+       default:
+               break;
+       }
+
+       return TRUE;
+}
+
+void MediaTransporterBase::makePipeline()
+{
+       MTPR_FENTER();
+       _gst.pipeline = gst_pipeline_new("mtpr-pipeline");
+       if (!(_gst.bus = gst_pipeline_get_bus(GST_PIPELINE(_gst.pipeline)))) {
+               LOG_ERROR("failed to gst_pipeline_get_bus()");
+               return;
+       }
+
+       if ((_gst.bus_watcher = gst_bus_add_watch(_gst.bus, __busWatchCb, this)) == 0) {
+               LOG_ERROR("failed to gst_bus_add_watch()");
+               return;
+       }
+       MTPR_FLEAVE();
+}
+
+void MediaTransporterBase::create()
+{
+       gst::_gstInit();
+       makePipeline();
+
+       _state = MTPR_STATE_IDLE;
+}
+
+ResourceSet MediaTransporterBase::build()
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       if (_state != MTPR_STATE_IDLE)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_STATE, "state must be IDLE");
+
+       return buildPipeline();
+}
+
+void MediaTransporterBase::start()
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       if (_state != MTPR_STATE_IDLE)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_STATE, "state must be IDLE");
+
+       startPipeline();
+       _state = MTPR_STATE_PLAYING;
+}
+
+void MediaTransporterBase::stopInternal()
+{
+       try {
+               stopPipeline();
+               _state = MTPR_STATE_IDLE;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+               throw;
+       }
+}
+
+void MediaTransporterBase::stop()
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       if (_state != MTPR_STATE_PLAYING)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_STATE, "state must be PLAYING");
+
+       stopInternal();
+}
+
+void MediaTransporterBase::destroy()
+{
+       stopInternal();
+
+       try {
+               if (_gst.bus_watcher > 0) {
+                       gst_bus_remove_watch(_gst.bus);
+                       _gst.bus_watcher = 0;
+               }
+
+               if (_gst.bus) {
+                       gst_object_unref(_gst.bus);
+                       _gst.bus = NULL;
+               }
+
+               if (_gst.signals) {
+                       g_list_free_full(_gst.signals, gst::_disconnectSignal);
+                       _gst.signals = NULL;
+               }
+
+               if (_gst.pipeline) {
+                       gst_object_unref(_gst.pipeline);
+                       _gst.pipeline = NULL;
+               }
+
+               _resourceManager->unsubscribe(this);
+
+               // _mtpr_destroy_resource_manager();
+               // _mtpr_release_connection_params();
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+               throw;
+       }
+}
+
+mtprState MediaTransporterBase::state()
+{
+       return _state;
+}
+
+void MediaTransporterBase::setSenderAddress(std::string address)
+{
+       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION,
+                                                                       "not supported, set receiver address instead!");
+}
+
+void MediaTransporterBase::setReceiverAddress(std::string address)
+{
+       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION,
+                                                                       "not supported, set sender address instead!");
+}
+
+std::string MediaTransporterBase::getSenderAddress()
+{
+       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION,
+                                                                       "not supported, get receiver address instead!");
+}
+
+std::string MediaTransporterBase::getReceiverAddress()
+{
+       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION,
+                                                                       "not supported, get sender address instead!");
+}
+
+void MediaTransporterBase::setErrorCallback(void* handle, mtprErrorCallback callback, void* userData)
+{
+       _errorCallback = std::unique_ptr<IInvokable>(new ErrorCallback(handle, callback, userData));
+}
+
+void MediaTransporterBase::unsetErrorCallback()
+{
+       _errorCallback = nullptr;
+}
+
+void MediaTransporterBase::setResourceManager(std::shared_ptr<MediaTransporterResource> resourceManager)
+{
+       _resourceManager = resourceManager;
+       _resourceManager->subscribe(this);
+}
+
+void MediaTransporterBase::changed()
+{
+       try {
+               stopInternal();
+               if (_errorCallback)
+                       _errorCallback->invoke(VariantData{MTPR_ERROR_RESOURCE_CONFLICT});
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("error : %s", e.what());
+               //return false;
+       }
+
+       //return true;
+}
diff --git a/src/MediaTransporterCallback.cpp b/src/MediaTransporterCallback.cpp
new file mode 100644 (file)
index 0000000..9df9feb
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+
+#include "MediaTransporterCallback.h"
+#include "MediaTransporterLog.h"
+
+using namespace tizen_media_transporter;
+
+TrackAddedCallback::TrackAddedCallback(void* handle, mtprTrackAddedCallback cb, void* userdata)
+                                                       : AbstractCallback(handle, userdata), _callback(cb)
+{
+       LOG_DEBUG(">>> callback[%p], handle[%p], user_data[%p] registered",
+                       cb, handle, userdata);
+}
+
+void TrackAddedCallback::invoke(VariantData data1, VariantData data2)
+{
+       auto mediaType = std::get<mtprMediaType>(data1);
+       auto trackId = std::get<unsigned int>(data2);
+
+       LOG_DEBUG(">>> callback[%p], mediaType[%d], trackId[%u], handle[%p], user_data[%p]",
+                       _callback, mediaType, trackId, _handle, _userdata);
+
+       _callback(_handle, mediaType, trackId, _userdata);
+}
+
+NoMoreTrackCallback::NoMoreTrackCallback(void* handle, mtprNoMoreTrackCallback cb, void* userdata)
+                                                       : AbstractCallback(handle, userdata), _callback(cb)
+{
+       LOG_DEBUG(">>> callback[%p], handle[%p], user_data[%p] registered",
+                       cb, handle, userdata);
+}
+
+void NoMoreTrackCallback::invoke()
+{
+       LOG_DEBUG(">>> callback[%p], handle[%p], user_data[%p]",
+                       _callback, _handle, _userdata);
+
+       _callback(_handle, _userdata);
+}
+
+PacketCallback::PacketCallback(void* handle, mtprMediaType type, mtprPacketCallback cb, void* userdata)
+                                                       : AbstractCallback(handle, userdata), _type(type), _callback(cb)
+{
+       LOG_DEBUG(">>> callback[%p], handle[%p], user_data[%p] registered",
+                       cb, handle, userdata);
+}
+
+void PacketCallback::invoke(VariantData data1, VariantData data2)
+{
+       auto trackId = std::get<unsigned int>(data1);
+       auto packet = std::get<media_packet_h>(data2);
+
+       LOG_VERBOSE(">>> callback[%p], handle[%p], type[%d], id[%d], packet[%p], user_data[%p]",
+                       _callback, _handle, _type, trackId, packet, _userdata);
+
+       _callback(_handle, _type, trackId, packet, _userdata);
+}
+
+ErrorCallback::ErrorCallback(void* handle, mtprErrorCallback cb,  void* userdata)
+                                                       : AbstractCallback(handle, userdata), _callback(cb)
+{
+       LOG_DEBUG(">>> callback[%p], handle[%p], user_data[%p] registered",
+                       cb, handle, userdata);
+}
+
+void ErrorCallback::invoke(VariantData data)
+{
+       auto error = std::get<mtprError>(data);
+
+       LOG_DEBUG(">>> callback[%p], handle[%p], error[%d], user_data[%p]",
+                       _callback, _handle, error, _userdata);
+
+       _callback(_handle, error, _userdata);
+}
\ No newline at end of file
diff --git a/src/MediaTransporterDisplay.cpp b/src/MediaTransporterDisplay.cpp
new file mode 100644 (file)
index 0000000..04fc8ad
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include <iostream>
+#include <string>
+#include <algorithm>
+#include <mm_error.h>
+#include <gst/video/videooverlay.h>
+
+#include "MediaTransporter.h"
+#include "MediaTransporterDisplay.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterParseIni.h"
+#include "MediaTransporterGst.h"
+
+using namespace tizen_media_transporter;
+
+MediaTransporterDisplay::MediaTransporterDisplay(mtprDisplayType type, void *surface)
+       : _type(type), _surface(surface)
+{
+       if (!surface)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "surface is NULL");
+
+       if (mm_display_interface_init(&_mmDisplay) != MM_ERROR_NONE)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to mm_display_interface_init()");
+
+       LOG_INFO("display[type:%d, surface:%p]", _type, _surface);
+       LOG_DEBUG("alloc display[mmDisplay:%p, mode:%u, visible:%u]", _mmDisplay, _mode, _visible);
+}
+
+MediaTransporterDisplay::~MediaTransporterDisplay()
+{
+       LOG_DEBUG("mmDisplay[%p]", _mmDisplay);
+
+       if (mm_display_interface_deinit(_mmDisplay) != MM_ERROR_NONE)
+               LOG_WARNING("failed to mm_display_interface_deinit()");
+
+       if (_sinkElement) {
+               gst_object_unref(_sinkElement);
+               _sinkElement = nullptr;
+       }
+}
+
+GstElement* MediaTransporterDisplay::videoSink()
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       try {
+               switch (_type) {
+               case MTPR_DISPLAY_TYPE_OVERLAY:
+                       LOG_INFO("it's OVERLAY type");
+                       break;
+               default:
+                       LOG_ERROR_IF_REACHED("type(%d)", _type);
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "_display is NULL");
+                       break;
+               }
+
+               std::string videoSinkName = MediaTransporterIni::get().renderingSink().videoSinkElement;
+               _sinkElement = gst::_createElement(videoSinkName);
+               applyModeProperty();
+               applyVisibleProperty();
+
+               int overlaySurfaceId = -1;
+               if (mm_display_interface_set_display_mainloop_sync(_mmDisplay, MM_DISPLAY_TYPE_OVERLAY, _surface, &overlaySurfaceId) != MM_ERROR_NONE)
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to mm_display_interface_set_display_mainloop_sync()");
+
+               LOG_INFO("surface[%p], got overlaySurfaceId[%d]", _surface, overlaySurfaceId);
+               gst_video_overlay_set_wl_window_wl_surface_id(GST_VIDEO_OVERLAY(_sinkElement), overlaySurfaceId);
+
+               return GST_ELEMENT(gst_object_ref(_sinkElement));
+       } catch (const MediaTransporterException& e) {
+               if (_sinkElement) {
+                       gst_object_unref(_sinkElement);
+                       _sinkElement = nullptr;
+               }
+
+               throw;
+       }
+}
+
+void MediaTransporterDisplay::setVisible(bool visible)
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       _visible = visible;
+
+       if (_sinkElement)
+               applyVisibleProperty();
+}
+
+void MediaTransporterDisplay::setMode(mtprDisplayMode mode)
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       _mode = mode;
+
+       if (_sinkElement)
+               applyModeProperty();
+}
+
+void MediaTransporterDisplay::applyVisibleProperty()
+{
+       if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(_sinkElement)), "visible"))
+               g_object_set(G_OBJECT(_sinkElement),
+                               "visible", _visible, NULL);
+}
+
+void MediaTransporterDisplay::applyModeProperty()
+{
+       if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(_sinkElement)), "display-geometry-method"))
+               g_object_set(G_OBJECT(_sinkElement),
+                               "display-geometry-method", (gint)_mode, /* 0: letter box, 1: origin size, 2: full screen */
+                               NULL);
+}
\ No newline at end of file
diff --git a/src/MediaTransporterFactory.cpp b/src/MediaTransporterFactory.cpp
new file mode 100644 (file)
index 0000000..27cdfee
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaTransporterFactory.h"
+#include "MediaTransporterSenderRist.h"
+#include "MediaTransporterSenderSrt.h"
+#include "MediaTransporterSenderRtsp.h"
+#include "MediaTransporterSenderToServerRtsp.h"
+#include "MediaTransporterReceiverRist.h"
+#include "MediaTransporterReceiverSrt.h"
+
+using namespace tizen_media_transporter;
+
+MediaTransporterBase* MediaTransporterFactory::create(mtprConnectionType type)
+{
+       switch (type)
+       {
+       case MTPR_CONNECTION_TYPE_RIST_SENDER:
+               return static_cast<MediaTransporterBase*>(new MediaTransporterSenderRist());
+       case MTPR_CONNECTION_TYPE_RIST_RECEIVER:
+               return static_cast<MediaTransporterBase*>(new MediaTransporterReceiverRist());
+       case MTPR_CONNECTION_TYPE_SRT_SENDER:
+               return static_cast<MediaTransporterBase*>(new MediaTransporterSenderSrt());
+       case MTPR_CONNECTION_TYPE_SRT_RECEIVER:
+               return static_cast<MediaTransporterBase*>(new MediaTransporterReceiverSrt());
+       case MTPR_CONNECTION_TYPE_RTSP_SENDER:
+               return static_cast<MediaTransporterBase*>(new MediaTransporterSenderRtsp());
+       case MTPR_CONNECTION_TYPE_RTSP_SENDER_TO_SERVER:
+               return static_cast<MediaTransporterBase*>(new MediaTransporterSenderToServerRtsp());
+       default:
+               return NULL;
+       }
+       return NULL;
+}
diff --git a/src/MediaTransporterGst.cpp b/src/MediaTransporterGst.cpp
new file mode 100644 (file)
index 0000000..5929c7a
--- /dev/null
@@ -0,0 +1,743 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include <iostream>
+#include <string>
+#include <algorithm>
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterParseIni.h"
+
+using namespace tizen_media_transporter;
+
+void gst::_clearElements(GstElements& elements)
+{
+       std::for_each(elements.begin(), elements.end(),
+                               [](GstElement*element){ gst_object_unref(element); });
+}
+
+void gst::_removeElement(GstElements& elements, GstElement* removeElement)
+{
+       auto it = std::find_if(elements.begin(), elements.end(),
+                                               [&removeElement](GstElement* element) {
+                                                       return (element == removeElement) ? true : false;
+                                               });
+       elements.erase(it);
+}
+
+GstElement* gst::_createElement(std::string factory_name, std::string name)
+{
+       GstElement* element = gst_element_factory_make(factory_name.c_str(), name.empty() ? NULL : name.c_str());
+       if (!element)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to create element");
+
+       LOG_INFO("created element[%p, %s, %s]",
+                       element, factory_name.c_str(), name.c_str());
+
+       return element;
+}
+
+void gst::_connectAndAppendSignal(GList** signals, GObject* obj, const char* sig_name, GCallback cb, gpointer user_data)
+{
+       auto sig_data = static_cast<gst::mtprSignal*>(g_malloc0(sizeof(gst::mtprSignal)));
+       sig_data->obj = obj;
+       sig_data->signal_id = g_signal_connect(sig_data->obj, sig_name, cb, user_data);
+       if (sig_data->signal_id == 0) {
+               LOG_ERROR("failed to g_signal_connect(), [%s] for object [%s]",
+                               sig_name, GST_OBJECT_NAME(obj));
+               g_free(sig_data);
+               return;
+       }
+
+       *signals = g_list_append(*signals, sig_data);
+
+       LOG_DEBUG("signal [%s] with id[%lu] is connected to object [%s]",
+                       sig_name, sig_data->signal_id, GST_OBJECT_NAME(sig_data->obj));
+}
+
+void gst::_disconnectSignal(gpointer data)
+{
+       auto sig_data = static_cast<gst::mtprSignal*>(data);
+
+       RET_IF(!sig_data, "sig_data is NULL");
+
+       if (g_signal_handler_is_connected(sig_data->obj, sig_data->signal_id)) {
+               g_signal_handler_disconnect(sig_data->obj, sig_data->signal_id);
+               LOG_DEBUG("signal with id[%lu] is disconnected from object [%s]",
+                               sig_data->signal_id, GST_OBJECT_NAME(sig_data->obj));
+       }
+
+       g_free(sig_data);
+}
+
+bool gst::_addNoTargetGhostpad(GstBin* bin, GstPad** new_pad, bool is_src)
+{
+       RET_VAL_IF(!bin, false, "bin is NULL");
+       RET_VAL_IF(!new_pad, false, "new_pad is NULL");
+
+       GstPad* ghost_pad = gst_ghost_pad_new_no_target(NULL, is_src ? GST_PAD_SRC : GST_PAD_SINK);
+       if (!ghost_pad) {
+               LOG_ERROR("failed to gst_ghost_pad_new_no_target()");
+               return false;
+       }
+
+       gchar* bin_name = gst_element_get_name(bin);
+       if (!gst_element_add_pad(GST_ELEMENT(bin), ghost_pad)) {
+               LOG_ERROR("failed to add empty [%s] ghostpad into [%s]", GST_PAD_NAME(ghost_pad), bin_name);
+               g_object_unref(ghost_pad);
+               g_free(bin_name);
+               return false;
+       }
+
+       gst_pad_set_active(ghost_pad, TRUE);
+       LOG_DEBUG("added [%s] empty ghostpad into [%s]", GST_PAD_NAME(ghost_pad), bin_name);
+       g_free(bin_name);
+
+       *new_pad = ghost_pad;
+
+       return true;
+}
+
+bool gst::_setGhostpadTarget(GstPad* ghost_pad, GstElement* target_element, bool is_src)
+{
+       RET_VAL_IF(!ghost_pad, false, "ghost_pad is NULL");
+       RET_VAL_IF(!target_element, false, "target_element is NULL");
+
+       GstPad* target_pad = gst_element_get_static_pad(target_element, is_src ? "src" : "sink");
+
+       if (!target_pad) {
+               LOG_ERROR("failed to gst_element_get_static_pad() of [%s]",
+                               GST_ELEMENT_NAME(target_element));
+               return false;
+       }
+       if (!gst_ghost_pad_set_target(GST_GHOST_PAD(ghost_pad), target_pad)) {
+               LOG_ERROR("failed to gst_ghost_pad_set_target(), ghostpad[%s] -> targetpad[%s]",
+                               GST_PAD_NAME(ghost_pad), GST_PAD_NAME(target_pad));
+               gst_object_unref(target_pad);
+               return false;
+       }
+
+       LOG_DEBUG("ghostpad[%s] -> target[%s:%s]",
+                       GST_PAD_NAME(ghost_pad), GST_ELEMENT_NAME(target_element), GST_PAD_NAME(target_pad));
+
+       gst_object_unref(target_pad);
+
+       return true;
+}
+
+void gst::_removeElementsFromBin(GstBin* bin, GstElements& elements)
+{
+       for (auto element : elements)
+               if (!gst_bin_remove(bin, element))
+                       return;
+
+       LOG_DEBUG("%zu elements are removed from bin[%s]", elements.size(), GST_ELEMENT_NAME(bin));
+}
+
+void gst::_addElementsToBin(GstBin* bin, GstElements elements)
+{
+       GstElements addedElements;
+
+       for (auto iter = elements.begin(); iter != elements.end(); iter++) {
+               GstElement* element = *iter;
+               if (!gst_bin_add(bin, element)) {
+                       LOG_ERROR("failed to gst_bin_add(), bin[%s], element[%s]",
+                                       GST_ELEMENT_NAME(bin), GST_ELEMENT_NAME(element));
+
+                       _removeElementsFromBin(bin, addedElements);
+
+                       /* rest of elements on the list should be unreferenced */
+                       for_each(iter, elements.end(), [](GstElement*e) { gst_object_unref(e); });
+
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to add element to bin");
+               }
+               addedElements.push_back(element);
+       }
+
+       LOG_DEBUG("%zu elements are added to bin[%s]", addedElements.size(), GST_ELEMENT_NAME(bin));
+}
+
+void gst::_linkElements(GstElements& elements)
+{
+       GstElement* prev = nullptr;
+
+       for (auto element : elements) {
+               if (prev && !gst_element_link(prev, element)) {
+                       LOG_ERROR("Failed to link %s - %s", GST_ELEMENT_NAME(prev), GST_ELEMENT_NAME(element));
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION,
+                                                                                               "Failed to link elements");
+               }
+               prev = element;
+       }
+
+       LOG_DEBUG("%zu elements are linked", elements.size());
+}
+
+static void __parse_type_and_set_value(GType type, GstElement* element, gchar** key_value_pair)
+{
+       RET_IF(!element, "element is NULL");
+       RET_IF(!key_value_pair, "key_value_pairs is NULL");
+       RET_IF(!key_value_pair[0], "key is NULL");
+       RET_IF(!key_value_pair[1], "value is NULL");
+
+       switch (type) {
+       case G_TYPE_STRING:
+               g_object_set(G_OBJECT(element), key_value_pair[0], key_value_pair[1], NULL);
+               break;
+       case G_TYPE_INT:
+       case G_TYPE_INT64:
+       case G_TYPE_ENUM: {
+               gint64 value = g_ascii_strtoll((const gchar*)key_value_pair[1], NULL, 10);
+               g_object_set(G_OBJECT(element), key_value_pair[0], value, NULL);
+               break;
+       }
+       case G_TYPE_UINT:
+       case G_TYPE_UINT64: {
+               guint64 value = g_ascii_strtoll((const gchar*)key_value_pair[1], NULL, 10);
+               g_object_set(G_OBJECT(element), key_value_pair[0], value, NULL);
+               break;
+       }
+       case G_TYPE_BOOLEAN: {
+               gboolean value = FALSE;
+               static const gchar* true_names[] = {
+                 "1", "yes", "on", "true", "TRUE", NULL
+               };
+
+               if (g_strv_contains (true_names, (const gchar*)key_value_pair[1]))
+                 value = TRUE;
+
+               g_object_set(G_OBJECT(element), key_value_pair[0], value, NULL);
+               break;
+       }
+       case G_TYPE_FLOAT:
+       case G_TYPE_DOUBLE: {
+               gdouble value = g_ascii_strtod((const gchar*)key_value_pair[1], NULL);
+               g_object_set(G_OBJECT(element), key_value_pair[0], value, NULL);
+               break;
+       }
+       default:
+               LOG_DEBUG("not supported type(0x%x) exactly, but try it with int64 type", (unsigned int)type); /* e.g.) custom enum type falls through here */
+               gint64 value = g_ascii_strtoll((const gchar*)key_value_pair[1], NULL, 10);
+               g_object_set(G_OBJECT(element), key_value_pair[0], value, NULL);
+               break;
+       }
+
+       LOG_DEBUG("element[%s] property[%s] value[type:0x%x, %s]", GST_ELEMENT_NAME(element), key_value_pair[0], (unsigned int)type, key_value_pair[1]);
+}
+
+void gst::_setElementProperties(GstElement* element, std::vector<std::string> key_value_pairs)
+{
+       RET_IF(!element, "element is NULL");
+
+       for (auto& kv : key_value_pairs) {
+               // FIXME: remove g_strsplit later
+               gchar** key_value_pair = g_strsplit(kv.c_str(), "=", 2);
+
+               if (!g_strcmp0(key_value_pair[0], "") ||
+                       !g_strcmp0(key_value_pair[1], "")) {
+                       LOG_ERROR("invalid key_value_pair, key[%s], value[%s]",
+                                       key_value_pair[0], key_value_pair[1]);
+                       g_strfreev(key_value_pair);
+                       continue;
+               }
+
+               GParamSpec* param_spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(element)), g_strstrip(key_value_pair[0]));;
+               if (param_spec)
+                       __parse_type_and_set_value(param_spec->value_type, element, key_value_pair);
+               else
+                       LOG_ERROR("element[%s] does not have this property[%s]", GST_ELEMENT_NAME(element), key_value_pair[0]);
+
+               g_strfreev(key_value_pair);
+       }
+}
+
+void gst::_applyStreamInfo(GstElement* element, std::string streamInfo)
+{
+       RET_IF(!element, "element is NULL");
+       if (!g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(element)), "stream-properties")) {
+               LOG_WARNING("%s element does not have 'stream-properties", GST_ELEMENT_NAME(element));
+               return;
+       }
+
+       RET_IF(streamInfo.empty(), "input streamInfo is NULL");
+       GstStructure *structure = gst_structure_from_string(streamInfo.c_str(), NULL);
+       RET_IF(!structure, "failed to gst_structure_from_string()");
+       LOG_INFO("stream-properties[%s]", streamInfo.c_str());
+
+       g_object_set(G_OBJECT(element), "stream-properties", structure, NULL);
+       gst_structure_free(structure);
+}
+
+void gst::_syncElementsStateWithParent(GstElements& elements)
+{
+       for (auto element : elements) {
+               if (!gst_element_sync_state_with_parent(element))
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION,
+                                                                                       "Failed to _syncElementsStateWithParent");
+       }
+
+       LOG_DEBUG("_syncElementsStateWithParent done");
+}
+
+std::string gst::_getMimeTypeFromPad(GstPad* pad)
+{
+       RET_VAL_IF(!pad, NULL, "pad is NULL");
+
+       GstCaps* caps = gst_pad_has_current_caps(pad) ? gst_pad_get_current_caps(pad) : gst_pad_query_caps(pad, NULL);
+       RET_VAL_IF(!caps, NULL, "caps is NULL");
+
+       std::string mimeType = gst_structure_get_name(gst_caps_get_structure(caps, 0));
+       LOG_DEBUG("mimeType [%s]", mimeType.c_str());
+
+       gst_caps_unref(caps);
+
+       return mimeType;
+}
+
+void gst::_generateDot(GstElement* pipeline, std::string name)
+{
+       std::string dotName = gst::DEFAULT_DOT_FILE_NAME_PREFIX + "." + name;
+
+       GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, dotName.c_str());
+
+       LOG_WARNING("dot file[%s] is generated", dotName.c_str());
+}
+
+GstAudioFormat gst::_getAudioFormatFromString(std::string format)
+{
+       if (format == "S16LE")
+               return GST_AUDIO_FORMAT_S16LE;
+       if (format == "F32LE")
+               return GST_AUDIO_FORMAT_F32LE;
+
+       LOG_ERROR("not supported raw format(%s)", format.c_str());
+
+       return GST_AUDIO_FORMAT_UNKNOWN;
+}
+
+std::string gst::_getVideoMediaType(std::string codec_name)
+{
+       if (codec_name == "mpeg")
+               return MEDIA_TYPE_VIDEO_MPEG;
+       if (codec_name == "vp8" || codec_name == "VP8")
+               return MEDIA_TYPE_VIDEO_VP8;
+       if (codec_name == "vp9" || codec_name == "VP9")
+               return MEDIA_TYPE_VIDEO_VP9;
+       if (codec_name == "theora" || codec_name == "THEORA")
+               return MEDIA_TYPE_VIDEO_THEORA;
+       if (codec_name == "h264" || codec_name == "H264")
+               return MEDIA_TYPE_VIDEO_H264;
+       if (codec_name == "h265" || codec_name == "H265")
+               return MEDIA_TYPE_VIDEO_H265;
+       if (codec_name == "jpeg" || codec_name == "JPEG" || codec_name == "mjpeg" || codec_name  == "MJPEG")
+               return MEDIA_TYPE_VIDEO_JPEG;
+
+       LOG_ERROR("not supported video codec_name[%s]", codec_name.c_str());
+
+       return "";
+}
+
+std::string gst::_getAudioMediaType(std::string codec_name)
+{
+       if (codec_name == "aac")
+               return MEDIA_TYPE_AUDIO_AAC;
+       if (codec_name == "pcmu" || codec_name == "PCMU")
+               return MEDIA_TYPE_AUDIO_MULAW;
+       if (codec_name == "pcma" || codec_name == "PCMA")
+               return MEDIA_TYPE_AUDIO_ALAW;
+       if (codec_name == "opus" || codec_name == "OPUS")
+               return MEDIA_TYPE_AUDIO_OPUS;
+       if (codec_name == "vorbis" || codec_name == "VORBIS")
+               return MEDIA_TYPE_AUDIO_VORBIS;
+
+       LOG_ERROR("not supported audio codec_name[%s]", codec_name.c_str());
+
+       return "";
+}
+
+GstCaps* gst::_getCapsFromEncodedVideoMediaType(std::string media_type, int width, int height)
+{
+       if (media_type == MEDIA_TYPE_VIDEO_MPEG)
+               return gst_caps_new_simple(media_type.c_str(),
+                       "mpegversion", G_TYPE_INT, 4,
+                       "systemstream", G_TYPE_BOOLEAN, FALSE,
+                       NULL);
+
+       if (media_type == MEDIA_TYPE_VIDEO_H264 ||
+               media_type == MEDIA_TYPE_VIDEO_H265)
+               return gst_caps_new_simple(media_type.c_str(),
+                       "stream-format", G_TYPE_STRING, "byte-stream",
+                       "alignment", G_TYPE_STRING, "au",
+                       "level", G_TYPE_STRING, "3",
+                       "width", G_TYPE_INT, width,
+                       "height", G_TYPE_INT, height,
+                       NULL);
+
+       if (media_type == MEDIA_TYPE_VIDEO_VP8 ||
+               media_type == MEDIA_TYPE_VIDEO_VP9 ||
+               media_type == MEDIA_TYPE_VIDEO_THEORA)
+               return gst_caps_new_simple(media_type.c_str(), NULL, NULL); /* NOTE: need to verify these codecs */
+
+       if (media_type == MEDIA_TYPE_VIDEO_JPEG)
+               return gst_caps_new_simple(media_type.c_str(),
+                       "width", G_TYPE_INT, width,
+                       "height", G_TYPE_INT, height,
+                       NULL);
+
+       LOG_ERROR_IF_REACHED("invalid media_type(%s)", media_type.c_str());
+
+       return NULL;
+}
+
+GstCaps* gst::_getCapsFromEncodedAudioMediaType(std::string media_type, int channels, int samplerate)
+{
+       if (media_type == MEDIA_TYPE_AUDIO_AAC)
+               return gst_caps_new_simple(media_type.c_str(),
+                       "channels", G_TYPE_INT, channels,
+                       "rate", G_TYPE_INT, samplerate,
+                       "mpegversion", G_TYPE_INT, 4,
+                       "base-profile", G_TYPE_STRING, "lc",
+                       NULL);
+
+       if (media_type == MEDIA_TYPE_AUDIO_MULAW ||
+                       media_type == MEDIA_TYPE_AUDIO_ALAW)
+               return gst_caps_new_simple(media_type.c_str(),
+                       "rate", G_TYPE_INT, samplerate,
+                       "channels", G_TYPE_INT, channels,
+                       NULL);
+
+       if (media_type == MEDIA_TYPE_AUDIO_OPUS ||
+               media_type == MEDIA_TYPE_AUDIO_VORBIS)
+               return gst_caps_new_simple(media_type.c_str(), NULL, NULL);
+
+       LOG_ERROR_IF_REACHED("invalid media_type(%s)", media_type.c_str());
+
+       return NULL;
+}
+
+static gboolean __elementFilter(GstPluginFeature* feature, gpointer data)
+{
+       gst::elementInfo* elem_info = static_cast<gst::elementInfo*>(data);
+       gboolean src_can_accept = FALSE;
+       gboolean sink_can_accept = FALSE;
+
+       if (!GST_IS_ELEMENT_FACTORY(feature))
+               return FALSE;
+
+       GstElementFactory* factory = GST_ELEMENT_FACTORY(feature);
+       gchar* factory_name = GST_OBJECT_NAME(factory);
+       const gchar* factory_klass = gst_element_factory_get_klass(factory);
+
+       if (!elem_info || !g_strrstr(factory_klass, elem_info->klass_name))
+               return FALSE;
+
+       auto it = std::find_if(elem_info->excludedElements.begin(), elem_info->excludedElements.end(),
+                                               [&factory_name](std::string& element) {
+                                                       return element.find(factory_name) != std::string::npos;
+                                               });
+       if (it != elem_info->excludedElements.end()) {
+               LOG_WARNING("this element[%s] is an item of excluded element list in ini file, skip it", factory_name);
+               return FALSE;
+       }
+
+       if (GST_IS_CAPS(elem_info->srcCaps))
+               src_can_accept = gst_element_factory_can_src_any_caps(factory, elem_info->srcCaps);
+
+       if (GST_IS_CAPS(elem_info->sinkCaps))
+               sink_can_accept = gst_element_factory_can_sink_any_caps(factory, elem_info->sinkCaps);
+
+       if (GST_IS_CAPS(elem_info->srcCaps) && GST_IS_CAPS(elem_info->sinkCaps)) {
+               if (src_can_accept && sink_can_accept) {
+                       LOG_DEBUG("found compatible factory [%s] for klass [%s]", factory_name, factory_klass);
+                       return TRUE;
+               }
+               return FALSE;
+       }
+
+       if (src_can_accept || sink_can_accept) {
+               LOG_DEBUG("found compatible factory [%s] for klass [%s]", factory_name, factory_klass);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static int __rankCompare(GstPluginFeature* first, GstPluginFeature* second)
+{
+       guint first_rank = gst_plugin_feature_get_rank(first);
+       guint second_rank = gst_plugin_feature_get_rank(second);
+
+       LOG_DEBUG("second[%s]_rank(%d) - first[%s]_rank(%d) = (%d)",
+               GST_OBJECT_NAME(GST_ELEMENT_FACTORY(second)), second_rank,
+               GST_OBJECT_NAME(GST_ELEMENT_FACTORY(first)), first_rank, second_rank - first_rank);
+
+       return second_rank - first_rank;
+}
+
+GstElement* gst::_createElementFromRegistry(std::string klassName, GstCaps* sinkCaps, GstCaps* srcCaps, const std::vector<std::string>& excludedElements)
+{
+       elementInfo info { klassName.c_str(), srcCaps, sinkCaps, excludedElements };
+
+       GList* factories = gst_registry_feature_filter(gst_registry_get(), __elementFilter, FALSE, &info);
+       factories = g_list_sort(factories, (GCompareFunc) __rankCompare);
+
+       if (!factories) {
+               LOG_DEBUG("could not find any compatible element for klass_name[%s]", klassName.c_str());
+               if (srcCaps)
+                       gst_caps_unref(srcCaps);
+               if (sinkCaps)
+                       gst_caps_unref(sinkCaps);
+               gst_plugin_list_free(factories);
+
+               return nullptr;
+       }
+
+       GstElementFactory* factory = GST_ELEMENT_FACTORY(factories->data);
+       LOG_INFO("sorted result element is [%s]", GST_OBJECT_NAME(factory));
+
+       GstElement* element = _createElement(GST_OBJECT_NAME(factory));
+
+       /* FIXME error handling */
+       if (srcCaps)
+               gst_caps_unref(srcCaps);
+
+       if (sinkCaps)
+               gst_caps_unref(sinkCaps);
+
+       gst_plugin_list_free(factories);
+
+       return element;
+}
+
+GstElement* gst::_findEncoderElement(GstElements& elements)
+{
+       auto it = std::find_if(elements.begin(), elements.end(),
+                                               [](GstElement* element) {
+                                                       return gst_element_factory_list_is_type(
+                                                                               gst_element_get_factory(element),
+                                                                               GST_ELEMENT_FACTORY_TYPE_ENCODER);
+                                               });
+
+       return (it != elements.end()) ? *it : nullptr;
+}
+
+bool gst::_containHwEncoderElement(GstElements& elements)
+{
+       return std::find_if(elements.begin(), elements.end(),
+                                               [](GstElement* element) {
+                                                       GstElementFactory* factory = gst_element_get_factory(element);
+                                                       return (gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_ENCODER) &&
+                                                               gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_HARDWARE));
+                                               }) != elements.end();
+}
+
+GstElement* gst::_findElementByName(GstElements& elements, const std::string& nameToFind)
+{
+       auto it = std::find_if(elements.begin(), elements.end(),
+                                               [&nameToFind](GstElement* element) {
+                                                       return nameToFind.compare(GST_ELEMENT_NAME(element)) == 0;
+                                               });
+
+       return (it != elements.end()) ? *it : nullptr;
+}
+
+void gst::_gstInit()
+{
+       auto& gstArgs = MediaTransporterIni::get().general().gstArgs;
+       int argc = gstArgs.size() + 1;
+       char** argv = static_cast<char**>(g_malloc0(sizeof(char *) * argc));
+
+       argv[0] = g_strdup("capi-media-transporter");
+
+       int i = 1;
+       for (auto& arg : gstArgs) {
+               LOG_DEBUG("adding [%s] is added", arg.c_str());
+               argv[i++] = g_strdup(arg.c_str());
+       }
+
+       GError* err = NULL;
+       if (!gst_init_check(&argc, &argv, &err)) {
+               LOG_ERROR("failed to gst_init_check(), err[%s]", err->message);
+               g_clear_error(&err);
+       }
+
+       for (i = 0; i < argc; i++) {
+               g_free(argv[i]);
+               argv[i] = NULL;
+       }
+       g_free(argv);
+}
+
+void gst::_dumpPipelineState(GstElement* pipeline)
+{
+       gboolean done = FALSE;
+
+       GValue item = G_VALUE_INIT;
+       GstElement* element = NULL;
+       GstElementFactory* factory = NULL;
+
+       GstState state = GST_STATE_VOID_PENDING;
+       GstState pending = GST_STATE_VOID_PENDING;
+       GstClockTime time = 200 * GST_MSECOND;
+
+       GstIterator* iter = gst_bin_iterate_recurse(GST_BIN(pipeline));
+
+       if (iter) {
+               while (!done) {
+                       switch (gst_iterator_next(iter, &item)) {
+                       case GST_ITERATOR_OK:
+                               element = static_cast<GstElement*>(g_value_get_object(&item));
+                               if (element) {
+                                       gst_element_get_state(element, &state, &pending, time);
+
+                                       factory = gst_element_get_factory(element) ;
+                                       if (factory)
+                                               LOG_ERROR("%s:%s : From:%s To:%s   refcount : %d", GST_OBJECT_NAME(factory) , GST_ELEMENT_NAME(element) ,
+                                                       gst_element_state_get_name(state), gst_element_state_get_name(pending) , GST_OBJECT_REFCOUNT_VALUE(element));
+                               }
+                               g_value_reset(&item);
+                               break;
+                       case GST_ITERATOR_RESYNC:
+                               gst_iterator_resync(iter);
+                               break;
+                       case GST_ITERATOR_ERROR:
+                               done = TRUE;
+                               break;
+                       case GST_ITERATOR_DONE:
+                               done = TRUE;
+                               break;
+                       }
+               }
+       }
+
+       element = GST_ELEMENT(pipeline);
+
+       gst_element_get_state(element, &state, &pending, time);
+
+       factory = gst_element_get_factory(element) ;
+
+       if (factory) {
+               LOG_ERROR("%s:%s : From:%s To:%s  refcount : %d",
+                       GST_OBJECT_NAME(factory),
+                       GST_ELEMENT_NAME(element),
+                       gst_element_state_get_name(state),
+                       gst_element_state_get_name(pending),
+                       GST_OBJECT_REFCOUNT_VALUE(element));
+       }
+
+       g_value_unset(&item);
+
+       if (iter)
+               gst_iterator_free(iter);
+}
+
+void gst::_setPipelineState(GstElement *pipeline, GstState state, int timeout)
+{
+       GstState element_state = GST_STATE_VOID_PENDING;
+       GstState element_pending_state = GST_STATE_VOID_PENDING;
+
+       if (!pipeline) {
+               LOG_ERROR("pipeline is null");
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "pipeline is null");
+       }
+
+       GstStateChangeReturn ret = gst_element_set_state(pipeline, state);
+       if (ret == GST_STATE_CHANGE_FAILURE) {
+               LOG_ERROR("failed to change [%s] element state to [%s]",
+                       GST_ELEMENT_NAME(pipeline), gst_element_state_get_name(state));
+
+               _dumpPipelineState(pipeline);
+
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to set state!!");
+       }
+
+       /* wait for state transition */
+       ret = gst_element_get_state(pipeline, &element_state, &element_pending_state, timeout * GST_SECOND);
+       if (ret == GST_STATE_CHANGE_FAILURE || (state != element_state)) {
+               LOG_ERROR("failed to change [%s] element state to [%s] within %d sec",
+                       GST_ELEMENT_NAME(pipeline), gst_element_state_get_name(state), timeout);
+
+               LOG_ERROR(" [%s] state : %s   pending : %s",
+                       GST_ELEMENT_NAME(pipeline),
+                       gst_element_state_get_name(element_state),
+                       gst_element_state_get_name(element_pending_state));
+
+               _dumpPipelineState(pipeline);
+
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to get state!!");
+       }
+
+       std::string dotName = std::string { GST_ELEMENT_NAME(pipeline) } + "." + std::string { gst_element_state_get_name(state) };
+       _generateDot(pipeline, dotName);
+}
+
+GstCaps* gst::_makeCapsForCapsfilter(GstPad *pad)
+{
+       gint mpegversion = 0;
+       GstCaps* new_caps = NULL;
+
+       RET_VAL_IF(!pad, NULL, "pad is NULL");
+
+       GstCaps* caps = gst_pad_has_current_caps(pad) ? gst_pad_get_current_caps(pad)
+                       : gst_pad_query_caps(pad, NULL);
+
+       // PRINT_CAPS(caps, "pad");
+       GstStructure* structure = gst_caps_get_structure(caps, 0);
+       std::string mediaType = gst_structure_get_name(structure);
+       gst_structure_get_int(structure, "mpegversion", &mpegversion);
+
+       if ((mediaType.find(MEDIA_TYPE_VIDEO_H264) != std::string::npos)
+                       || mediaType.find(MEDIA_TYPE_VIDEO_H265) != std::string::npos) {
+               /* FIXME: get value of stream-format, alignment from ini file */
+               new_caps = gst_caps_new_simple(mediaType.c_str(),
+                               "stream-format", G_TYPE_STRING, "byte-stream",
+                               "alignment", G_TYPE_STRING, "au",
+                               NULL);
+       } else if ((mediaType.find(MEDIA_TYPE_AUDIO_AAC) != std::string::npos)
+                       && (mpegversion == 2 || mpegversion == 4)) {
+               new_caps = gst_caps_new_simple(mediaType.c_str(),
+                               "mpegversion", G_TYPE_INT, mpegversion,
+                               "stream-format", G_TYPE_STRING, "adts",
+                               NULL);
+       }
+
+       gst_caps_unref(caps);
+       return new_caps;
+}
+
+void gst::_destroyElementFromParent(GstElement* element)
+{
+       if (!element)
+               return;
+
+       GstElement* parent = GST_ELEMENT_CAST(gst_element_get_parent(element));
+       if (parent) {
+               gst_bin_remove(GST_BIN(parent), element);
+               gst_object_unref(parent);
+       } else {
+               gst_object_unref(GST_OBJECT(element));
+       }
+}
+
+void gst::_printCaps(GstCaps* caps, std::string prefix)
+{
+       if (!caps)
+               return;
+
+       std::string caps_str = gst_caps_to_string(caps);
+       LOG_DEBUG("%s caps[%s]", prefix.c_str(), caps_str.c_str());
+}
diff --git a/src/MediaTransporterObserver.cpp b/src/MediaTransporterObserver.cpp
new file mode 100644 (file)
index 0000000..f909568
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+
+#include "MediaTransporterObserver.h"
+#include "MediaTransporterLog.h"
+#include <algorithm>
+
+using namespace tizen_media_transporter;
+
+void ObservableBase::subscribe(IObserver * observer)
+{
+       _observers.push_back(observer);
+}
+
+void ObservableBase::notify()
+{
+       std::vector<IObserver *>::iterator iter = _observers.begin();
+       for (; iter != _observers.end(); iter++) {
+               IObserver *observer = *iter;
+               observer->changed();
+       }
+}
+
+void ObservableBase::unsubscribe(IObserver *observer)
+{
+       std::vector<IObserver *>::iterator iter;
+       iter = std::find(_observers.begin(), _observers.end(), observer);
+       if (iter != _observers.end()) {
+               _observers.erase(iter);
+       }
+}
diff --git a/src/MediaTransporterParseIni.cpp b/src/MediaTransporterParseIni.cpp
new file mode 100644 (file)
index 0000000..a8008a6
--- /dev/null
@@ -0,0 +1,305 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include <numeric>
+#include <sstream>
+#include <algorithm>
+#include <glib.h>
+#include "MediaTransporterGst.h"
+#include "MediaTransporterParseIni.h"
+#include "MediaTransporterLog.h"
+
+using namespace tizen_media_transporter;
+
+static void dumpItem(const char* prefix_str, bool item)
+{
+       LOG_INFO("- %-19s = %s", prefix_str, item ? "yes" : "no");
+}
+
+static void dumpItem(const char* prefix_str, int item)
+{
+       LOG_INFO("- %-19s = %d", prefix_str, item);
+}
+
+static void dumpItem(const char* prefix_str, std::string& item)
+{
+       LOG_INFO("- %-19s = %s", prefix_str, item.c_str());
+}
+
+static void dumpItem(const char* prefix_str, std::vector<std::string>& item)
+{
+       std::string joined = std::accumulate(item.begin(), item.end(), std::string("|"));
+
+       LOG_INFO("- %-19s = %s", prefix_str, joined.c_str());
+}
+
+static std::vector<std::string> iniReadList(dictionary* dict, std::string category, std::string item, char delimeters)
+{
+       std::vector<std::string> strings;
+
+       if (!dict)
+               return strings;
+
+       std::string key = category + ":" + item;
+
+       const char *str = iniparser_getstring(dict, key.c_str(), NULL);
+       if (str && strlen(str) > 0) {
+               g_autofree gchar* strtmp = g_strdup(str);
+               g_strstrip(strtmp);
+
+               std::string tmp { strtmp };
+               std::stringstream ss(tmp);
+               tmp.clear();
+
+               while (getline(ss, tmp, delimeters))
+                       strings.push_back(tmp);
+       }
+
+       return strings;
+}
+
+static std::string iniGetString(dictionary* dict, std::string category, std::string item, std::string default_value)
+{
+       if (!dict)
+               return default_value;
+
+       std::string key = category + ":" + item;
+
+       const char *str =  iniparser_getstring(dict, key.c_str(), default_value.c_str());
+       return str;
+}
+
+static int iniGetInt(dictionary* dict, std::string category, std::string item, int default_value)
+{
+       if (!dict)
+               return default_value;
+
+       std::string key = category + ":" + item;
+
+       return iniparser_getint(dict, key.c_str(), default_value);
+}
+
+static  bool iniGetBoolean(dictionary* dict, std::string category, std::string item, bool default_value)
+{
+       if (!dict)
+               return default_value;
+
+       std::string key = category + ":" + item;
+
+       return static_cast<bool>(iniparser_getboolean(dict, key.c_str(), static_cast<int>(default_value)));
+}
+
+void MediaTransporterIni::dumpItemsOfGeneral()
+{
+       dumpItem(INI_ITEM_DOT_GENERATE, _ini.general.generateDot);
+       dumpItem(INI_ITEM_DOT_PATH, _ini.general.dotPath);
+       dumpItem(INI_ITEM_VERBOSE_LOG, _ini.general.verboseLog);
+       dumpItem(INI_ITEM_GST_ARGS, _ini.general.gstArgs);
+       dumpItem(INI_ITEM_GST_EXCLUDED_ELEMENTS, _ini.general.gstExcludedElements);
+       dumpItem(INI_ITEM_GST_STATE_CHANGE_TIMEOUT, _ini.general.timeout);
+}
+
+#if 0
+void MediaTransporterIni::dumpItemsOfRtspConnection()
+{
+       dumpItem(INI_ITEM_RTSP_SERVER_IP, connection->rtsp_ip);
+       dumpItem(INI_ITEM_RTSP_SERVER_PORT, connection->rtsp_port);
+       dumpItem(INI_ITEM_RTSP_SERVER_MOUNT_POINT, connection->rtsp_mount_point);
+}
+#endif
+
+void MediaTransporterIni::dumpItemsOfRenderingSink()
+{
+       dumpItem(INI_ITEM_AUDIO_SINK_ELEMENT, _ini.renderingSink.audioSinkElement);
+       dumpItem(INI_ITEM_VIDEO_SINK_ELEMENT, _ini.renderingSink.videoSinkElement);
+       dumpItem(INI_ITEM_AUDIO_HW_DECODER_ELEMENTS, _ini.renderingSink.audioHWDecoderElements);
+       dumpItem(INI_ITEM_VIDEO_HW_DECODER_ELEMENTS, _ini.renderingSink.videoHWDecoderElements);
+}
+
+void MediaTransporterIni::dumpItemsOfMediaSource(MtprMediaSourceIni& source)
+{
+       dumpItem(INI_ITEM_SOURCE_ELEMENT, source.sourceElement);
+       dumpItem(INI_ITEM_SOURCE_ELEMENT_PROPERTIES, source.sourceElementProperties);
+       dumpItem(INI_ITEM_VIDEO_RAW_FORMAT, source.videoRawFormat);
+       dumpItem(INI_ITEM_VIDEO_WIDTH, source.videoWidth);
+       dumpItem(INI_ITEM_VIDEO_HEIGHT, source.videoHeight);
+       dumpItem(INI_ITEM_VIDEO_FRAMERATE, source.videoFramerate);
+       dumpItem(INI_ITEM_VIDEO_DRC_SUPPORT, source.videoDRCSupport);
+       dumpItem(INI_ITEM_VIDEO_ENCODED_FMT_SUPPORT, source.videoEncodedFMTSupport);
+       dumpItem(INI_ITEM_VIDEO_CODEC, source.videoCodec);
+       dumpItem(INI_ITEM_VIDEO_HW_ENCODER_ELEMENT, source.videoHWEncoderElement);
+       dumpItem(INI_ITEM_AUDIO_RAW_FORMAT, source.audioRawFormat);
+       dumpItem(INI_ITEM_AUDIO_SAMPLERATE, source.audioSamplerate);
+       dumpItem(INI_ITEM_AUDIO_CHANNELS, source.audioChannels);
+       dumpItem(INI_ITEM_AUDIO_CODEC, source.audioCodec);
+       dumpItem(INI_ITEM_AUDIO_HW_ENCODER_ELEMENT, source.audioHWEncoderElement);
+}
+
+void MediaTransporterIni::applyGeneralSetting(dictionary* dict)
+{
+       /* general */
+       _ini.general.generateDot = iniGetBoolean(dict, INI_CATEGORY_GENERAL, INI_ITEM_DOT_GENERATE, DEFAULT_GENERATE_DOT);
+       _ini.general.dotPath = iniGetString(dict, INI_CATEGORY_GENERAL, INI_ITEM_DOT_PATH, DEFAULT_DOT_PATH);
+       if (_ini.general.generateDot) {
+               LOG_INFO("dot file will be stored in [%s]", _ini.general.dotPath.c_str());
+               g_setenv("GST_DEBUG_DUMP_DOT_DIR", _ini.general.dotPath.c_str(), FALSE);
+       }
+
+       _ini.general.verboseLog = iniGetBoolean(dict, INI_CATEGORY_GENERAL, INI_ITEM_VERBOSE_LOG, DEFAULT_VERBOSE_LOG);
+       _ini.general.gstArgs = iniReadList(dict, INI_CATEGORY_GENERAL, INI_ITEM_GST_ARGS, '|');
+       _ini.general.gstExcludedElements = iniReadList(dict, INI_CATEGORY_GENERAL, INI_ITEM_GST_EXCLUDED_ELEMENTS, ',');
+       _ini.general.timeout = iniGetInt(dict, INI_CATEGORY_GENERAL, INI_ITEM_GST_STATE_CHANGE_TIMEOUT, DEFAULT_STATE_CHANGE_TIMEOUT);
+}
+
+#if 0
+void MediaTransporterIni::applyRtspConnection(dictionary* dict, MtprRtspConnectionIni* connection)
+{
+       connection->rtsp_ip = iniGetString(dict, INI_CATEGORY_CONNECTION, INI_ITEM_RTSP_SERVER_IP, DEFAULT_RTSP_SERVER_IP);
+       connection->rtsp_port = iniGetString(dict, INI_CATEGORY_CONNECTION, INI_ITEM_RTSP_SERVER_PORT, DEFAULT_RTSP_SERVER_PORT);
+       connection->rtsp_mount_point = iniGetString(dict, INI_CATEGORY_CONNECTION, INI_ITEM_RTSP_SERVER_MOUNT_POINT, DEFAULT_RTSP_SERVER_MOUNT_POINT);
+}
+#endif
+
+void MediaTransporterIni::applyRenderingSink(dictionary* dict)
+{
+       _ini.renderingSink.audioSinkElement = iniGetString(dict, INI_CATEGORY_RENDERING_SINK, INI_ITEM_AUDIO_SINK_ELEMENT, gst::DEFAULT_AUDIO_SINK_ELEMENT);
+       _ini.renderingSink.videoSinkElement = iniGetString(dict, INI_CATEGORY_RENDERING_SINK, INI_ITEM_VIDEO_SINK_ELEMENT, gst::DEFAULT_VIDEO_SINK_ELEMENT);
+       _ini.renderingSink.audioHWDecoderElements = iniReadList(dict, INI_CATEGORY_RENDERING_SINK, INI_ITEM_AUDIO_HW_DECODER_ELEMENTS, ',');
+       _ini.renderingSink.videoHWDecoderElements = iniReadList(dict, INI_CATEGORY_RENDERING_SINK, INI_ITEM_VIDEO_HW_DECODER_ELEMENTS, ',');
+}
+
+void MediaTransporterIni::applyMediaSourceDefault(dictionary* dict, std::string category)
+{
+       _ini.mediaSourceDefault.videoRawFormat = iniGetString(dict, category, INI_ITEM_VIDEO_RAW_FORMAT, DEFAULT_VIDEO_RAW_FORMAT);
+       _ini.mediaSourceDefault.videoWidth = iniGetInt(dict, category, INI_ITEM_VIDEO_WIDTH, DEFAULT_VIDEO_WIDTH);
+       _ini.mediaSourceDefault.videoHeight = iniGetInt(dict, category, INI_ITEM_VIDEO_HEIGHT, DEFAULT_VIDEO_HEIGHT);
+       _ini.mediaSourceDefault.videoFramerate = iniGetInt(dict, category, INI_ITEM_VIDEO_FRAMERATE, DEFAULT_VIDEO_FRAMERATE);
+       _ini.mediaSourceDefault.videoDRCSupport = iniGetBoolean(dict, category, INI_ITEM_VIDEO_DRC_SUPPORT, DEFAULT_VIDEO_DRC_SUPPORT);
+       _ini.mediaSourceDefault.videoEncodedFMTSupport = iniGetBoolean(dict, category, INI_ITEM_VIDEO_ENCODED_FMT_SUPPORT, DEFAULT_VIDEO_ENCODED_FMT_SUPPORT);
+       _ini.mediaSourceDefault.videoCodec = iniGetString(dict, category, INI_ITEM_VIDEO_CODEC, DEFAULT_VIDEO_CODEC);
+       _ini.mediaSourceDefault.videoHWEncoderElement = iniGetString(dict, category, INI_ITEM_VIDEO_HW_ENCODER_ELEMENT, "");
+       _ini.mediaSourceDefault.audioRawFormat = iniGetString(dict, category, INI_ITEM_AUDIO_RAW_FORMAT, DEFAULT_AUDIO_RAW_FORMAT);
+       _ini.mediaSourceDefault.audioSamplerate = iniGetInt(dict, category, INI_ITEM_AUDIO_SAMPLERATE, DEFAULT_AUDIO_SAMPLERATE);
+       _ini.mediaSourceDefault.audioChannels = iniGetInt(dict, category, INI_ITEM_AUDIO_CHANNELS, DEFAULT_AUDIO_CHANNELS);
+       _ini.mediaSourceDefault.audioCodec = iniGetString(dict, category, INI_ITEM_AUDIO_CODEC, DEFAULT_AUDIO_CODEC);
+       _ini.mediaSourceDefault.audioHWEncoderElement = iniGetString(dict, category, INI_ITEM_AUDIO_HW_ENCODER_ELEMENT, "");
+}
+
+void MediaTransporterIni::applyMediaSource(dictionary* dict, MtprMediaSourceIni& source, std::string category)
+{
+       source.sourceElement = iniGetString(dict, category, INI_ITEM_SOURCE_ELEMENT, "");
+       source.sourceElementProperties = iniReadList(dict, category, INI_ITEM_SOURCE_ELEMENT_PROPERTIES, ',');
+
+       source.videoRawFormat = iniGetString(dict, category, INI_ITEM_VIDEO_RAW_FORMAT, _ini.mediaSourceDefault.videoRawFormat);
+       source.videoWidth = iniGetInt(dict, category, INI_ITEM_VIDEO_WIDTH, _ini.mediaSourceDefault.videoWidth);
+       source.videoHeight = iniGetInt(dict, category, INI_ITEM_VIDEO_HEIGHT, _ini.mediaSourceDefault.videoHeight);
+       source.videoFramerate = iniGetInt(dict, category, INI_ITEM_VIDEO_FRAMERATE, _ini.mediaSourceDefault.videoFramerate);
+       source.videoDRCSupport = iniGetBoolean(dict, category, INI_ITEM_VIDEO_DRC_SUPPORT, _ini.mediaSourceDefault.videoDRCSupport);
+       source.videoEncodedFMTSupport = iniGetBoolean(dict, category, INI_ITEM_VIDEO_ENCODED_FMT_SUPPORT, _ini.mediaSourceDefault.videoEncodedFMTSupport);
+       source.videoCodec = iniGetString(dict, category, INI_ITEM_VIDEO_CODEC, _ini.mediaSourceDefault.videoCodec);
+       source.videoHWEncoderElement = iniGetString(dict, category, INI_ITEM_VIDEO_HW_ENCODER_ELEMENT, _ini.mediaSourceDefault.videoHWEncoderElement);
+       source.audioRawFormat = iniGetString(dict, category, INI_ITEM_AUDIO_RAW_FORMAT, _ini.mediaSourceDefault.audioRawFormat);
+       source.audioSamplerate = iniGetInt(dict, category, INI_ITEM_AUDIO_SAMPLERATE, _ini.mediaSourceDefault.audioSamplerate);
+       source.audioChannels = iniGetInt(dict, category, INI_ITEM_AUDIO_CHANNELS, _ini.mediaSourceDefault.audioChannels);
+       source.audioCodec = iniGetString(dict, category, INI_ITEM_AUDIO_CODEC, _ini.mediaSourceDefault.audioCodec);
+       source.audioHWEncoderElement = iniGetString(dict, category, INI_ITEM_AUDIO_HW_ENCODER_ELEMENT, _ini.mediaSourceDefault.audioHWEncoderElement);
+}
+
+
+static const char* category_source_names[] = {
+       [MTPR_SOURCE_TYPE_CAMERA] = INI_CATEGORY_SOURCE_CAMERA,
+       [MTPR_SOURCE_TYPE_MIC] = INI_CATEGORY_SOURCE_MIC,
+       [MTPR_SOURCE_TYPE_VIDEOTEST] = INI_CATEGORY_SOURCE_VIDEOTEST,
+       [MTPR_SOURCE_TYPE_AUDIOTEST] = INI_CATEGORY_SOURCE_AUDIOTEST,
+       [MTPR_SOURCE_TYPE_AUDIOTEST + 1] = NULL,
+};
+
+void MediaTransporterIni::dumpIni()
+{
+       LOG_INFO("[%s]", INI_CATEGORY_GENERAL);
+       dumpItemsOfGeneral();
+
+       LOG_INFO("[%s]", INI_CATEGORY_MEDIA_SOURCE);
+       dumpItemsOfMediaSource(_ini.mediaSourceDefault);
+
+       for (int i = 0; category_source_names[i]; i++) {
+               LOG_INFO("[%s]", category_source_names[i]);
+               dumpItemsOfMediaSource(_ini.mediaSources[i]);
+       }
+
+       LOG_INFO("[%s]", INI_CATEGORY_RENDERING_SINK);
+       dumpItemsOfRenderingSink();
+}
+
+
+MediaTransporterIni::MediaTransporterIni()
+{
+       load();
+}
+
+MediaTransporterIni& MediaTransporterIni::get()
+{
+       static MediaTransporterIni ini;
+       return ini;
+}
+
+const MtprGeneralIni& MediaTransporterIni::general()
+{
+       return _ini.general;
+}
+
+const MtprMediaSourceIni& MediaTransporterIni::mediaSource(int type)
+{
+       return (type == -1) ? _ini.mediaSourceDefault : _ini.mediaSources.at(type);
+}
+
+const MtprRenderingSinkIni& MediaTransporterIni::renderingSink()
+{
+       return _ini.renderingSink;
+}
+
+void MediaTransporterIni::load()
+{
+       dictionary* dict = iniparser_load(MTPR_INI_PATH);
+       if (!dict) {
+               LOG_ERROR("could not open ini[%s], use default values", MTPR_INI_PATH);
+               return;
+       }
+       applyGeneralSetting(dict);
+
+       /* default setting for a media source */
+       applyMediaSourceDefault(dict, INI_CATEGORY_MEDIA_SOURCE);
+
+       /* it overrides the default values above */
+       for (int i = 0; category_source_names[i] != NULL; i++) {
+               if (iniparser_getsecnkeys(dict, category_source_names[i]) > 0) {
+                       MtprMediaSourceIni source;
+                       applyMediaSource(dict, source, category_source_names[i]);
+
+                       _ini.mediaSources.push_back(source);
+               }
+       }
+
+       applyRenderingSink(dict);
+
+       dumpIni();
+
+       if (dict) {
+               iniparser_freedict(dict);
+               LOG_DEBUG("ini instance[%p] is freed", dict);
+               dict = NULL;
+       }
+}
\ No newline at end of file
diff --git a/src/MediaTransporterReceiver.cpp b/src/MediaTransporterReceiver.cpp
new file mode 100644 (file)
index 0000000..1bd0ef2
--- /dev/null
@@ -0,0 +1,618 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaTransporterBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterReceiver.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterUtil.h"
+#include "MediaTransporterParseIni.h"
+
+#include <media_packet_internal.h>
+#include <sound_manager_internal.h>
+#include <pulse/proplist.h>
+#include <sstream>
+
+using namespace tizen_media_transporter;
+
+void MediaTransporterReceiver::setTrackAddedCallback(void* handle, mtprTrackAddedCallback callback, void* userData)
+{
+       _trackAddedCallback = std::unique_ptr<IInvokable>(new TrackAddedCallback(handle, callback, userData));
+}
+
+void MediaTransporterReceiver::unsetTrackAddedCallback()
+{
+       _trackAddedCallback = nullptr;
+}
+
+void MediaTransporterReceiver::setNoMoreTrackCallback(void* handle, mtprNoMoreTrackCallback callback, void* userData)
+{
+       _noMoreTrackCallback = std::unique_ptr<IInvokable>(new NoMoreTrackCallback(handle, callback, userData));
+}
+
+void MediaTransporterReceiver::unsetNoMoreTrackCallback()
+{
+       _noMoreTrackCallback = nullptr;
+}
+
+void MediaTransporterReceiver::setVideoPacketCallback(void* handle, mtprPacketCallback callback, void* userData)
+{
+       _videoCallback._callback = std::unique_ptr<IInvokable>(new PacketCallback(handle, MTPR_MEDIA_TYPE_VIDEO, callback, userData));
+}
+
+void MediaTransporterReceiver::setAudioPacketCallback(void* handle, mtprPacketCallback callback, void* userData)
+{
+       _audioCallback._callback = std::unique_ptr<IInvokable>(new PacketCallback(handle, MTPR_MEDIA_TYPE_AUDIO, callback, userData));
+}
+
+void MediaTransporterReceiver::unsetVideoPacketCallback()
+{
+       _videoCallback._callback = nullptr;
+}
+
+void MediaTransporterReceiver::unsetAudioPacketCallback()
+{
+       _audioCallback._callback = nullptr;
+}
+
+static media_format_mimetype_e _getMediaFormatMimeType(std::string mime)
+{
+       if (mime.find(gst::MEDIA_TYPE_AUDIO_AAC) != std::string::npos)
+               return MEDIA_FORMAT_AAC;
+       else if (mime.find(gst::MEDIA_TYPE_AUDIO_OPUS) != std::string::npos)
+               return MEDIA_FORMAT_OPUS;
+       else if (mime.find(gst::MEDIA_TYPE_AUDIO_VORBIS) != std::string::npos)
+               return MEDIA_FORMAT_VORBIS;
+       else if (mime.find(gst::MEDIA_TYPE_VIDEO_MPEG) != std::string::npos)
+               return MEDIA_FORMAT_MPEG4_SP; /* FIXME: need to check format , MEDIA_FORMAT_MPEG4_ASP */
+       else if (mime.find(gst::MEDIA_TYPE_VIDEO_VP8) != std::string::npos)
+               return MEDIA_FORMAT_VP8;
+       else if (mime.find(gst::MEDIA_TYPE_VIDEO_VP9) != std::string::npos)
+               return MEDIA_FORMAT_VP9;
+       else if (mime.find(gst::MEDIA_TYPE_VIDEO_H264) != std::string::npos)
+               return MEDIA_FORMAT_H264_HP;
+       else if (mime.find(gst::MEDIA_TYPE_VIDEO_JPEG) != std::string::npos)
+               return MEDIA_FORMAT_MJPEG;
+
+       LOG_ERROR("not supported mime[%s]", mime.c_str());
+       return MEDIA_FORMAT_MAX;
+}
+
+static media_format_h _makeMediaFormat(GstPad* pad)
+{
+       int ret;
+
+       RET_VAL_IF(!pad, NULL, "pad is NULL");
+
+       media_format_h format;
+       RET_VAL_IF(media_format_create(&format) != MEDIA_FORMAT_ERROR_NONE, NULL,
+                       "failed to media_format_create()");
+
+       GstCaps* caps = gst_pad_get_current_caps(pad);
+       GstStructure* structure = gst_caps_get_structure(caps, 0);
+       std::string mime = gst_structure_get_name(structure);
+
+       media_format_mimetype_e mimetype = _getMediaFormatMimeType(mime);
+       LOG_INFO("media format mimetype[0x%x]", mimetype);
+
+       if (mimetype & MEDIA_FORMAT_VIDEO) {
+               gint width = 0;
+               gint height = 0;
+               gint fps_n = 0;
+               gint fps_d = 0;
+
+               ret = media_format_set_video_mime(format, mimetype);
+               if (ret != MEDIA_FORMAT_ERROR_NONE) {
+                       LOG_ERROR("failed to media_format_set_video_mime()");
+                       goto ERROR;
+               }
+
+               /* FIXME: We also need to get width/height in case of H26x with not this way. */
+               gst_structure_get_int(structure, "width", &width);
+               gst_structure_get_int(structure, "height", &height);
+               if (width > 0 && height > 0) {
+                       ret |= media_format_set_video_width(format, width);
+                       ret |= media_format_set_video_height(format, height);
+                       if (ret != MEDIA_FORMAT_ERROR_NONE) {
+                               LOG_ERROR("failed to media_format_set_video_width/height()");
+                               goto ERROR;
+                       }
+               }
+
+               gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d);
+               if (fps_n > 0 && fps_d > 0)
+                       media_format_set_video_frame_rate(format, fps_n / fps_d);
+       } else if (mimetype & MEDIA_FORMAT_AUDIO) {
+               gint channels = 0;
+               gint rate = 0;
+
+               ret = media_format_set_audio_mime(format, mimetype);
+               if (ret != MEDIA_FORMAT_ERROR_NONE) {
+                       LOG_ERROR("failed to media_format_set_audio_mime()");
+                       goto ERROR;
+               }
+
+               gst_structure_get_int(structure, "channels", &channels);
+               gst_structure_get_int(structure, "rate", &rate);
+               if (channels > 0 && rate > 0) {
+                       ret |= media_format_set_audio_channel(format, channels);
+                       ret |= media_format_set_audio_samplerate(format, rate);
+                       if (ret != MEDIA_FORMAT_ERROR_NONE) {
+                               LOG_ERROR("failed to media_format_set_audio_channel/samplerate()");
+                               goto ERROR;
+                       }
+               }
+       } else {
+               LOG_ERROR("Error on _getMediaFormatMimeType");
+       }
+
+       gst_caps_unref(caps);
+       return format;
+
+ERROR:
+       gst_caps_unref(caps);
+       media_format_unref(format);
+       return NULL;
+}
+
+static void _setCodecDataIfExist(media_packet_h packet, GstPad* pad)
+{
+       GstCaps* caps = gst_pad_get_current_caps(pad);
+
+       const GValue* codec_data_value = gst_structure_get_value(gst_caps_get_structure(caps, 0), "codec_data");
+       if (codec_data_value) {
+               GstBuffer* codec_data = gst_value_get_buffer(codec_data_value);
+
+               GstMapInfo buff_info;
+               gst_buffer_map(codec_data, &buff_info, GST_MAP_READ);
+
+               int ret = media_packet_set_codec_data(packet, buff_info.data, buff_info.size);
+               if (ret != MEDIA_PACKET_ERROR_NONE)
+                       LOG_ERROR("failed to media_packet_set_codec_data()");
+               else
+                       LOG_DEBUG("codec_data[%p, size:%" G_GSIZE_FORMAT "] is set to the media packet[%p]",
+                                       buff_info.data, buff_info.size, packet);
+
+               gst_buffer_unmap(codec_data, &buff_info);
+       }
+
+       gst_caps_unref(caps);
+}
+
+/* userData should not be handle */
+static int __mediaPacketFinalizeCallBack(media_packet_h packet, int error_code, void* userData)
+{
+       GstBuffer* buffer = NULL;
+       if (media_packet_get_extra(packet, (void**)&buffer) == MEDIA_PACKET_ERROR_NONE)
+               gst_buffer_unref(buffer);
+
+       LOG_VERBOSE("this[%p], packet[%p], buffer[%p]", userData, packet, buffer);
+
+       return MEDIA_PACKET_FINALIZE;
+}
+
+media_packet_h MediaTransporterReceiver::_makeMediaPacket(GstBuffer* buffer, GstPad* pad, media_format_h* format)
+{
+       int ret;
+       media_packet_h packet = NULL;
+       GstMapInfo info = GST_MAP_INFO_INIT;
+
+       RET_VAL_IF(!buffer, NULL, "buffer is NULL");
+       RET_VAL_IF(!pad, NULL, "pad is NULL");
+
+       if (!(*format))
+               *format = _makeMediaFormat(pad);
+
+       gst_buffer_ref(buffer);
+
+       if (!gst_buffer_map(buffer, &info, GST_MAP_READ)) {
+               LOG_ERROR("failed to gst_buffer_map()");
+               goto ERROR;
+       }
+
+       ret = media_packet_create_from_external_memory(*format, info.data, gst_buffer_get_size(buffer), __mediaPacketFinalizeCallBack, nullptr, &packet);
+       gst_buffer_unmap(buffer, &info);
+       if (ret != MEDIA_PACKET_ERROR_NONE) {
+               LOG_ERROR("failed to media_packet_create_from_external_memory()");
+               goto ERROR;
+       }
+
+       ret |= media_packet_set_pts(packet, GST_BUFFER_PTS(buffer));
+       ret |= media_packet_set_dts(packet, GST_BUFFER_DTS(buffer));
+       ret |= media_packet_set_duration(packet, GST_BUFFER_DURATION(buffer));
+
+       /* FIXME: We put the gstbuffer to extra field of media packet. It does not guarantee the validity of the gstbuffer
+        * after destroying this media packet. Currently, the gstbuffer must be used before destroying the packet. */
+       ret |= media_packet_set_extra(packet, (void*)buffer);
+       if (ret != MEDIA_PACKET_ERROR_NONE) {
+               LOG_ERROR("failed to media_packet_set_*()");
+               goto ERROR;
+       }
+
+       if (!GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT)) {
+               /* key frame */
+               _setCodecDataIfExist(packet, pad);
+               // if (ret != MTPR_ERROR_NONE)
+               //      goto ERROR;
+       }
+
+       LOG_VERBOSE("packet[%p], buffer[%p, pts:%" G_GUINT64_FORMAT ", dts:%" G_GUINT64_FORMAT ", duration:%" G_GUINT64_FORMAT "]",
+               packet, buffer, GST_BUFFER_PTS(buffer), GST_BUFFER_DTS(buffer), GST_BUFFER_DURATION(buffer));
+
+       return packet;
+
+ERROR:
+       if (packet)
+               media_packet_destroy(packet);
+       gst_buffer_unref(buffer);
+       media_format_unref(*format);
+       *format = NULL;
+
+       return NULL;
+}
+
+static unsigned int _get_id_from_name(std::string name)
+{
+       auto pos = name.rfind("_");
+       return (strtoul(name.substr(pos + 1).c_str(), NULL, 10) + 1);
+}
+
+void MediaTransporterReceiver::_streamAddedCallback(GstPad* pad, gpointer data)
+{
+       mtprMediaType type;
+       auto mtprReceiver = static_cast<MediaTransporterReceiver*>(data);
+       RET_IF(!mtprReceiver, "mtprReceiver is NULL");
+
+       auto id = _get_id_from_name(GST_PAD_NAME(pad));
+
+       std::string mediaType = gst::_getMimeTypeFromPad(pad);
+       if (mediaType.find("audio") != std::string::npos) {
+               type = MTPR_MEDIA_TYPE_AUDIO;
+       } else if (mediaType.find("video") != std::string::npos) {
+               type = MTPR_MEDIA_TYPE_VIDEO;
+       } else {
+               LOG_INFO("not supported track %s", mediaType.c_str());
+               return;
+       }
+
+       LOG_INFO("pad [%p][%s:%s], type %s, id %u", pad, GST_DEBUG_PAD_NAME(pad), mediaType.c_str(), id);
+
+       if (mtprReceiver->_trackAddedCallback)
+               mtprReceiver->_trackAddedCallback->invoke(VariantData{type}, VariantData{id});
+}
+
+void MediaTransporterReceiver::_noMoreStreamCallback(gpointer data)
+{
+       auto mtprReceiver = static_cast<MediaTransporterReceiver*>(data);
+       RET_IF(!mtprReceiver, "mtprReceiver is NULL");
+
+       if (mtprReceiver->_noMoreTrackCallback)
+               mtprReceiver->_noMoreTrackCallback->invoke();
+}
+
+/* handoff signal handler */
+void MediaTransporterReceiver::_encodedAudioStreamCallback(GstElement* object, GstBuffer* buffer, GstPad* pad, gpointer data)
+{
+       auto mtprReceiver = static_cast<MediaTransporterReceiver*>(data);
+       RET_IF(!mtprReceiver, "mtprReceiver is NULL");
+
+       LOG_VERBOSE("object[%p] buffer[%p] pad[%p]", object, buffer, pad);
+
+       // FIXME : extract common method for callback invoke
+
+       auto id = _get_id_from_name(GST_ELEMENT_NAME(object));
+       media_packet_h packet = _makeMediaPacket(buffer, pad, &mtprReceiver->_audioCallback.mediaFormat);
+       RET_IF(!packet, "packet is NULL");
+
+       if (mtprReceiver->_audioCallback._callback)
+               mtprReceiver->_audioCallback._callback->invoke(VariantData{id}, VariantData{packet});
+}
+
+void MediaTransporterReceiver::_encodedVideoStreamCallback(GstElement* object, GstBuffer* buffer, GstPad* pad, gpointer data)
+{
+       auto mtprReceiver = static_cast<MediaTransporterReceiver*>(data);
+       RET_IF(!mtprReceiver, "mtprReceiver is NULL");
+
+       LOG_VERBOSE("object[%p] buffer[%p] pad[%p]", object, buffer, pad);
+
+       auto id = _get_id_from_name(GST_ELEMENT_NAME(object));
+       media_packet_h packet = _makeMediaPacket(buffer, pad, &mtprReceiver->_videoCallback.mediaFormat);
+       RET_IF(!packet, "packet is NULL");
+
+       if (mtprReceiver->_videoCallback._callback)
+               mtprReceiver->_videoCallback._callback->invoke(VariantData{id}, VariantData{packet});
+}
+
+void MediaTransporterReceiver::_buildForwardingSink(gst::GstElements& elements, GstPad* pad, GCallback callback)
+{
+       GstElement* capsfilter = gst::_createElement(gst::DEFAULT_ELEMENT_CAPSFILTER);
+       GstCaps* sink_caps = gst::_makeCapsForCapsfilter(pad);
+       if (sink_caps) {
+               gst::_printCaps(sink_caps, "capsfilter");
+               g_object_set(G_OBJECT(capsfilter), "caps", sink_caps, NULL);
+               gst_caps_unref(sink_caps);
+       }
+       elements.push_back(capsfilter);
+
+       auto id = _get_id_from_name(GST_PAD_NAME(pad));
+       std::string name = "fakesink_" + std::to_string(id);
+       LOG_INFO("pad %s:%s, sink name = %s", GST_DEBUG_PAD_NAME(pad), name.c_str());
+
+       GstElement* fakesink = gst::_createElement(gst::DEFAULT_ELEMENT_FAKESINK, name);
+       g_object_set(G_OBJECT(fakesink), "signal-handoffs", TRUE, NULL);
+       elements.push_back(fakesink);
+
+       gst::_connectAndAppendSignal(&_gst.signals, G_OBJECT(fakesink), "handoff", callback, this);
+}
+
+// FIXME : exception handling
+int MediaTransporterReceiver::_buildForwardingElements(GstElement* demux, GstPad* pad, GCallback callback)
+{
+       gst::GstElements elements;
+
+       RET_VAL_IF(!demux, MTPR_ERROR_INVALID_PARAMETER, "demux element is NULL");
+       RET_VAL_IF(!pad, MTPR_ERROR_INVALID_PARAMETER, "pad is NULL");
+
+       GstElement* queue = gst::_createElement(gst::DEFAULT_ELEMENT_QUEUE);
+       /* FIXME: need to check queue size */
+       g_object_set(G_OBJECT(queue),
+                               "max-size-buffers", DEFAULT_QUEUE_MAX_SIZE_BUFFERS,
+                               "max-size-bytes", DEFAULT_QUEUE_MAX_SIZE_BYTES,
+                               "max-size-time", DEFAULT_QUEUE_MAX_SIZE_TIME,
+                               NULL);
+       elements.push_back(queue);
+
+       GstElement* parser = gst::_createElementFromRegistry("Parser", gst_pad_get_current_caps(pad), NULL,
+                                                                                                               MediaTransporterIni::get().general().gstExcludedElements);
+       if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(parser)), "config-interval")) {
+               g_object_set(G_OBJECT(parser), "config-interval", -1, NULL);
+               LOG_DEBUG("[%s] set config-interval -1", GST_ELEMENT_NAME(parser) );
+       }
+       elements.push_back(parser);
+
+       _buildForwardingSink(elements, pad, callback);
+
+       gst::_addElementsToBin(GST_BIN(_gst.pipeline), elements);
+
+       if (!gst_element_link_pads(GST_ELEMENT(demux),
+                                                       GST_PAD_NAME(pad),
+                                                       GST_ELEMENT(queue),
+                                                       "sink")) {
+               LOG_ERROR("failed to link demuxer and queue");
+       }
+
+       gst::_linkElements(elements);
+       gst::_syncElementsStateWithParent(elements);
+
+       return MTPR_ERROR_NONE;
+}
+
+void MediaTransporterReceiver::_buildAudioRenderingSink(gst::GstElements& elements)
+{
+       if (_streamInfo.empty()) {
+               LOG_WARNING("Not set streaminfo, link fakesink");
+               elements.push_back(gst::_createElement(gst::DEFAULT_ELEMENT_FAKESINK));
+               return;
+       }
+
+       GstElement* converter = nullptr;
+       GstElement* resample = nullptr;
+       GstElement* sink = nullptr;
+
+       try {
+               // display RM acquire
+               converter = gst::_createElement(gst::DEFAULT_ELEMENT_AUDIOCONVERT);
+               elements.push_back(converter);
+
+               resample = gst::_createElement(gst::DEFAULT_ELEMENT_AUDIORESAMPLE);
+               elements.push_back(resample);
+
+               std::string audioSinkName = MediaTransporterIni::get().renderingSink().audioSinkElement;
+               sink = gst::_createElement(audioSinkName);
+               gst::_applyStreamInfo(sink, _streamInfo);
+               elements.push_back(sink);
+       } catch (const MediaTransporterException& e) {
+               if (converter) {
+                       gst::_removeElement(elements, converter);
+                       gst_object_unref(converter);
+               }
+               if (resample) {
+                       gst::_removeElement(elements, resample);
+                       gst_object_unref(resample);
+               }
+               if (sink) {
+                       gst::_removeElement(elements, sink);
+                       gst_object_unref(sink);
+               }
+               throw;
+       }
+}
+
+void MediaTransporterReceiver::_buildVideoRenderingSink(gst::GstElements& elements)
+{
+       if (!_display) {
+               LOG_WARNING("Not set display, link fakesink");
+               elements.push_back(gst::_createElement(gst::DEFAULT_ELEMENT_FAKESINK));
+               return;
+       }
+
+       GstElement* converter = nullptr;
+       GstElement* sink = nullptr;
+
+       try {
+               // display RM acquire
+               _resourceManager->acquire(MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_OVERLAY);
+               converter = gst::_createElement(gst::DEFAULT_ELEMENT_VIDEOCONVERT);
+               elements.push_back(converter);
+               sink = _display->videoSink();
+               elements.push_back(sink);
+       } catch (const MediaTransporterException& e) {
+               if (converter) {
+                       gst::_removeElement(elements, converter);
+                       gst_object_unref(converter);
+               }
+               if (sink) {
+                       gst::_removeElement(elements, sink);
+                       gst_object_unref(sink);
+               }
+               throw;
+       }
+}
+
+int MediaTransporterReceiver::_buildAudioRenderingElements(GstElement* demux, GstPad* pad)
+{
+       gst::GstElements elements;
+
+       RET_VAL_IF(!demux, MTPR_ERROR_INVALID_PARAMETER, "demux element is NULL");
+       RET_VAL_IF(!pad, MTPR_ERROR_INVALID_PARAMETER, "pad is NULL");
+
+       GstElement* queue = gst::_createElement(gst::DEFAULT_ELEMENT_QUEUE);
+       /* FIXME: need to check queue size */
+       g_object_set(G_OBJECT(queue),
+                               "max-size-buffers", DEFAULT_QUEUE_MAX_SIZE_BUFFERS,
+                               "max-size-bytes", DEFAULT_QUEUE_MAX_SIZE_BYTES,
+                               "max-size-time", DEFAULT_QUEUE_MAX_SIZE_TIME,
+                               NULL);
+       elements.push_back(queue);
+
+       GstElement* parser = gst::_createElementFromRegistry("Parser", gst_pad_get_current_caps(pad), NULL,
+                                                                                                               MediaTransporterIni::get().general().gstExcludedElements);
+       GstPad* srcPad;
+       if (parser) {
+               srcPad = gst_element_get_static_pad(parser, "src");
+               elements.push_back(parser);
+       } else {
+               srcPad = gst_element_get_static_pad(queue, "src");
+       }
+
+       GstCaps* caps = gst_pad_has_current_caps(srcPad) ? gst_pad_get_current_caps(srcPad)
+                       : gst_pad_query_caps(srcPad, NULL);
+       GstElement* decoder = gst::_createElementFromRegistry("Codec/Decoder/Audio", caps, NULL,
+                                                                                                               MediaTransporterIni::get().general().gstExcludedElements);
+       elements.push_back(decoder);
+       g_object_unref(srcPad);
+
+       _buildAudioRenderingSink(elements);
+
+       gst::_addElementsToBin(GST_BIN(_gst.pipeline), elements);
+
+       if (!gst_element_link_pads(GST_ELEMENT(demux),
+                                                       GST_PAD_NAME(pad),
+                                                       GST_ELEMENT(queue),
+                                                       "sink")) {
+               LOG_ERROR("failed to link demuxer and queue");
+       }
+
+       gst::_linkElements(elements);
+       gst::_syncElementsStateWithParent(elements);
+
+       return MTPR_ERROR_NONE;
+}
+
+int MediaTransporterReceiver::_buildVideoRenderingElements(GstElement* demux, GstPad* pad)
+{
+       gst::GstElements elements;
+
+       RET_VAL_IF(!demux, MTPR_ERROR_INVALID_PARAMETER, "demux element is NULL");
+       RET_VAL_IF(!pad, MTPR_ERROR_INVALID_PARAMETER, "pad is NULL");
+
+       GstElement* queue = gst::_createElement(gst::DEFAULT_ELEMENT_QUEUE);
+       /* FIXME: need to check queue size */
+       g_object_set(G_OBJECT(queue),
+                               "max-size-buffers", DEFAULT_QUEUE_MAX_SIZE_BUFFERS,
+                               "max-size-bytes", DEFAULT_QUEUE_MAX_SIZE_BYTES,
+                               "max-size-time", DEFAULT_QUEUE_MAX_SIZE_TIME,
+                               NULL);
+       elements.push_back(queue);
+
+       GstElement* parser = gst::_createElementFromRegistry("Parser", gst_pad_get_current_caps(pad), NULL,
+                                                                                                               MediaTransporterIni::get().general().gstExcludedElements);
+       GstPad* srcPad;
+       if (parser) {
+               if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(parser)), "config-interval")) {
+                       g_object_set(G_OBJECT(parser), "config-interval", -1, NULL);
+                       LOG_DEBUG("[%s] set config-interval -1", GST_ELEMENT_NAME(parser) );
+               }
+               srcPad = gst_element_get_static_pad(parser, "src");
+               elements.push_back(parser);
+       } else {
+               srcPad = gst_element_get_static_pad(queue, "src");
+       }
+
+       GstCaps* caps = gst_pad_has_current_caps(srcPad) ? gst_pad_get_current_caps(srcPad)
+                       : gst_pad_query_caps(srcPad, NULL);
+       GstElement* decoder = gst::_createElementFromRegistry("Codec/Decoder/Video", caps, NULL,
+                                                                                                               MediaTransporterIni::get().general().gstExcludedElements);
+       elements.push_back(decoder);
+       g_object_unref(srcPad);
+
+       _buildVideoRenderingSink(elements);
+
+       gst::_addElementsToBin(GST_BIN(_gst.pipeline), elements);
+
+       if (!gst_element_link_pads(GST_ELEMENT(demux),
+                                                       GST_PAD_NAME(pad),
+                                                       GST_ELEMENT(queue),
+                                                       "sink")) {
+               LOG_ERROR("failed to link demuxer and queue");
+       }
+
+       gst::_linkElements(elements);
+       gst::_syncElementsStateWithParent(elements);
+
+       return MTPR_ERROR_NONE;
+}
+
+void MediaTransporterReceiver::setDisplay(std::shared_ptr<MediaTransporterDisplay> display)
+{
+       _display = display;
+}
+
+void MediaTransporterReceiver::setSoundStreamInfo(sound_stream_info_h streamInfo)
+{
+       char *stream_type;
+       int stream_index;
+       // bool available = false;
+
+       RET_IF(!streamInfo, "streamInfo is NULL");
+
+       sound_manager_get_type_from_stream_information(streamInfo, &stream_type);
+       sound_manager_get_index_from_stream_information(streamInfo, &stream_index);
+
+       /* FIXEME: need to add NATIVE_API_MEDIATRANSPORTER in sound-manager */
+       // ret = sound_manager_is_available_stream_information(stream_info, NATIVE_API_MEDIATRANSPORTER, &available);
+       // if (ret != SOUND_MANAGER_ERROR_NONE) {
+       //      LOG_ERROR("failed to sound_manager_is_available_stream_information()");
+       //      return MTPR_ERROR_INVALID_OPERATION;
+       // }
+
+       // if (!available) {
+       //      LOG_ERROR("this stream info[%p, type:%s, index:%d] is not allowed to this framework", stream_info, stream_type, stream_index);
+       //      return MTPR_ERROR_INVALID_PARAMETER;
+       // }
+
+       RET_IF(!stream_type, "stream_type is NULL");
+
+       LOG_INFO("stream_info[%p, type:%s, index:%d",
+               streamInfo, stream_type, stream_index);
+
+       std::ostringstream stringStream;
+       stringStream << "props," << PA_PROP_MEDIA_ROLE << "=" <<
+               stream_type << ", " << PA_PROP_MEDIA_PARENT_ID << "=" << stream_index;
+
+       _streamInfo = stringStream.str();
+}
diff --git a/src/MediaTransporterReceiverRist.cpp b/src/MediaTransporterReceiverRist.cpp
new file mode 100644 (file)
index 0000000..5282ba0
--- /dev/null
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaTransporterBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterReceiverRist.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterUtil.h"
+#include <cassert>
+
+using namespace tizen_media_transporter;
+
+static bool _isSupportedMediaType(std::string mediaType)
+{
+       if ((mediaType.find("audio") == std::string::npos) &&
+                       (mediaType.find("video") == std::string::npos)) {
+               LOG_ERROR("not supported media type [%s]", mediaType.c_str());
+               return false;
+       }
+
+       return true;
+}
+
+static bool _isAudioMediaType(std::string mediaType)
+{
+       return mediaType.find("audio") != std::string::npos;
+}
+
+void MediaTransporterReceiverRist::_demuxNoMorePadsCallback(GstElement *demux, gpointer userData)
+{
+       MediaTransporterReceiverRist* rist = static_cast<MediaTransporterReceiverRist*>(userData);
+       std::string dotName = std::string { GST_ELEMENT_NAME(rist->_gst.pipeline) } + ".complete";
+       gst::_generateDot(rist->_gst.pipeline, dotName);
+
+       _noMoreStreamCallback(userData);
+}
+
+void MediaTransporterReceiverRist::_demuxPadAddedCallback(GstElement *demux, GstPad *new_pad, gpointer userData)
+{
+       if (GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC)
+               return;
+
+       std::string mediaType = gst::_getMimeTypeFromPad(new_pad);
+
+       if (!_isSupportedMediaType(mediaType))
+               return;
+
+       LOG_INFO("new_pad[%s] media_type[%s]", GST_PAD_NAME(new_pad), mediaType.c_str());
+
+       auto rist = static_cast<MediaTransporterReceiverRist*>(userData);
+       assert(rist);
+       if (_isAudioMediaType(mediaType)) {
+               if (rist->_audioCallback._callback)
+                       rist->_buildForwardingElements(demux, new_pad, G_CALLBACK(_encodedAudioStreamCallback));
+               else
+                       rist->_buildAudioRenderingElements(demux, new_pad);
+       } else {
+               if (rist->_videoCallback._callback)
+                       rist->_buildForwardingElements(demux, new_pad, G_CALLBACK(_encodedVideoStreamCallback));
+               else
+                       rist->_buildVideoRenderingElements(demux, new_pad);
+       }
+
+       _streamAddedCallback(new_pad, userData);
+}
+
+ResourceSet MediaTransporterReceiverRist::buildPipeline()
+{
+       GstElement* src = NULL;
+       GstElement* rtpmp2tdepay = NULL;
+       GstElement* queue = NULL;
+       GstElement* tsdemux = NULL;
+
+       ResourceSet allResourceRequired;
+
+       MTPR_FENTER();
+
+       /* create mux to sink */
+       try {
+               src = gst::_createElement(gst::DEFAULT_ELEMENT_RISTSRC);
+               g_object_set(G_OBJECT(src), "bonding-addresses", _receiverAddress.c_str(),
+                               "receiver-buffer", 0, NULL);
+
+               rtpmp2tdepay = gst::_createElement("rtpmp2tdepay");
+
+               queue = gst::_createElement(gst::DEFAULT_ELEMENT_QUEUE);
+
+               tsdemux = gst::_createElement(gst::DEFAULT_ELEMENT_TSDEMUX);
+               g_object_set(G_OBJECT(tsdemux), "latency", 0, NULL);
+
+               gst_bin_add_many(GST_BIN(_gst.pipeline), src, rtpmp2tdepay, queue, tsdemux, NULL);
+
+               if (!gst_element_link_many (src, rtpmp2tdepay, queue, tsdemux, NULL))
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to link elements");
+
+               gst::_connectAndAppendSignal(&_gst.signals, G_OBJECT(tsdemux), "pad-added", G_CALLBACK(_demuxPadAddedCallback), this);
+               gst::_connectAndAppendSignal(&_gst.signals, G_OBJECT(tsdemux), "no-more-pads", G_CALLBACK(_demuxNoMorePadsCallback), this);
+               LOG_INFO("linked mux and sink");
+
+               return allResourceRequired;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+
+               gst::_destroyElementFromParent(src);
+               gst::_destroyElementFromParent(rtpmp2tdepay);
+               gst::_destroyElementFromParent(queue);
+               gst::_destroyElementFromParent(tsdemux);
+
+               throw;
+       }
+}
+
+void MediaTransporterReceiverRist::startPipeline()
+{
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
+                                                       MediaTransporterIni::get().general().timeout);
+}
+
+void MediaTransporterReceiverRist::stopPipeline()
+{
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_NULL,
+                                                       MediaTransporterIni::get().general().timeout);
+}
+
+void MediaTransporterReceiverRist::setReceiverAddress(std::string address)
+{
+       _receiverAddress = address;
+}
diff --git a/src/MediaTransporterReceiverSrt.cpp b/src/MediaTransporterReceiverSrt.cpp
new file mode 100644 (file)
index 0000000..9d3bdfb
--- /dev/null
@@ -0,0 +1,139 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaTransporterBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterReceiverSrt.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterUtil.h"
+#include <cassert>
+
+using namespace tizen_media_transporter;
+
+static bool _isSupportedMediaType(std::string mediaType)
+{
+       if ((mediaType.find("audio") == std::string::npos) &&
+                       (mediaType.find("video") == std::string::npos)) {
+               LOG_ERROR("not supported media type [%s]", mediaType.c_str());
+               return false;
+       }
+
+       return true;
+}
+
+static bool _isAudioMediaType(std::string mediaType)
+{
+       return mediaType.find("audio") != std::string::npos;
+}
+
+void MediaTransporterReceiverSrt::_demuxNoMorePadsCallback(GstElement *demux, gpointer userData)
+{
+       MediaTransporterReceiverSrt* srt = static_cast<MediaTransporterReceiverSrt*>(userData);
+       std::string dotName = std::string { GST_ELEMENT_NAME(srt->_gst.pipeline) } + ".complete";
+       gst::_generateDot(srt->_gst.pipeline, dotName);
+
+       _noMoreStreamCallback(userData);
+}
+
+void MediaTransporterReceiverSrt::_demuxPadAddedCallback(GstElement *demux, GstPad *new_pad, gpointer userData)
+{
+       if (GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC)
+               return;
+
+       std::string mediaType = gst::_getMimeTypeFromPad(new_pad);
+
+       if (!_isSupportedMediaType(mediaType))
+               return;
+
+       LOG_INFO("new_pad[%s] media_type[%s]", GST_PAD_NAME(new_pad), mediaType.c_str());
+
+       auto srt = static_cast<MediaTransporterReceiverSrt*>(userData);
+       assert(srt);
+       if (_isAudioMediaType(mediaType)) {
+               if (srt->_audioCallback._callback)
+                       srt->_buildForwardingElements(demux, new_pad, G_CALLBACK(_encodedAudioStreamCallback));
+               else
+                       srt->_buildAudioRenderingElements(demux, new_pad);
+       } else {
+               if (srt->_videoCallback._callback)
+                       srt->_buildForwardingElements(demux, new_pad, G_CALLBACK(_encodedVideoStreamCallback));
+               else
+                       srt->_buildVideoRenderingElements(demux, new_pad);
+       }
+
+       _streamAddedCallback(new_pad, userData);
+}
+
+ResourceSet MediaTransporterReceiverSrt::buildPipeline()
+{
+       GstElement* src = NULL;
+       GstElement* queue = NULL;
+       GstElement* tsdemux = NULL;
+
+       ResourceSet allResourceRequired;
+
+       MTPR_FENTER();
+
+       /* create mux to sink */
+       try {
+               src = gst::_createElement(gst::DEFAULT_ELEMENT_SRTSRC);
+               g_object_set(G_OBJECT(src), "uri", _senderAddress.c_str(), "latency", 10, NULL);
+
+               queue = gst::_createElement(gst::DEFAULT_ELEMENT_QUEUE);
+
+               tsdemux = gst::_createElement(gst::DEFAULT_ELEMENT_TSDEMUX);
+               g_object_set(G_OBJECT(tsdemux), "latency", 0, NULL);
+
+               gst_bin_add_many(GST_BIN(_gst.pipeline), src, queue, tsdemux, NULL);
+
+               if (!gst_element_link_many (src, queue, tsdemux, NULL))
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to link elements");
+
+               gst::_connectAndAppendSignal(&_gst.signals, G_OBJECT(tsdemux), "pad-added", G_CALLBACK(_demuxPadAddedCallback), this);
+               gst::_connectAndAppendSignal(&_gst.signals, G_OBJECT(tsdemux), "no-more-pads", G_CALLBACK(_demuxNoMorePadsCallback), this);
+               LOG_INFO("linked mux and sink");
+
+               return allResourceRequired;
+
+               // return MTPR_ERROR_NONE;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+
+               gst::_destroyElementFromParent(src);
+               gst::_destroyElementFromParent(queue);
+               gst::_destroyElementFromParent(tsdemux);
+
+               throw;
+       }
+}
+
+void MediaTransporterReceiverSrt::startPipeline()
+{
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
+                                                       MediaTransporterIni::get().general().timeout);
+}
+
+void MediaTransporterReceiverSrt::stopPipeline()
+{
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_NULL,
+                                                       MediaTransporterIni::get().general().timeout);
+}
+
+void MediaTransporterReceiverSrt::setSenderAddress(std::string address)
+{
+       _senderAddress = address;
+}
diff --git a/src/MediaTransporterResource.cpp b/src/MediaTransporterResource.cpp
new file mode 100644 (file)
index 0000000..ab1649a
--- /dev/null
@@ -0,0 +1,174 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+
+#include "MediaTransporterResource.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterBase.h"
+#include <algorithm>
+
+using namespace tizen_media_transporter;
+
+int MediaTransporterResource::_resourceReleaseCallback(mm_resource_manager_h mgr,
+                                                                                                       mm_resource_manager_res_h res,
+                                                                                                       void *user_data)
+{
+       auto resourceClass = static_cast<MediaTransporterResource*>(user_data);
+       RET_VAL_IF(!resourceClass, FALSE, "mtpr is NULL");
+
+       return resourceClass->resourceReleased(res);
+}
+
+bool MediaTransporterResource::resourceReleased(mm_resource_manager_res_h res)
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       bool interrupted = false;
+
+       _onReleaseCallback = true;
+
+       for (auto& [ type, handle ] : _resources) {
+               if (res == handle) {
+                       LOG_INFO("type[%d] resource was released by resource manager", static_cast<int>(type));
+                       _resources.erase(type);
+                       interrupted = true;
+               }
+       }
+
+       if (interrupted)
+               notify();
+
+       _onReleaseCallback = false;
+
+       return true;
+}
+
+int MediaTransporterResource::create()
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       if (mm_resource_manager_create(MM_RESOURCE_MANAGER_APP_CLASS_MEDIA,
+                               _resourceReleaseCallback, this, &_mgr) != MM_RESOURCE_MANAGER_ERROR_NONE) {
+               LOG_ERROR("failed to create resource manager");
+               return MTPR_ERROR_RESOURCE_FAILED;
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int MediaTransporterResource::acquireInternal(mm_resource_manager_res_type_e type)
+{
+       int ret = MM_RESOURCE_MANAGER_ERROR_NONE;
+       mm_resource_manager_res_h handle;
+
+       if (!_mgr)
+               return MTPR_ERROR_NONE;
+
+       if (_resources.find(type) != _resources.end()) {
+               LOG_ERROR("type[%d] resource was already acquired", type);
+               return MTPR_ERROR_RESOURCE_FAILED;
+       }
+
+       LOG_DEBUG("mark for acquire type[%d] resource", type);
+       ret = mm_resource_manager_mark_for_acquire(_mgr, type,
+                               MM_RESOURCE_MANAGER_RES_VOLUME_FULL, &handle);
+       if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+               LOG_ERROR("failed to mark resource for acquire, ret[0x%x]", ret);
+               return MTPR_ERROR_RESOURCE_FAILED;
+       }
+
+       LOG_DEBUG("commit type[%d] resource", type);
+       ret = mm_resource_manager_commit(_mgr);
+       if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+               LOG_ERROR("failed to commit of resource, ret([0x%x]", ret);
+               return MTPR_ERROR_RESOURCE_FAILED;
+       }
+
+       _resources[type] = handle;
+
+       return MTPR_ERROR_NONE;
+}
+
+int MediaTransporterResource::acquire(mm_resource_manager_res_type_e type)
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       return acquireInternal(type);
+}
+
+int MediaTransporterResource::acquire(ResourceSet resourceSet)
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       int ret = MTPR_ERROR_NONE;
+
+       for (auto& resource : resourceSet) {
+               if ((ret = acquireInternal(resource)) != MTPR_ERROR_NONE)
+                       return ret;
+       }
+
+       return ret;
+}
+
+int MediaTransporterResource::releaseAll()
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       int ret = MM_RESOURCE_MANAGER_ERROR_NONE;
+
+       if (!_mgr)
+               return MTPR_ERROR_NONE;
+
+       if (_onReleaseCallback) {
+               LOG_INFO("__resource_release_cb is calling, so skip");
+               return MTPR_ERROR_NONE;
+       }
+
+       ret = mm_resource_manager_mark_all_for_release(_mgr);
+       if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+               LOG_ERROR("failed to mark all for release, ret[0x%x]", ret);
+               return MTPR_ERROR_RESOURCE_FAILED;
+       }
+       ret = mm_resource_manager_commit(_mgr);
+       if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+               LOG_ERROR("failed to commit resource, ret[0x%x]", ret);
+               return MTPR_ERROR_RESOURCE_FAILED;
+       }
+       LOG_DEBUG("all resources were released by resource manager");
+
+       return MTPR_ERROR_NONE;
+}
+
+
+int MediaTransporterResource::destroy()
+{
+       std::lock_guard<std::mutex> mutex(_mutex);
+
+       if (!_mgr)
+               return MTPR_ERROR_NONE;
+
+       int ret = mm_resource_manager_destroy(_mgr);
+       if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+               LOG_ERROR("failed to destroy resource manager, ret[0x%x]", ret);
+               return MTPR_ERROR_RESOURCE_FAILED;
+       }
+
+       _mgr = nullptr;
+       LOG_DEBUG("destroyed resource manager");
+
+       return MTPR_ERROR_NONE;
+}
+
diff --git a/src/MediaTransporterSender.cpp b/src/MediaTransporterSender.cpp
new file mode 100644 (file)
index 0000000..ff79058
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include <map>
+#include "MediaTransporterBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterSender.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterUtil.h"
+
+using namespace tizen_media_transporter;
+
+MediaTransporterSender::~MediaTransporterSender()
+{
+}
+
+int MediaTransporterSender::addMediaSource(IMediaSourceBin* sourceBin)
+{
+       static int id = 0;
+       _mediaSources.insert(std::make_pair(id, std::unique_ptr<IMediaSourceBin>(sourceBin)));
+       LOG_INFO("media source id:%d has been added bin:%p", id, sourceBin);
+       return id++;
+}
+
+void MediaTransporterSender::removeMediaSource(int id)
+{
+       if (_mediaSources.erase(id) != 1)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid id to remove");
+
+       LOG_INFO("media source id:%d has been removed", id);
+}
+
+IMediaSourceBin* MediaTransporterSender::getMediaSource(int id)
+{
+       try {
+               return _mediaSources.at(id).get();
+       } catch (...) {
+               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid id to get Source");
+       }
+}
\ No newline at end of file
diff --git a/src/MediaTransporterSenderRist.cpp b/src/MediaTransporterSenderRist.cpp
new file mode 100644 (file)
index 0000000..490d1a1
--- /dev/null
@@ -0,0 +1,239 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaTransporterBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterSenderRist.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterUtil.h"
+#include "MediaTransporterParseIni.h"
+
+#define RTCP_BANDWIDTH_PARAM_MIN 0
+#define RTCP_BANDWIDTH_PARAM_MAX 0.05
+#define RTCP_INTERVAL_PARAM_MIN 0
+#define RTCP_INTERVAL_PARAM_MAX 100
+
+using namespace tizen_media_transporter;
+
+static GstPad* _getGhostPadFromBin(GstBin* bin)
+{
+       GstElement* queue = gst_bin_get_by_name(bin, "srcQueue");
+       if (!queue)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to get srcQueue");
+
+       GstPad* srcPad = nullptr;
+
+       if (!gst::_addNoTargetGhostpad(bin, &srcPad, true))
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to _addNoTargetGhostpad");
+
+       if (!gst::_setGhostpadTarget(srcPad, queue, true))
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to _setGhostpadTarget");
+
+       return srcPad;
+}
+
+ResourceSet MediaTransporterSenderRist::buildPipeline()
+{
+       GstElement* mux = NULL;
+       GstElement* sink = NULL;
+       GstElement* pay = NULL;
+       GstPad* sinkPad = NULL;
+
+       ResourceSet allResourceRequired;
+
+       /* create mux to sink */
+       try {
+               mux = gst::_createElement(gst::DEFAULT_ELEMENT_TSMUX);
+               g_object_set(G_OBJECT(mux), "alignment", 7, NULL);
+
+               sink = gst::_createElement(gst::DEFAULT_ELEMENT_RISTSINK);
+               g_object_set(G_OBJECT(sink), "bonding-addresses", _receiverAddress.c_str(), NULL);
+
+               if (_connectionParam.maxRtcpBandwidth > 0 && _connectionParam.maxRtcpBandwidth <= 0.05)
+                       g_object_set(G_OBJECT(sink), "max-rtcp-bandwidth", _connectionParam.maxRtcpBandwidth, NULL);
+
+               if (_connectionParam.minRtcpInterval != UINT_MAX && _connectionParam.minRtcpInterval <= 100)
+                       g_object_set(G_OBJECT(sink), "min-rtcp-interval", _connectionParam.minRtcpInterval, NULL);
+
+               if (_connectionParam.senderBuffer != UINT_MAX)
+                       g_object_set(G_OBJECT(sink), "sender-buffer", _connectionParam.senderBuffer, NULL);
+
+               SECURE_LOG_INFO("max-rtcp-bandwidth %f, min-rtcp-interval %u, sender-buffer %u",
+                               _connectionParam.maxRtcpBandwidth, _connectionParam.minRtcpInterval, _connectionParam.senderBuffer);
+
+               pay = gst::_createElement("rtpmp2tpay");
+
+               gst_bin_add_many(GST_BIN(_gst.pipeline), mux, pay, sink, NULL);
+               if (!gst_element_link_many(mux, pay, sink, NULL))
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to gst_element_link()");
+
+               LOG_ERROR("pp %s, ref %d",
+                       GST_ELEMENT_NAME(_gst.pipeline), GST_OBJECT_REFCOUNT_VALUE(_gst.pipeline));
+
+               /* link each element */
+               for (auto& [id, mediaSource] : _mediaSources) {
+                       auto[ type, bin, resourceRequired ] = mediaSource->generate();
+
+                       LOG_INFO("mediaSource(type:%d, bin:%p, resourceRequired:%zu) generated",
+                                       static_cast<int>(type), bin, resourceRequired.size());
+
+                       allResourceRequired.insert(resourceRequired.begin(), resourceRequired.end());
+
+                       gst_bin_add(GST_BIN(_gst.pipeline), GST_ELEMENT(bin));
+
+                       GstPad* srcPad = _getGhostPadFromBin(bin);
+                       if (gst_pad_is_linked(srcPad)) {
+                               LOG_ERROR("pad %s:%s is already linked", GST_DEBUG_PAD_NAME(srcPad));
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "already linked");
+                       }
+
+                       sinkPad = gst_element_request_pad_simple(mux, "sink_%d");
+                       if (!sinkPad)
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to get request pad");
+
+                       GstPadLinkReturn res = gst_pad_link(srcPad, sinkPad);
+                       if (GST_PAD_LINK_FAILED(res)) {
+                               LOG_ERROR("failed to link pads, [%s:%s] - [%s:%s]",
+                                       GST_DEBUG_PAD_NAME(srcPad), GST_DEBUG_PAD_NAME(sinkPad));
+
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to link pads");
+                       }
+                       LOG_INFO("pad is linked %s:%s - %s:%s", GST_DEBUG_PAD_NAME(srcPad), GST_DEBUG_PAD_NAME(sinkPad));
+               }
+
+               _ristSink = sink;
+               LOG_INFO("linked mux and sink");
+
+               return allResourceRequired;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+
+               if (sinkPad) {
+                       gst_element_release_request_pad(mux, sinkPad);
+                       g_object_unref(sinkPad);
+               }
+
+               gst::_destroyElementFromParent(mux);
+               gst::_destroyElementFromParent(pay);
+               gst::_destroyElementFromParent(sink);
+
+               throw;
+       }
+}
+
+void MediaTransporterSenderRist::startPipeline()
+{
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
+                                                       MediaTransporterIni::get().general().timeout);
+}
+
+void MediaTransporterSenderRist::stopPipeline()
+{
+       _ristSink = NULL;
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_NULL,
+                                                       MediaTransporterIni::get().general().timeout);
+}
+
+void MediaTransporterSenderRist::setConnection(std::string name, std::string string_val)
+{
+       SECURE_LOG_INFO("%s : %s", name.c_str(), string_val.c_str());
+
+       try {
+               if (name.compare(MTPR_CONNECTION_PARAM_RIST_BONDING_ADDRESS) == 0) {
+                       _connectionParam.bondingAddress = string_val;
+
+               } else if (name.compare(MTPR_CONNECTION_PARAM_RIST_MAX_RTCP_BANDWIDTH) == 0) {
+                       size_t size;
+                       double val = stod(string_val, &size);
+                       if ((string_val.size() != size) ||
+                               (val < RTCP_BANDWIDTH_PARAM_MIN) || (val > RTCP_BANDWIDTH_PARAM_MAX)) {
+                               LOG_ERROR("invalid rtcp bandwidth value %s", string_val.c_str());
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid rtcp bandwidth value");
+                       }
+                       _connectionParam.maxRtcpBandwidth = val;
+
+               } else if (name.compare(MTPR_CONNECTION_PARAM_RIST_MIN_RTCP_INTERVAL) == 0) {
+                       size_t size;
+                       int val = stoi(string_val, &size);
+                       if ((string_val.size() != size) ||
+                               (val < RTCP_INTERVAL_PARAM_MIN) || (val > RTCP_INTERVAL_PARAM_MAX)) {
+                               LOG_ERROR("invalid rtcp interval value %s", string_val.c_str());
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid rtcp interval value");
+                       }
+                       _connectionParam.minRtcpInterval = val;
+
+               } else if (name.compare(MTPR_CONNECTION_PARAM_RIST_SENDER_BUFFER) == 0) {
+                       size_t size;
+                       int val = stoi(string_val, &size);
+                       if (string_val.size() != size) {
+                               LOG_ERROR("invalid sender buffer value %s", string_val.c_str());
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid sender buffer value");
+                       }
+                       _connectionParam.senderBuffer = val;
+
+               } else {
+                       LOG_ERROR("invalid param name %s", name.c_str());
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid param name");
+               }
+       } catch (const std::exception& e) {
+               LOG_ERROR("error : %s, throw invalid param", e.what());
+               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid parameter");
+       }
+}
+
+void MediaTransporterSenderRist::setConnection(bundle* params)
+{
+       char* string_val = NULL;
+
+       if (bundle_get_str(params, MTPR_CONNECTION_PARAM_RIST_BONDING_ADDRESS, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE)
+               _connectionParam.bondingAddress = string_val;
+
+       if (bundle_get_str(params, MTPR_CONNECTION_PARAM_RIST_MAX_RTCP_BANDWIDTH, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE)
+               setConnection(MTPR_CONNECTION_PARAM_RIST_MAX_RTCP_BANDWIDTH, string_val);
+
+       if (bundle_get_str(params, MTPR_CONNECTION_PARAM_RIST_MIN_RTCP_INTERVAL, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE)
+               setConnection(MTPR_CONNECTION_PARAM_RIST_MIN_RTCP_INTERVAL, string_val);
+
+       if (bundle_get_str(params, MTPR_CONNECTION_PARAM_RIST_SENDER_BUFFER, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE)
+               setConnection(MTPR_CONNECTION_PARAM_RIST_SENDER_BUFFER, string_val);
+}
+
+void MediaTransporterSenderRist::getConnection(bundle* params)
+{
+       if (!_connectionParam.bondingAddress.empty())
+               bundle_add_str(params, MTPR_CONNECTION_PARAM_RIST_BONDING_ADDRESS, _connectionParam.bondingAddress.c_str());
+
+       if (_connectionParam.maxRtcpBandwidth > -1)
+               bundle_add_str(params,
+                       MTPR_CONNECTION_PARAM_RIST_MAX_RTCP_BANDWIDTH,
+                       std::to_string(_connectionParam.maxRtcpBandwidth).c_str());
+
+       if (_connectionParam.minRtcpInterval != UINT_MAX)
+               bundle_add_str(params,
+                       MTPR_CONNECTION_PARAM_RIST_MIN_RTCP_INTERVAL,
+                       std::to_string(_connectionParam.minRtcpInterval).c_str());
+
+       if (_connectionParam.senderBuffer != UINT_MAX)
+               bundle_add_str(params,
+                       MTPR_CONNECTION_PARAM_RIST_SENDER_BUFFER,
+                       std::to_string(_connectionParam.senderBuffer).c_str());
+}
+
+void MediaTransporterSenderRist::setReceiverAddress(std::string address)
+{
+       _receiverAddress = address;
+}
diff --git a/src/MediaTransporterSenderRtsp.cpp b/src/MediaTransporterSenderRtsp.cpp
new file mode 100644 (file)
index 0000000..eea9f5b
--- /dev/null
@@ -0,0 +1,317 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+#include <string>
+#include <gst/rtsp-server/rtsp-server.h>
+#include <gst/rtsp-server/rtsp-media-factory-mtpr.h>
+
+#include "MediaTransporterBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterSenderRtsp.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterUtil.h"
+
+using namespace tizen_media_transporter;
+
+#define ADDR_PORT_DELIM ":"
+#define ADDR_PATH_DELIM "/"
+#define ADDR_RTSP_PREFIX "rtsp://"
+
+static GstPad* _getGhostPadFromBin(GstBin* bin)
+{
+       GstElement* queue = gst_bin_get_by_name(bin, "srcQueue");
+       if (!queue)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to get srcQueue");
+
+       GstPad* srcPad = nullptr;
+
+       if (!gst::_addNoTargetGhostpad(bin, &srcPad, true))
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to _addNoTargetGhostpad");
+
+       if (!gst::_setGhostpadTarget(srcPad, queue, true))
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to _setGhostpadTarget");
+
+       return srcPad;
+}
+
+static GstRTSPFilterResult
+__clientFilter(GstRTSPServer *server, GstRTSPClient *client, gpointer userData)
+{
+  /* Simple filter that shuts down all clients. */
+  return GST_RTSP_FILTER_REMOVE;
+}
+
+ResourceSet MediaTransporterSenderRtsp::buildPipeline()
+{
+       GstElement* mux = NULL;
+       GstElement* pay = NULL;
+       GstPad* sinkPad = NULL;
+
+       ResourceSet allResourceRequired;
+
+       try {
+               /* create mux and pay */
+               mux = gst::_createElement(gst::DEFAULT_ELEMENT_TSMUX);
+               g_object_set(G_OBJECT(mux), "alignment", 7, NULL);
+
+               /* pay element name have to be "pay0" */
+               pay = gst::_createElement("rtpmp2tpay", "pay0");
+               gst_bin_add_many(GST_BIN(_gst.pipeline), mux, pay, NULL);
+               if (!gst_element_link(mux, pay)) {
+                       LOG_ERROR("failed to gst_element_link()");
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to gst_element_link()");
+               }
+
+               LOG_ERROR("pp %s, ref %d",
+                       GST_ELEMENT_NAME(_gst.pipeline), GST_OBJECT_REFCOUNT_VALUE(_gst.pipeline));
+
+               /* create sources and link elements */
+               for (auto& [id, mediaSource] : _mediaSources) {
+                       auto[ type, bin, resourceRequired ] = mediaSource->generate();
+
+                       LOG_INFO("mediaSource(type:%d, bin:%p, resourceRequired:%zu) generated",
+                                       static_cast<int>(type), bin, resourceRequired.size());
+
+                       allResourceRequired.insert(resourceRequired.begin(), resourceRequired.end());
+
+                       gst_bin_add(GST_BIN(_gst.pipeline), GST_ELEMENT(bin));
+
+                       GstPad* srcPad = _getGhostPadFromBin(bin);
+                       if (gst_pad_is_linked(srcPad)) {
+                               LOG_ERROR("pad %s:%s is already linked", GST_DEBUG_PAD_NAME(srcPad));
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "already linked");
+                       }
+
+                       sinkPad = gst_element_request_pad_simple(mux, "sink_%d");
+                       if (!sinkPad)
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to get request pad");
+
+                       GstPadLinkReturn res = gst_pad_link(srcPad, sinkPad);
+                       if (GST_PAD_LINK_FAILED(res)) {
+                               LOG_ERROR("failed to link pads, [%s:%s] - [%s:%s]",
+                                       GST_DEBUG_PAD_NAME(srcPad), GST_DEBUG_PAD_NAME(sinkPad));
+
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to get request pad");
+                       }
+                       LOG_INFO("pad is linked %s:%s - %s:%s", GST_DEBUG_PAD_NAME(srcPad), GST_DEBUG_PAD_NAME(sinkPad));
+               }
+
+               LOG_INFO("linked mux and pay");
+
+               return  allResourceRequired;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+
+               if (sinkPad) {
+                       gst_element_release_request_pad(mux, sinkPad);
+                       g_object_unref(sinkPad);
+               }
+
+               gst::_destroyElementFromParent(mux);
+               gst::_destroyElementFromParent(pay);
+
+               throw;
+       }
+}
+
+void MediaTransporterSenderRtsp::startPipeline()
+{
+       startRtspServer();
+}
+
+void MediaTransporterSenderRtsp::stopPipeline()
+{
+       stopRtspServer();
+}
+
+void MediaTransporterSenderRtsp::setSenderAddress(std::string address)
+{
+       _senderAddress = address; // rtsp server addr
+}
+
+static void __clientConnectedCb(GstRTSPServer* server, GstRTSPClient* client)
+{
+       LOG_DEBUG("client connected %p", client);
+}
+
+static void __mediaConstructedCb(GstRTSPMediaFactory* factory, GstRTSPMedia* media, gpointer userData)
+{
+       LOG_DEBUG("media %p is constructed and has %u streams", media, gst_rtsp_media_n_streams(media));
+}
+
+static void __onSsrcActive(GObject* session, GObject* source, GstRTSPMedia* media)
+{
+       GstStructure* stats;
+
+       LOG_DEBUG("source %p in session %p is active", source, session);
+
+       g_object_get(source, "stats", &stats, NULL);
+       if (stats) {
+               gchar* sstr;
+
+               sstr = gst_structure_to_string(stats);
+               LOG_DEBUG("structure: %s", sstr);
+               g_free(sstr);
+
+               gst_structure_free(stats);
+       }
+}
+
+static void __onSenderSsrcActive(GObject* session, GObject* source, GstRTSPMedia* media)
+{
+       GstStructure* stats;
+
+       LOG_DEBUG("source %p in session %p is active", source, session);
+
+       g_object_get(source, "stats", &stats, NULL);
+       if (stats) {
+               gchar* sstr;
+
+               sstr = gst_structure_to_string(stats);
+               LOG_DEBUG("Sender stats:\nstructure: %s", sstr);
+               g_free(sstr);
+
+               gst_structure_free(stats);
+       }
+}
+
+static void __mediaPreparedCb(GstRTSPMedia* media)
+{
+       guint i, n_streams;
+
+       n_streams = gst_rtsp_media_n_streams(media);
+
+       LOG_DEBUG("media %p is prepared and has %u streams", media, n_streams);
+
+       for (i = 0; i < n_streams; i++) {
+               GstRTSPStream *stream;
+               GObject *session;
+
+               stream = gst_rtsp_media_get_stream(media, i);
+               if (!stream)
+                       continue;
+
+               session = gst_rtsp_stream_get_rtpsession(stream);
+               LOG_DEBUG("watching session %p on stream %u", session, i);
+
+               g_signal_connect(session, "on-ssrc-active",
+                                               G_CALLBACK(__onSsrcActive), media);
+               g_signal_connect(session, "on-sender-ssrc-active",
+                                               G_CALLBACK(__onSenderSsrcActive), media);
+       }
+}
+
+static void __mediaConfigureCb(GstRTSPMediaFactory* factory, GstRTSPMedia* media)
+{
+       LOG_DEBUG("media %p is prepared and has %u streams", media, gst_rtsp_media_n_streams(media));
+       g_signal_connect(media, "prepared", G_CALLBACK(__mediaPreparedCb), factory);
+}
+
+void MediaTransporterSenderRtsp::startRtspServer()
+{
+       GstRTSPMountPoints *mounts;
+       GstRTSPMediaFactoryMTPR *factoryMtpr;
+       GstRTSPServer *server = NULL;
+       std::string rtspPrefix("rtsp://");
+       std::string address;
+       std::string ipAddr;
+       std::string portNum;
+       std::string mountPoint;
+
+       try {
+               server = gst_rtsp_server_new();
+               if (!server) {
+                       LOG_ERROR("Failed to create rtsp server");
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to create rtsp server");
+               }
+
+               /* addr format "rtsp://{ip address}:{port number}/{mount_point}" */
+               if (_senderAddress.find(ADDR_RTSP_PREFIX) == 0) {
+                       address = _senderAddress.substr(rtspPrefix.size());
+               } else {
+                       address = _senderAddress;
+               }
+
+               if ((address.find(ADDR_PORT_DELIM) == std::string::npos) &&
+                               (address.find(ADDR_PATH_DELIM) == std::string::npos)) {
+                       SECURE_LOG_ERROR("invalid address %s", address.c_str());
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "invalid sender address");
+               }
+
+               auto portPos = address.find(ADDR_PORT_DELIM);
+               ipAddr = address.substr(0, portPos);
+               auto mountPos = address.find(ADDR_PATH_DELIM);
+               portPos++;
+               portNum = address.substr(portPos, mountPos - portPos);
+               mountPoint = address.substr(mountPos);
+
+               gst_rtsp_server_set_address(GST_RTSP_SERVER(server), ipAddr.c_str());
+               gst_rtsp_server_set_service(GST_RTSP_SERVER(server), portNum.c_str());
+
+               g_signal_connect(server, "client-connected", G_CALLBACK(__clientConnectedCb), NULL);
+
+               mounts = gst_rtsp_server_get_mount_points(server);
+               factoryMtpr = gst_rtsp_media_factory_mtpr_new();
+               g_object_ref(_gst.pipeline);
+               gst_rtsp_media_factory_mtpr_set_custom_element(factoryMtpr, GST_ELEMENT(_gst.pipeline));
+               gst::_generateDot(_gst.pipeline, "rtsp-sender");
+
+               gst_rtsp_mount_points_add_factory(mounts, mountPoint.c_str(), GST_RTSP_MEDIA_FACTORY(factoryMtpr));
+               g_object_unref(mounts);
+
+               g_signal_connect(GST_RTSP_MEDIA_FACTORY(factoryMtpr), "media-constructed", G_CALLBACK(__mediaConstructedCb), NULL);
+               g_signal_connect(GST_RTSP_MEDIA_FACTORY(factoryMtpr), "media-configure", G_CALLBACK(__mediaConfigureCb), NULL);
+
+               if (gst_rtsp_server_attach(server, NULL) == 0) {
+                       LOG_ERROR("failed to attach server to default context");
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to attach server");
+               }
+
+               SECURE_LOG_DEBUG("stream ready at rtsp://%s:%s%s", ipAddr.c_str(), portNum.c_str(), mountPoint.c_str());
+
+               _rtspServer = static_cast<void*>(server);
+               _rtspMountPoint = mountPoint;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+               if (server) {
+                       mounts = gst_rtsp_server_get_mount_points(server);
+                       gst_rtsp_mount_points_remove_factory(mounts, mountPoint.c_str());
+                       g_object_unref(mounts);
+                       gst_object_unref(server);
+               }
+               throw;
+       }
+}
+
+void MediaTransporterSenderRtsp::stopRtspServer()
+{
+       guint ref_cnt = 0;
+       GstRTSPMountPoints* mounts = NULL;
+       GstRTSPServer* server = static_cast<GstRTSPServer*>(_rtspServer);
+
+       mounts = gst_rtsp_server_get_mount_points(server);
+       gst_rtsp_mount_points_remove_factory(mounts, _rtspMountPoint.c_str());
+       g_object_unref(mounts);
+
+       gst_rtsp_server_client_filter(server, __clientFilter, NULL);
+
+       ref_cnt = GST_OBJECT_REFCOUNT_VALUE(server);
+       for (guint i = 0; i < ref_cnt; i++)
+               gst_object_unref(server);
+
+       _rtspServer = NULL;
+       _rtspMountPoint.clear();
+}
diff --git a/src/MediaTransporterSenderSrt.cpp b/src/MediaTransporterSenderSrt.cpp
new file mode 100644 (file)
index 0000000..a3ebdb3
--- /dev/null
@@ -0,0 +1,271 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaTransporterBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterSenderSrt.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterUtil.h"
+#include "MediaTransporterParseIni.h"
+
+using namespace tizen_media_transporter;
+
+static GstPad* _getGhostPadFromBin(GstBin* bin)
+{
+       GstElement* queue = gst_bin_get_by_name(bin, "srcQueue");
+       if (!queue)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to get srcQueue");
+
+       GstPad* srcPad = nullptr;
+
+       if (!gst::_addNoTargetGhostpad(bin, &srcPad, true))
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to _addNoTargetGhostpad");
+
+       if (!gst::_setGhostpadTarget(srcPad, queue, true))
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to _setGhostpadTarget");
+
+       return srcPad;
+}
+
+static void __callerAddedCb(GstElement* element, gint socket, GSocketAddress* address, gpointer userData)
+{
+       LOG_INFO("socket %d, address %s", socket,
+               g_inet_address_to_string(g_inet_socket_address_get_address((GInetSocketAddress*)address)));
+}
+
+static void __callerRemovedCb(GstElement* element, gint socket, GSocketAddress* address, gpointer userData)
+{
+       LOG_INFO("socket %d, address %s", socket,
+               g_inet_address_to_string(g_inet_socket_address_get_address((GInetSocketAddress*)address)));
+}
+
+static void __callerRejectedCb(GstElement* element, GSocketAddress* peerAddress, const gchar* stream_id, gpointer userData)
+{
+       LOG_INFO("stream_id %s, peer_address %s", stream_id,
+               g_inet_address_to_string(g_inet_socket_address_get_address((GInetSocketAddress*)peerAddress)));
+}
+
+static void __callerConnectingCb(GstElement* element, GSocketAddress* peerAddress, const gchar* stream_id, gpointer userData)
+{
+       LOG_INFO("stream_id %s, peer_address %s", stream_id,
+               g_inet_address_to_string(g_inet_socket_address_get_address((GInetSocketAddress*)peerAddress)));
+}
+
+ResourceSet MediaTransporterSenderSrt::buildPipeline()
+{
+       GstElement* mux = NULL;
+       GstElement* sink = NULL;
+       GstPad* sinkPad = NULL;
+
+       ResourceSet allResourceRequired;
+
+       /* create mux to sink */
+       try {
+               mux = gst::_createElement(gst::DEFAULT_ELEMENT_TSMUX);
+               g_object_set(G_OBJECT(mux), "alignment", 7, NULL);
+
+               sink = gst::_createElement(gst::DEFAULT_ELEMENT_SRTSINK);
+               g_object_set(G_OBJECT(sink), "uri", _senderAddress.c_str(), "wait-for-connection", FALSE, "sync", FALSE, "latency", 10, NULL);
+
+               gst_bin_add_many(GST_BIN(_gst.pipeline), mux, sink, NULL);
+               if (!gst_element_link(mux, sink)) {
+                       LOG_ERROR("failed to gst_element_link()");
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to gst_element_link()");
+               }
+               if (_connectionParam.mode != MTPR_CONNECTION_SRT_MODE_NONE)
+                       g_object_set(G_OBJECT(sink),
+                               "mode", static_cast<int>(_connectionParam.mode), NULL);
+
+               if (!_connectionParam.pbKeyLen != MTPR_CONNECTION_SRT_NO_KEY)
+                       g_object_set(G_OBJECT(sink),
+                               "pbkeylen", static_cast<int>(_connectionParam.pbKeyLen), NULL);
+
+               if (!_connectionParam.passPhrase.empty())
+                       g_object_set(G_OBJECT(sink),
+                               "passphrase", _connectionParam.passPhrase.c_str(), NULL);
+
+               if (!_connectionParam.streamId.empty())
+                       g_object_set(G_OBJECT(sink),
+                               "streamid", _connectionParam.streamId.c_str(), NULL);
+
+               gst::_connectAndAppendSignal(&_gst.signals, G_OBJECT(sink), "caller-added", G_CALLBACK(__callerAddedCb), &_gst);
+               gst::_connectAndAppendSignal(&_gst.signals, G_OBJECT(sink), "caller-removed", G_CALLBACK(__callerRemovedCb), &_gst);
+               gst::_connectAndAppendSignal(&_gst.signals, G_OBJECT(sink), "caller-rejected", G_CALLBACK(__callerRejectedCb), &_gst);
+               gst::_connectAndAppendSignal(&_gst.signals, G_OBJECT(sink), "caller-connecting", G_CALLBACK(__callerConnectingCb), &_gst);
+
+               LOG_ERROR("pp %s, ref %d",
+                       GST_ELEMENT_NAME(_gst.pipeline), GST_OBJECT_REFCOUNT_VALUE(_gst.pipeline));
+
+               /* link each element */
+               for (auto& [id, mediaSource] : _mediaSources) {
+                       auto[ type, bin, resourceRequired ] = mediaSource->generate();
+
+                       LOG_INFO("mediaSource(type:%d, bin:%p, resourceRequired:%zu) generated",
+                                       static_cast<int>(type), bin, resourceRequired.size());
+
+                       allResourceRequired.insert(resourceRequired.begin(), resourceRequired.end());
+
+                       gst_bin_add(GST_BIN(_gst.pipeline), GST_ELEMENT(bin));
+
+                       GstPad* srcPad = _getGhostPadFromBin(bin);
+                       if (gst_pad_is_linked(srcPad)) {
+                               LOG_ERROR("pad %s:%s is already linked", GST_DEBUG_PAD_NAME(srcPad));
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "already linked");
+                       }
+
+                       sinkPad = gst_element_request_pad_simple(mux, "sink_%d");
+                       if (!sinkPad)
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to get request pad");
+
+                       GstPadLinkReturn res = gst_pad_link(srcPad, sinkPad);
+                       if (GST_PAD_LINK_FAILED(res)) {
+                               LOG_ERROR("failed to link pads, [%s:%s] - [%s:%s]",
+                                       GST_DEBUG_PAD_NAME(srcPad), GST_DEBUG_PAD_NAME(sinkPad));
+
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to get request pad");
+                       }
+                       LOG_INFO("pad is linked %s:%s - %s:%s", GST_DEBUG_PAD_NAME(srcPad), GST_DEBUG_PAD_NAME(sinkPad));
+               }
+
+               _srtSink = sink;
+               LOG_INFO("linked mux and sink");
+
+               return allResourceRequired;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+
+               if (sinkPad) {
+                       gst_element_release_request_pad(mux, sinkPad);
+                       g_object_unref(sinkPad);
+               }
+
+               gst::_destroyElementFromParent(mux);
+               gst::_destroyElementFromParent(sink);
+
+               throw;
+       }
+}
+
+void MediaTransporterSenderSrt::startPipeline()
+{
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
+                                                       MediaTransporterIni::get().general().timeout);
+}
+
+void MediaTransporterSenderSrt::stopPipeline()
+{
+       _srtSink = NULL;
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_NULL,
+                                                               MediaTransporterIni::get().general().timeout);
+}
+
+bool MediaTransporterSenderSrt::_isValidKeyLength(int val)
+{
+       switch(val)
+       {
+       case MTPR_CONNECTION_SRT_NO_KEY:
+       case MTPR_CONNECTION_SRT_KEY_LEN_16:
+       case MTPR_CONNECTION_SRT_KEY_LEN_24:
+       case MTPR_CONNECTION_SRT_KEY_LEN_32:
+               return true;
+       default:
+               return false;
+       }
+}
+
+void MediaTransporterSenderSrt::setConnection(std::string name, std::string string_val)
+{
+       SECURE_LOG_INFO("%s : %s", name.c_str(), string_val.c_str());
+
+       try {
+               if (name.compare(MTPR_CONNECTION_PARAM_SRT_MODE) == 0) {
+                       size_t size;
+                       int val = stoi(string_val, &size);
+                       if ((string_val.size() != size) ||
+                               (val < MTPR_CONNECTION_SRT_MODE_NONE) || (val > MTPR_CONNECTION_SRT_MODE_RENDEZVOUS)) {
+                               LOG_ERROR("invalid srt mode value %d", val);
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid srt mode value");
+                       }
+                       _connectionParam.mode = static_cast<connectionMode>(val);
+
+               } else if (name.compare(MTPR_CONNECTION_PARAM_SRT_STREAMID) == 0) {
+                       _connectionParam.streamId = string_val;
+
+               } else if (name.compare(MTPR_CONNECTION_PARAM_SRT_PASSPHRASE) == 0) {
+                       _connectionParam.passPhrase = string_val;
+
+               } else if (name.compare(MTPR_CONNECTION_PARAM_SRT_PBKEYLEN) == 0) {
+                       size_t size;
+                       int val = stoi(string_val, &size);
+                       if (string_val.size() != size || !_isValidKeyLength(val)) {
+                               LOG_ERROR("invalid pbkeylen value %d", val);
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid pbkeylen value");
+                       }
+                       _connectionParam.pbKeyLen = static_cast<connectionKeyLength>(val);
+
+               } else {
+                       LOG_ERROR("invalid param name %s", name.c_str());
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid param name");
+               }
+       } catch (const std::exception& e) {
+               LOG_ERROR("error : %s, throw invalid param", e.what());
+               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "invalid parameter");
+       }
+}
+
+void MediaTransporterSenderSrt::setConnection(bundle* params)
+{
+       char* string_val = NULL;
+
+       if (bundle_get_str(params, MTPR_CONNECTION_PARAM_SRT_MODE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               setConnection(MTPR_CONNECTION_PARAM_SRT_MODE, string_val);
+       }
+
+       if (bundle_get_str(params, MTPR_CONNECTION_PARAM_SRT_STREAMID, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _connectionParam.streamId = std::string {string_val};
+       }
+
+       if (bundle_get_str(params, MTPR_CONNECTION_PARAM_SRT_PASSPHRASE, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               _connectionParam.passPhrase = std::string {string_val};
+       }
+
+       if (bundle_get_str(params, MTPR_CONNECTION_PARAM_SRT_PBKEYLEN, &string_val) != BUNDLE_ERROR_KEY_NOT_AVAILABLE) {
+               setConnection(MTPR_CONNECTION_PARAM_SRT_PBKEYLEN, string_val);
+       }
+}
+
+void MediaTransporterSenderSrt::getConnection(bundle* params)
+{
+       bundle_add_str(params,
+               MTPR_CONNECTION_PARAM_SRT_MODE,
+               std::to_string(static_cast<int>(_connectionParam.mode)).c_str());
+
+       if (!_connectionParam.streamId.empty())
+               bundle_add_str(params, MTPR_CONNECTION_PARAM_SRT_STREAMID, _connectionParam.streamId.c_str());
+
+       if (!_connectionParam.passPhrase.empty())
+               bundle_add_str(params, MTPR_CONNECTION_PARAM_SRT_PASSPHRASE, _connectionParam.passPhrase.c_str());
+
+       bundle_add_str(params,
+               MTPR_CONNECTION_PARAM_SRT_PBKEYLEN,
+               std::to_string(static_cast<int>(_connectionParam.pbKeyLen)).c_str());
+}
+
+void MediaTransporterSenderSrt::setSenderAddress(std::string address)
+{
+       _senderAddress = address;
+}
diff --git a/src/MediaTransporterSenderToServerRtsp.cpp b/src/MediaTransporterSenderToServerRtsp.cpp
new file mode 100644 (file)
index 0000000..1c267d3
--- /dev/null
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaTransporterBase.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterSenderToServerRtsp.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterUtil.h"
+#include "MediaTransporterParseIni.h"
+
+using namespace tizen_media_transporter;
+
+static GstPad* _getGhostPadFromBin(GstBin* bin)
+{
+       GstElement* queue = gst_bin_get_by_name(bin, "srcQueue");
+       if (!queue)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to get srcQueue");
+
+       GstPad* srcPad = nullptr;
+
+       if (!gst::_addNoTargetGhostpad(bin, &srcPad, true))
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to _addNoTargetGhostpad");
+
+       if (!gst::_setGhostpadTarget(srcPad, queue, true))
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to _setGhostpadTarget");
+
+       return srcPad;
+}
+
+ResourceSet MediaTransporterSenderToServerRtsp::buildPipeline()
+{
+       GstElement *mux = NULL;
+       GstElement *sink = NULL;
+       GstPad* sinkPad = NULL;
+
+       ResourceSet allResourceRequired;
+
+       /* create mux to sink */
+       try {
+               mux = gst::_createElement(gst::DEFAULT_ELEMENT_TSMUX);
+               g_object_set(G_OBJECT(mux), "alignment", 7, NULL);
+
+               sink = gst::_createElement(gst::DEFAULT_ELEMENT_RTSPSINK);
+               g_object_set(G_OBJECT(sink), "location", _receiverAddress.c_str(), NULL);
+
+               gst_bin_add_many(GST_BIN(_gst.pipeline), mux, sink, NULL);
+               if (!gst_element_link(mux, sink)) {
+                       LOG_ERROR("failed to gst_element_link()");
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to gst_element_link()");
+               }
+
+               LOG_ERROR("pp %s, ref %d",
+                       GST_ELEMENT_NAME(_gst.pipeline), GST_OBJECT_REFCOUNT_VALUE(_gst.pipeline));
+
+               /* link each element */
+               for (auto& [id, mediaSource] : _mediaSources) {
+                       auto[ type, bin, resourceRequired ] = mediaSource->generate();
+
+                       LOG_INFO("mediaSource(type:%d, bin:%p, resourceRequired:%zu) generated",
+                                       static_cast<int>(type), bin, resourceRequired.size());
+
+                       gst_bin_add(GST_BIN(_gst.pipeline), GST_ELEMENT(bin));
+
+                       GstPad* srcPad = _getGhostPadFromBin(bin);
+                       if (gst_pad_is_linked(srcPad)) {
+                               LOG_ERROR("pad %s:%s is already linked", GST_DEBUG_PAD_NAME(srcPad));
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "already linked");
+                       }
+
+                       sinkPad = gst_element_request_pad_simple(mux, "sink_%d");
+                       if (!sinkPad)
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to get request pad");
+
+                       GstPadLinkReturn res = gst_pad_link(srcPad, sinkPad);
+                       if (GST_PAD_LINK_FAILED(res)) {
+                               LOG_ERROR("failed to link pads, [%s:%s] - [%s:%s]",
+                                       GST_DEBUG_PAD_NAME(srcPad), GST_DEBUG_PAD_NAME(sinkPad));
+
+                               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to get request pad");
+                       }
+                       LOG_INFO("pad is linked %s:%s - %s:%s", GST_DEBUG_PAD_NAME(srcPad), GST_DEBUG_PAD_NAME(sinkPad));
+               }
+
+               _rtspSink = sink;
+               LOG_INFO("linked mux and sink");
+
+               return allResourceRequired;
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+
+               if (sinkPad) {
+                       gst_element_release_request_pad(mux, sinkPad);
+                       g_object_unref(sinkPad);
+               }
+
+               gst::_destroyElementFromParent(mux);
+               gst::_destroyElementFromParent(sink);
+
+               throw;
+       }
+}
+
+void MediaTransporterSenderToServerRtsp::startPipeline()
+{
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
+                                                       MediaTransporterIni::get().general().timeout);
+}
+
+void MediaTransporterSenderToServerRtsp::stopPipeline()
+{
+       _rtspSink = NULL;
+       gst::_setPipelineState(_gst.pipeline, GST_STATE_NULL,
+                                                       MediaTransporterIni::get().general().timeout);
+}
+
+void MediaTransporterSenderToServerRtsp::setReceiverAddress(std::string address)
+{
+       _receiverAddress = address;
+}
diff --git a/src/MediaTransporterUtil.cpp b/src/MediaTransporterUtil.cpp
new file mode 100644 (file)
index 0000000..cc103df
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "MediaTransporterUtil.h"
+#include "MediaTransporterException.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <cynara-client.h>
+#include <sys/smack.h>
+#include <system_info.h>
+
+using namespace tizen_media_transporter;
+
+void util::throw_if_not_privileged(const std::string& privilege)
+{
+       // int ret = MTPR_ERROR_NONE;
+       cynara *cynara_h;
+       char *smack_label = nullptr;
+
+       try {
+               if (cynara_initialize(&cynara_h, NULL) != CYNARA_API_SUCCESS)
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION,
+                                                                                       "failed to cynara_initialize()");
+
+               if (smack_new_label_from_self(&smack_label) == -1)
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION,
+                                                                                       "failed to smack_new_label_from_self()");
+
+               char uid[10];
+               snprintf(uid, sizeof(uid), "%d", getuid());
+               //LOG_DEBUG("smack_label[%s] uid[%s]\n", smack_label, uid);
+
+               int cynara_ret = cynara_check(cynara_h, smack_label, "", uid, privilege.c_str());
+               if (cynara_ret != CYNARA_API_ACCESS_ALLOWED) {
+                       //LOG_ERROR("NOT ALLOWED, privilege[%s], cynara_ret[%d]", privilege, cynara_ret);
+                       throw MediaTransporterException(MTPR_ERROR_PERMISSION_DENIED,
+                                                                                       "[" + privilege + "] is NOT ALLOWED");
+               }
+
+               //LOG_INFO("ALLOWED, privilege[%s]", privilege);
+       } catch (const MediaTransporterException& e) {
+               free(smack_label);
+               if (cynara_h)
+                       cynara_finish(cynara_h);
+               throw;
+       }
+
+       free(smack_label);
+       cynara_finish(cynara_h);
+}
+
+void util::throw_if_feature_not_supported(const std::string& feature)
+{
+       bool supported = false;
+
+       if (system_info_get_platform_bool(feature.c_str(), &supported) != SYSTEM_INFO_ERROR_NONE)
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION,
+                                                                               "failed to system_info_get_platform_bool(" + feature + ")");
+
+       if (!supported)
+               throw MediaTransporterException(MTPR_ERROR_NOT_SUPPORTED,
+                                                                               "[" + feature + "] is not supported");
+
+       //LOG_INFO("feature[%s] is supported", feature);
+}
+
+void util::throw_if_network_features_not_supported()
+{
+       try {
+               throw_if_feature_not_supported(MTPR_FEATURE_NETWORK_WIFI);
+               throw_if_feature_not_supported(MTPR_FEATURE_NETWORK_TELE);
+               throw_if_feature_not_supported(MTPR_FEATURE_NETWORK_ETH);
+       } catch (const MediaTransporterException& e) {
+               throw MediaTransporterException(MTPR_ERROR_NOT_SUPPORTED, e.what());
+       }
+}
+
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..cba788f
--- /dev/null
@@ -0,0 +1,25 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+SET(fw_test "${fw_name}-test")
+
+INCLUDE_DIRECTORIES(../include)
+
+link_directories(${CMAKE_SOURCE_DIR}/../)
+
+INCLUDE(FindPkgConfig)
+    pkg_check_modules(${fw_test} REQUIRED glib-2.0 appcore-efl elementary esplusplayer)
+
+FOREACH(flag ${${fw_test}_CFLAGS})
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -pie -Wall -Werror")
+
+aux_source_directory(. sources)
+
+FOREACH(src ${sources})
+    GET_FILENAME_COMPONENT(src_name ${src} NAME_WE)
+    MESSAGE("${src_name}")
+    ADD_EXECUTABLE(${src_name} ${src})
+    TARGET_LINK_LIBRARIES(${src_name} capi-media-transporter ${${fw_test}_LDFLAGS})
+    INSTALL(TARGETS ${src_name} DESTINATION bin)
+ENDFOREACH()
diff --git a/test/mtpr_rtsp_test.c b/test/mtpr_rtsp_test.c
new file mode 100644 (file)
index 0000000..3e54417
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+* Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* 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.
+*/
+
+#include <gst/gst.h>
+#include <gst/rtsp-server/rtsp-server.h>
+
+#ifdef PACKAGE
+#undef PACKAGE
+#endif
+#define PACKAGE "mtpr_rtsp_test"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "MTPR_TEST"
+
+#define DEFAULT_RTSP_PORT "8554"
+
+int main(int argc, char *argv[])
+{
+       GMainLoop *loop;
+       GstRTSPServer *server;
+       GstRTSPMountPoints *mounts;
+       GstRTSPMediaFactory *factory;
+
+       if (argc < 2) {
+               g_print("Enter launch cmd : \"( decodebin name=depay0 ! videoconvert ! tizenwlsink )\"\n");
+               return 1;
+       }
+
+       gst_init(NULL, NULL);
+       loop = g_main_loop_new(NULL, FALSE);
+
+       server = gst_rtsp_server_new();
+       g_object_set(server, "service", DEFAULT_RTSP_PORT, NULL);
+
+       mounts = gst_rtsp_server_get_mount_points(server);
+
+       factory = gst_rtsp_media_factory_new();
+       gst_rtsp_media_factory_set_transport_mode(factory, GST_RTSP_TRANSPORT_MODE_RECORD);
+       gst_rtsp_media_factory_set_launch(factory, argv[1]);
+
+       gst_rtsp_mount_points_add_factory(mounts, "/test", factory);
+       g_object_unref(mounts);
+
+       gst_rtsp_server_attach(server, NULL);
+
+       g_print("stream ready at rtsp://127.0.0.1:%s/test\n", DEFAULT_RTSP_PORT);
+       g_main_loop_run(loop);
+
+       return 0;
+}
diff --git a/test/mtpr_test.c b/test/mtpr_test.c
new file mode 100644 (file)
index 0000000..a05a2e5
--- /dev/null
@@ -0,0 +1,1284 @@
+/*
+* Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+*
+* 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.
+*/
+
+#include <mtpr_internal.h>
+
+#include <appcore-efl.h>
+#include <Elementary.h>
+#include <glib.h>
+#include <bundle.h>
+#include <sound_manager_internal.h>
+
+#ifdef PACKAGE
+#undef PACKAGE
+#endif
+#define PACKAGE "mtpr_test"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "MTPR_TEST"
+
+#define MAX_STRING_LEN 512
+
+enum {
+       CURRENT_STATUS_MAINMENU,
+       CURRENT_STATUS_CONNECTION_TYPE,
+       CURRENT_STATUS_SENDER_ADDRESS,
+       CURRENT_STATUS_RECEIVER_ADDRESS,
+       CURRENT_STATUS_CONNECTION_SRT_MODE,
+       CURRENT_STATUS_CONNECTION_SRT_STREAMID,
+       CURRENT_STATUS_CONNECTION_SRT_PASSPHRASE,
+       CURRENT_STATUS_CONNECTION_SRT_PBKEYLEN,
+       CURRENT_STATUS_CONNECTION_RIST_BONDING_ADDRESS,
+       CURRENT_STATUS_CONNECTION_RIST_MAX_RTCP_BANDWIDTH,
+       CURRENT_STATUS_CONNECTION_RIST_MIN_RTCP_INTERVAL,
+       CURRENT_STATUS_CONNECTION_RIST_SENDER_BUFFER,
+       CURRENT_STATUS_CONNECTION_RTSP_SERVER_IP,
+       CURRENT_STATUS_CONNECTION_RTSP_SERVER_PORT,
+       CURRENT_STATUS_CONNECTION_RTSP_SERVER_MOUNT_POINT,
+       CURRENT_STATUS_ADD_MEDIA_SOURCE,
+       CURRENT_STATUS_SOURCE_VIDEO_WIDTH,
+       CURRENT_STATUS_SOURCE_VIDEO_HEIGHT,
+       CURRENT_STATUS_SOURCE_VIDEO_FRAMERATE,
+       CURRENT_STATUS_SOURCE_AUDIO_CHANNEL,
+       CURRENT_STATUS_SOURCE_AUDIO_RATE,
+       CURRENT_STATUS_SOURCE_AUDIO_FORMAT,
+       CURRENT_STATUS_ENCODING_BITRATE,
+       CURRENT_STATUS_REMOVE_MEDIA_SOURCE,
+       CURRENT_STATUS_SET_DISPLAY,
+       CURRENT_STATUS_SET_DISPLAY_MODE,
+       CURRENT_STATUS_SET_DISPLAY_VISIBLE,
+       CURRENT_STATUS_SET_SOUND_STREAM_INFO,
+};
+
+/* for video display */
+static Evas_Object *g_eo;
+static Evas_Object *g_win_id;
+static Evas_Object *g_selected_win_id;
+
+typedef struct {
+       Evas_Object *win;
+       Evas_Object *layout_main;
+} appdata;
+
+static appdata g_ad;
+static mtpr_h g_mtpr;
+static bundle *g_connection_param;
+static bundle *g_source_param;
+static mtpr_source_type_e g_source_type;
+
+static bool g_dump_data = false;
+
+static FILE* video_dump = NULL;
+static FILE* audio_dump = NULL;
+
+int g_menu_state = CURRENT_STATUS_MAINMENU;
+sound_stream_info_h g_mic_stream_info = NULL;
+sound_stream_info_h g_stream_info = NULL;
+
+static void _mtpr_test_unset_track_cb();
+static void _mtpr_test_unset_encoded_audio_frame_cb();
+static void _mtpr_test_unset_encoded_video_frame_cb();
+
+static void win_del(void *data, Evas_Object *obj, void *event)
+{
+       elm_exit();
+}
+
+static Evas_Object *create_win(const char *name)
+{
+       Evas_Object *eo = NULL;
+       int w = 0;
+       int h = 0;
+
+       g_print("[%s][%d] name=%s\n", __func__, __LINE__, name);
+
+       eo = elm_win_add(NULL, name, ELM_WIN_BASIC);
+       if (eo) {
+               elm_win_title_set(eo, name);
+               elm_win_borderless_set(eo, EINA_TRUE);
+               evas_object_smart_callback_add(eo, "delete,request", win_del, NULL);
+               elm_win_screen_size_get(eo, NULL, NULL, &w, &h);
+               g_print("window size :%d,%d", w, h);
+               evas_object_resize(eo, w, h);
+               elm_win_autodel_set(eo, EINA_TRUE);
+               elm_win_alpha_set(eo, EINA_TRUE);
+       }
+       return eo;
+}
+
+static Evas_Object *create_image_object(Evas_Object *eo_parent)
+{
+       if (!eo_parent)
+               return NULL;
+
+       Evas *evas = evas_object_evas_get(eo_parent);
+       Evas_Object *eo = NULL;
+
+       eo = evas_object_image_add(evas);
+
+       return eo;
+}
+
+void create_render_rect_and_bg(Evas_Object *win)
+{
+       if (!win) {
+               g_print("no win");
+               return;
+       }
+       Evas_Object *bg, *rect;
+
+       bg = elm_bg_add(win);
+       elm_win_resize_object_add(win, bg);
+       evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+       evas_object_show(bg);
+
+       rect = evas_object_rectangle_add(evas_object_evas_get(win));
+       if (!rect) {
+               g_print("no rect");
+               return;
+       }
+       evas_object_color_set(rect, 0, 0, 0, 0);
+       evas_object_render_op_set(rect, EVAS_RENDER_COPY);
+
+       elm_win_resize_object_add(win, rect);
+       evas_object_size_hint_weight_set(rect, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+       evas_object_show(rect);
+       evas_object_show(win);
+}
+
+static int app_create(void *data)
+{
+       appdata *ad = data;
+       Evas_Object *win = NULL;
+
+       /* use gl backend */
+       elm_config_accel_preference_set("opengl");
+
+       /* create window */
+       win = create_win(PACKAGE);
+       if (win == NULL)
+               return -1;
+       ad->win = win;
+       g_win_id = win;
+       g_selected_win_id = g_win_id;
+       create_render_rect_and_bg(ad->win);
+       /* Create evas image object for EVAS surface */
+       g_eo = create_image_object(ad->win);
+       evas_object_image_size_set(g_eo, 500, 500);
+       evas_object_image_fill_set(g_eo, 0, 0, 500, 500);
+       evas_object_resize(g_eo, 500, 500);
+
+       elm_win_activate(win);
+       evas_object_show(win);
+
+       return 0;
+}
+
+static int app_terminate(void *data)
+{
+       appdata *ad = data;
+
+       if (g_eo) {
+               evas_object_del(g_eo);
+               g_eo = NULL;
+       }
+
+       if (g_win_id) {
+               evas_object_del(g_win_id);
+               g_win_id = NULL;
+       }
+
+       ad->win = NULL;
+       g_selected_win_id = NULL;
+
+       return 0;
+}
+
+struct appcore_ops ops = {
+       .create = app_create,
+       .terminate = app_terminate,
+};
+
+static void __error_cb(mtpr_h mtpr, mtpr_error_e error, void *user_data)
+{
+       g_print("                       => [ERROR_CB] mtpr[%p], error[0x%x], user_data[%p]\n",
+               mtpr, error, user_data);
+}
+
+static void __track_added_cb(mtpr_h mtpr, mtpr_media_type_e type, unsigned int id, void *user_data)
+{
+       g_print("                       => [TRACK ADDED CB] mtpr[%p], type[0x%x], id[%u], use_data[%p]\n",
+               mtpr, type, id, user_data);
+}
+
+static void __no_more_track_cb(mtpr_h mtpr, void *user_data)
+{
+       g_print("                       => [NO MORE TRACK CB] mtpr[%p], use_data[%p]\n", mtpr, user_data);
+}
+
+static void __encoded_frame_cb(mtpr_h mtpr, mtpr_media_type_e type, unsigned int id, media_packet_h packet, void *user_data)
+{
+       uint64_t gst_pts;
+       void *temp;
+       uint64_t buf_size;
+       FILE *fp;
+
+       if (!packet) {
+               g_print("encoded_frame packet is NULL\n");
+               return;
+       }
+
+       /* FIXME: dump file have to be created for each id */
+
+       if (g_dump_data) {
+               media_packet_get_buffer_data_ptr(packet, &temp);
+               media_packet_get_buffer_size(packet, &buf_size);
+
+               fp = (type == MTPR_MEDIA_TYPE_AUDIO) ? audio_dump : video_dump;
+               fwrite(temp, (int)buf_size, 1, fp);
+       }
+
+       media_packet_get_pts(packet, &gst_pts);
+       g_print("[%s][%u] frame [%llu ms]\n", (type == MTPR_MEDIA_TYPE_AUDIO) ? "Audio" : "Video", id, (unsigned long long)(gst_pts / 1000));
+
+       media_packet_destroy(packet);
+}
+
+static void _mtpr_test_set_error_cb()
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_set_error_cb(g_mtpr, __error_cb, g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_error_cb()\n");
+       else
+               g_print("                       => mtpr_set_error_cb() success\n");
+}
+
+static void _mtpr_test_unset_error_cb()
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_unset_error_cb(g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_unset_error_cb()\n");
+       else
+               g_print("                       => mtpr_unset_error_cb() success\n");
+}
+
+static void _mtpr_test_destroy()
+{
+       int ret = MTPR_ERROR_NONE;
+
+       if (!g_mtpr) {
+               g_print("                       => failed to _mtpr_test_destroy(), g_mtpr is NULL\n");
+               return;
+       }
+
+       _mtpr_test_unset_error_cb();
+       _mtpr_test_unset_track_cb();
+       _mtpr_test_unset_encoded_audio_frame_cb();
+       _mtpr_test_unset_encoded_video_frame_cb();
+
+       ret = mtpr_destroy(g_mtpr);
+       if (ret != MTPR_ERROR_NONE) {
+               g_print("                       => failed to mtpr_destroy() returned [0x%x]\n", ret);
+       } else {
+               g_print("                       => mtpr_destroy() success\n");
+               g_mtpr = NULL;
+       }
+
+       if (g_mic_stream_info) {
+               sound_manager_destroy_stream_information(g_mic_stream_info);
+               g_mic_stream_info = NULL;
+       }
+       if (g_stream_info) {
+               sound_manager_destroy_stream_information(g_stream_info);
+               g_stream_info = NULL;
+       }
+}
+
+static void _mtpr_test_start()
+{
+       int ret = MTPR_ERROR_NONE;
+
+       if (!g_mtpr) {
+               g_print("                       => failed to _mtpr_test_start(), g_mtpr is NULL\n");
+               return;
+       }
+
+       ret = mtpr_start(g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_start() returned [0x%x]\n", ret);
+       else
+               g_print("                       => mtpr_start() success\n");
+}
+
+static void _mtpr_test_stop()
+{
+       int ret = MTPR_ERROR_NONE;
+
+       if (!g_mtpr) {
+               g_print("                       => failed to _mtpr_test_stop(), g_mtpr is NULL\n");
+               return;
+       }
+
+       ret = mtpr_stop(g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_stop() returned [0x%x]\n", ret);
+       else
+               g_print("                       => mtpr_stop() success\n");
+
+}
+
+static void _mtpr_test_set_display(unsigned int type)
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_set_display(g_mtpr, (mtpr_display_type_e)type, g_win_id);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_display() returned [0x%x]\n", ret);
+       else
+               g_print("                       => mtpr_set_display() success\n");
+}
+
+static void _mtpr_test_set_display_mode(unsigned int mode)
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_set_display_mode(g_mtpr, (mtpr_display_mode_e)mode);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_display_mode() returned [0x%x]\n", ret);
+       else
+               g_print("                       => mtpr_set_display_mode() success\n");
+}
+
+static void _mtpr_test_set_display_visible(unsigned int visible)
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_set_display_visible(g_mtpr, (bool)visible);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_display_visible() returned [0x%x]\n", ret);
+       else
+               g_print("                       => mtpr_set_display_visible() success\n");
+}
+
+static void _mtpr_test_create(int type)
+{
+       int ret = MTPR_ERROR_NONE;
+
+       if (g_mtpr) {
+               g_print("                       => failed to _mtpr_test_create(), already created\n");
+               return;
+       }
+
+       ret = mtpr_create(type, &g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_create(), ret[0x%x]\n", ret);
+       else
+               g_print("                       => mtpr_create(type(%d)) success\n", type);
+}
+
+static void _mtpr_test_get_connection_type()
+{
+       int ret = MTPR_ERROR_NONE;
+       mtpr_connection_type_e type = MTPR_CONNECTION_TYPE_RIST_SENDER;
+
+       ret = mtpr_get_connection_type(g_mtpr, &type);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_get_connection_type() returned [0x%x]\n", ret);
+       else
+               g_print("                       => type: [%d]\n", type);
+}
+
+static void _mtpr_test_set_sender_address(char *address)
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_set_sender_address(g_mtpr, address);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_sender_address(), ret[0x%x]\n", ret);
+       else
+               g_print("                       => mtpr_set_sender_address() success\n");
+}
+
+static void _mtpr_test_set_receiver_address(char *address)
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_set_receiver_address(g_mtpr, address);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_receiver_address(), ret[0x%x]\n", ret);
+       else
+               g_print("                       => mtpr_set_receiver_address() success\n");
+}
+
+static void _mtpr_test_get_sender_address()
+{
+       int ret = MTPR_ERROR_NONE;
+       char *address = NULL;
+
+       ret = mtpr_get_sender_address(g_mtpr, &address);
+       if (ret != MTPR_ERROR_NONE) {
+               g_print("                       => failed to mtpr_get_sender_address(), ret[0x%x]\n", ret);
+       } else {
+               g_print("                       => sender address (%s)\n", address);
+       }
+
+       if (address)
+               free(address);
+}
+
+static void _mtpr_test_get_receiver_address()
+{
+       int ret = MTPR_ERROR_NONE;
+       char *address = NULL;
+
+       ret = mtpr_get_receiver_address(g_mtpr, &address);
+       if (ret != MTPR_ERROR_NONE) {
+               g_print("                       => failed to mtpr_get_receiver_address(), ret[0x%x]\n", ret);
+       } else {
+               g_print("                       => receiver address (%s)\n", address);
+       }
+
+       if (address)
+               free(address);
+}
+
+static void __set_aec_reference_device(sound_stream_info_h stream_info)
+{
+       int ret;
+       sound_device_list_h device_list = NULL;
+       sound_device_h device;
+       sound_device_type_e cur_playback_device_type;
+       bool found = false;
+
+       if(stream_info == NULL) {
+               g_printerr("stream_info is NULL\n");
+               return;
+       }
+
+       ret = sound_manager_get_current_media_playback_device_type(&cur_playback_device_type);
+       if (ret != SOUND_MANAGER_ERROR_NONE) {
+               g_printerr("failed to sound_manager_get_current_media_playback_device_type()\n");
+               return;
+       }
+
+       ret = sound_manager_get_device_list(SOUND_DEVICE_IO_DIRECTION_OUT_MASK, &device_list);
+       if (ret != SOUND_MANAGER_ERROR_NONE && ret != SOUND_MANAGER_ERROR_NO_DATA) {
+               g_printerr("failed to sound_manager_get_device_list()\n");
+               return;
+       }
+
+       while (sound_manager_get_next_device(device_list, &device) == SOUND_MANAGER_ERROR_NONE) {
+               sound_device_type_e device_type;
+               int device_id;
+
+               ret = sound_manager_get_device_type(device, &device_type);
+               if (ret != SOUND_MANAGER_ERROR_NONE) {
+                       g_printerr("failed to sound_manager_get_device_type()\n");
+                       break;
+               }
+
+               if (cur_playback_device_type != device_type)
+                       continue;
+
+               ret = sound_manager_get_device_id(device, &device_id);
+               if (ret != SOUND_MANAGER_ERROR_NONE) {
+                       g_printerr("failed to sound_manager_get_device_id()\n");
+                       break;
+               }
+
+               found = true;
+
+               ret = sound_manager_set_echo_cancel_reference_device(stream_info, device);
+               if (ret != SOUND_MANAGER_ERROR_NONE) {
+                       g_printerr("failed to sound_manager_set_echo_cancel_reference_device()\n");
+               } else {
+                       g_print("set a reference device[type:%u, id:%d]\n", device_type, device_id);
+                       break;
+               }
+       }
+
+       sound_manager_free_device_list(device_list);
+
+       if (!found)
+               g_printerr("could not find a candidate for reference device\n");
+}
+
+static int __get_mic_sound_stream_info(bool aec, sound_stream_info_h *stream_info)
+{
+       int ret;
+       sound_device_list_h device_list = NULL;
+       sound_stream_info_h _stream_info = NULL;
+
+       if (!stream_info) {
+               g_printerr("stream_info is NULL\n");
+               return -1;
+       }
+
+       ret = sound_manager_get_device_list(SOUND_DEVICE_IO_DIRECTION_IN_MASK | SOUND_DEVICE_TYPE_EXTERNAL_MASK, &device_list);
+       if (ret != SOUND_MANAGER_ERROR_NONE && ret != SOUND_MANAGER_ERROR_NO_DATA) {
+               g_printerr("failed to sound_manager_get_device_list()\n");
+               return -1;
+       }
+
+       if (ret == SOUND_MANAGER_ERROR_NO_DATA) {
+               ret = sound_manager_create_stream_information(SOUND_STREAM_TYPE_MEDIA, NULL, NULL, &_stream_info);
+               if (ret != SOUND_MANAGER_ERROR_NONE) {
+                       g_printerr("failed to sound_manager_create_stream_information()\n");
+                       goto error;
+               }
+
+       } else {
+               sound_device_h device;
+               char *device_name = NULL;
+               ret = sound_manager_create_stream_information(SOUND_STREAM_TYPE_MEDIA_EXTERNAL_ONLY, NULL, NULL, &_stream_info);
+               if (ret != SOUND_MANAGER_ERROR_NONE) {
+                       g_printerr("failed to sound_manager_create_stream_information()\n");
+                       goto error;
+               }
+               ret = sound_manager_get_next_device(device_list, &device);
+               if (ret != SOUND_MANAGER_ERROR_NONE) {
+                       g_printerr("failed to get device\n");
+                       goto error;
+               }
+               ret |= sound_manager_add_device_for_stream_routing(_stream_info, device);
+               ret |= sound_manager_apply_stream_routing(_stream_info);
+               if (ret != SOUND_MANAGER_ERROR_NONE) {
+                       g_printerr("failed to apply stream routing\n");
+                       goto error;
+               }
+               sound_manager_get_device_name(device, &device_name);
+               g_print("device[%s] is applied.\n", device_name);
+       }
+
+       sound_manager_free_device_list(device_list);
+
+       *stream_info = _stream_info;
+
+       if (aec)
+               __set_aec_reference_device(*stream_info);
+
+       return 0;
+
+error:
+       if (device_list)
+               sound_manager_free_device_list(device_list);
+       if (_stream_info)
+               sound_manager_destroy_stream_information(_stream_info);
+       return -1;
+}
+
+static void _mtpr_test_add_media_source(int type)
+{
+       int ret = MTPR_ERROR_NONE;
+       unsigned int source_id = 0;
+       int param_len = 0;
+
+       param_len = bundle_get_count(g_source_param);
+       g_print("                       => num of source param : %d", param_len);
+       if (param_len > 1)
+               ret = mtpr_add_media_source(g_mtpr, (mtpr_source_type_e)type, g_source_param, &source_id);
+       else
+               ret = mtpr_add_media_source(g_mtpr, (mtpr_source_type_e)type, NULL, &source_id);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_add_media_source(), ret[0x%x]\n", ret);
+       else
+               g_print("                       => mtpr_add_media_source() success, source_id[%d]\n", source_id);
+
+       if (type == MTPR_SOURCE_TYPE_CAMERA || type == MTPR_SOURCE_TYPE_VIDEOTEST) {
+               // mtpr_source_set_video_loopback(g_mtpr, source_id, MTPR_DISPLAY_TYPE_OVERLAY, g_win_id);
+       } else if (type == MTPR_SOURCE_TYPE_MIC) {
+               if (__get_mic_sound_stream_info(false, &g_mic_stream_info) < 0) {
+                       g_print("failed to __get_mic_sound_stream_info()\n");
+               } else {
+                       mtpr_mic_source_set_sound_stream_info(g_mtpr, source_id, g_mic_stream_info);
+               }
+       }
+}
+
+static void _mtpr_test_remove_media_source(unsigned int source_id)
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_remove_media_source(g_mtpr, source_id);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_remove_media_source(), source_id[%d], ret[0x%x]\n", source_id, ret);
+       else
+               g_print("                       => mtpr_remove_media_source() success, source_id[%d]\n", source_id);
+}
+
+static void _mtpr_test_get_state()
+{
+       int ret = MTPR_ERROR_NONE;
+       mtpr_state_e state = MTPR_STATE_IDLE;
+
+       ret = mtpr_get_state(g_mtpr, &state);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_get_state() returned [0x%x]\n", ret);
+       else
+               g_print("                       => state: [%d]\n", state);
+}
+
+static void _mtpr_test_set_connection_params()
+{
+       int ret = MTPR_ERROR_NONE;
+       int param_len = 0;
+
+       param_len = bundle_get_count(g_connection_param);
+       g_print("                                               => num of connection param : %d\n", param_len);
+       if (param_len > 0)
+               ret = mtpr_set_connection_params(g_mtpr, g_connection_param);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                                               => failed to mtpr_set_connection_params() returned [0x%x]\n", ret);
+       bundle_free(g_connection_param);
+       g_connection_param = NULL;
+}
+
+static void __bundle_iter_cb(const char *key, const int type, const bundle_keyval_t *kv, void *user_data)
+{
+       void *val = NULL;
+       size_t size = 0;
+
+       bundle_keyval_get_basic_val((bundle_keyval_t *)kv, &val, &size);
+
+       g_print("                       => key : %s, size : %zu,\t val : %s\n", key, size, (char *)val);
+}
+
+static void _mtpr_test_get_connection_params()
+{
+       int ret = MTPR_ERROR_NONE;
+       bundle *param = NULL;
+       int param_len = 0;
+
+       ret = mtpr_get_connection_params(g_mtpr, &param);
+       if (ret != MTPR_ERROR_NONE) {
+               g_print("                       => failed to mtpr_get_connection_params() returned [0x%x]\n", ret);
+               return;
+       }
+
+       param_len = bundle_get_count(param);
+       g_print("                       => num of connection param : %d\n", param_len);
+
+       bundle_foreach(param, __bundle_iter_cb, NULL);
+       bundle_free(param);
+}
+
+static void _mtpr_test_set_track_cb()
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_set_track_added_cb(g_mtpr, __track_added_cb, g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_track_added_cb()\n");
+       else
+               g_print("                       => mtpr_set_track_added_cb() success\n");
+
+       ret = mtpr_set_no_more_track_cb(g_mtpr, __no_more_track_cb, g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_no_more_track_cb()\n");
+       else
+               g_print("                       => mtpr_set_no_more_track_cb() success\n");
+}
+
+static void _mtpr_test_unset_track_cb()
+{
+       mtpr_unset_track_added_cb(g_mtpr);
+       mtpr_unset_no_more_track_cb(g_mtpr);
+}
+
+static void _mtpr_test_set_encoded_audio_frame_cb()
+{
+       int ret = MTPR_ERROR_NONE;
+       if (g_dump_data)
+               audio_dump = fopen ("/tmp/audio_encoded.dump", "w+");
+
+       ret = mtpr_set_audio_packet_cb(g_mtpr, __encoded_frame_cb, g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_audio_packet_cb()\n");
+       else
+               g_print("                       => mtpr_set_audio_packet_cb() success\n");
+}
+
+static void _mtpr_test_unset_encoded_audio_frame_cb()
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_unset_audio_packet_cb(g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_unset_audio_packet_cb()\n");
+       else
+               g_print("                       => mtpr_unset_audio_packet_cb() success\n");
+
+       if (g_dump_data && audio_dump) {
+               fclose (audio_dump);
+               audio_dump = NULL;
+       }
+}
+
+static void _mtpr_test_set_encoded_video_frame_cb()
+{
+       int ret = MTPR_ERROR_NONE;
+
+       if (g_dump_data)
+               video_dump = fopen ("/tmp/video_encoded.dump", "w+");
+
+       ret = mtpr_set_video_packet_cb(g_mtpr, __encoded_frame_cb, g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_set_video_packet_cb()\n");
+       else
+               g_print("                       => mtpr_set_video_packet_cb() success\n");
+}
+
+static void _mtpr_test_unset_encoded_video_frame_cb()
+{
+       int ret = MTPR_ERROR_NONE;
+
+       ret = mtpr_unset_video_packet_cb(g_mtpr);
+       if (ret != MTPR_ERROR_NONE)
+               g_print("                       => failed to mtpr_unset_video_packet_cb()\n");
+       else
+               g_print("                       => mtpr_unset_video_packet_cb() success\n");
+
+       if (g_dump_data && video_dump) {
+               fclose (video_dump);
+               video_dump = NULL;
+       }
+}
+
+static void _mtpr_test_set_sound_stream_info(int type)
+{
+       sound_device_list_h device_list = NULL;
+       int ret = SOUND_MANAGER_ERROR_NONE;
+
+       if (g_stream_info) {
+               g_print("stream information is already set, please destroy handle and try again\n");
+               return;
+       }
+       if (sound_manager_create_stream_information(type, NULL, NULL, &g_stream_info)) {
+               g_print("failed to create stream_information()\n");
+               return;
+       }
+       /* In case of MEDIA_EXTERNAL_ONLY, we need to set external device manually */
+       if (type == (int)SOUND_STREAM_TYPE_MEDIA_EXTERNAL_ONLY) {
+               sound_device_h device = NULL;
+               sound_device_type_e device_type;
+
+               if ((ret = sound_manager_get_device_list(SOUND_DEVICE_ALL_MASK, &device_list))) {
+                       g_print("failed to sound_manager_get_device_list(), ret(0x%x)\n", ret);
+                       goto END;
+               }
+               while (!(ret = sound_manager_get_next_device(device_list, &device))) {
+                       if ((ret = sound_manager_get_device_type(device, &device_type))) {
+                               g_print("failed to sound_manager_get_device_type(), ret(0x%x)\n", ret);
+                               goto END;
+                       }
+                       if (device_type == SOUND_DEVICE_BLUETOOTH_MEDIA || device_type == SOUND_DEVICE_USB_AUDIO) {
+                               if ((ret = sound_manager_add_device_for_stream_routing(g_stream_info, device))) {
+                                       g_print("failed to sound_manager_add_device_for_stream_routing(), ret(0x%x)\n", ret);
+                                       goto END;
+                               }
+                               if ((ret = sound_manager_apply_stream_routing(g_stream_info))) {
+                                       g_print("failed to sound_manager_apply_stream_routing(), ret(0x%x)\n", ret);
+                                       goto END;
+                               }
+                               break;
+                       }
+               }
+               if (ret != SOUND_MANAGER_ERROR_NONE) {
+                       g_print("failed to sound_manager_get_next_device(), ret(0x%x)\n", ret);
+                       goto END;
+               }
+       }
+
+       if (mtpr_set_sound_stream_info(g_mtpr, g_stream_info) != MTPR_ERROR_NONE)
+               g_print("failed to set sound stream information(%p)\n", g_stream_info);
+       else
+               g_print("set stream information(%p) success", g_stream_info);
+
+END:
+       if (device_list)
+               sound_manager_free_device_list(device_list);
+       return;
+}
+
+void quit_program()
+{
+       if (g_mtpr != NULL) {
+               _mtpr_test_stop();
+               _mtpr_test_destroy();
+       }
+
+       g_print("                       => EXIT\n");
+       elm_exit();
+}
+
+void _interpret_main_menu(char *cmd)
+{
+       int len = strlen(cmd);
+       if (len == 1) {
+               if (strncmp(cmd, "c", 1) == 0) {
+                       g_menu_state = CURRENT_STATUS_CONNECTION_TYPE;
+               } else if (strncmp(cmd, "d", 1) == 0) {
+                       _mtpr_test_destroy();
+               } else if (strncmp(cmd, "s", 1) == 0) {
+                       _mtpr_test_start();
+               } else if (strncmp(cmd, "t", 1) == 0) {
+                       _mtpr_test_stop();
+               } else if (strncmp(cmd, "a", 1) == 0) {
+                       g_menu_state = CURRENT_STATUS_ADD_MEDIA_SOURCE;
+               } else if (strncmp(cmd, "r", 1) == 0) {
+                       g_menu_state = CURRENT_STATUS_REMOVE_MEDIA_SOURCE;
+               } else if (strncmp(cmd, "g", 1) == 0) {
+                       _mtpr_test_get_state();
+               } else if (strncmp(cmd, "q", 1) == 0) {
+                       quit_program();
+               } else {
+                       g_print("unknown menu \n");
+               }
+       } else if (len == 2) {
+               if (strncmp(cmd, "se", 2) == 0) {
+                       _mtpr_test_set_error_cb();
+               } else if (strncmp(cmd, "cs", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_CONNECTION_SRT_MODE;
+               } else if (strncmp(cmd, "cr", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_CONNECTION_RIST_BONDING_ADDRESS;
+               } else if (strncmp(cmd, "cg", 2) == 0) {
+                       _mtpr_test_get_connection_params();
+               } else if (strncmp(cmd, "ds", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_SET_DISPLAY;
+               } else if (strncmp(cmd, "dm", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_SET_DISPLAY_MODE;
+               } else if (strncmp(cmd, "dv", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_SET_DISPLAY_VISIBLE;
+               } else if (strncmp(cmd, "ea", 2) == 0) {
+                       _mtpr_test_set_encoded_audio_frame_cb();
+               } else if (strncmp(cmd, "ev", 2) == 0) {
+                       _mtpr_test_set_encoded_video_frame_cb();
+               } else if (strncmp(cmd, "ed", 2) == 0) {
+                       g_dump_data = true;
+               } else if (strncmp(cmd, "et", 2) == 0) {
+                       _mtpr_test_set_track_cb();
+               } else if (strncmp(cmd, "rs", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_SET_SOUND_STREAM_INFO;
+               } else {
+                       g_print("unknown menu \n");
+               }
+       } else if (len == 3) {
+               if (strncmp(cmd, "cgt", 3) == 0) {
+                       _mtpr_test_get_connection_type();
+               } else if (strncmp(cmd, "csa", 3) == 0) {
+                       g_menu_state = CURRENT_STATUS_SENDER_ADDRESS;
+               } else if (strncmp(cmd, "cra", 3) == 0) {
+                       g_menu_state = CURRENT_STATUS_RECEIVER_ADDRESS;
+               } else if (strncmp(cmd, "crs", 3) == 0) {
+                       g_menu_state = CURRENT_STATUS_CONNECTION_RTSP_SERVER_IP;
+               } else if (strncmp(cmd, "cgs", 3) == 0) {
+                       _mtpr_test_get_sender_address();
+               } else if (strncmp(cmd, "cgr", 3) == 0) {
+                       _mtpr_test_get_receiver_address();
+               } else {
+                       g_print("unknown menu \n");
+               }
+       } else {
+               g_print("unknown menu \n");
+       }
+}
+
+void display_sub_basic()
+{
+       g_print("\n");
+       g_print("=========================================================================================\n");
+       g_print("                          Media Transporter Test (press q to quit) \n");
+       g_print("-----------------------------------------------------------------------------------------\n");
+
+       g_print("c. Create\t");
+       g_print("d. Destroy\n");
+       g_print("se. Set error callback\t");
+       g_print("s. Start\t");
+       g_print("t. Stop\n");
+       g_print("g. Get state\n\n");
+
+       g_print("-- << Connection Info >> ----------------------------------------------------------------\n");
+
+       g_print("csa. set sender address\t\t");
+       g_print("cra. set receiver address\n");
+       g_print("cgs. get sender address\t\t");
+       g_print("cgr. get receiver address\n");
+       g_print("cs. set SRT params\t");
+       g_print("cr. set RIST params\t");
+       g_print("crs. set RTSP sender params\n");
+       g_print("cgt. get connection type\t");
+       g_print("cg. get connection params\n\n");
+
+       g_print("-- << Sender source >> ------------------------------------------------------------------\n");
+
+       g_print("a. Add media source\t");
+       g_print("r. Remove media source\n\n");
+
+       g_print("-- << Receiver sink >> ------------------------------------------------------------------\n");
+       g_print("ds. set display \t");
+       g_print("dm. set display mode \t");
+       g_print("dv. set display visible \n");
+       g_print("rs. set sound stream info \t");
+       g_print("ed. dump frame data \n");
+       g_print("ea. set audio frame cb \t");
+       g_print("ev. set video frame cb \t");
+       g_print("et. set track added & no more track cb \n\n");
+
+       g_print("=========================================================================================\n");
+}
+
+static void displaymenu()
+{
+       if (g_menu_state == CURRENT_STATUS_MAINMENU) {
+               display_sub_basic();
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_TYPE) {
+               g_print("*** Input connection type.\n\
+    (1:RIST_SENDER, 2:RIST_RECEIVER, 3:SRT_SENDER, 4:SRT_RECEIVER, 5:RTSP_SENDER, 6:RTSP_SENDER_TO_SERVER)\n");
+       } else if (g_menu_state == CURRENT_STATUS_SENDER_ADDRESS) {
+               g_print("*** Input sender address:\n");
+       } else if (g_menu_state == CURRENT_STATUS_RECEIVER_ADDRESS) {
+               g_print("*** Input receiver address:\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_SRT_MODE) {
+               g_print("*** Input SRT mode:\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_SRT_STREAMID) {
+               g_print("*** Input SRT streamid:\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_SRT_PASSPHRASE) {
+               g_print("*** Input SRT passphrase:\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_SRT_PBKEYLEN) {
+               g_print("*** Input SRT crypto key len (0, 16, 24, 32):\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_RIST_BONDING_ADDRESS) {
+               g_print("*** Input RIST bonding address(Comma(,) separated)\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_RIST_MAX_RTCP_BANDWIDTH) {
+               g_print("*** Input RIST max rtcp bandwidth (0 ~ 0.05)(default: 0.05)\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_RIST_MIN_RTCP_INTERVAL) {
+               g_print("*** Input RIST min RTCP interval (0 ~ 100ms)(default:100)\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_RIST_SENDER_BUFFER) {
+               g_print("*** Input RIST sender buffer size (default:1200)\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_RTSP_SERVER_IP) {
+               g_print("*** Input RTSP server ip\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_RTSP_SERVER_PORT) {
+               g_print("*** Input RTSP server port\n");
+       } else if (g_menu_state == CURRENT_STATUS_CONNECTION_RTSP_SERVER_MOUNT_POINT) {
+               g_print("*** Input RTSP mount point\n");
+       } else if (g_menu_state == CURRENT_STATUS_ADD_MEDIA_SOURCE) {
+               g_print("*** Input media source type.(1:camera, 2:mic, 3:videotest, 4:audiotest)\n");
+       } else if (g_menu_state == CURRENT_STATUS_SOURCE_VIDEO_WIDTH) {
+               g_print("*** Input source video width\n");
+       } else if (g_menu_state == CURRENT_STATUS_SOURCE_VIDEO_HEIGHT) {
+               g_print("*** Input source video height\n");
+       } else if (g_menu_state == CURRENT_STATUS_SOURCE_VIDEO_FRAMERATE) {
+               g_print("*** Input source video framerate\n");
+       } else if (g_menu_state == CURRENT_STATUS_SOURCE_AUDIO_CHANNEL) {
+               g_print("*** Input source audio channel\n");
+       } else if (g_menu_state == CURRENT_STATUS_SOURCE_AUDIO_RATE) {
+               g_print("*** Input source audio rate\n");
+       } else if (g_menu_state == CURRENT_STATUS_SOURCE_AUDIO_FORMAT) {
+               g_print("*** Input source audio format (ex, F32LE)\n");
+       } else if (g_menu_state == CURRENT_STATUS_ENCODING_BITRATE) {
+               g_print("*** Input encoding bitrate: (bits/s)\n");
+       } else if (g_menu_state == CURRENT_STATUS_REMOVE_MEDIA_SOURCE) {
+               g_print("*** Input media source id to remove.\n");
+       } else if (g_menu_state == CURRENT_STATUS_SET_DISPLAY) {
+               g_print("*** Input display surface type. (0:overlay, 1:evas)\n");
+       } else if (g_menu_state == CURRENT_STATUS_SET_DISPLAY_MODE) {
+               g_print("*** Input display surface type. (0:Letter box, 1:Origin size, 2:Full mode)\n");
+       } else if (g_menu_state == CURRENT_STATUS_SET_DISPLAY_VISIBLE) {
+               g_print("*** Input display surface type. (0:invisible, 1:visible)\n");
+       } else if (g_menu_state == CURRENT_STATUS_SET_SOUND_STREAM_INFO) {
+               g_print("*** input sound stream type.(0:MEDIA 1:SYSTEM 2:ALARM 3:NOTIFICATION 4:EMERGENCY 5:VOICE_INFORMATION 9:MEDIA_EXT_ONLY)\n");
+       } else {
+               g_print("*** Unknown status.\n");
+               quit_program();
+       }
+       g_print(" >>> ");
+}
+
+gboolean timeout_menu_display(void *data)
+{
+       displaymenu();
+       return FALSE;
+}
+
+gboolean timeout_quit_program(void *data)
+{
+       quit_program();
+       return FALSE;
+}
+
+void reset_menu_state(void)
+{
+       g_menu_state = CURRENT_STATUS_MAINMENU;
+}
+
+static void interpret(char *cmd)
+{
+       int value = 0;
+       double value_d = 0;
+
+       switch (g_menu_state) {
+       case CURRENT_STATUS_MAINMENU:
+               _interpret_main_menu(cmd);
+               break;
+       case CURRENT_STATUS_CONNECTION_TYPE: {
+               value = atoi(cmd);
+               _mtpr_test_create(value - 1);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_SENDER_ADDRESS: {
+               _mtpr_test_set_sender_address(cmd);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_RECEIVER_ADDRESS: {
+               _mtpr_test_set_receiver_address(cmd);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_SRT_MODE: {
+               if (g_connection_param != NULL)
+                       bundle_free(g_connection_param);
+               g_connection_param = bundle_create();
+               value = atoi(cmd);
+               if (value > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_SRT_MODE, g_strdup_printf("%u", (guint)value));
+               g_menu_state = CURRENT_STATUS_CONNECTION_SRT_STREAMID;
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_SRT_STREAMID: {
+               if (strlen(cmd) > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_SRT_STREAMID, strdup(cmd));
+               g_menu_state = CURRENT_STATUS_CONNECTION_SRT_PASSPHRASE;
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_SRT_PASSPHRASE: {
+               if (strlen(cmd) > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_SRT_PASSPHRASE, strdup(cmd));
+               g_menu_state = CURRENT_STATUS_CONNECTION_SRT_PBKEYLEN;
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_SRT_PBKEYLEN: {
+               value = atoi(cmd);
+               if (value > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_SRT_PBKEYLEN, g_strdup_printf("%u", (guint)value));
+               _mtpr_test_set_connection_params();
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_RIST_BONDING_ADDRESS: {
+               if (g_connection_param != NULL)
+                       bundle_free(g_connection_param);
+               g_connection_param = bundle_create();
+               if (strlen(cmd) > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_RIST_BONDING_ADDRESS, strdup(cmd));
+               g_menu_state = CURRENT_STATUS_CONNECTION_RIST_MAX_RTCP_BANDWIDTH;
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_RIST_MAX_RTCP_BANDWIDTH: {
+               value_d = atof(cmd);
+               if (value_d >= 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_RIST_MAX_RTCP_BANDWIDTH, g_strdup_printf("%f", (gdouble)value_d));
+               g_menu_state = CURRENT_STATUS_CONNECTION_RIST_MIN_RTCP_INTERVAL;
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_RIST_MIN_RTCP_INTERVAL: {
+               value = atoi(cmd);
+               if (value > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_RIST_MIN_RTCP_INTERVAL, g_strdup_printf("%u", (guint)value));
+               g_menu_state = CURRENT_STATUS_CONNECTION_RIST_SENDER_BUFFER;
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_RIST_SENDER_BUFFER: {
+               value = atoi(cmd);
+               if (value > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_RIST_SENDER_BUFFER, g_strdup_printf("%u", (guint)value));
+               _mtpr_test_set_connection_params();
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_RTSP_SERVER_IP: {
+               if (g_connection_param != NULL)
+                       bundle_free(g_connection_param);
+               g_connection_param = bundle_create();
+               if (strlen(cmd) > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_RTSP_SERVER_IP, strdup(cmd));
+               g_menu_state = CURRENT_STATUS_CONNECTION_RTSP_SERVER_PORT;
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_RTSP_SERVER_PORT: {
+               if (strlen(cmd) > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_RTSP_SERVER_PORT, strdup(cmd));
+               g_menu_state = CURRENT_STATUS_CONNECTION_RTSP_SERVER_MOUNT_POINT;
+               break;
+       }
+       case CURRENT_STATUS_CONNECTION_RTSP_SERVER_MOUNT_POINT: {
+               if (strlen(cmd) > 0)
+                       bundle_add_str(g_connection_param, MTPR_CONNECTION_PARAM_RTSP_SERVER_MOUNT_POINT, strdup(cmd));
+               _mtpr_test_set_connection_params();
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_ADD_MEDIA_SOURCE: {
+               value = atoi(cmd);
+               g_source_type = (mtpr_source_type_e) value - 1;
+               switch (g_source_type)
+               {
+               case MTPR_SOURCE_TYPE_CAMERA:
+               case MTPR_SOURCE_TYPE_VIDEOTEST:
+                       g_menu_state = CURRENT_STATUS_SOURCE_VIDEO_WIDTH;
+                       break;
+               case MTPR_SOURCE_TYPE_MIC:
+               case MTPR_SOURCE_TYPE_AUDIOTEST:
+                       g_menu_state = CURRENT_STATUS_SOURCE_AUDIO_CHANNEL;
+               default:
+                       break;
+               }
+               break;
+       }
+       case CURRENT_STATUS_SOURCE_VIDEO_WIDTH: {
+               value = atoi(cmd);
+               if (g_source_param != NULL)
+                       bundle_free(g_source_param);
+               g_source_param = bundle_create();
+               if (value > 0)
+                       bundle_add_str(g_source_param, MTPR_SOURCE_PARAM_VIDEO_WIDTH, g_strdup_printf("%u", (guint)value));
+               g_menu_state = CURRENT_STATUS_SOURCE_VIDEO_HEIGHT;
+               break;
+       }
+       case CURRENT_STATUS_SOURCE_VIDEO_HEIGHT: {
+               value = atoi(cmd);
+               if (value > 0)
+                       bundle_add_str(g_source_param, MTPR_SOURCE_PARAM_VIDEO_HEIGHT, g_strdup_printf("%u", (guint)value));
+               g_menu_state = CURRENT_STATUS_SOURCE_VIDEO_FRAMERATE;
+               break;
+       }
+       case CURRENT_STATUS_SOURCE_VIDEO_FRAMERATE: {
+               value = atoi(cmd);
+               if (value > 0)
+                       bundle_add_str(g_source_param, MTPR_SOURCE_PARAM_VIDEO_FRAMERATE, g_strdup_printf("%u", (guint)value));
+               g_menu_state = CURRENT_STATUS_ENCODING_BITRATE;
+               break;
+       }
+       case CURRENT_STATUS_SOURCE_AUDIO_CHANNEL: {
+               value = atoi(cmd);
+               if (g_source_param != NULL)
+                       bundle_free(g_source_param);
+               g_source_param = bundle_create();
+               if (value > 0)
+                       bundle_add_str(g_source_param, MTPR_SOURCE_PARAM_AUDIO_CHANNEL, g_strdup_printf("%u", (guint)value));
+               g_menu_state = CURRENT_STATUS_SOURCE_AUDIO_RATE;
+               break;
+       }
+       case CURRENT_STATUS_SOURCE_AUDIO_RATE: {
+               value = atoi(cmd);
+               if (value > 0)
+                       bundle_add_str(g_source_param, MTPR_SOURCE_PARAM_AUDIO_RATE, g_strdup_printf("%u", (guint)value));
+               g_menu_state = CURRENT_STATUS_SOURCE_AUDIO_FORMAT;
+               break;
+       }
+       case CURRENT_STATUS_SOURCE_AUDIO_FORMAT: {
+               if (strlen(cmd) > 0)
+                       bundle_add_str(g_source_param, MTPR_SOURCE_PARAM_AUDIO_FORMAT, strdup(cmd));
+               g_menu_state = CURRENT_STATUS_ENCODING_BITRATE;
+               break;
+       }
+       case CURRENT_STATUS_REMOVE_MEDIA_SOURCE: {
+               value = atoi(cmd);
+               _mtpr_test_remove_media_source(value);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_SET_DISPLAY:{
+               value = atoi(cmd);
+               _mtpr_test_set_display(value);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_SET_DISPLAY_MODE:{
+               value = atoi(cmd);
+               _mtpr_test_set_display_mode(value);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_SET_DISPLAY_VISIBLE:{
+               value = atoi(cmd);
+               _mtpr_test_set_display_visible(value);
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_ENCODING_BITRATE:{
+               value = atoi(cmd);
+               if (value > 0)
+                       bundle_add_str(g_source_param, MTPR_ENCODING_PARAM_BITRATE, g_strdup_printf("%u", (guint)value));
+               _mtpr_test_add_media_source(g_source_type);
+               bundle_free(g_source_param);
+               g_source_param = NULL;
+               reset_menu_state();
+               break;
+       }
+       case CURRENT_STATUS_SET_SOUND_STREAM_INFO:{
+               value = atoi(cmd);
+               _mtpr_test_set_sound_stream_info(value);
+               reset_menu_state();
+               break;
+       }
+       default:
+               break;
+       }
+
+       g_timeout_add(100, timeout_menu_display, 0);
+}
+
+
+static gboolean input(GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+       gchar buf[MAX_STRING_LEN];
+       gsize read;
+       GIOStatus status;
+
+       status = g_io_channel_read_chars(channel, buf, MAX_STRING_LEN, &read, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               g_printerr("failed to g_io_channel_read_chars(), status[%d]", status);
+               return TRUE;
+       }
+
+       buf[read] = '\0';
+       g_strstrip(buf);
+       interpret(buf);
+
+       return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+       GIOChannel *stdin_channel;
+       stdin_channel = g_io_channel_unix_new(0);
+       g_io_channel_set_flags(stdin_channel, G_IO_FLAG_NONBLOCK, NULL);
+       g_io_add_watch(stdin_channel, G_IO_IN, (GIOFunc)input, NULL);
+
+       displaymenu();
+       memset(&g_ad, 0x0, sizeof(appdata));
+       ops.data = &g_ad;
+
+       return appcore_efl_main(PACKAGE, &argc, &argv, &ops);
+}
+
diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8a314c3
--- /dev/null
@@ -0,0 +1,67 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
+SET(fw_name "mtpr_ut")
+
+SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
+SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
+
+SET(${fw_name}_CXXFLAGS "-Wall -pthread -fPIE -Wl,-z,relro -fstack-protector -fno-delete-null-pointer-checks -DEFL_BETA_API_SUPPORT")
+
+SET(${fw_name}_LDFLAGS)
+
+SET(ADD_LIBS
+  "capi-media-transporter"
+)
+
+SET(dependents
+               "glib-2.0 gstreamer-base-1.0 gstreamer-app-1.0 dlog"
+)
+
+find_package(GTest REQUIRED)
+set(GTEST_LIBRARY gtest)
+
+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}")
+SET(EXTRA_CXXFLAGS "${EXTRA_CXXFLAGS} ${flag}")
+ENDFOREACH(flag)
+FOREACH(flag ${${fw_name}_CXXFLAGS})
+SET(EXTRA_CXXFLAGS "${EXTRA_CXXFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS}")
+
+INCLUDE_DIRECTORIES(
+  ${PROJECT_SOURCE_DIR}/src
+  ${PROJECT_SOURCE_DIR}/include
+  ${PROJECT_SOURCE_DIR}
+)
+
+SET(UT_SRC
+  ut_srt_sender.cpp
+  ut_main.cpp
+)
+
+ADD_EXECUTABLE(${fw_name} ${UT_SRC})
+
+LINK_DIRECTORIES(${LIB_INSTALL_DIR})
+
+TARGET_LINK_LIBRARIES(${fw_name}
+  ${GTEST_LIBRARY}
+  ${CMAKE_THREAD_LIBS_INIT}
+  ${ADD_LIBS}
+  ${${fw_name}_LDFLAGS}
+  "-pie"
+)
+
+INSTALL(
+  TARGETS ${fw_name}
+  DESTINATION bin
+)
diff --git a/unittest/testbase.hpp b/unittest/testbase.hpp
new file mode 100644 (file)
index 0000000..5e18925
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include <chrono>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <thread>
+#include <malloc.h>
+#include <dlog.h>
+#include <glib.h>
+
+#include "gtest/gtest.h"
+#include <mtpr.h>
+
+#undef LOG_TAG
+#define LOG_TAG "MTPR_UT"
+
+class MediaTransporterTestBase : public ::testing::Test {
+public:
+    MediaTransporterTestBase() : _mtpr(nullptr) {}
+
+    void SetUp() override {}
+    void TearDown() override {}
+
+    mtpr_h GetHandle() { return _mtpr; }
+    const std::string& GetSRTSenderPath() { return _srtSenderPath; }
+    const std::string& GetSRTReceiverPath() { return _srtReceiverPath; }
+    const std::string& GetRTSPSenderPath() { return _rtspSenderPath; }
+
+protected:
+    mtpr_h _mtpr;
+
+private:
+    const std::string _srtSenderPath   = "srt://:8888";
+    const std::string _srtReceiverPath = "srt://127.0.0.1:8888";
+    const std::string _rtspSenderPath  = "rtsp://127.0.0.1:8554/test";
+};
diff --git a/unittest/ut_main.cpp b/unittest/ut_main.cpp
new file mode 100644 (file)
index 0000000..d84a5b4
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "gtest/gtest.h"
+
+int main(int argc, char *argv[])
+{
+    try {
+        ::testing::InitGoogleTest(&argc, argv);
+        return RUN_ALL_TESTS();
+    } catch (const std::exception &e) {
+        std::cout << "caught exception: " << e.what() << std::endl;
+        return -1;
+    }
+}
diff --git a/unittest/ut_srt_sender.cpp b/unittest/ut_srt_sender.cpp
new file mode 100644 (file)
index 0000000..504680c
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "testbase.hpp"
+
+class MediaTransporterSrtSenderTest : public MediaTransporterTestBase {
+public:
+    MediaTransporterSrtSenderTest() {}
+
+    void SetUp() override {
+        LOGD("Enter");
+
+        int ret = MTPR_ERROR_NONE;
+        ret = mtpr_create(MTPR_CONNECTION_TYPE_SRT_SENDER, &_mtpr);
+        ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+        LOGD("Leave");
+    }
+
+    void TearDown() override {
+        LOGD("Enter");
+
+        if (_mtpr) {
+            int ret = mtpr_destroy(_mtpr);
+            ASSERT_EQ(ret, MTPR_ERROR_NONE);
+        }
+
+        LOGD("Leave");
+    }
+};
+
+TEST_F(MediaTransporterSrtSenderTest, set_address)
+{
+    int ret = MTPR_ERROR_NONE;
+       char *address = NULL;
+
+    ret = mtpr_set_receiver_address(GetHandle(), GetSRTSenderPath().c_str());
+    ASSERT_EQ(ret, MTPR_ERROR_INVALID_OPERATION);
+
+    ret = mtpr_set_sender_address(GetHandle(), GetSRTSenderPath().c_str());
+    ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+    ret = mtpr_get_sender_address(GetHandle(), &address);
+    ASSERT_EQ(ret, MTPR_ERROR_NONE);
+    ASSERT_TRUE(address);
+
+    LOGD("sender address : %s", address);
+
+    ASSERT_STREQ(GetSRTSenderPath().c_str(), address);
+}
+
+TEST_F(MediaTransporterSrtSenderTest, get_connection_type)
+{
+       mtpr_connection_type_e type;
+
+    int ret = mtpr_get_connection_type(GetHandle(), &type);
+    ASSERT_EQ(ret, MTPR_ERROR_NONE);
+    ASSERT_EQ(type, MTPR_CONNECTION_TYPE_SRT_SENDER);
+}
+
+TEST_F(MediaTransporterSrtSenderTest, add_media_source)
+{
+    int ret = MTPR_ERROR_NONE;
+       unsigned int source_id = 0;
+
+    ret = mtpr_add_media_source(GetHandle(), MTPR_SOURCE_TYPE_VIDEOTEST, NULL, &source_id);
+    ASSERT_EQ(ret, MTPR_ERROR_NONE);
+    ASSERT_TRUE(source_id >= 0);
+
+    ret = mtpr_add_media_source(GetHandle(), MTPR_SOURCE_TYPE_AUDIOTEST, NULL, &source_id);
+    ASSERT_EQ(ret, MTPR_ERROR_NONE);
+    ASSERT_TRUE(source_id >= 0);
+}
+
+TEST_F(MediaTransporterSrtSenderTest, remove_media_source)
+{
+    int ret = MTPR_ERROR_NONE;
+       unsigned int source_id = 0;
+
+    ret = mtpr_add_media_source(GetHandle(), MTPR_SOURCE_TYPE_VIDEOTEST, NULL, &source_id);
+    ASSERT_EQ(ret, MTPR_ERROR_NONE);
+    ASSERT_TRUE(source_id >= 0);
+
+    ret = mtpr_remove_media_source(GetHandle(), source_id);
+    ASSERT_EQ(ret, MTPR_ERROR_NONE);
+}