--- /dev/null
+Haesu Gwon <haesu.gwon@samsung.com>
--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
+SET(fw_name "capi-media-editor")
+
+PROJECT(${fw_name})
+
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+SET(INC_DIR include)
+INCLUDE_DIRECTORIES(${INC_DIR})
+
+SET(dependents "glib-2.0 gio-2.0 dlog iniparser capi-base-common mm-common mm-display-interface capi-system-info\
+ gstreamer-1.0 gstreamer-plugins-base-1.0 gstreamer-pbutils-1.0 gst-editing-services-1.0")
+SET(pc_dependents "capi-base-common")
+
+IF(NOT TIZEN_PROFILE_TV)
+ SET(dependents "${dependents} mm-resource-manager")
+ELSE()
+ ADD_DEFINITIONS("-DTIZEN_TV")
+ENDIF()
+
+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 -Werror -Wextra -Wno-array-bounds -Wno-empty-body -Wno-ignored-qualifiers -Wno-unused-parameter -Wshadow -Wwrite-strings -Wswitch-default -Wno-unused-but-set-parameter -Wno-unused-but-set-variable")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DTIZEN_DEBUG -DPRINT_CLIP_DEBUG_INFO -DPRINT_LAYER_DEBUG_INFO")
+
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}")
+
+AUX_SOURCE_DIRECTORY (src MAIN_SRC)
+
+LIST (APPEND SOURCES
+ ${MAIN_SRC}
+)
+IF(TIZEN_PROFILE_TV)
+ LIST (REMOVE_ITEM SOURCES src/media_editor_resource.c)
+ENDIF()
+
+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 "*_private.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)
+ADD_SUBDIRECTORY(gtest)
+
+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)
--- /dev/null
+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.
+
+
+
--- /dev/null
+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.APLv2 file for Apache License terms and conditions.
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+</manifest>
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+</manifest>
--- /dev/null
+
+# Package Information for pkg-config
+
+prefix=@PREFIX@
+exec_prefix=/usr
+libdir=@LIB_INSTALL_DIR@
+includedir=/usr/include/media
+
+Name: @PC_NAME@
+Description: @PACKAGE_DESCRIPTION@
+Version: @VERSION@
+Requires: @PC_REQUIRED@
+Libs: -L${libdir} @PC_LDFLAGS@
+Cflags: -I${includedir}
+
--- /dev/null
+/*
+ * 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_EDITOR_DOC_H__
+#define __TIZEN_MEDIA_EDITOR_DOC_H__
+
+/**
+ * @file media_editor_doc.h
+ * @brief This file contains high level documentation for the media editor API.
+ */
+
+/**
+ * @ingroup CAPI_MEDIA_FRAMEWORK
+ * @defgroup CAPI_MEDIA_EDITOR_MODULE Mediaeditor
+ * @brief The @ref CAPI_MEDIA_EDITOR_MODULE API provides functions for editing media clip and creating output media file.
+ *
+ * @section CAPI_MEDIA_EDITOR_MODULE_HEADER Required Header
+ * \#include <media_editor.h>
+ *
+ * @section CAPI_MEDIA_EDITOR_MODULE_OVERVIEW Overview
+ * The Mediaeditor API allows application developers to edit media clip.
+ *
+ * The Mediaeditor API set allows you to:
+ * - add/remove/split/group/ungroup the media clip
+ * - add/remove/move layer
+ * - add/remove audio, video effect to media clip
+ *
+ * @subsection CAPI_MEDIA_EDITOR_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> mediaeditor_create() </td>
+ * <td> NONE </td>
+ * <td> IDLE </td>
+ * <td> SYNC </td>
+ * </tr>
+ * <tr>
+ * <td> mediaeditor_destroy() </td>
+ * <td> IDLE/RENDERING/PREVIEW </td>
+ * <td> NONE </td>
+ * <td> SYNC </td>
+ * </tr>
+ * <tr>
+ * <td> mediaeditor_start_preview() </td>
+ * <td> IDLE </td>
+ * <td> PREVIEW </td>
+ * <td> ASYNC </td>
+ * </tr>
+ * <tr>
+ * <td> mediaeditor_start_render() </td>
+ * <td> IDLE </td>
+ * <td> RENDERING </td>
+ * <td> ASYNC </td>
+ * </tr>
+ * </table></div>
+ *
+ * (*) The transition from the RENDERING state to the IDLE state will be processed after finishing the rendering. \n
+ *
+ * @subsection CAPI_MEDIA_EDITOR_LIFE_CYCLE_CALLBACK_OPERATIONS Callback(Event) Operations
+ * The callback mechanism is used to notify the application about significant mediaeditor 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>mediaeditor_set_state_changed_cb()</td>
+ * <td>mediaeditor_unset_state_changed_cb()</td>
+ * <td>mediaeditor_state_changed_cb()</td>
+ * <td>This callback is used to notify that the mediaeditor state has changed</td>
+ * </tr>
+ * <tr>
+ * <td>mediaeditor_set_error_cb()</td>
+ * <td>mediaeditor_unset_error_cb()</td>
+ * <td>mediaeditor_error_cb()</td>
+ * <td>This callback is used to notify that an error has occurred</td>
+ * </tr>
+ *</table></div>
+ *
+ * @section CAPI_MEDIA_EDITOR_MODULE_FEATURE Related Feature
+ * This API is related with the following feature:\n
+ * - %http://tizen.org/feature/display\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_EDITOR_MODULE
+ * @defgroup CAPI_MEDIA_EDITOR_LAYER_MODULE Layer
+ * @brief The @ref CAPI_MEDIA_EDITOR_LAYER_MODULE API provides functions to manage layers.
+ *
+ * @section CAPI_MEDIA_EDITOR_LAYER_MODULE_HEADER Required Header
+ * \#include <media_editor.h>
+ *
+ * @section CAPI_MEDIA_EDITOR_LAYER_MODULE_OVERVIEW Overview
+ * The Mediaeditor Layer API allows you to:
+ * - add/remove layer to internal timeline
+ * - activate/deactivate layer which is already added
+ * - move layer
+ *
+*/
+
+/**
+ * @ingroup CAPI_MEDIA_EDITOR_MODULE
+ * @defgroup CAPI_MEDIA_EDITOR_CLIP_MODULE Clip
+ * @brief The @ref CAPI_MEDIA_EDITOR_CLIP_MODULE API provides functions to manage clips.
+ *
+ * @section CAPI_MEDIA_EDITOR_CLIP_MODULE_HEADER Required Header
+ * \#include <media_editor.h>
+ *
+ * @section CAPI_MEDIA_EDITOR_CLIP_MODULE_OVERVIEW Overview
+ * The Mediaeditor Clip API allows you to:
+ * - add/remove media clips to internal timeline
+ * - split media clips with user given time position
+ * - group/ungroup media clips
+ * - get/set clip start, duration, resolution, volume
+ *
+*/
+
+/**
+ * @ingroup CAPI_MEDIA_EDITOR_MODULE
+ * @defgroup CAPI_MEDIA_EDITOR_EFFECT_MODULE Effect
+ * @brief The @ref CAPI_MEDIA_EDITOR_EFFECT_MODULE API provides functions to manage layers.
+ *
+ * @section CAPI_MEDIA_EDITOR_EFFECT_MODULE_HEADER Required Header
+ * \#include <media_editor.h>
+ *
+ * @section CAPI_MEDIA_EDITOR_EFFECT_MODULE_OVERVIEW Overview
+ * The Mediaeditor Effect API allows you to:
+ * - add/remove effect
+ * - add transition effect where clips are overlapped
+ *
+*/
+
+/**
+ * @ingroup CAPI_MEDIA_EDITOR_MODULE
+ * @defgroup CAPI_MEDIA_EDITOR_PROJECT_MODULE Project
+ * @brief The @ref CAPI_MEDIA_EDITOR_PROJECT_MODULE API provides functions to manage project.
+ *
+ * @section CAPI_MEDIA_EDITOR_PROJECT_MODULE_HEADER Required Header
+ * \#include <media_editor.h>
+ *
+ * @section CAPI_MEDIA_EDITOR_PROJECT_MODULE_OVERVIEW Overview
+ * The Mediaeditor Project API allows you to:
+ * - create/save editorial cut information as project file
+ * - load editorial cut information from project file and user can edit clip again
+ *
+*/
+
+#endif /* __TIZEN_MEDIA_EDITOR_DOC_H__ */
--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
+SET(fw_name "mediaeditor_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-editor"
+)
+
+SET(dependents
+ "glib-2.0 gstreamer-base-1.0 gstreamer-app-1.0 dlog"
+ "boost"
+ "appcore-efl elementary ecore evas ecore-wl2"
+)
+
+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_clip.cpp
+ ut_composition.cpp
+ ut_effect.cpp
+ ut_layer.cpp
+ ut_main.cpp
+ ut_project.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
+)
--- /dev/null
+/*
+ * 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 <media_editor.h>
+
+#undef LOG_TAG
+#define LOG_TAG "MEDIAEDITOR_UT"
+
+class MediaeditorTestBase : public ::testing::Test {
+public:
+ MediaeditorTestBase() : _editor(nullptr) {}
+
+ void SetUp() override {
+ LOGD("Enter");
+
+ int ret = mediaeditor_create(&_editor);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ LOGD("Leave");
+ }
+
+ void TearDown() override {
+ LOGD("Enter");
+
+ if (_editor) {
+ int ret = mediaeditor_destroy(_editor);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ }
+
+ LOGD("Leave");
+ }
+
+ mediaeditor_h GetHandle() { return _editor; }
+ const std::string& GetBgmPath() { return _bgmPath; }
+ const std::string& GetMp4Path(int idx) { return _mp4Ppath[idx]; }
+ const std::string& GetProjectPath(int idx) { return _projectPath[idx]; }
+
+private:
+ mediaeditor_h _editor;
+
+ const std::string _basePath = "/tmp/";
+ const std::string _bgmPath = _basePath + "bg.mp3";
+ const std::vector<std::string> _mp4Ppath = {
+ _basePath + "test1.mp4",
+ _basePath + "test2.mp4",
+ _basePath + "test3.mp4",
+ _basePath + "test4.mp4",
+ _basePath + "test5.mp4"
+ };
+ const std::vector<std::string> _projectPath = {
+ _basePath + "test1.xges",
+ _basePath + "test2.xges"
+ };
+};
--- /dev/null
+/*
+ * 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"
+
+static GMainLoop *mainloop;
+
+class MediaeditorClipTest : public MediaeditorTestBase {
+public:
+ MediaeditorClipTest() {}
+
+ static void RenderCompletedCb(void *user_data)
+ {
+ LOGD("Enter");
+ if (mainloop)
+ g_main_loop_quit(mainloop);
+ }
+
+ static void ErrorCb(mediaeditor_error_e error, mediaeditor_state_e state, void *user_data)
+ {
+ LOGD("Enter. error[%d] state[%d]", error, state);
+ if (mainloop)
+ g_main_loop_quit(mainloop);
+ }
+
+ static void WaitForAsync()
+ {
+ mainloop = g_main_loop_new(NULL, TRUE);
+
+ LOGD("Wait render completed cb");
+ g_main_loop_run(mainloop);
+
+ g_main_loop_unref(mainloop);
+ mainloop = NULL;
+ }
+};
+
+TEST_F(MediaeditorClipTest, DISABLED_clip_create)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_clip_delete)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_remove_clip(GetHandle(), clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_clip_split)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int new_clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_split_clip(GetHandle(), clip_id, (start + duration) / 2, &new_clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_clip_group_ungroup)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int src_ids[5] = { 0 };
+ unsigned int *dst_ids = NULL;
+ unsigned int group_id = 0;
+ unsigned int ungroup_size = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ for (int i = 0; i < 5; i++) {
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, i * 100, &src_ids[i]);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ LOGD("clip id : %d", src_ids[i]);
+
+ start += duration;
+ }
+
+ for (int i = 0 ; i < 4 ; i++)
+ src_ids[i] += 1;
+
+ ret = mediaeditor_group_clip(GetHandle(), src_ids, 4, &group_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ LOGD("grouped id : %d", group_id);
+
+ ret = mediaeditor_ungroup_clip(GetHandle(), group_id, &dst_ids, &ungroup_size);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ for (int i = 0 ; i < (int)ungroup_size ; i++) {
+ LOGD("src_ids[%d], ungrouped_ids[%d]", src_ids[i], dst_ids[i]);
+ ASSERT_EQ(src_ids[i], dst_ids[i]);
+ }
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_clip_move)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ LOGD("clip id : %d", clip_id);
+
+ ret = mediaeditor_move_clip_layer(GetHandle(), clip_id, 0);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_clip_set_duration)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+ unsigned int set_duration = 200;
+ unsigned int get_duration = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ LOGD("original duration : %d", duration);
+
+ ret = mediaeditor_set_clip_duration(GetHandle(), clip_id, set_duration);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ LOGD("set_duration : %d", set_duration);
+
+ ret = mediaeditor_get_clip_duration(GetHandle(), clip_id, &get_duration);
+ ASSERT_EQ(set_duration, get_duration);
+ LOGD("get_duration : %d", get_duration);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_clip_set_get_resolution)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+ unsigned int width = 0;
+ unsigned int height = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_get_clip_resolution(GetHandle(), clip_id, &width, &height);
+ LOGD("original resolution : %dx%d", width, height);
+
+ width = 640;
+ height = 360;
+ ret = mediaeditor_set_clip_resolution(GetHandle(), clip_id, width, height);
+ LOGD("set resolution : %dx%d", width, height);
+
+ ret = mediaeditor_get_clip_resolution(GetHandle(), clip_id, &width, &height);
+ LOGD("get resolution : %dx%d", width, height);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_audio_clip_set_get_resolution)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+ unsigned int width = 0;
+ unsigned int height = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetBgmPath().c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_get_clip_resolution(GetHandle(), clip_id, &width, &height);
+ LOGD("Audio clip doesn't have resolution information. Should return error.");
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_INVALID_OPERATION);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_volume)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+ double volume = 0.0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_get_clip_volume(GetHandle(), clip_id, &volume);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ ASSERT_TRUE(volume == 1.0);
+
+ ret = mediaeditor_set_clip_volume(GetHandle(), clip_id, 2.0);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_get_clip_volume(GetHandle(), clip_id, &volume);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ ASSERT_TRUE(volume == 2.0);
+ LOGD("get volume [%f]", volume);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_transition)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(1).c_str(), layer_id, start + (duration / 2), duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_transition(GetHandle(), MEDIAEDITOR_TRANSITION_TYPE_CROSSFADE, layer_id, start + (duration / 2), duration / 2);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_transition_n)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 1000;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_set_error_cb(GetHandle(), ErrorCb, GetHandle());
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ /* Test for no overlapped area */
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(1).c_str(), layer_id, start + (duration * 2), duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_transition(GetHandle(), MEDIAEDITOR_TRANSITION_TYPE_CROSSFADE, layer_id, start + duration, duration / 2);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_start_render(GetHandle(), "/tmp/out_trans.ogv", RenderCompletedCb, GetHandle());
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ WaitForAsync();
+}
+
+#ifdef TIZEN_MEDIAEDITOR_TEXTOVERLAY_SUPPORT
+TEST_F(MediaeditorClipTest, DISABLED_text_overlay)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id1 = 0;
+ unsigned int clip_id2 = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id1);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_text_overlay(GetHandle(), "Text Overlay", "sans-serif 13", start, duration, 0xFFFFFFFF, 0.5, 0.8, &clip_id2);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorClipTest, DISABLED_title)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id1 = 0;
+ unsigned int layer_id2 = 0;
+ unsigned int layer_priority = 0;
+ unsigned int layer_priority2 = 0;
+ unsigned int clip_id1 = 0;
+ unsigned int clip_id2 = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id1, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id2, &layer_priority2);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id2, start, duration, in_point, &clip_id1);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_title(GetHandle(), "Title", "sans-serif 20", start, duration, 0x80FFFF00, 0.5, 0.8, &clip_id2);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+#endif
--- /dev/null
+/*
+ * 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"
+
+enum LAYER
+{
+ VIDEO_LAYER,
+ BGM_LAYER
+};
+
+enum EDITING_ELEMENT
+{
+ VIDEO_ELEMENT,
+ TRANSITION_ELEMENT,
+ BGM_ELEMENT,
+#ifdef TIZEN_MEDIAEDITOR_TEXTOVERLAY_SUPPORT
+ TEXTOVERLAY_ELEMENT,
+#endif
+ EFFECT_ELEMENT,
+ ELEMENT_MAX
+};
+
+static GMainLoop *mainloop;
+
+class MediaeditorCompositionTest : public MediaeditorTestBase {
+public:
+ MediaeditorCompositionTest() {}
+
+ static void RenderCompletedCb(void *user_data)
+ {
+ LOGD("Enter");
+ if (mainloop)
+ g_main_loop_quit(mainloop);
+ }
+
+ static void WaitForAsync()
+ {
+ mainloop = g_main_loop_new(NULL, TRUE);
+
+ LOGD("Wait render completed cb");
+ g_main_loop_run(mainloop);
+
+ g_main_loop_unref(mainloop);
+ mainloop = NULL;
+ }
+};
+
+TEST_F(MediaeditorCompositionTest, DISABLED_mp4_bgm_transition_text)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ const int num_of_clip = 2;
+ const int num_of_layer = 2;
+ unsigned int layer_id[num_of_layer] = { 0 };
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int total = 0;
+
+ bool editing_element[ELEMENT_MAX] = {
+ true, // video
+ true, // transition
+ true, // bgm
+#ifdef TIZEN_MEDIAEDITOR_TEXTOVERLAY_SUPPORT
+ false, // textoverlay
+#endif
+ true // effect
+ };
+
+ unsigned int duration[num_of_clip] = {
+ G_TIME_SPAN_MILLISECOND * 3,
+ G_TIME_SPAN_MILLISECOND * 3
+ };
+ unsigned int duration_bgm = G_TIME_SPAN_MILLISECOND * 3;
+
+ bool is_transition[num_of_clip] = {
+ true,
+ false // The last is always false
+ };
+ unsigned int transition_duration = G_TIME_SPAN_MILLISECOND * 2;
+
+ // Create layer
+ for (int i = 0 ; i < num_of_layer ; i++) {
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id[i], &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ }
+
+ // Add clips
+ for (int i = 0 ; i < num_of_clip ; i++) {
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(i).c_str(), layer_id[VIDEO_LAYER], start, duration[i], 0, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ start += duration[i];
+
+ if (editing_element[TRANSITION_ELEMENT] && is_transition[i])
+ {
+ ASSERT_TRUE(start > transition_duration);
+ start -= transition_duration;
+
+ ret = mediaeditor_add_transition(GetHandle(), MEDIAEDITOR_TRANSITION_TYPE_CROSSFADE, layer_id[VIDEO_LAYER], start, transition_duration);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ }
+
+ total = start;
+ }
+
+ LOGI("total = %d", total);
+
+ // Add BGM
+ if (editing_element[BGM_ELEMENT]) {
+ start = 0;
+ for (int i = 0, step = (int)(duration_bgm / G_TIME_SPAN_MILLISECOND) ; i < (int)(total / G_TIME_SPAN_MILLISECOND) ; i += step) {
+ if (start + duration_bgm > total)
+ {
+ duration_bgm = total - start;
+ }
+
+ ret = mediaeditor_add_clip(GetHandle(), GetBgmPath().c_str(), layer_id[BGM_LAYER], start, duration_bgm, G_TIME_SPAN_MILLISECOND * 0, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_set_clip_volume(GetHandle(), clip_id, 0.3);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ start += duration_bgm;
+ }
+ }
+
+#ifdef TIZEN_MEDIAEDITOR_TEXTOVERLAY_SUPPORT
+ // Add textoverlay
+ if (editing_element[TEXTOVERLAY_ELEMENT]) {
+ ret = mediaeditor_add_text_overlay(GetHandle(), "Editing test", "sans-serif 30", 0, total, 0xFFFFFF00, 0.5, 0.9, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_set_text_overlay_outline(GetHandle(), clip_id, false);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_set_text_overlay_shadow(GetHandle(), clip_id, true);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ }
+#endif
+
+ // Add effect
+ ret = mediaeditor_add_effect(GetHandle(), MEDIAEDITOR_EFFECT_VIDEO_TYPE_AGINGTV, layer_id[VIDEO_LAYER],
+ G_TIME_SPAN_MILLISECOND * 0, duration[0], &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_start_render(GetHandle(), "/tmp/out_bgm_trans.ogv", RenderCompletedCb, GetHandle());
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ WaitForAsync();
+}
+
+TEST_F(MediaeditorCompositionTest, DISABLED_mp4_concat_many)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ for (int i = 0 ; i < (int)(G_TIME_SPAN_MILLISECOND * 10) ; i += 100) {
+ LOGD("offset : %d", i);
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, i, 100, G_TIME_SPAN_MILLISECOND * 20 - i, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ }
+
+ ret = mediaeditor_start_render(GetHandle(), "/tmp/out_concat_many.ogv", RenderCompletedCb, GetHandle());
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ WaitForAsync();
+}
--- /dev/null
+/*
+ * 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 MediaeditorEffectTest : public MediaeditorTestBase {
+public:
+ MediaeditorEffectTest() {}
+};
+
+TEST_F(MediaeditorEffectTest, DISABLED_effect_add_remove)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int path_clip_id = 0;
+ unsigned int effect_clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &path_clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_effect(GetHandle(), MEDIAEDITOR_EFFECT_VIDEO_TYPE_AGINGTV, layer_id, start, duration, &effect_clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_remove_effect(GetHandle(), effect_clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorEffectTest, DISABLED_audio_fade_in_out)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 400;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_effect(GetHandle(), MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_IN, 0, start, duration / 2, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_effect(GetHandle(), MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_OUT, 0, start + (duration / 2), duration / 2, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
--- /dev/null
+/*
+ * 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 MediaeditorLayerTest : public MediaeditorTestBase {
+public:
+ MediaeditorLayerTest() {}
+
+ static void LayerPriorityChangedCb(mediaeditor_layer_info_s *layer_info, unsigned int size, void *user_data)
+ {
+ for (unsigned int i = 0 ; i < size ; i++)
+ LOGD("layer id[%d], priority[%d]", layer_info[i].id, layer_info[i].priority);
+ }
+};
+
+TEST_F(MediaeditorLayerTest, DISABLED_layer_add)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority[2] = { 0 };
+
+ for (int i = 0 ; i < 2 ; i++) {
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority[i]);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ ASSERT_EQ(layer_priority[i], i);
+ }
+}
+
+TEST_F(MediaeditorLayerTest, DISABLED_layer_remove)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id[4] = { 0 };
+ unsigned int layer_priority;
+
+ ret = mediaeditor_set_layer_priority_changed_cb(GetHandle(), LayerPriorityChangedCb, NULL);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ for (int i = 0 ; i < 4 ; i++) {
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id[i], &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ }
+
+ ret = mediaeditor_remove_layer(GetHandle(), layer_id[0]);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_remove_layer(GetHandle(), layer_id[1]);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorLayerTest, DISABLED_layer_remove_n)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ ret = mediaeditor_remove_layer(GetHandle(), 0);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_INVALID_PARAMETER);
+
+ ret = mediaeditor_remove_layer(GetHandle(), 10);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_INVALID_PARAMETER);
+}
+
+TEST_F(MediaeditorLayerTest, DISABLED_layer_move)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id[4] = { 0 };
+ unsigned int layer_priority = 0;
+
+ ret = mediaeditor_set_layer_priority_changed_cb(GetHandle(), LayerPriorityChangedCb, NULL);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ for (int i = 0 ; i < 4 ; i++) {
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id[i], &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ }
+
+ ret = mediaeditor_move_layer(GetHandle(), layer_id[0], 3);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_move_layer(GetHandle(), layer_id[0], 2);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorLayerTest, DISABLED_layer_move_to_bottom)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id[3] = { 0 };
+ unsigned int layer_priority = 0;
+
+ for (int i = 0 ; i < 3 ; i++) {
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id[i], &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+ }
+
+ ret = mediaeditor_get_layer_lowest_priority(GetHandle(), &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_move_layer(GetHandle(), layer_id[0], layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorLayerTest, DISABLED_layer_activate_deactivate)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_activate_layer(GetHandle(), layer_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_deactivate_layer(GetHandle(), layer_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_remove_layer(GetHandle(), layer_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_activate_layer(GetHandle(), layer_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_INVALID_PARAMETER);
+
+ ret = mediaeditor_deactivate_layer(GetHandle(), layer_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_INVALID_PARAMETER);
+}
--- /dev/null
+/*
+ * 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[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ auto ret = -1;
+ ret = RUN_ALL_TESTS();
+
+ return ret;
+}
--- /dev/null
+/*
+ * 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"
+
+static GMainLoop *mainloop;
+
+class MediaeditorProjectTest : public MediaeditorTestBase {
+public:
+ MediaeditorProjectTest() {}
+
+ static void TimelineLoadedCb(void *user_data)
+ {
+ LOGD("Enter");
+ if (mainloop)
+ g_main_loop_quit(mainloop);
+ }
+
+ static void WaitForAsync()
+ {
+ mainloop = g_main_loop_new(NULL, TRUE);
+
+ LOGD("Wait timeline loaded cb");
+ g_main_loop_run(mainloop);
+
+ g_main_loop_unref(mainloop);
+ mainloop = NULL;
+ }
+};
+
+TEST_F(MediaeditorProjectTest, DISABLED_project_create_save)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+ unsigned int start = 0;
+ unsigned int duration = 100;
+ unsigned int in_point = 0;
+
+ ret = mediaeditor_create_project(GetHandle(), GetProjectPath(0).c_str());
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_layer(GetHandle(), &layer_id, &layer_priority);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_clip(GetHandle(), GetMp4Path(0).c_str(), layer_id, start, duration, in_point, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_add_effect(GetHandle(), MEDIAEDITOR_EFFECT_VIDEO_TYPE_AGINGTV, layer_id, start, duration, &clip_id);
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ ret = mediaeditor_save_project(GetHandle());
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
+
+TEST_F(MediaeditorProjectTest, DISABLED_project_load_save)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ ret = mediaeditor_load_project(GetHandle(), GetProjectPath(1).c_str(), TimelineLoadedCb, GetHandle());
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+
+ WaitForAsync();
+
+ ret = mediaeditor_save_project(GetHandle());
+ ASSERT_TRUE(ret == MEDIAEDITOR_ERROR_NONE);
+}
--- /dev/null
+/*
+ * 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_EDITOR_H__
+#define __TIZEN_MEDIA_EDITOR_H__
+
+#include <tizen.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file media_editor.h
+ * @brief This file contains the media editor API, related structures and enumerations.
+ * @since_tizen 7.0
+ */
+
+/**
+ * @addtogroup CAPI_MEDIA_EDITOR_MODULE
+ * @{
+ */
+
+#ifndef TIZEN_ERROR_MEDIA_EDITOR
+#define TIZEN_ERROR_MEDIA_EDITOR -0x030D0000 /* It'll be removed at end of ACR process */
+#endif
+
+/**
+ * @brief Enumeration for the error codes of media editor.
+ * @since_tizen 7.0
+ */
+typedef enum {
+ MEDIAEDITOR_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */
+ MEDIAEDITOR_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< Not supported */
+ MEDIAEDITOR_ERROR_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION, /**< Internal error */
+ MEDIAEDITOR_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */
+ MEDIAEDITOR_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED, /**< The access to the resources can not be granted*/
+ MEDIAEDITOR_ERROR_INVALID_STATE = TIZEN_ERROR_MEDIA_EDITOR | 0x01, /**< Invalid state */
+ MEDIAEDITOR_ERROR_RESOURCE_CONFLICT = TIZEN_ERROR_MEDIA_EDITOR | 0x02, /**< Blocked by resource conflicted */
+ MEDIAEDITOR_ERROR_RESOURCE_FAILED = TIZEN_ERROR_MEDIA_EDITOR | 0x03, /**< Blocked by resource failed */
+} mediaeditor_error_e;
+
+/**
+ * @brief Enumeration for the media editor state.
+ * @since_tizen 7.0
+ */
+typedef enum {
+ MEDIAEDITOR_STATE_IDLE, /**< Created, but not started to render */
+ MEDIAEDITOR_STATE_RENDERING, /**< Start to rendering and saving */
+ MEDIAEDITOR_STATE_PREVIEW /**< Start to preview without saving */
+} mediaeditor_state_e;
+
+/**
+ * @brief Enumeration for the media editor display type.
+ * @since_tizen 7.0
+ */
+typedef enum {
+ MEDIAEDITOR_DISPLAY_TYPE_OVERLAY, /**< Overlay surface display */
+ MEDIAEDITOR_DISPLAY_TYPE_EVAS, /**< Evas object surface display */
+ MEDIAEDITOR_DISPLAY_TYPE_ECORE, /**< Ecore object surface display */
+ MEDIAEDITOR_DISPLAY_TYPE_NONE /**< This disposes of buffers */
+} mediaeditor_display_type_e;
+
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup CAPI_MEDIA_EDITOR_EFFECT_MODULE
+ * @{
+ */
+
+/**
+ * @brief Enumeration for the transition type.
+ * @since_tizen 7.0
+ */
+typedef enum {
+ MEDIAEDITOR_TRANSITION_TYPE_NONE = 0, /**< Transition none */
+ MEDIAEDITOR_TRANSITION_TYPE_BAR_WIPE_LR = 1, /**< A bar moves from left to right */
+ MEDIAEDITOR_TRANSITION_TYPE_BAR_WIPE_TB = 2, /**< A bar moves from top to bottom */
+ MEDIAEDITOR_TRANSITION_TYPE_BOX_WIPE_TL = 3, /**< A box expands from the upper-left corner to the lower-right corner */
+ MEDIAEDITOR_TRANSITION_TYPE_BOX_WIPE_TR = 4, /**< A box expands from the upper-right corner to the lower-left corner */
+ MEDIAEDITOR_TRANSITION_TYPE_BOX_WIPE_BR = 5, /**< A box expands from the lower-right corner to the upper-left corner */
+ MEDIAEDITOR_TRANSITION_TYPE_BOX_WIPE_BL = 6, /**< A box expands from the lower-left corner to the upper-right corner */
+ MEDIAEDITOR_TRANSITION_TYPE_FOUR_BOX_WIPE_CI = 7, /**< A box shape expands from each of the four corners toward the center */
+ MEDIAEDITOR_TRANSITION_TYPE_FOUR_BOX_WIPE_CO = 8, /**< A box shape expands from the center of each quadrant toward the corners of each quadrant */
+ MEDIAEDITOR_TRANSITION_TYPE_BARNDOOR_V = 21, /**< A central, vertical line splits and expands toward the left and right edges */
+ MEDIAEDITOR_TRANSITION_TYPE_BARNDOOR_H = 22, /**< A central, horizontal line splits and expands toward the top and bottom edges */
+ MEDIAEDITOR_TRANSITION_TYPE_BOX_WIPE_TC = 23, /**< A box expands from the top edge's midpoint to the bottom corners */
+ MEDIAEDITOR_TRANSITION_TYPE_BOX_WIPE_RC = 24, /**< A box expands from the right edge's midpoint to the left corners */
+ MEDIAEDITOR_TRANSITION_TYPE_BOX_WIPE_BC = 25, /**< A box expands from the bottom edge's midpoint to the top corners */
+ MEDIAEDITOR_TRANSITION_TYPE_BOX_WIPE_LC = 26, /**< A box expands from the left edge's midpoint to the right corners */
+ MEDIAEDITOR_TRANSITION_TYPE_DIAGONAL_TL = 41, /**< A diagonal line moves from the upper-left corner to the lower-right corner */
+ MEDIAEDITOR_TRANSITION_TYPE_DIAGONAL_TR = 42, /**< A diagonal line moves from the upper right corner to the lower-left corner */
+ MEDIAEDITOR_TRANSITION_TYPE_BOWTIE_V = 43, /**< Two wedge shapes slide in from the top and bottom edges toward the center */
+ MEDIAEDITOR_TRANSITION_TYPE_BOWTIE_H = 44, /**< Two wedge shapes slide in from the left and right edges toward the center */
+ MEDIAEDITOR_TRANSITION_TYPE_BARNDOOR_DBL = 45, /**< A diagonal line from the lower-left to upper-right corners splits and expands toward the opposite corners */
+ MEDIAEDITOR_TRANSITION_TYPE_BARNDOOR_DTL = 46, /**< A diagonal line from upper-left to lower-right corners splits and expands toward the opposite corners */
+ MEDIAEDITOR_TRANSITION_TYPE_MISC_DIAGONAL_DBD = 47, /**< Four wedge shapes split from the center and retract toward the four edges */
+ MEDIAEDITOR_TRANSITION_TYPE_MISC_DIAGONAL_DD = 48, /**< A diamond connecting the four edge midpoints simultaneously contracts toward the center and expands toward the edges */
+ MEDIAEDITOR_TRANSITION_TYPE_VEE_D = 61, /**< A wedge shape moves from top to bottom */
+ MEDIAEDITOR_TRANSITION_TYPE_VEE_L = 62, /**< A wedge shape moves from right to left */
+ MEDIAEDITOR_TRANSITION_TYPE_VEE_U = 63, /**< A wedge shape moves from bottom to top */
+ MEDIAEDITOR_TRANSITION_TYPE_VEE_R = 64, /**< A wedge shape moves from left to right */
+ MEDIAEDITOR_TRANSITION_TYPE_BARNVEE_D = 65, /**< A 'V' shape extending from the bottom edge's midpoint to the opposite corners contracts toward the center and expands toward the edges */
+ MEDIAEDITOR_TRANSITION_TYPE_BARNVEE_L = 66, /**< A 'V' shape extending from the left edge's midpoint to the opposite corners contracts toward the center and expands toward the edges */
+ MEDIAEDITOR_TRANSITION_TYPE_BARNVEE_U = 67, /**< A 'V' shape extending from the top edge's midpoint to the opposite corners contracts toward the center and expands toward the edges */
+ MEDIAEDITOR_TRANSITION_TYPE_BARNVEE_R = 68, /**< A 'V' shape extending from the right edge's midpoint to the opposite corners contracts toward the center and expands toward the edges */
+ MEDIAEDITOR_TRANSITION_TYPE_IRIS_RECT = 101, /**< A rectangle expands from the center. */
+ MEDIAEDITOR_TRANSITION_TYPE_CLOCK_CW12 = 201, /**< A radial hand sweeps clockwise from the twelve o'clock position */
+ MEDIAEDITOR_TRANSITION_TYPE_CLOCK_CW3 = 202, /**< A radial hand sweeps clockwise from the three o'clock position */
+ MEDIAEDITOR_TRANSITION_TYPE_CLOCK_CW6 = 203, /**< A radial hand sweeps clockwise from the six o'clock position */
+ MEDIAEDITOR_TRANSITION_TYPE_CLOCK_CW9 = 204, /**< A radial hand sweeps clockwise from the nine o'clock position */
+ MEDIAEDITOR_TRANSITION_TYPE_PINWHEEL_TBV = 205, /**< Two radial hands sweep clockwise from the twelve and six o'clock positions */
+ MEDIAEDITOR_TRANSITION_TYPE_PINWHEEL_TBH = 206, /**< Two radial hands sweep clockwise from the nine and three o'clock positions */
+ MEDIAEDITOR_TRANSITION_TYPE_PINWHEEL_FB = 207, /**< Four radial hands sweep clockwise */
+ MEDIAEDITOR_TRANSITION_TYPE_FAN_CT = 211, /**< A fan unfolds from the top edge, the fan axis at the center */
+ MEDIAEDITOR_TRANSITION_TYPE_FAN_CR = 212, /**< A fan unfolds from the right edge, the fan axis at the center */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLEFAN_FOV = 213, /**< Two fans, their axes at the center, unfold from the top and bottom */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLEFAN_FOH = 214, /**< Two fans, their axes at the center, unfold from the left and right */
+ MEDIAEDITOR_TRANSITION_TYPE_SINGLESWEEP_CWT = 221, /**< A radial hand sweeps clockwise from the top edge's midpoint */
+ MEDIAEDITOR_TRANSITION_TYPE_SINGLESWEEP_CWR = 222, /**< A radial hand sweeps clockwise from the right edge's midpoint */
+ MEDIAEDITOR_TRANSITION_TYPE_SINGLESWEEP_CWB = 223, /**< A radial hand sweeps clockwise from the bottom edge's midpoint */
+ MEDIAEDITOR_TRANSITION_TYPE_SINGLESWEEP_CWL = 224, /**< A radial hand sweeps clockwise from the left edge's midpoint */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLESWEEP_PV = 225, /**< Two radial hands sweep clockwise and counter-clockwise from the top and bottom edges' midpoints */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLESWEEP_PD = 226, /**< Two radial hands sweep clockwise and counter-clockwise from the left and right edges' midpoints */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLESWEEP_OV = 227, /**< Two radial hands attached at the top and bottom edges' midpoints sweep from right to left */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLESWEEP_OH = 228, /**< Two radial hands attached at the left and right edges' midpoints sweep from top to bottom */
+ MEDIAEDITOR_TRANSITION_TYPE_FAN_T = 231, /**< A fan unfolds from the bottom, the fan axis at the top edge's midpoint */
+ MEDIAEDITOR_TRANSITION_TYPE_FAN_R = 232, /**< A fan unfolds from the left, the fan axis at the right edge's midpoint */
+ MEDIAEDITOR_TRANSITION_TYPE_FAN_B = 233, /**< A fan unfolds from the top, the fan axis at the bottom edge's midpoint */
+ MEDIAEDITOR_TRANSITION_TYPE_FAN_L = 234, /**< A fan unfolds from the right, the fan axis at the left edge's midpoint */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLEFAN_FIV = 235, /**< Two fans, their axes at the top and bottom, unfold from the center */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLEFAN_FIH = 236, /**< Two fans, their axes at the left and right, unfold from the center */
+ MEDIAEDITOR_TRANSITION_TYPE_SINGLESWEEP_CWTL = 241, /**< A radial hand sweeps clockwise from the upper-left corner */
+ MEDIAEDITOR_TRANSITION_TYPE_SINGLESWEEP_CCWBL = 242, /**< A radial hand sweeps counter-clockwise from the lower-left corner */
+ MEDIAEDITOR_TRANSITION_TYPE_SINGLESWEEP_CWBR = 243, /**< A radial hand sweeps clockwise from the lower-right corner */
+ MEDIAEDITOR_TRANSITION_TYPE_SINGLESWEEP_CCWTR = 244, /**< A radial hand sweeps counter-clockwise from the upper-right corner */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLESWEEP_PDTL = 245, /**< Two radial hands attached at the upper-left and lower-right corners sweep down and up */
+ MEDIAEDITOR_TRANSITION_TYPE_DOUBLESWEEP_PDBL = 246, /**< Two radial hands attached at the lower-left and upper-right corners sweep down and up */
+ MEDIAEDITOR_TRANSITION_TYPE_SALOONDOOR_T = 251, /**< Two radial hands attached at the upper-left and upper-right corners sweep down */
+ MEDIAEDITOR_TRANSITION_TYPE_SALOONDOOR_L = 252, /**< Two radial hands attached at the upper-left and lower-left corners sweep to the right */
+ MEDIAEDITOR_TRANSITION_TYPE_SALOONDOOR_B = 253, /**< Two radial hands attached at the lower-left and lower-right corners sweep up */
+ MEDIAEDITOR_TRANSITION_TYPE_SALOONDOOR_R = 254, /**< Two radial hands attached at the upper-right and lower-right corners sweep to the left */
+ MEDIAEDITOR_TRANSITION_TYPE_WINDSHIELD_R = 261, /**< Two radial hands attached at the midpoints of the top and bottom halves sweep from right to left */
+ MEDIAEDITOR_TRANSITION_TYPE_WINDSHIELD_U = 262, /**< Two radial hands attached at the midpoints of the left and right halves sweep from top to bottom */
+ MEDIAEDITOR_TRANSITION_TYPE_WINDSHIELD_V = 263, /**< Two sets of radial hands attached at the midpoints of the top and bottom halves sweep from top to bottom and bottom to top */
+ MEDIAEDITOR_TRANSITION_TYPE_WINDSHIELD_H = 264, /**< Two sets of radial hands attached at the midpoints of the left and right halves sweep from left to right and right to left */
+ MEDIAEDITOR_TRANSITION_TYPE_CROSSFADE = 512 /**< Crossfade */
+} mediaeditor_transition_type_e;
+
+/**
+ * @brief Enumeration for the effect type.
+ * @since_tizen 7.0
+ * @remarks #MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_IN and #MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_OUT Can not be rolled back *
+ */
+typedef enum {
+ MEDIAEDITOR_EFFECT_TYPE_NONE, /**< None */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_EDGETV, /**< Applies edge detect on video */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_AGINGTV, /**< Adds age to video input using scratches and dust */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_DICETV, /**< Dices the screen up into many small squares */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_WARPTV, /**< Realtime goo'ing of the video input */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_SHAGADELICTV, /**< Makes images shagadelic */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_VERTIGOTV, /**< A loopback alpha blending effector with rotating and scaling */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_REVTV, /**< A video waveform monitor for each line of video processed */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_QUARKTV, /**< Motion dissolver */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_OPTV, /**< Optical art meets real-time video effect */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_RADIOACTV, /**< Detects a difference from previous frame and blurs it */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_STREAKTV, /**< Makes after images of moving objects */
+ MEDIAEDITOR_EFFECT_VIDEO_TYPE_RIPPLETV, /**< Makes ripple mark effect on the video input */
+ MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_IN, /**< Audio fade in */
+ MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_OUT, /**< Audio fade out */
+ MEDIAEDITOR_EFFECT_AUDIO_TYPE_ECHO /**< Adds an echo or reverb effect to an audio stream */
+} mediaeditor_effect_type_e;
+
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup CAPI_MEDIA_EDITOR_MODULE
+ * @{
+ */
+
+
+/**
+ * @brief The media editor handle.
+ * @since_tizen 7.0
+ */
+typedef void *mediaeditor_h;
+
+/**
+ * @brief The media editor display handle type.
+ * @since_tizen 7.0
+ */
+typedef void *mediaeditor_display_h;
+
+/**
+ * @brief Called when an error occurs.
+ * @details The following error codes can be received:\n
+ * #MEDIAEDITOR_ERROR_INVALID_OPERATION\n
+ * #MEDIAEDITOR_ERROR_INVALID_STATE\n
+ * #MEDIAEDITOR_ERROR_RESOURCE_CONFLICT\n
+ * #MEDIAEDITOR_ERROR_RESOURCE_FAILED
+ *
+ * @since_tizen 7.0
+ * @param[in] error The error code
+ * @param[in] state The state when error was occurred
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see mediaeditor_set_error_cb()
+ * @see mediaeditor_unset_error_cb()
+ */
+typedef void (*mediaeditor_error_cb)(mediaeditor_error_e error, mediaeditor_state_e state, void *user_data);
+
+/**
+ * @brief Called when the state of media editor is changed.
+ * @since_tizen 7.0
+ * @param[in] previous The previous state
+ * @param[in] current The current state
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see mediaeditor_set_state_changed_cb()
+ * @see mediaeditor_unset_state_changed_cb()
+ */
+typedef void (*mediaeditor_state_changed_cb)(mediaeditor_state_e previous, mediaeditor_state_e current, void *user_data);
+
+/**
+ * @brief Called when rendering output is completed.
+ * @since_tizen 7.0
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see mediaeditor_start_render()
+ */
+typedef void (*mediaeditor_render_completed_cb)(void *user_data);
+
+/**
+ * @brief Creates a new media editor handle.
+ * @since_tizen 7.0
+ * @remarks The @a editor must be released using mediaeditor_destroy().\n
+ * The timeline which is composed of a set of layers and clips will be created.
+ * @param[out] editor A newly returned handle to the media editor
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_RESOURCE_FAILED Resource manager initialization error
+ * @post @a editor state will be #MEDIAEDITOR_STATE_IDLE.
+ *
+ * @see mediaeditor_destroy()
+ */
+int mediaeditor_create(mediaeditor_h *editor);
+
+/**
+ * @brief Destroys a media editor handle and releases all its resources.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_RESOURCE_FAILED Resource manager deinitialization error
+ *
+ * @see mediaeditor_create()
+ */
+int mediaeditor_destroy(mediaeditor_h editor);
+
+/**
+ * @brief Sets a display for preview.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] type The display type
+ * @param[in] display The display handle
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_NOT_SUPPORTED The feature is not supported
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ *
+ * @see mediaeditor_start_preview()
+ */
+int mediaeditor_set_display(mediaeditor_h editor, mediaeditor_display_type_e type, mediaeditor_display_h display);
+
+/**
+ * @brief Gets the current state.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[out] state The media editor state
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see #mediaeditor_state_e
+ */
+int mediaeditor_get_state(mediaeditor_h editor, mediaeditor_state_e *state);
+
+/**
+ * @brief Starts to render with given path, asynchronously.
+ * @since_tizen 7.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/mediastorage
+ * @privilege %http://tizen.org/privilege/externalstorage
+ * @remarks If you want to access only internal storage by using this function, you should add privilege %http://tizen.org/privilege/mediastorage.\n
+ * Or if you want to access only external storage by using this function, you should add privilege %http://tizen.org/privilege/externalstorage.\n
+ * If you want to access both storage, you must add both privileges.
+ * @param[in] editor The media editor handle
+ * @param[in] path The path to save rendered output
+ * @param[in] callback The callback function to register
+ * @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 #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre User must add clip by calling mediaeditor_add_clip().
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @post @a editor state will be #MEDIAEDITOR_STATE_RENDERING.
+ * @post mediaeditor_render_completed_cb() will be invoked.
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_cancel_render()
+ */
+int mediaeditor_start_render(mediaeditor_h editor, const char *path, mediaeditor_render_completed_cb callback, void *user_data);
+
+/**
+ * @brief Cancels rendering.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_RENDERING.
+ * @post @a editor state will be #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_start_render()
+ */
+int mediaeditor_cancel_render(mediaeditor_h editor);
+
+/**
+ * @brief Starts to preview.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_NOT_SUPPORTED The feature is not supported
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre The display handle must be set by calling mediaeditor_set_display()
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @post @a editor state will be #MEDIAEDITOR_STATE_PREVIEW.
+ * @see mediaeditor_set_display()
+ */
+int mediaeditor_start_preview(mediaeditor_h editor);
+
+/**
+ * @brief Stops to preview.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_NOT_SUPPORTED The feature is not supported
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_PREVIEW.
+ * @post @a editor state will be #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_start_preview()
+ */
+int mediaeditor_stop_preview(mediaeditor_h editor);
+
+/**
+ * @brief Sets a callback function to be invoked when an asynchronous operation error occurs.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor 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 #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @post mediaeditor_error_cb() will be invoked.
+ * @see mediaeditor_unset_error_cb()
+ * @see mediaeditor_error_cb()
+ */
+int mediaeditor_set_error_cb(mediaeditor_h editor, mediaeditor_error_cb callback, void *user_data);
+
+/**
+ * @brief Unsets an error callback function.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_set_error_cb()
+ */
+int mediaeditor_unset_error_cb(mediaeditor_h editor);
+
+/**
+ * @brief Sets a callback function to be invoked when the media editor state is changed.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor 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 #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @post mediaeditor_state_changed_cb() will be invoked.
+ * @see mediaeditor_unset_state_changed_cb()
+ * @see mediaeditor_state_changed_cb()
+ */
+int mediaeditor_set_state_changed_cb(mediaeditor_h editor, mediaeditor_state_changed_cb callback, void *user_data);
+
+/**
+ * @brief Unsets a state changed callback function.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_set_state_changed_cb()
+ */
+int mediaeditor_unset_state_changed_cb(mediaeditor_h editor);
+
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup CAPI_MEDIA_EDITOR_LAYER_MODULE
+ * @{
+ */
+
+/**
+ * @brief The structure type of the layer information.
+ * @since_tizen 7.0
+ */
+typedef struct {
+ unsigned int id; /**< The layer ID */
+ unsigned int priority; /**< The layer priority */
+} mediaeditor_layer_info_s;
+
+/**
+ * @brief Called when the priority of layers is changed.
+ * @since_tizen 7.0
+ * @remarks @a layer_info should be released using free().
+ * @param[in] layer_infos The layer information including layer id and its priority. It's array.
+ * @param[in] size The number of layer_infos array
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see mediaeditor_set_layer_priority_changed_cb()
+ * @see mediaeditor_unset_layer_priority_changed_cb()
+ */
+typedef void (*mediaeditor_layer_priority_changed_cb)(mediaeditor_layer_info_s *layer_infos, unsigned int size, void *user_data);
+
+/**
+ * @brief Adds a layer to timeline.
+ * @since_tizen 7.0
+ * @remarks Layers are responsible for ordering of contained clips. The order is determined by layers' priorities.\n
+ * The layers are stacked in a hierarchical structure.\n
+ * e.g. If we have 3 layers, it will have the following hierarchy.\n
+ * <pre>
+ * Top : layer ID 1, layer priority 0 (The highest priority)
+ * layer ID 2, layer priority 1
+ * Bottom : layer ID 3, layer priority 2 (The lowest priority)
+ * </pre>
+ * Priorities are always a continuous sequence, with no numbers missing in-between.\n
+ * For example, priorities 0, 1, 3, 4 are not possible.\n
+ * But, layer Id could be a discontinuous sequence. Please refer to examples of mediaeditor_remove_layer().\n
+ * The @a layer_priority of newly added layer will be lowest priority.
+ * @param[in] editor The media editor handle
+ * @param[out] layer_id The layer ID. It'll be used when you want to control this layer.
+ * @param[out] layer_priority The layer priority represents the hierarchical ordering of contained clips.
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_remove_layer()
+ * @see mediaeditor_move_layer()
+ * @see mediaeditor_activate_layer()
+ * @see mediaeditor_deactivate_layer()
+ */
+int mediaeditor_add_layer(mediaeditor_h editor, unsigned int *layer_id, unsigned int *layer_priority);
+
+/**
+ * @brief Removes a layer from timeline.
+ * @since_tizen 7.0
+ * @remarks The other layer's layer ID is not changed after removing @a layer_id.\n
+ * If @a layer_id is not bottom layer, layer priority will be rearranged.\n
+ * e.g. There're 3 layers like the followings.\n
+ * \n
+ * case 1 : remove layer ID 3.\n
+ * The layer ID 3 is removed and other layers' priorities are not rearranged.\n
+ * <pre>
+ * | top : layer ID 1, layer priority 0 (including 1 clips)
+ * Before | layer ID 2, layer priority 1 (including 3 clips)
+ * removing | bottom : layer ID 3, layer priority 2 (including 5 clips)
+ * --------------------------------------------------------------------
+ * After | top : layer ID 1, layer priority 0 (including 1 clips)
+ * removing | bottom : layer ID 2, layer priority 1 (including 3 clips)
+ * </pre>
+ * \n
+ * case 2 : remove layer ID 1.\n
+ * <pre>
+ * | top : layer ID 1, layer priority 0 (including 1 clips)
+ * Before | layer ID 2, layer priority 1 (including 3 clips)
+ * removing | bottom : layer ID 3, layer priority 2 (including 5 clips)
+ * --------------------------------------------------------------------
+ * After | top : layer ID 2, layer priority 0 (including 3 clips)
+ * removing | bottom : layer ID 3, layer priority 1 (including 5 clips)
+ * </pre>
+ * \n
+ * * case 3 : remove layer ID 2 and add a new layer.\n
+ * <pre>
+ * | top : layer ID 1, layer priority 0 (including 1 clips)
+ * Before | layer ID 2, layer priority 1 (including 3 clips)
+ * removing | bottom : layer ID 3, layer priority 2 (including 5 clips)
+ * --------------------------------------------------------------------
+ * After | top : layer ID 1, layer priority 0 (including 1 clips)
+ * removing | bottom : layer ID 3, layer priority 1 (including 5 clips)
+ * --------------------------------------------------------------------
+ * After | top : layer ID 1, layer priority 0 (including 1 clips)
+ * adding | : layer ID 3, layer priority 1 (including 5 clips)
+ * a layer | bottom : layer ID 4, layer priority 2 (including 7 clips)
+ * </pre>
+ * @param[in] editor The media editor handle
+ * @param[in] layer_id The layer ID
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @post mediaeditor_layer_priority_changed_cb() will be invoked if it's set.
+ * @see mediaeditor_add_layer()
+ * @see mediaeditor_set_layer_priority_changed_cb()
+ * @see mediaeditor_unset_layer_priority_changed_cb()
+ */
+int mediaeditor_remove_layer(mediaeditor_h editor, unsigned int layer_id);
+
+/**
+ * @brief Moves a @a layer_id layer to @a layer_priority position.
+ * @since_tizen 7.0
+ * @remarks e.g. There're 3 layers.\n
+ * case 1 : move layer ID 1 to layer priority 0.\n
+ * Nothing happens.\n
+ * <pre>
+ * | top : layer ID 1, layer priority 0 (including 1 clips)
+ * Before | layer ID 2, layer priority 1 (including 3 clips)
+ * moving | bottom : layer ID 3, layer priority 2 (including 5 clips)
+ * ------------------------------------------------------------------
+ * | top : layer ID 1, layer priority 0 (including 1 clips)
+ * After | layer ID 2, layer priority 1 (including 3 clips)
+ * moving | bottom : layer ID 3, layer priority 2 (including 5 clips)
+ * </pre>
+ * \n
+ * case 2 : move layer ID 3 to layer priority 1.\n
+ * <pre>
+ * | top : layer ID 1, layer priority 0 (including 1 clips)
+ * Before | layer ID 2, layer priority 1 (including 3 clips)
+ * moving | bottom : layer ID 3, layer priority 2 (including 5 clips)
+ * ------------------------------------------------------------------
+ * | top : layer ID 1, layer priority 0 (including 1 clips)
+ * After | layer ID 3, layer priority 1 (including 5 clips)
+ * moving | bottom : layer ID 2, layer priority 2 (including 3 clips)
+ * </pre>
+ * \n
+ * case 3 : move layer ID 1 to layer priority 3.\n
+ * (Currently, there's no priority 3 layer.)\n
+ * <pre>
+ * | top : layer ID 1, layer priority 0 (including 1 clips)
+ * Before | layer ID 2, layer priority 1 (including 3 clips)
+ * moving | bottom : layer ID 3, layer priority 2 (including 5 clips)
+ * ------------------------------------------------------------------
+ * | top : layer ID 2, layer priority 0 (including 3 clips)
+ * After | layer ID 3, layer priority 1 (including 5 clips)
+ * moving | bottom : layer ID 1, layer priority 2 (including 1 clips)
+ * </pre>
+ * \n
+ * case 4 : move layer ID 1 to layer priority 5.\n
+ * (Currently, there's no priority 5 layer and it's not continuous sequence.)\n
+ * <pre>
+ * | top : layer ID 1, layer priority 0 (including 1 clips)
+ * Before | layer ID 2, layer priority 1 (including 3 clips)
+ * moving | bottom : layer ID 3, layer priority 2 (including 5 clips)
+ * ------------------------------------------------------------------
+ * | top : layer ID 2, layer priority 0 (including 3 clips)
+ * After | layer ID 3, layer priority 1 (including 5 clips)
+ * moving | bottom : layer ID 1, layer priority 2 (including 1 clips)
+ * </pre>
+ * \n
+ * If you can move layer to the lowest priority position, you can get the lowest priority using mediaeditor_get_layer_lowest_priority()
+ * @param[in] editor The media editor handle
+ * @param[in] layer_id The layer ID
+ * @param[in] layer_priority The target layer priority that @a layer_id is moved
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @post mediaeditor_layer_priority_changed_cb() will be invoked if it's set.
+ * @see mediaeditor_add_layer()
+ * @see mediaeditor_get_layer_lowest_priority()
+ * @see mediaeditor_set_layer_priority_changed_cb()
+ * @see mediaeditor_unset_layer_priority_changed_cb()
+ */
+int mediaeditor_move_layer(mediaeditor_h editor, unsigned int layer_id, unsigned int layer_priority);
+
+/**
+ * @brief Activates given layer on timeline. The layer will be included when it's rendered.
+ * @since_tizen 7.0
+ * @remarks Note that the newly created layer will be activated by default.
+ * @param[in] editor The media editor handle
+ * @param[in] layer_id The layer ID
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_deactivate_layer()
+ */
+int mediaeditor_activate_layer(mediaeditor_h editor, unsigned int layer_id);
+
+/**
+ * @brief Deactivates given layer on timeline.
+ * @since_tizen 7.0
+ * @remarks The layer is not removed actually but just excluded when it's rendered.
+ * @param[in] editor The media editor handle
+ * @param[in] layer_id The layer ID
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_activate_layer()
+ */
+int mediaeditor_deactivate_layer(mediaeditor_h editor, unsigned int layer_id);
+
+/**
+ * @brief Gets the priority of @a layer_id layer.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] layer_id The layer ID
+ * @param[out] layer_priority The layer priority
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mediaeditor_add_layer()
+ * @see mediaeditor_get_layer_lowest_priority()
+ * @see mediaeditor_get_layer_id()
+ */
+int mediaeditor_get_layer_priority(mediaeditor_h editor, unsigned int layer_id, unsigned int *layer_priority);
+
+/**
+ * @brief Gets the lowest priority of all layers.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[out] layer_priority The lowest layer priority
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mediaeditor_add_layer()
+ * @see mediaeditor_get_layer_priority()
+ * @see mediaeditor_get_layer_id()
+ */
+int mediaeditor_get_layer_lowest_priority(mediaeditor_h editor, unsigned int *layer_priority);
+
+/**
+ * @brief Gets the layer ID of @a layer_priority layer.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] layer_priority The layer priority
+ * @param[out] layer_id The layer ID
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mediaeditor_add_layer()
+ * @see mediaeditor_get_layer_priority()
+ * @see mediaeditor_get_layer_lowest_priority()
+ */
+int mediaeditor_get_layer_id(mediaeditor_h editor, unsigned int layer_priority, unsigned int *layer_id);
+
+/**
+ * @brief Sets a callback function to be invoked when a layer priority is changed.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor 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 #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @post mediaeditor_layer_priority_changed_cb() will be invoked.
+ * @see mediaeditor_unset_layer_priority_changed_cb()
+ * @see mediaeditor_layer_priority_changed_cb()
+ */
+int mediaeditor_set_layer_priority_changed_cb(mediaeditor_h editor, mediaeditor_layer_priority_changed_cb callback, void *user_data);
+
+/**
+ * @brief Unsets a layer priority changed callback function.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_set_layer_priority_changed_cb()
+ */
+int mediaeditor_unset_layer_priority_changed_cb(mediaeditor_h editor);
+
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup CAPI_MEDIA_EDITOR_CLIP_MODULE
+ * @{
+ */
+
+/**
+ * @brief Adds a clip to timeline.
+ * @since_tizen 7.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/mediastorage
+ * @privilege %http://tizen.org/privilege/externalstorage
+ * @remarks If you want to access only internal storage by using this function, you should add privilege %http://tizen.org/privilege/mediastorage.\n
+ * Or if you want to access only external storage by using this function, you should add privilege %http://tizen.org/privilege/externalstorage.\n
+ * If you want to access both storage, you must add both privileges.
+ * @param[in] editor The media editor handle
+ * @param[in] path The content location to add
+ * @param[in] layer_id The layer ID to add clip
+ * @param[in] start The starting position of @a path clip which is placed in timeline (in milliseconds)\n
+ * If this is less than 0, clip will be added to the end of layer. i.e. it will be set to layer's duration
+ * @param[in] duration The duration that the clip is in effect for in the timeline (in milliseconds)\n
+ * It should be lesser than or equal to clip's duration. If not, error will be occurred in runtime.\n
+ * The clip should have enough internal content greater than @a duration.
+ * @param[in] in_point The initial offset of @a path clip to use internally when outputting content (in milliseconds)
+ * @param[out] clip_id The ID of added clip. It'll be used when you want to delete this clip.
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre The layer must be added by calling mediaeditor_add_layer().
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_layer()
+ * @see mediaeditor_remove_clip()
+ */
+int mediaeditor_add_clip(mediaeditor_h editor, const char *path, unsigned int layer_id,
+ int start, unsigned int duration, unsigned int in_point, unsigned int *clip_id);
+
+/**
+ * @brief Removes a clip from timeline.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID that will be removed.
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_clip()
+ */
+int mediaeditor_remove_clip(mediaeditor_h editor, unsigned int clip_id);
+
+/**
+ * @brief Splits a clip.
+ * @since_tizen 7.0
+ * @remarks After splitting the clip into two clips, the source clip's duration will be changed.
+ * @param[in] editor The media editor handle
+ * @param[in] src_clip_id The current clip ID that will be split.
+ * @param[in] position The time position at which the source clip will be split. (in milliseconds)
+ * @param[out] new_clip_id The newly created clip ID.
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_clip()
+ */
+int mediaeditor_split_clip(mediaeditor_h editor, unsigned int src_clip_id, unsigned int position,
+ unsigned int *new_clip_id);
+
+/**
+ * @brief Groups clips.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] clip_ids The clip IDs to be grouped. Array.
+ * @param[in] size The number of clips
+ * @param[out] group_id The grouped clip ID.
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_ungroup_clip()
+ */
+int mediaeditor_group_clip(mediaeditor_h editor, unsigned int *clip_ids, unsigned int size, unsigned int *group_id);
+
+/**
+ * @brief Ungroups a clip.
+ * @since_tizen 7.0
+ * @remarks Layer priorities of ungrouped clips are the same as before grouping.\n
+ * If there's no matched @a group_id, #MEDIAEDITOR_ERROR_INVALID_PARAMETER will be returned.\n
+ * @a clip_ids should be released using free().
+ * @param[in] editor The media editor handle
+ * @param[in] group_id The grouped clip ID
+ * @param[out] clip_ids The ungrouped clip IDs. Array
+ * @param[out] size The size of ungrouped clips
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_group_clip()
+ */
+int mediaeditor_ungroup_clip(mediaeditor_h editor, unsigned int group_id, unsigned int **clip_ids, unsigned int *size);
+
+/**
+ * @brief Moves a clip to different layer.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID that will be moved
+ * @param[in] layer_priority The destination layer priority
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_clip()
+ */
+int mediaeditor_move_clip_layer(mediaeditor_h editor, unsigned int clip_id, unsigned int layer_priority);
+
+/**
+ * @brief Gets the start position of clip.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID
+ * @param[out] start The current start position of clip (in milliseconds)
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_set_clip_start()
+ * @see mediaeditor_get_clip_duration()
+ * @see mediaeditor_set_clip_duration()
+ * @see mediaeditor_get_clip_in_point()
+ * @see mediaeditor_set_clip_in_point()
+ */
+int mediaeditor_get_clip_start(mediaeditor_h editor, unsigned int clip_id, unsigned int *start);
+
+/**
+ * @brief Sets the start position of clip.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID that will be changed
+ * @param[in] start The new start position (in milliseconds)
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_get_clip_start()
+ * @see mediaeditor_get_clip_duration()
+ * @see mediaeditor_set_clip_duration()
+ * @see mediaeditor_get_clip_in_point()
+ * @see mediaeditor_set_clip_in_point()
+ */
+int mediaeditor_set_clip_start(mediaeditor_h editor, unsigned int clip_id, unsigned int start);
+
+/**
+ * @brief Gets the duration of clip.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID
+ * @param[out] duration The current time duration of clip (in milliseconds)
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_get_clip_start()
+ * @see mediaeditor_set_clip_start()
+ * @see mediaeditor_set_clip_duration()
+ * @see mediaeditor_get_clip_in_point()
+ * @see mediaeditor_set_clip_in_point()
+ */
+int mediaeditor_get_clip_duration(mediaeditor_h editor, unsigned int clip_id, unsigned int *duration);
+
+/**
+ * @brief Sets the duration of clip.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID that will be changed
+ * @param[in] duration The new time duration (in milliseconds)
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_get_clip_start()
+ * @see mediaeditor_set_clip_start()
+ * @see mediaeditor_get_clip_duration()
+ * @see mediaeditor_get_clip_in_point()
+ * @see mediaeditor_set_clip_in_point()
+ */
+int mediaeditor_set_clip_duration(mediaeditor_h editor, unsigned int clip_id, unsigned int duration);
+
+/**
+ * @brief Gets the offset of clip.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID that will be changed
+ * @param[out] in_point The offset of clip to use internally when outputting content (in milliseconds)
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_get_clip_start()
+ * @see mediaeditor_set_clip_start()
+ * @see mediaeditor_get_clip_duration()
+ * @see mediaeditor_set_clip_duration()
+ * @see mediaeditor_set_clip_in_point()
+ */
+int mediaeditor_get_clip_in_point(mediaeditor_h editor, unsigned int clip_id, unsigned int *in_point);
+
+/**
+ * @brief Sets the offset of clip.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID
+ * @param[in] in_point The offset of clip to use internally when outputting content (in milliseconds)
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_get_clip_start()
+ * @see mediaeditor_set_clip_start()
+ * @see mediaeditor_get_clip_duration()
+ * @see mediaeditor_set_clip_duration()
+ * @see mediaeditor_get_clip_in_point()
+ */
+int mediaeditor_set_clip_in_point(mediaeditor_h editor, unsigned int clip_id, unsigned int in_point);
+
+/**
+ * @brief Gets the resolution of clip.
+ * @since_tizen 7.0
+ * @remarks If the clip doesn't have video, #MEDIAEDITOR_ERROR_INVALID_OPERATION will be returned.
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID
+ * @param[out] width The width
+ * @param[out] height The height
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_set_clip_resolution()
+ */
+int mediaeditor_get_clip_resolution(mediaeditor_h editor, unsigned int clip_id, unsigned int *width, unsigned int *height);
+
+/**
+ * @brief Sets the resolution of clip.
+ * @since_tizen 7.0
+ * @remarks If the clip doesn't have video, #MEDIAEDITOR_ERROR_INVALID_OPERATION will be returned.
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID
+ * @param[in] width The width
+ * @param[in] height The height
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_get_clip_resolution()
+ */
+int mediaeditor_set_clip_resolution(mediaeditor_h editor, unsigned int clip_id, unsigned int width, unsigned int height);
+
+/**
+ * @brief Gets the volume of clip.
+ * @since_tizen 7.0
+ * @remarks If the clip doesn't have audio, #MEDIAEDITOR_ERROR_INVALID_OPERATION will be returned.
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID
+ * @param[out] volume The current audio volume (0.0 ~ 10.0)
+ * The default value is 1.0 (100%)
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_set_clip_volume()
+ */
+int mediaeditor_get_clip_volume(mediaeditor_h editor, unsigned int clip_id, double *volume);
+
+/**
+ * @brief Sets the volume of clip.
+ * @since_tizen 7.0
+ * @remarks If the clip doesn't have audio, #MEDIAEDITOR_ERROR_INVALID_OPERATION will be returned.
+ * @param[in] editor The media editor handle
+ * @param[in] clip_id The clip ID
+ * @param[in] volume The new audio volume (0.0 ~ 10.0)\n
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_clip()
+ * @see mediaeditor_get_clip_volume()
+ */
+int mediaeditor_set_clip_volume(mediaeditor_h editor, unsigned int clip_id, double volume);
+
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup CAPI_MEDIA_EDITOR_EFFECT_MODULE
+ * @{
+ */
+
+/**
+ * @brief Adds transition effect to the overlapped clip section.
+ * @since_tizen 7.0
+ * @remarks Note that clips should be already overlapped. If not, error will be occurred.\n
+ * Only one transition effect can be applied in the overlapped section.\n
+ * For each overlapped section, this function should be called to apply transition effect.
+ * @param[in] editor The media editor handle
+ * @param[in] type The transition type
+ * @param[in] layer_id The layer ID
+ * @param[in] start The start position of overlapped clip area to be applied transition effect
+ * @param[in] duration The duration of overlapped clip area to be applied transition effect
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_layer()
+ * @see mediaeditor_add_clip()
+ */
+int mediaeditor_add_transition(mediaeditor_h editor, mediaeditor_transition_type_e type, unsigned int layer_id,
+ unsigned int start, unsigned int duration);
+
+/**
+ * @brief Adds a new effect.
+ * @since_tizen 7.0
+ * @remarks #MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_IN and #MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_OUT can not be rolled back using
+ * mediaeditor_remove_effect().
+ * @param[in] editor The media editor handle
+ * @param[in] type The effect type
+ * @param[in] layer_id The layer priority
+ * @param[in] start The starting position of effect which is placed in timeline (in milliseconds)
+ * @param[in] duration The duration of effect (in milliseconds)
+ * @param[out] effect_id The effect ID
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_remove_effect()
+ */
+int mediaeditor_add_effect(mediaeditor_h editor, mediaeditor_effect_type_e type, unsigned int layer_id,
+ unsigned int start, unsigned int duration, unsigned int *effect_id);
+
+/**
+ * @brief Removes an effect from timeline.
+ * @since_tizen 7.0
+ * @param[in] editor The media editor handle
+ * @param[in] effect_id The effect ID
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_add_effect()
+ */
+int mediaeditor_remove_effect(mediaeditor_h editor, unsigned int effect_id);
+
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup CAPI_MEDIA_EDITOR_PROJECT_MODULE
+ * @{
+ */
+
+/**
+ * @brief Called when a timeline of the project is loaded.
+ * @since_tizen 7.0
+ * @param[in] user_data The user data passed from the callback registration function
+ * @see mediaeditor_load_project()
+ */
+typedef void (*mediaeditor_project_loaded_cb)(void *user_data);
+
+/**
+ * @brief Creates a project to @a path.
+ * @since_tizen 7.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/mediastorage
+ * @privilege %http://tizen.org/privilege/externalstorage
+ * @remarks If you want to access only internal storage by using this function, you should add privilege %http://tizen.org/privilege/mediastorage.\n
+ * Or if you want to access only external storage by using this function, you should add privilege %http://tizen.org/privilege/externalstorage.\n
+ * If you want to access both storage, you must add both privileges.
+ * @param[in] editor The media editor handle
+ * @param[in] path The path to create project
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_PERMISSION_DENIED The access to the resources can not be granted
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_save_project()
+ */
+int mediaeditor_create_project(mediaeditor_h editor, const char *path);
+
+/**
+ * @brief Loads a project from @a path, asynchronously.
+ * @since_tizen 7.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/mediastorage
+ * @privilege %http://tizen.org/privilege/externalstorage
+ * @remarks If you want to access only internal storage by using this function, you should add privilege %http://tizen.org/privilege/mediastorage.\n
+ * Or if you want to access only external storage by using this function, you should add privilege %http://tizen.org/privilege/externalstorage.\n
+ * If you want to access both storage, you must add both privileges.
+ * @param[in] editor The media editor handle
+ * @param[in] path The path to the saved project
+ * @param[in] callback The callback function to register
+ * @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 #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_PERMISSION_DENIED The access to the resources can not be granted
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @post mediaeditor_project_loaded_cb() will be invoked.
+ * @see mediaeditor_save_project()
+ */
+int mediaeditor_load_project(mediaeditor_h editor, const char *path, mediaeditor_project_loaded_cb callback, void *user_data);
+
+/**
+ * @brief Saves the current editing information.
+ * @since_tizen 7.0
+ * @remarks The project will be saved to the project's path, which was set during creation or loading.
+ * @param[in] editor The media editor handle
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @pre The project must be created or loaded.
+ * @see mediaeditor_create_project()
+ * @see mediaeditor_load_project()
+ */
+int mediaeditor_save_project(mediaeditor_h editor);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIZEN_MEDIA_EDITOR_H__ */
--- /dev/null
+/*
+ * 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_EDITOR_INTERNAL_H__
+#define __TIZEN_MEDIA_EDITOR_INTERNAL_H__
+
+#include <media_editor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal
+ * @brief Sets the ecore wayland video display.
+ * @since_tizen 7.0
+ * @remarks This function must be called in main thread of the application.
+ * Otherwise, it will return #MEDIAEDITOR_ERROR_INVALID_OPERATION by internal restriction.
+ * To avoid #MEDIAEDITOR_ERROR_INVALID_OPERATION in sub thread, ecore_thread_main_loop_begin() and
+ * ecore_thread_main_loop_end() can be used, but deadlock can occur if the main thread is busy.
+ * So, it's not recommended to use them.
+ * @param[in] editor The handle to the mediaeditor
+ * @param[in] ecore_wl_window The ecore wayland window handle
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #MEDIAEDITOR_ERROR_NONE Successful
+ * @retval #MEDIAEDITOR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MEDIAEDITOR_ERROR_INVALID_STATE Invalid state
+ * @retval #MEDIAEDITOR_ERROR_INVALID_OPERATION Invalid operation
+ * @pre @a editor state must be set to #MEDIAEDITOR_STATE_IDLE.
+ * @see mediaeditor_start_preview()
+ * @see ecore_thread_main_loop_begin()
+ * @see ecore_thread_main_loop_end()
+ */
+int mediaeditor_set_ecore_wl_display(mediaeditor_h editor, void *ecore_wl_window);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIZEN_MEDIA_EDITOR_INTERNAL_H__ */
--- /dev/null
+/*
+ * 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_EDITOR_PRIVATE_H__
+#define __TIZEN_MEDIA_EDITOR_PRIVATE_H__
+
+#include <stdio.h>
+#include <dlog.h>
+
+#include <gst/gst.h>
+#include <ges/ges.h>
+
+#include <iniparser.h>
+#include <mm_display_interface.h>
+#ifndef TIZEN_TV
+#include <mm_resource_manager.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "TIZEN_N_MEDIAEDITOR"
+
+#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_PURPLE "\033[35m"
+
+#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_WITH_UNLOCK_IF(expr, mutex, fmt, arg...) \
+do { \
+ if ((expr)) { \
+ LOG_ERROR(""fmt"", ##arg); \
+ g_mutex_unlock(mutex); \
+ return; \
+ } \
+} while (0)
+
+#define RET_VAL_WITH_UNLOCK_IF(expr, val, mutex, fmt, arg...) \
+do { \
+ if ((expr)) { \
+ LOG_ERROR(""fmt"", ##arg); \
+ g_mutex_unlock(mutex); \
+ return (val);\
+ } \
+} while (0)
+
+#define RET_ERR_IF_FEATURE_IS_NOT_SUPPORTED(x_feature) \
+do { \
+ int fret = _check_feature(x_feature); \
+ if (fret != MEDIAEDITOR_ERROR_NONE) return fret; \
+} while (0)
+
+#define SAFE_FREE(x) { if (x) { free(x); x = NULL; } }
+#define SAFE_G_FREE(x) { if (x) { g_free(x); x = NULL; } }
+
+#define GENERATE_DOT(x_mediaeditor, x_fmt, x_arg...) \
+do { \
+ gchar *dot_name; \
+ if (!x_mediaeditor->ini.general.generate_dot) break; \
+ dot_name = g_strdup_printf(""x_fmt"", x_arg); \
+ _generate_dot(x_mediaeditor, dot_name); \
+ g_free(dot_name); \
+} while (0)
+
+#define URI_TO_PATH_OFFSET 7
+
+#define MILLI_TO_NANO(x) ((guint64)((x) * G_GUINT64_CONSTANT(1000000)))
+#define NANO_TO_MILLI(x) ((unsigned int)((x) / G_GUINT64_CONSTANT(1000000)))
+
+#define CONTAINER_TYPE_OGG "ogg"
+#define CONTAINER_TYPE_MPEG "mpeg"
+#define AUDIO_ENCODER_TYPE_VORBIS "vorbis"
+#define AUDIO_ENCODER_TYPE_MPEG "mpeg"
+#define VIDEO_ENCODER_TYPE_THEORA "theora"
+#define VIDEO_ENCODER_TYPE_MPEG "mpeg"
+
+#define CONTAINER_MIME_TYPE_OGG "application/ogg"
+#define CONTAINER_MIME_TYPE_MPEG "video/quicktime,variant=iso"
+#define AUDIO_ENCODER_MIME_TYPE_VORBIS "audio/x-vorbis"
+#define AUDIO_ENCODER_MIME_TYPE_MPEG "audio/mpeg,channels=2,rate=44100,mpegversion=4,stream-format=raw,base-profile=lc"
+#define VIDEO_ENCODER_MIME_TYPE_THEORA "video/x-theora"
+#define VIDEO_ENCODER_MIME_TYPE_MPEG "video/mpeg,mpegversion=4,chroma-site=raw"
+
+#define MEDIAEDITOR_CLIP(x) (((mediaeditor_clip_s *)((x)->data))->clip)
+#define MEDIAEDITOR_GROUP(x) (((mediaeditor_group_s *)((x)->data))->group)
+
+#ifndef TIZEN_TV
+#define RESOURCE_TYPE_MAX MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_ENCODER + 1
+
+typedef struct _mediaeditor_resource_s {
+ mm_resource_manager_h mgr;
+ mm_resource_manager_res_h res[RESOURCE_TYPE_MAX];
+ gboolean need_to_acquire[RESOURCE_TYPE_MAX];
+ gboolean release_cb_is_calling;
+} mediaeditor_resource_s;
+#endif
+
+typedef struct _mediaeditor_callbacks_s {
+ void *callback;
+ void *user_data;
+} mediaeditor_callbacks_s;
+
+typedef struct _mediaeditor_gst_s {
+ GESPipeline *pipeline;
+ GESTimeline *timeline;
+ GESProject *project;
+
+ GstBus *bus;
+} mediaeditor_gst_s;
+
+typedef struct _ini_item_general_s {
+ bool generate_dot;
+ const char *dot_path;
+ gchar **gst_args;
+} ini_item_general_s;
+
+typedef struct _ini_item_encoder_s {
+ const char *container;
+ const char *audio_hw_encoder;
+ const char *video_hw_encoder;
+} ini_item_encoder_s;
+
+typedef struct _ini_item_decoder_s {
+ const char *audio_hw_decoder;
+ const char *video_hw_decoder;
+} ini_item_decoder_s;
+
+typedef struct _ini_item_resource_acquisition_s {
+ bool video_encoder;
+ bool video_decoder;
+} ini_item_resource_acquisition_s;
+
+typedef struct _mediaeditor_ini_s {
+ dictionary *dict;
+ ini_item_general_s general;
+ ini_item_encoder_s encoder;
+ ini_item_decoder_s decoder;
+ ini_item_resource_acquisition_s resource_acquisition;
+} mediaeditor_ini_s;
+
+typedef struct _mediaeditor_display {
+ GMutex mutex;
+
+ int type;
+ int overlay_surface_id;
+
+ mediaeditor_display_h object; // The window object to display
+ mm_display_interface_h mm_display;
+} mediaeditor_display_s;
+
+typedef struct _mediaeditor_effect_s {
+ GESEffect *effect;
+ unsigned int id;
+} mediaeditor_effect_s;
+
+typedef struct _mediaeditor_layer_s {
+ GESLayer *layer;
+ unsigned int id;
+} mediaeditor_layer_s;
+
+typedef struct _mediaeditor_clip_s {
+ GESClip *clip;
+ unsigned int id;
+ mediaeditor_effect_type_e effect_type;
+} mediaeditor_clip_s;
+
+typedef struct _mediaeditor_group_s {
+ GESGroup *group;
+ unsigned int id;
+} mediaeditor_group_s;
+
+typedef enum _idle_cb_type_e {
+ IDLE_CB_TYPE_STATE,
+ IDLE_CB_TYPE_ERROR,
+ IDLE_CB_TYPE_NUM
+} idle_cb_type_e;
+
+typedef struct _mediaeditor_s {
+ GMutex mutex;
+ GMutex event_src_mutex;
+ GCond cond;
+
+ mediaeditor_gst_s gst;
+
+ mediaeditor_state_e state;
+ mediaeditor_state_e pend_state;
+
+ mediaeditor_callbacks_s error_cb;
+ mediaeditor_callbacks_s state_changed_cb;
+ mediaeditor_callbacks_s layer_priority_changed_cb;
+ mediaeditor_callbacks_s render_completed_cb;
+ mediaeditor_callbacks_s project_loaded_cb;
+
+ guint idle_cb_event_source_ids[IDLE_CB_TYPE_NUM];
+
+ mediaeditor_ini_s ini;
+
+#ifndef TIZEN_TV
+ mediaeditor_resource_s resource;
+#endif
+ mediaeditor_display_s *display;
+
+ GList *layers;
+ GList *clips;
+ GList *groups;
+ GSList *audio_fade_effects;
+
+ unsigned int layer_id;
+ unsigned int clip_id;
+ unsigned int group_id;
+} mediaeditor_s;
+
+/* media_editor_private */
+void _invoke_error_cb(mediaeditor_s *editor, mediaeditor_error_e error);
+void _post_error_cb_in_idle(mediaeditor_s *editor, mediaeditor_error_e error);
+void _remove_remained_event_sources(mediaeditor_s *editor);
+int _gst_init(mediaeditor_s *editor);
+int _gst_pipeline_set_state(mediaeditor_s *editor, GstState state);
+void _gst_pipeline_destroy(mediaeditor_s *editor);
+int _mediaeditor_stop(mediaeditor_s *editor);
+int _mediaeditor_create_pipeline(mediaeditor_s *editor);
+int _mediaeditor_start_render(mediaeditor_s *editor, const char *path);
+int _mediaeditor_cancel_render(mediaeditor_s *editor);
+int _mediaeditor_start_preview(mediaeditor_s *editor);
+int _mediaeditor_stop_preview(mediaeditor_s *editor);
+int _check_privilege(const char *path, bool file_exist, int mode);
+int _check_feature(const char *feature);
+
+#ifndef TIZEN_TV
+/* media_editor_resource */
+int _create_resource_manager(mediaeditor_s *editor);
+int _acquire_resource_for_type(mediaeditor_s *editor, mm_resource_manager_res_type_e type);
+int _release_all_resources(mediaeditor_s *editor);
+int _destroy_resource_manager(mediaeditor_s *editor);
+#endif
+
+/* media_editor_layer */
+int _mediaeditor_get_layer(mediaeditor_s *editor, unsigned int layer_id, GESLayer **layer);
+int _mediaeditor_add_layer(mediaeditor_s *editor, unsigned int *layer_id, unsigned int *layer_priority);
+int _mediaeditor_remove_layer(mediaeditor_s *editor, unsigned int layer_id);
+int _mediaeditor_move_layer(mediaeditor_s *editor, unsigned int layer_id, unsigned int layer_priority);
+int _mediaeditor_activate_layer(mediaeditor_s *editor, unsigned int layer_id);
+int _mediaeditor_deactivate_layer(mediaeditor_s *editor, unsigned int layer_id);
+int _mediaeditor_get_layer_priority(mediaeditor_s *editor, unsigned int layer_id, unsigned int *layer_priority);
+int _mediaeditor_get_layer_lowest_priority(mediaeditor_s *editor, unsigned int *layer_priority);
+int _mediaeditor_get_layer_id(mediaeditor_s *editor, unsigned int layer_priority, unsigned int *layer_id);
+
+/* media_editor_clip */
+mediaeditor_clip_s * _mediaeditor_find_clip_data(GList *lists, unsigned int id);
+int _mediaeditor_create_clip_common(mediaeditor_s *editor, GESClip *clip, mediaeditor_effect_type_e effect_type, unsigned int *clip_id);
+int _mediaeditor_add_clip(mediaeditor_s *editor, const char *path, unsigned int layer_id,
+ int start, unsigned int duration, unsigned int in_point, unsigned int *clip_id);
+int _mediaeditor_remove_clip(mediaeditor_s *editor, unsigned int clip_id);
+int _mediaeditor_split_clip(mediaeditor_s *editor, unsigned int src_clip_id, unsigned int position, unsigned int *new_clip_id);
+int _mediaeditor_group_clip(mediaeditor_s *editor, unsigned int *clip_ids, unsigned int size, unsigned int *group_id);
+int _mediaeditor_ungroup_clip(mediaeditor_s *editor, unsigned int group_id, unsigned int **clip_ids, unsigned int *size);
+int _mediaeditor_move_clip_layer(mediaeditor_s *editor, unsigned int clip_id, unsigned int layer_id);
+int _mediaeditor_get_clip_start(mediaeditor_s *editor, unsigned int clip_id, unsigned int *start);
+int _mediaeditor_set_clip_start(mediaeditor_s *editor, unsigned int clip_id, unsigned int start);
+int _mediaeditor_get_clip_duration(mediaeditor_s *editor, unsigned int clip_id, unsigned int *duration);
+int _mediaeditor_set_clip_duration(mediaeditor_s *editor, unsigned int clip_id, unsigned int duration);
+int _mediaeditor_get_clip_in_point(mediaeditor_s *editor, unsigned int clip_id, unsigned int *in_point);
+int _mediaeditor_set_clip_in_point(mediaeditor_s *editor, unsigned int clip_id, unsigned int in_point);
+int _mediaeditor_get_clip_resolution(mediaeditor_s *editor, unsigned int clip_id, unsigned int *width, unsigned int *height);
+int _mediaeditor_set_clip_resolution(mediaeditor_s *editor, unsigned int clip_id, unsigned int width, unsigned int height);
+int _mediaeditor_get_clip_volume(mediaeditor_s *editor, unsigned int clip_id, double *volume);
+int _mediaeditor_set_clip_volume(mediaeditor_s *editor, unsigned int clip_id, double volume);
+
+/* media_editor_display */
+int _mediaeditor_set_display(mediaeditor_h editor, mediaeditor_display_type_e type, mediaeditor_display_h object);
+void _mediaeditor_release_display(mediaeditor_display_s *display);
+
+/* media_editor_effect */
+int _mediaeditor_add_transition(mediaeditor_s *editor, mediaeditor_transition_type_e type, unsigned int layer_id,
+ unsigned int start, unsigned int duration);
+int _mediaeditor_add_effect(mediaeditor_s *editor, mediaeditor_effect_type_e type, unsigned int layer_id,
+ unsigned int start, unsigned int duration, unsigned int *effect_id);
+int _mediaeditor_remove_effect(mediaeditor_s *editor, unsigned int effect_id);
+
+/* media_editor_project */
+int _mediaeditor_create_project(mediaeditor_s *editor, const char *path);
+int _mediaeditor_load_project(mediaeditor_s *editor, const char *path);
+int _mediaeditor_save_project(mediaeditor_s *editor, const gchar *uri);
+
+/* media_editor_ini */
+int _load_ini(mediaeditor_s *editor);
+void _unload_ini(mediaeditor_s *editor);
+bool _is_resource_required(mediaeditor_ini_s *ini);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIZEN_MEDIA_EDITOR_PRIVATE_H__ */
--- /dev/null
+Name: capi-media-editor
+Summary: A Tizen Media Editor API
+Version: 0.0.1
+Release: 0
+Group: Multimedia/API
+License: Apache-2.0
+Source0: %{name}-%{version}.tar.gz
+BuildRequires: cmake
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(gio-2.0)
+BuildRequires: pkgconfig(mm-common)
+BuildRequires: pkgconfig(mm-display-interface)
+BuildRequires: pkgconfig(capi-base-common)
+BuildRequires: pkgconfig(capi-media-tool)
+BuildRequires: pkgconfig(appcore-efl)
+BuildRequires: pkgconfig(elementary)
+BuildRequires: pkgconfig(ecore)
+BuildRequires: pkgconfig(evas)
+BuildRequires: pkgconfig(gstreamer-1.0)
+BuildRequires: pkgconfig(gstreamer-plugins-base-1.0)
+BuildRequires: pkgconfig(gstreamer-pbutils-1.0)
+BuildRequires: pkgconfig(gst-editing-services-1.0)
+BuildRequires: pkgconfig(boost)
+BuildRequires: pkgconfig(ecore-wl2)
+BuildRequires: pkgconfig(wayland-client)
+BuildRequires: pkgconfig(tizen-extension-client)
+BuildRequires: pkgconfig(iniparser)
+BuildRequires: pkgconfig(capi-system-info)
+BuildRequires: gtest-devel
+BuildRequires: gtest
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+%if "%{tizen_profile_name}" != "tv"
+BuildRequires: pkgconfig(mm-resource-manager)
+%endif
+
+%description
+A Media Editor library in Tizen Native API.
+
+%package devel
+Summary: A Media Editor API (Development)
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+Development related files for a Media Editor library in Tizen Native API.
+
+%package tool
+Summary: A Media Editor API testsuite with gtest
+Requires: %{name} = %{version}-%{release}
+BuildRequires: gtest-devel
+BuildRequires: gtest
+
+%description tool
+Tizen Native Media Editor API testsuite with gtest.
+
+%files tool
+%defattr(-,root,root,-)
+%{_bindir}/mediaeditor_ut
+
+%if 0%{?gcov:1}
+%package gcov
+Summary: Line Coverage of Media Editor library in Tizen Native API
+Group: Development/Multimedia
+
+%description gcov
+Collection of files related to Line Coverage. It is teseted as gcov for a Media Editor library in Tizen native API
+%endif
+
+%prep
+%setup -q
+
+%build
+export CFLAGS+=" -DSYSCONFDIR=\\\"%{_hal_sysconfdir}\\\""
+export CXXFLAGS+=" -DSYSCONFDIR=\\\"%{_hal_sysconfdir}\\\""
+
+%if 0%{?sec_build_binary_debug_enable}
+export CFLAGS+=" -DTIZEN_DEBUG_ENABLE"
+%endif
+
+%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
+
+make %{?jobs:-j%jobs}
+
+%if 0%{?gcov:1}
+mkdir -p gcov-obj
+find . -name '*.gcno' -exec cp '{}' gcov-obj ';'
+%endif
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}%{_bindir}
+cp test/media_editor_test %{buildroot}%{_bindir}
+
+%make_install
+
+%if 0%{?gcov:1}
+mkdir -p %{buildroot}%{_datadir}/gcov/obj
+install -m 0644 gcov-obj/* %{buildroot}%{_datadir}/gcov/obj
+%endif
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%manifest capi-media-editor.manifest
+%license LICENSE.APLv2
+%{_libdir}/libcapi-media-editor.so.*
+
+%files devel
+%{_includedir}/media/media_editor.h
+%{_includedir}/media/media_editor_internal.h
+%{_libdir}/pkgconfig/*.pc
+%{_libdir}/libcapi-media-editor.so
+
+%files tool
+%manifest capi-media-editor-tool.manifest
+%license LICENSE.APLv2
+%{_bindir}/*
+
+%if 0%{?gcov:1}
+%files gcov
+%{_datadir}/gcov/obj/*
+%endif
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "media_editor.h"
+#include "media_editor_private.h"
+
+#define _MEDIAEDITOR_FEATURE_DISPLAY "http://tizen.org/feature/display"
+
+int mediaeditor_create(mediaeditor_h *editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_s *_editor = NULL;
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ _editor = g_new0(mediaeditor_s, 1);
+
+ g_mutex_init(&_editor->mutex);
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ g_mutex_init(&_editor->event_src_mutex);
+ g_cond_init(&_editor->cond);
+
+ _load_ini(_editor);
+
+ if (_is_resource_required(&_editor->ini)) {
+#ifndef TIZEN_TV
+ if ((ret = _create_resource_manager(_editor)) != MEDIAEDITOR_ERROR_NONE)
+ goto error;
+#else
+ LOG_WARNING("no resource manager integration yet");
+#endif
+ }
+
+ if ((ret = _gst_init(_editor)) != MEDIAEDITOR_ERROR_NONE)
+ goto error;
+
+ if ((ret = _mediaeditor_create_pipeline(_editor)) != MEDIAEDITOR_ERROR_NONE)
+ goto error;
+
+ _editor->state = MEDIAEDITOR_STATE_IDLE;
+
+ _editor->layer_id = 1;
+ _editor->clip_id = 1;
+ _editor->group_id = 1;
+
+ *editor = _editor;
+
+ LOG_INFO("media editor[%p] is created", *editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+
+error:
+ _unload_ini(_editor);
+ g_free(_editor);
+
+ return ret;
+}
+
+int mediaeditor_destroy(mediaeditor_h editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", _editor);
+
+ _remove_remained_event_sources(_editor);
+
+ ret = _gst_pipeline_set_state(_editor, GST_STATE_NULL);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to set gst state");
+
+ if (_editor->display)
+ _mediaeditor_release_display(_editor->display);
+
+ _gst_pipeline_destroy(_editor);
+
+#ifndef TIZEN_TV
+ if (_destroy_resource_manager(_editor) != MEDIAEDITOR_ERROR_NONE)
+ LOG_ERROR("failed to destroy editor[%p]", _editor);
+#else
+ LOG_WARNING("no resource manager integration yet");
+#endif
+
+ _unload_ini(_editor);
+
+ ges_deinit();
+
+ g_cond_clear(&_editor->cond);
+ g_mutex_clear(&_editor->event_src_mutex);
+
+ g_clear_pointer(&locker, g_mutex_locker_free);
+ g_mutex_clear(&_editor->mutex);
+
+ LOG_INFO("media editor[%p] is destroyed", _editor);
+
+ SAFE_G_FREE(_editor);
+
+ LOG_INFO("Leave");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int mediaeditor_set_display(mediaeditor_h editor, mediaeditor_display_type_e type, mediaeditor_display_h display)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_ERR_IF_FEATURE_IS_NOT_SUPPORTED(_MEDIAEDITOR_FEATURE_DISPLAY);
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(display == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "display is NULL");
+ RET_VAL_IF(type < MEDIAEDITOR_DISPLAY_TYPE_OVERLAY || type > MEDIAEDITOR_DISPLAY_TYPE_NONE,
+ MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid display type");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_set_display(_editor, type, display);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_get_state(mediaeditor_h editor, mediaeditor_state_e *state)
+{
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ g_mutex_lock(&_editor->mutex);
+
+ *state = _editor->state;
+
+ g_mutex_unlock(&_editor->mutex);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int mediaeditor_start_render(mediaeditor_h editor, const char* path, mediaeditor_render_completed_cb callback, void *user_data)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(path == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "path is NULL");
+ ret = _check_privilege(path, false, R_OK | W_OK);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to check privilege");
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(callback == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ _editor->render_completed_cb.callback = callback;
+ _editor->render_completed_cb.user_data = user_data;
+
+ ret = _mediaeditor_start_render(_editor, path);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_cancel_render(mediaeditor_h editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_RENDERING, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be RENDERING");
+
+ ret = _mediaeditor_cancel_render(_editor);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_start_preview(mediaeditor_h editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_ERR_IF_FEATURE_IS_NOT_SUPPORTED(_MEDIAEDITOR_FEATURE_DISPLAY);
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_start_preview(_editor);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_stop_preview(mediaeditor_h editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_ERR_IF_FEATURE_IS_NOT_SUPPORTED(_MEDIAEDITOR_FEATURE_DISPLAY);
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p], state [%d], pend_state[%d]", editor, _editor->state, _editor->pend_state);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_PREVIEW, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be PREVIEW");
+
+ ret = _mediaeditor_stop_preview(_editor);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_add_layer(mediaeditor_h editor, unsigned int *layer_id, unsigned int *layer_priority)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(layer_id == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_id is NULL");
+ RET_VAL_IF(layer_priority == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_priority is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_add_layer(_editor, layer_id, layer_priority);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_remove_layer(mediaeditor_h editor, unsigned int layer_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_remove_layer(_editor, layer_id);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_move_layer(mediaeditor_h editor, unsigned int layer_id, unsigned int layer_priority)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_move_layer(_editor, layer_id, layer_priority);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_activate_layer(mediaeditor_h editor, unsigned int layer_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_activate_layer(_editor, layer_id);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_deactivate_layer(mediaeditor_h editor, unsigned int layer_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_deactivate_layer(_editor, layer_id);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_get_layer_priority(mediaeditor_h editor, unsigned int layer_id, unsigned int *layer_priority)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(layer_priority == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_priority is NULL");
+
+ g_mutex_lock(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ ret = _mediaeditor_get_layer_priority(_editor, layer_id, layer_priority);
+
+ g_mutex_unlock(&_editor->mutex);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_get_layer_lowest_priority(mediaeditor_h editor, unsigned int *layer_priority)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(layer_priority == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_priority is NULL");
+
+ g_mutex_lock(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ ret = _mediaeditor_get_layer_lowest_priority(_editor, layer_priority);
+
+ g_mutex_unlock(&_editor->mutex);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_get_layer_id(mediaeditor_h editor, unsigned int layer_priority, unsigned int *layer_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(layer_id == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_id is NULL");
+
+ g_mutex_lock(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ ret = _mediaeditor_get_layer_id(_editor, layer_priority, layer_id);
+
+ g_mutex_unlock(&_editor->mutex);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_add_clip(mediaeditor_h editor, const char *path, unsigned int layer_id,
+ int start, unsigned int duration, unsigned int in_point, unsigned int *clip_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(path == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "path is NULL");
+ ret = _check_privilege(path, true, R_OK);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to check privilege");
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_add_clip(_editor, path, layer_id, start, duration, in_point, clip_id);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_remove_clip(mediaeditor_h editor, unsigned int clip_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_remove_clip(_editor, clip_id);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_split_clip(mediaeditor_h editor, unsigned int src_clip_id, unsigned int position,
+ unsigned int *new_clip_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_split_clip(_editor, src_clip_id, position, new_clip_id);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_group_clip(mediaeditor_h editor, unsigned int *clip_ids, unsigned int size, unsigned int *group_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_group_clip(_editor, clip_ids, size, group_id);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_ungroup_clip(mediaeditor_h editor, unsigned int group_id, unsigned int **clip_ids, unsigned int *size)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_ungroup_clip(_editor, group_id, clip_ids, size);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_move_clip_layer(mediaeditor_h editor, unsigned int clip_id, unsigned int layer_priority)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_move_clip_layer(_editor, clip_id, layer_priority);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_get_clip_start(mediaeditor_h editor, unsigned int clip_id, unsigned int *start)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(start == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "start is NULL");
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ g_mutex_lock(&_editor->mutex);
+
+ ret = _mediaeditor_get_clip_start(_editor, clip_id, start);
+
+ g_mutex_unlock(&_editor->mutex);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_set_clip_start(mediaeditor_h editor, unsigned int id, unsigned int start)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_set_clip_start(_editor, id, start);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_get_clip_duration(mediaeditor_h editor, unsigned int id, unsigned int *duration)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(duration == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "duration is NULL");
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ g_mutex_lock(&_editor->mutex);
+
+ ret = _mediaeditor_get_clip_duration(_editor, id, duration);
+
+ g_mutex_unlock(&_editor->mutex);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_set_clip_duration(mediaeditor_h editor, unsigned int id, unsigned int duration)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_set_clip_duration(_editor, id, duration);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_get_clip_in_point(mediaeditor_h editor, unsigned int id, unsigned int *in_point)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(in_point == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "in_point is NULL");
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ g_mutex_lock(&_editor->mutex);
+
+ ret = _mediaeditor_get_clip_in_point(_editor, id, in_point);
+
+ g_mutex_unlock(&_editor->mutex);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_set_clip_in_point(mediaeditor_h editor, unsigned int id, unsigned int in_point)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_set_clip_in_point(_editor, id, in_point);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_get_clip_resolution(mediaeditor_h editor, unsigned int id, unsigned int *width, unsigned int *height)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(width == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "width is NULL");
+ RET_VAL_IF(height == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "height is NULL");
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ g_mutex_lock(&_editor->mutex);
+
+ ret = _mediaeditor_get_clip_resolution(_editor, id, width, height);
+
+ g_mutex_unlock(&_editor->mutex);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_set_clip_resolution(mediaeditor_h editor, unsigned int id, unsigned int width, unsigned int height)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_set_clip_resolution(_editor, id, width, height);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_get_clip_volume(mediaeditor_h editor, unsigned int id, double *volume)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(volume == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "volume is NULL");
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ g_mutex_lock(&_editor->mutex);
+
+ ret = _mediaeditor_get_clip_volume(_editor, id, volume);
+
+ g_mutex_unlock(&_editor->mutex);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_set_clip_volume(mediaeditor_h editor, unsigned int id, double volume)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(volume < 0.0 || volume > 10.0, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "volume is out of range");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_set_clip_volume(_editor, id, volume);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_add_transition(mediaeditor_h editor, mediaeditor_transition_type_e type, unsigned int layer_id,
+ unsigned int start, unsigned int duration)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(type < MEDIAEDITOR_TRANSITION_TYPE_NONE || type > MEDIAEDITOR_TRANSITION_TYPE_CROSSFADE,
+ MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid transition type");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_add_transition(_editor, type, layer_id, start, duration);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_add_effect(mediaeditor_h editor, mediaeditor_effect_type_e type, unsigned int layer_id,
+ unsigned int start, unsigned int duration, unsigned int *effect_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(type <= MEDIAEDITOR_EFFECT_TYPE_NONE || type > MEDIAEDITOR_EFFECT_AUDIO_TYPE_ECHO,
+ MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid effect type");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_add_effect(_editor, type, layer_id, start, duration, effect_id);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_remove_effect(mediaeditor_h editor, unsigned int effect_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_remove_effect(_editor, effect_id);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_create_project(mediaeditor_h editor, const char *path)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(path == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "path is NULL");
+ ret = _check_privilege(path, false, R_OK | W_OK);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to check privilege");
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_create_project(_editor, path);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_load_project(mediaeditor_h editor, const char *path, mediaeditor_project_loaded_cb callback, void *user_data)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(path == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "path is NULL");
+ ret = _check_privilege(path, true, R_OK);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to check privilege");
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(callback == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ _editor->project_loaded_cb.callback = callback;
+ _editor->project_loaded_cb.user_data = user_data;
+
+ ret = _mediaeditor_load_project(_editor, path);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_save_project(mediaeditor_h editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+ g_autofree gchar *uri = NULL;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(_editor->gst.project == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "project is not loaded or created");
+
+ uri = ges_project_get_uri(_editor->gst.project);
+ RET_VAL_IF(uri == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to get uri from project");
+ ret = _check_privilege(uri + URI_TO_PATH_OFFSET, false, W_OK);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to check privilege");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ ret = _mediaeditor_save_project(_editor, uri);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return ret;
+}
+
+int mediaeditor_set_error_cb(mediaeditor_h editor, mediaeditor_error_cb callback, void *user_data)
+{
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(callback == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ _editor->error_cb.callback = callback;
+ _editor->error_cb.user_data = user_data;
+
+ LOG_INFO("callback[%p] user_data[%p]", callback, user_data);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int mediaeditor_unset_error_cb(mediaeditor_h editor)
+{
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+ RET_VAL_IF(_editor->error_cb.callback == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION,
+ "callback was not set");
+
+ _editor->error_cb.callback = NULL;
+ _editor->error_cb.user_data = NULL;
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int mediaeditor_set_state_changed_cb(mediaeditor_h editor, mediaeditor_state_changed_cb callback, void *user_data)
+{
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(callback == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ _editor->state_changed_cb.callback = callback;
+ _editor->state_changed_cb.user_data = user_data;
+
+ LOG_INFO("callback[%p] user_data[%p]", callback, user_data);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int mediaeditor_unset_state_changed_cb(mediaeditor_h editor)
+{
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+ RET_VAL_IF(_editor->state_changed_cb.callback == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION,
+ "callback was not set");
+
+ _editor->state_changed_cb.callback = NULL;
+ _editor->state_changed_cb.user_data = NULL;
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int mediaeditor_set_layer_priority_changed_cb(mediaeditor_h editor, mediaeditor_layer_priority_changed_cb callback, void *user_data)
+{
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(callback == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+
+ _editor->layer_priority_changed_cb.callback = callback;
+ _editor->layer_priority_changed_cb.user_data = user_data;
+
+ LOG_INFO("callback[%p] user_data[%p]", callback, user_data);
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int mediaeditor_unset_layer_priority_changed_cb(mediaeditor_h editor)
+{
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s *)editor;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&_editor->mutex);
+
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_VAL_IF(_editor->state != MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should be IDLE");
+ RET_VAL_IF(_editor->layer_priority_changed_cb.callback == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION,
+ "callback was not set");
+
+ _editor->layer_priority_changed_cb.callback = NULL;
+ _editor->layer_priority_changed_cb.user_data = NULL;
+
+ LOG_DEBUG("Leave [%p]", editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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 <media_editor.h>
+#include <media_editor_private.h>
+
+static void __print_clip_data(mediaeditor_clip_s *clip_data)
+{
+#ifdef PRINT_CLIP_DEBUG_INFO
+ RET_IF(clip_data == NULL, "clip_data is null");
+ LOG_DEBUG("clip_data[%p], clip[%p], id[%d], effect_type[%d]", clip_data, clip_data->clip, clip_data->id, (int)clip_data->effect_type);
+#endif
+}
+
+static mediaeditor_clip_s *__mediaeditor_create_clip_data(GESClip *clip, mediaeditor_effect_type_e effect_type, unsigned int id)
+{
+ mediaeditor_clip_s *data = NULL;
+
+ data = g_new0(mediaeditor_clip_s, 1);
+
+ data->clip = clip;
+ data->effect_type = effect_type;
+ data->id = id;
+
+ return data;
+}
+
+static mediaeditor_group_s *__mediaeditor_create_group_data(GESGroup *group, unsigned int id)
+{
+ mediaeditor_group_s *data = NULL;
+
+ data = g_new0(mediaeditor_group_s, 1);
+
+ data->group = group;
+ data->id = id;
+
+ return data;
+}
+
+static gint __mediaeditor_comparefunc_find_clip(gconstpointer a, gconstpointer b)
+{
+ mediaeditor_clip_s *clip_data = (mediaeditor_clip_s *)a;
+ unsigned int *id = (unsigned int *)b;
+
+ if (clip_data == NULL || clip_data->id != *id)
+ return -1;
+
+ LOG_DEBUG("clip_data->id[%d], *id[%d]", clip_data->id,*id);
+
+ return 0;
+}
+
+static GList *__mediaeditor_find_clip_list(GList *lists, unsigned int id)
+{
+ GList *list = NULL;
+
+ list = g_list_find_custom(lists, (gconstpointer)&id, __mediaeditor_comparefunc_find_clip);
+ RET_VAL_IF(list == NULL, NULL, "failed to find matched clip");
+
+ LOG_DEBUG("list->data(clip)[%p]", list->data);
+
+ return list;
+}
+
+mediaeditor_clip_s *_mediaeditor_find_clip_data(GList *lists, unsigned int id)
+{
+ GList *list = NULL;
+
+ list = __mediaeditor_find_clip_list(lists, id);
+ RET_VAL_IF(list == NULL, NULL, "failed to find clip list");
+
+ __print_clip_data(list->data);
+
+ return list->data;
+}
+
+static GESClip *__mediaeditor_find_clip(GList *lists, unsigned int id)
+{
+ mediaeditor_clip_s *clip_data = NULL;
+
+ clip_data = _mediaeditor_find_clip_data(lists, id);
+ RET_VAL_IF(clip_data == NULL, NULL, "failed to find clip data");
+
+ return clip_data->clip;
+}
+
+/* this method find/return effect and don't remove it from list */
+static GList *__mediaeditor_peek_clips(GList *lists, unsigned int *ids, unsigned int size)
+{
+ GList *list_new = NULL;
+ GESClip *clip = NULL;
+
+ for (unsigned int i = 0 ; i < size ; i++) {
+ if ((clip = __mediaeditor_find_clip(lists, ids[i])))
+ list_new = g_list_append(list_new, clip);
+ }
+
+ return list_new;
+}
+
+static void __mediaeditor_find_id(mediaeditor_s *editor, GList *lists, unsigned int *ids)
+{
+ int i = 0;
+ GList *sl = NULL;
+ GList *dl = NULL;
+ mediaeditor_clip_s *clip_data = NULL;
+
+ for (sl = lists ; sl ; sl = sl->next, i++) {
+ for (dl = editor->clips ; dl ; dl = dl->next) {
+ clip_data = dl->data;
+
+ if (clip_data->clip == (GESClip *)sl->data) {
+ ids[i] = clip_data->id;
+ LOG_DEBUG("found clip : [%p]", clip_data->clip);
+ break;
+ }
+ }
+ }
+}
+
+static gint __mediaeditor_comparefunc_find_group(gconstpointer a, gconstpointer b)
+{
+ mediaeditor_group_s *group_data = (mediaeditor_group_s *)a;
+ unsigned int *id = (unsigned int *)b;
+
+ if (group_data == NULL || group_data->id != *id)
+ return -1;
+
+ return 0;
+}
+
+static GList *__mediaeditor_find_group_list(GList *lists, unsigned int id)
+{
+ GList *list = NULL;
+
+ list = g_list_find_custom(lists, &id, __mediaeditor_comparefunc_find_group);
+ RET_VAL_IF(list == NULL, NULL, "failed to find matched group");
+
+ LOG_DEBUG("list-data(clip)[%p]", list->data);
+
+ return list;
+}
+
+static mediaeditor_group_s *__mediaeditor_find_group_data(GList *lists, unsigned int id)
+{
+ GList *list = NULL;
+
+ list = __mediaeditor_find_group_list(lists, id);
+ RET_VAL_IF(list == NULL, NULL, "failed to find group list");
+
+ return list->data;
+}
+
+static GESGroup *__mediaeditor_find_group(GList *lists, unsigned int id)
+{
+ mediaeditor_group_s *group_data = NULL;
+
+ group_data = __mediaeditor_find_group_data(lists, id);
+ RET_VAL_IF(group_data == NULL, NULL, "failed to find group data");
+
+ return group_data->group;
+}
+
+static bool __has_audio_track(GESClip *clip)
+{
+ RET_VAL_IF(clip == NULL, false, "clip is NULL");
+
+ if (ges_clip_find_track_element(clip, NULL, GES_TYPE_AUDIO_SOURCE) != NULL) {
+ LOG_DEBUG("The clip have audio");
+ return true;
+ }
+
+ return false;
+}
+
+static bool __has_video_track(GESClip *clip)
+{
+ RET_VAL_IF(clip == NULL, false, "clip is NULL");
+
+ if (ges_clip_find_track_element(clip, NULL, GES_TYPE_VIDEO_SOURCE) != NULL) {
+ LOG_DEBUG("The clip have video");
+ return true;
+ }
+
+ return false;
+}
+
+static void __mediaeditor_add_child_properties_resolution(GESClip *clip)
+{
+ GValue val = G_VALUE_INIT;
+
+ g_value_init(&val, G_TYPE_INT);
+
+ if (!ges_timeline_element_get_child_property(GES_TIMELINE_ELEMENT(clip), "width", &val)) {
+ GObjectClass *eklass = G_OBJECT_GET_CLASS(clip);
+
+ if (ges_timeline_element_add_child_property(GES_TIMELINE_ELEMENT(clip), g_object_class_find_property(eklass, "width"), G_OBJECT(clip)))
+ LOG_DEBUG("width property is added in URI clip");
+
+ if (ges_timeline_element_add_child_property(GES_TIMELINE_ELEMENT(clip), g_object_class_find_property(eklass, "height"), G_OBJECT(clip)))
+ LOG_DEBUG("height property is added in URI clip");
+ }
+}
+
+int _mediaeditor_create_clip_common(mediaeditor_s *editor, GESClip *clip, mediaeditor_effect_type_e effect_type, unsigned int *clip_id)
+{
+ mediaeditor_clip_s *clip_data = NULL;
+
+ clip_data = __mediaeditor_create_clip_data(clip, effect_type, editor->clip_id);
+ RET_VAL_IF(clip_data == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "clip_data is null");
+
+ __print_clip_data(clip_data);
+
+ editor->clips = g_list_append(editor->clips, clip_data);
+ RET_VAL_IF(editor->clips == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "list is null");
+
+ *clip_id = (editor->clip_id)++;
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_add_clip(mediaeditor_s *editor, const char *path, unsigned int layer_id,
+ int start, unsigned int duration, unsigned int in_point, unsigned int *clip_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ GESClip *clip = NULL;
+ GESLayer *layer = NULL;
+ guint64 start_nsec = 0;
+ guint64 duration_nsec = 0;
+ guint64 in_point_nsec = 0;
+ g_autofree gchar *uri = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(path == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "path is NULL");
+ RET_VAL_IF(clip_id == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "clip_id is NULL");
+
+ uri = gst_filename_to_uri(path, NULL);
+ RET_VAL_IF(uri == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "failed to get uri");
+ LOGI("uri : [%s]", uri);
+
+ clip = GES_CLIP(ges_uri_clip_new(uri));
+
+ start_nsec = MILLI_TO_NANO(start);
+ duration_nsec = MILLI_TO_NANO(duration);
+ in_point_nsec = MILLI_TO_NANO(in_point);
+
+ LOG_INFO("uri[%s], layer_id[%d], clip[%p], start[%"G_GUINT64_FORMAT"], duration[%"G_GUINT64_FORMAT"]",
+ uri, layer_id, clip, start_nsec, duration_nsec);
+ ges_timeline_element_set_start(GES_TIMELINE_ELEMENT(clip), start_nsec);
+ ges_timeline_element_set_duration(GES_TIMELINE_ELEMENT(clip), duration_nsec);
+ ges_timeline_element_set_inpoint(GES_TIMELINE_ELEMENT(clip), in_point_nsec);
+
+ ret = _mediaeditor_get_layer(editor, layer_id, &layer);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to get layer");
+
+ if (!ges_layer_add_clip(layer, clip)) {
+ LOG_ERROR("failed to add clip to layer");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ ret = _mediaeditor_create_clip_common(editor, clip, MEDIAEDITOR_EFFECT_TYPE_NONE, clip_id);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create clip common");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_remove_clip(mediaeditor_s *editor, unsigned int clip_id)
+{
+ GESClip *clip = NULL;
+ GESLayer *layer = NULL;
+ g_autofree mediaeditor_clip_s *clip_data = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ clip_data = _mediaeditor_find_clip_data(editor->clips, clip_id);
+ RET_VAL_IF(clip_data == NULL || clip_data->clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ clip = clip_data->clip;
+ layer = ges_clip_get_layer(clip);
+ RET_VAL_IF(layer == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to get layer");
+
+ if (!ges_layer_remove_clip(layer, clip)) {
+ LOG_ERROR("failed to remove clip from layer");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ editor->clips = g_list_remove(editor->clips, clip_data);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_split_clip(mediaeditor_s *editor, unsigned int src_clip_id, unsigned int position,
+ unsigned int *new_clip_id)
+{
+ GESClip *clip = NULL;
+ GESClip *clip_new = NULL;
+ mediaeditor_clip_s *clip_data_new = NULL;
+ guint64 position_nsec = 0;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(src_clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ clip = __mediaeditor_find_clip(editor->clips, src_clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ position_nsec = MILLI_TO_NANO(position);
+ LOG_DEBUG("src_clip_id[%d], position[%"G_GUINT64_FORMAT"]", src_clip_id, position_nsec);
+
+ clip_new = ges_clip_split(clip, position_nsec);
+ RET_VAL_IF(clip_new == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to split clip");
+
+ clip_data_new = __mediaeditor_create_clip_data(clip_new, MEDIAEDITOR_EFFECT_TYPE_NONE, editor->clip_id);
+
+ /* add new clip to managed clip list. */
+ editor->clips = g_list_append(editor->clips, clip_data_new);
+ *new_clip_id = (editor->clip_id)++;
+
+ __print_clip_data(clip_data_new);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_group_clip(mediaeditor_s *editor, unsigned int *clip_ids, unsigned int size, unsigned int *group_id)
+{
+ GList *list = NULL;
+ GESGroup *group = NULL;
+ mediaeditor_group_s *group_data = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ for (unsigned int i = 0 ; i < size ; i++)
+ RET_VAL_IF(clip_ids[i] >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ list = __mediaeditor_peek_clips(editor->clips, clip_ids, size);
+ RET_VAL_IF(list == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to get clip from list");
+
+ group = GES_GROUP(ges_container_group(list));
+ group_data = __mediaeditor_create_group_data(group, editor->group_id);
+
+ editor->groups = g_list_append(editor->groups, group_data);
+ *group_id = (editor->group_id)++;
+
+ g_list_free(list);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_ungroup_clip(mediaeditor_s *editor, unsigned int group_id, unsigned int **clip_ids, unsigned int *size)
+{
+ GESGroup *group = NULL;
+ GList *clips = NULL;
+ int number_of_clips = 0;
+ unsigned int *ids = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(group_id <= 0 || group_id >= editor->group_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid group id");
+
+ group = __mediaeditor_find_group(editor->groups, group_id);
+ RET_VAL_IF(group == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid group id");
+
+ clips = ges_container_ungroup(GES_CONTAINER(group), FALSE);
+ RET_VAL_IF(clips == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to ungroup");
+
+ number_of_clips = g_list_length(clips);
+ LOG_DEBUG("The ungrouped clips are %d", number_of_clips);
+
+ ids = (unsigned int *)malloc(sizeof(unsigned int) * number_of_clips);
+ RET_VAL_IF(ids == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to alloc ids");
+
+ memset(ids, 0, sizeof(unsigned int) * number_of_clips);
+
+ __mediaeditor_find_id(editor, clips, ids);
+
+ *clip_ids = ids;
+ *size = number_of_clips;
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_move_clip_layer(mediaeditor_s *editor, unsigned int clip_id, unsigned int layer_priority)
+{
+ GESLayer *layer = NULL;
+ GESClip *clip = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ layer = ges_timeline_get_layer(editor->gst.timeline, layer_priority);
+ RET_VAL_IF(layer == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid layer id");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ if (!ges_clip_move_to_layer(clip, layer)) {
+ LOG_ERROR("failed to move layer");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_get_clip_start(mediaeditor_s *editor, unsigned int clip_id, unsigned int *start)
+{
+ GESClip *clip = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ *start = NANO_TO_MILLI(ges_timeline_element_get_start(GES_TIMELINE_ELEMENT(clip)));
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_set_clip_start(mediaeditor_s *editor, unsigned int clip_id, unsigned int start)
+{
+ GESClip *clip = NULL;
+ guint64 start_nsec = 0;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ start_nsec = MILLI_TO_NANO(start);
+ LOG_DEBUG("clip_id[%d], start[%"G_GUINT64_FORMAT"]", clip_id, start_nsec);
+
+ if (!ges_timeline_element_set_start(GES_TIMELINE_ELEMENT(clip), start_nsec)) {
+ LOG_ERROR("failed to set start");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_get_clip_duration(mediaeditor_s *editor, unsigned int clip_id, unsigned int *duration)
+{
+ GESClip *clip = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ *duration = NANO_TO_MILLI(ges_timeline_element_get_duration(GES_TIMELINE_ELEMENT(clip)));
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_set_clip_duration(mediaeditor_s *editor, unsigned int clip_id, unsigned int duration)
+{
+ GESClip *clip = NULL;
+ guint64 duration_nsec = 0;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ duration_nsec = MILLI_TO_NANO(duration);
+ LOG_DEBUG("clip_id[%d], duration[%"G_GUINT64_FORMAT"]", clip_id, duration_nsec);
+
+ if (!ges_timeline_element_set_duration(GES_TIMELINE_ELEMENT(clip), duration_nsec)) {
+ LOG_ERROR("failed to set duration");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_get_clip_in_point(mediaeditor_s *editor, unsigned int clip_id, unsigned int *in_point)
+{
+ GESClip *clip = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ *in_point = NANO_TO_MILLI(ges_timeline_element_get_inpoint(GES_TIMELINE_ELEMENT(clip)));
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_set_clip_in_point(mediaeditor_s *editor, unsigned int clip_id, unsigned int in_point)
+{
+ GESClip *clip = NULL;
+ guint64 in_point_nsec = 0;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+
+ in_point_nsec = MILLI_TO_NANO(in_point);
+ LOG_DEBUG("clip_id[%d], in_point[%"G_GUINT64_FORMAT"]", clip_id, in_point_nsec);
+
+ if (!ges_timeline_element_set_inpoint(GES_TIMELINE_ELEMENT(clip), in_point_nsec)) {
+ LOG_ERROR("failed to set in_point");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_get_clip_resolution(mediaeditor_s *editor, unsigned int clip_id,
+ unsigned int *width, unsigned int *height)
+{
+ GESClip *clip = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid id");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+ RET_VAL_IF(!__has_video_track(clip), MEDIAEDITOR_ERROR_INVALID_OPERATION, "Not video clip");
+
+ __mediaeditor_add_child_properties_resolution(clip);
+
+ ges_timeline_element_get_child_properties(GES_TIMELINE_ELEMENT(clip), "width", width, "height", height, NULL);
+
+ LOGD("width:%d, height:%d", *width, *height);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_set_clip_resolution(mediaeditor_s *editor, unsigned int clip_id,
+ unsigned int width, unsigned int height)
+{
+ GESClip *clip = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(clip_id >= editor->clip_id, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid id");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+ RET_VAL_IF(!__has_video_track(clip), MEDIAEDITOR_ERROR_INVALID_OPERATION, "Not video clip");
+
+ LOGD("width:%d, height:%d", width, height);
+
+ __mediaeditor_add_child_properties_resolution(clip);
+
+ ges_timeline_element_set_child_properties(GES_TIMELINE_ELEMENT(clip), "width", width, "height", height, NULL);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_get_clip_volume(mediaeditor_s *editor, unsigned int clip_id, double *volume)
+{
+ GESClip *clip = NULL;
+ GValue val = { 0 };
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+ RET_VAL_IF(!__has_audio_track(clip), MEDIAEDITOR_ERROR_INVALID_OPERATION, "Not audio clip");
+
+ g_value_init(&val, G_TYPE_DOUBLE);
+ if (!ges_timeline_element_get_child_property(GES_TIMELINE_ELEMENT(clip), "volume", &val)) {
+ LOG_ERROR("failed to get volume");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ *volume = g_value_get_double(&val);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_set_clip_volume(mediaeditor_s *editor, unsigned int clip_id, double volume)
+{
+ GESClip *clip = NULL;
+ GValue val = { 0 };
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ clip = __mediaeditor_find_clip(editor->clips, clip_id);
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+ RET_VAL_IF(!__has_audio_track(clip), MEDIAEDITOR_ERROR_INVALID_OPERATION, "Not audio clip");
+
+ g_value_init(&val, G_TYPE_DOUBLE);
+ g_value_set_double(&val, volume);
+ if (!ges_timeline_element_set_child_property(GES_TIMELINE_ELEMENT(clip), "volume", &val)) {
+ LOG_ERROR("failed to set volume");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 <mm_error.h>
+#include "media_editor.h"
+#include "media_editor_private.h"
+#include <gst/video/videooverlay.h>
+
+static int __set_evas_display(mediaeditor_display_s *display)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ RET_VAL_IF(display == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "display is NULL");
+
+ ret = mm_display_interface_set_display_mainloop_sync(display->mm_display, MM_DISPLAY_TYPE_EVAS, display->object, NULL);
+ RET_VAL_IF(ret != MM_ERROR_NONE, MEDIAEDITOR_ERROR_INVALID_OPERATION,
+ "failed to mm_display_interface_set_display_mainloop_sync()");
+
+ ret = mm_display_interface_evas_set_mode(display->mm_display, 0);
+ RET_VAL_IF(ret != MM_ERROR_NONE, MEDIAEDITOR_ERROR_INVALID_OPERATION,
+ "failed to mm_display_interface_evas_set_mode()");
+
+ ret = mm_display_interface_evas_set_rotation(display->mm_display, 0);
+ RET_VAL_IF(ret != MM_ERROR_NONE, MEDIAEDITOR_ERROR_INVALID_OPERATION,
+ "failed to mm_display_interface_evas_set_rotation()");
+
+ ret = mm_display_interface_evas_set_visible(display->mm_display, true);
+ RET_VAL_IF(ret != MM_ERROR_NONE, MEDIAEDITOR_ERROR_INVALID_OPERATION,
+ "failed to mm_display_interface_evas_set_visible()");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static int __set_wl_sink(mediaeditor_s *_editor)
+{
+ GstElement *videosink = NULL;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(_editor->gst.pipeline == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "pipeline is NULL");
+
+ videosink = gst_element_factory_make("tizenwlsink", NULL);
+ RET_VAL_IF(videosink == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to create element");
+
+ ges_pipeline_preview_set_video_sink(_editor->gst.pipeline, videosink);
+
+ gst_video_overlay_set_wl_window_wl_surface_id(GST_VIDEO_OVERLAY(videosink), _editor->display->overlay_surface_id);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static int __set_overlay_display(mediaeditor_s *_editor, mediaeditor_display_s *display)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mm_display_type_e type = MM_DISPLAY_TYPE_OVERLAY;
+
+ RET_VAL_IF(display == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "display is NULL");
+
+ if (display->type == MEDIAEDITOR_DISPLAY_TYPE_ECORE)
+ type = MM_DISPLAY_TYPE_OVERLAY_EXT;
+
+ ret = mm_display_interface_set_display_mainloop_sync(display->mm_display, type, display->object, &display->overlay_surface_id);
+ RET_VAL_IF(ret != MM_ERROR_NONE, MEDIAEDITOR_ERROR_INVALID_OPERATION,
+ "failed to mm_display_interface_set_display_mainloop_sync()");
+
+ ret = __set_wl_sink(_editor);
+ RET_VAL_IF(ret != MM_ERROR_NONE, ret, "failed to set wayland sink");
+
+ LOG_INFO("overlay_surface_id[%d]", display->overlay_surface_id);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static int __init_display(mediaeditor_display_s **display)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ RET_VAL_IF(*display != NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION,
+ "display is already initialized.");
+
+ mediaeditor_display_s *_display = g_new0(mediaeditor_display_s, 1);
+
+ g_mutex_init(&_display->mutex);
+
+ if ((ret = mm_display_interface_init(&_display->mm_display)) != MM_ERROR_NONE) {
+ LOG_ERROR("failed to init display. (0x%x)", ret);
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ LOG_DEBUG("initialized display[%p], mm_display[%p]", _display, _display->mm_display);
+
+ *display = _display;
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_set_display(mediaeditor_h editor, mediaeditor_display_type_e type, mediaeditor_display_h object)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+ mediaeditor_s *_editor = (mediaeditor_s*)editor;
+ mediaeditor_display_s *_display = _editor->display;
+
+ RET_VAL_IF(_editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ if (_display == NULL) {
+ if ((ret = __init_display(&_display)) != MEDIAEDITOR_ERROR_NONE) {
+ LOG_ERROR("failed to initialize display[0x%x]", ret);
+ return ret;
+ }
+ }
+
+ LOG_DEBUG("_display[%p]", _display);
+ LOG_DEBUG("mutex[%p]", &_display->mutex);
+
+ locker = g_mutex_locker_new(&_display->mutex);
+
+ _display->type = type;
+ _display->object = object;
+
+ LOG_DEBUG("display type : %d", type);
+ switch (type) {
+ case MEDIAEDITOR_DISPLAY_TYPE_OVERLAY:
+ ret = __set_overlay_display(_editor, _display);
+ break;
+ case MEDIAEDITOR_DISPLAY_TYPE_EVAS:
+ ret = __set_evas_display(_display);
+ break;
+ case MEDIAEDITOR_DISPLAY_TYPE_ECORE:
+ ret = __set_overlay_display(_editor, _display);
+ break;
+ default:
+ LOG_DEBUG("display type none");
+ break;
+ }
+
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to set display[%d]", type);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+void _mediaeditor_release_display(mediaeditor_display_s *display)
+{
+ RET_IF(display == NULL, "display is NULL");
+
+ g_mutex_lock(&display->mutex);
+
+ if (display->mm_display) {
+ LOG_DEBUG("deinit display->mm_display[%p]", display->mm_display);
+ mm_display_interface_deinit(display->mm_display);
+ display->mm_display = NULL;
+ }
+
+ g_mutex_unlock(&display->mutex);
+ g_mutex_clear(&display->mutex);
+
+ LOG_DEBUG("free display[%p]", display);
+
+ g_free(display);
+}
--- /dev/null
+/*
+ * 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 "media_editor.h"
+#include "media_editor_private.h"
+
+static gchar *__get_effect_name(mediaeditor_effect_type_e type)
+{
+ switch (type) {
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_EDGETV:
+ return g_strdup("edgetv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_AGINGTV:
+ return g_strdup("agingtv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_DICETV:
+ return g_strdup("dicetv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_WARPTV:
+ return g_strdup("warptv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_SHAGADELICTV:
+ return g_strdup("shagadelictv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_VERTIGOTV:
+ return g_strdup("vertigotv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_REVTV:
+ return g_strdup("revtv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_QUARKTV:
+ return g_strdup("quarktv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_OPTV:
+ return g_strdup("optv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_RADIOACTV:
+ return g_strdup("radioactv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_STREAKTV:
+ return g_strdup("streaktv");
+ case MEDIAEDITOR_EFFECT_VIDEO_TYPE_RIPPLETV:
+ return g_strdup("rippletv");
+ case MEDIAEDITOR_EFFECT_AUDIO_TYPE_ECHO:
+ return g_strdup("audioecho");
+ default:
+ LOG_ERROR("Not supported effect");
+ break;
+ }
+
+ return NULL;
+}
+
+static GstTimedValue *__new_timed_value(GstClockTime time, gdouble val)
+{
+ GstTimedValue *tmval = g_new0(GstTimedValue, 1);
+
+ tmval->value = val;
+ tmval->timestamp = time;
+
+ return tmval;
+}
+
+static int __keep_audio_fade_effect(mediaeditor_s *editor, unsigned int start, unsigned int duration, bool is_fade_in)
+{
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ if (is_fade_in) {
+ LOG_DEBUG("FADE IN, start[%"G_GUINT64_FORMAT"], end[%"G_GUINT64_FORMAT"]",
+ start * GST_MSECOND, (start + duration) * GST_MSECOND);
+ editor->audio_fade_effects = g_slist_prepend(editor->audio_fade_effects, __new_timed_value(start * GST_MSECOND, 0.0));
+ editor->audio_fade_effects = g_slist_prepend(editor->audio_fade_effects, __new_timed_value((start + 100) * GST_MSECOND, 0.0));
+ editor->audio_fade_effects = g_slist_prepend(editor->audio_fade_effects, __new_timed_value((start + duration) * GST_MSECOND, 1.0));
+ } else {
+ LOG_DEBUG("FADE OUT, start[%"G_GUINT64_FORMAT"], end[%"G_GUINT64_FORMAT"]",
+ start * GST_MSECOND, (start + duration) * GST_MSECOND);
+ editor->audio_fade_effects = g_slist_prepend(editor->audio_fade_effects, __new_timed_value(start * GST_MSECOND, 1.0));
+ editor->audio_fade_effects = g_slist_prepend(editor->audio_fade_effects, __new_timed_value((start + duration - 100) * GST_MSECOND, 0.0));
+ editor->audio_fade_effects = g_slist_prepend(editor->audio_fade_effects, __new_timed_value((start + duration) * GST_MSECOND, 0.0));
+ }
+
+ RET_VAL_IF(editor->audio_fade_effects == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to prepend fade effect");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_add_transition(mediaeditor_s *editor, mediaeditor_transition_type_e type, unsigned int layer_id,
+ unsigned int start, unsigned int duration)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ gboolean ges_ret = FALSE;
+ GESLayer *layer = NULL;
+ GESTransitionClip *transition_clip = NULL;
+ guint64 start_nsec = 0;
+ guint64 duration_nsec = 0;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ if (!(transition_clip = ges_transition_clip_new((GESVideoStandardTransitionType)type))) {
+ LOGE("Failed to create transition clip :%d", type);
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ start_nsec = MILLI_TO_NANO(start);
+ duration_nsec = MILLI_TO_NANO(duration);
+
+ LOG_DEBUG("layer_id[%d], layer[%p], start[%"G_GUINT64_FORMAT"], duration[%"G_GUINT64_FORMAT"]",
+ layer_id, layer, start_nsec, duration_nsec);
+
+ ges_ret = ges_timeline_element_set_start(GES_TIMELINE_ELEMENT(transition_clip), start_nsec);
+ RET_VAL_IF(!ges_ret, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to set start");
+ ges_ret = ges_timeline_element_set_duration(GES_TIMELINE_ELEMENT(transition_clip), duration_nsec);
+ RET_VAL_IF(!ges_ret, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to set duration");
+
+ ret = _mediaeditor_get_layer(editor, layer_id, &layer);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to get layer");
+
+ ges_layer_add_clip(layer, GES_CLIP(transition_clip));
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static int __mediaeditor_create_effect_clip(mediaeditor_s *editor, mediaeditor_effect_type_e type, unsigned int layer_id,
+ unsigned int start, unsigned int duration, unsigned int *effect_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ gboolean ges_ret = FALSE;
+ GESClip *clip = NULL;
+ GESLayer *layer = NULL;
+ g_autofree gchar *effect_name = NULL;
+ guint64 start_nsec = 0;
+ guint64 duration_nsec = 0;
+
+ effect_name = __get_effect_name(type);
+ RET_VAL_IF(effect_name == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to get effect name");
+
+ clip = GES_CLIP(ges_effect_clip_new(effect_name, NULL));
+ RET_VAL_IF(clip == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to create effect clip");
+
+ start_nsec = MILLI_TO_NANO(start);
+ duration_nsec = MILLI_TO_NANO(duration);
+
+ LOG_DEBUG("start[%"G_GUINT64_FORMAT"], duration[%"G_GUINT64_FORMAT"]", start_nsec, duration_nsec);
+
+ ges_ret = ges_timeline_element_set_start(GES_TIMELINE_ELEMENT(clip), start_nsec);
+ RET_VAL_IF(!ges_ret, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to set start");
+ ges_ret = ges_timeline_element_set_duration(GES_TIMELINE_ELEMENT(clip), duration_nsec);
+ RET_VAL_IF(!ges_ret, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to set duration");
+
+ ret = _mediaeditor_get_layer(editor, layer_id, &layer);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ LOG_ERROR("failed to get layer");
+ g_object_unref(clip);
+ return ret;
+ }
+
+ if (!ges_layer_add_clip(layer, clip)) {
+ LOG_ERROR("failed to add clip to layer");
+ g_object_unref(clip);
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ LOG_INFO("add effect layer[%p] clip[%p]", layer, clip);
+
+ ret = _mediaeditor_create_clip_common(editor, clip, type, effect_id);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ LOG_ERROR("failed to create clip common");
+ g_object_unref(clip);
+ return ret;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static int __mediaeditor_create_effect(mediaeditor_s *editor, mediaeditor_effect_type_e type,
+ unsigned int start, unsigned int duration, unsigned int *effect_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ switch (type) {
+ case MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_IN:
+ ret = __keep_audio_fade_effect(editor, start, duration, true);
+ break;
+ case MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_OUT:
+ ret = __keep_audio_fade_effect(editor, start, duration, false);
+ break;
+ default:
+ LOG_ERROR("There's no matched effect");
+ ret = MEDIAEDITOR_ERROR_INVALID_PARAMETER;
+ break;
+ }
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to apply audio effect");
+
+ ret = _mediaeditor_create_clip_common(editor, NULL, type, effect_id);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create clip common");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_add_effect(mediaeditor_s *editor, mediaeditor_effect_type_e type, unsigned int layer_id,
+ unsigned int start, unsigned int duration, unsigned int *effect_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ switch (type) {
+ case MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_IN:
+ case MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_OUT:
+ ret = __mediaeditor_create_effect(editor, type, start, duration, effect_id);
+ break;
+ default:
+ ret = __mediaeditor_create_effect_clip(editor, type, layer_id, start, duration, effect_id);
+ break;
+ }
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create effect");
+
+ return ret;
+}
+
+int _mediaeditor_remove_effect(mediaeditor_s *editor, unsigned int effect_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ mediaeditor_clip_s *clip_data = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ clip_data = _mediaeditor_find_clip_data(editor->clips, effect_id);
+ RET_VAL_IF(clip_data == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid clip id");
+ RET_VAL_IF(clip_data->effect_type == MEDIAEDITOR_EFFECT_TYPE_NONE, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "not effect clip id");
+
+ switch (clip_data->effect_type) {
+ case MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_IN:
+ case MEDIAEDITOR_EFFECT_AUDIO_TYPE_FADE_OUT:
+ editor->clips = g_list_remove(editor->clips, clip_data);
+ free(clip_data);
+ break;
+ default:
+ ret = _mediaeditor_remove_clip(editor, effect_id);
+ break;
+ }
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to remove effect");
+
+ return ret;
+}
--- /dev/null
+/*
+ * 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 <media_editor.h>
+#include <media_editor_private.h>
+
+#define MEDIAEDITOR_INI_PATH SYSCONFDIR"/multimedia/mmfw_media_editor.ini"
+#define DEFAULT_GENERATE_DOT false
+#define DEFAULT_DOT_PATH "/tmp"
+
+/* categories */
+#define INI_CATEGORY_GENERAL "general"
+#define INI_CATEGORY_ENCODER "encoder"
+#define INI_CATEGORY_DECODER "decoder"
+#define INI_CATEGORY_RESOURCE_ACQUISITION "resource acquisition"
+
+/* items for general */
+#define INI_ITEM_DOT_PATH "dot path"
+#define INI_ITEM_DOT_GENERATE "generate dot"
+#define INI_ITEM_GST_ARGS "gstreamer arguments"
+
+/* items for encoder */
+#define INI_ITEM_ENCODER_CONTAINER "container"
+#define INI_ITEM_AUDIO_HW_ENCODER "audio hw encoder"
+#define INI_ITEM_VIDEO_HW_ENCODER "video hw encoder"
+
+/* items for decoder */
+#define INI_ITEM_AUDIO_HW_DECODER "audio hw decoder"
+#define INI_ITEM_VIDEO_HW_DECODER "video hw decoder"
+
+/* items for resource acquisition */
+#define INI_ITEM_RESOURCE_VIDEO_ENCODER "video encoder"
+#define INI_ITEM_RESOURCE_VIDEO_DECODER "video decoder"
+
+#define DEFAULT_RESOURCE_VIDEO_ENCODER_REQUIRED false
+#define DEFAULT_RESOURCE_VIDEO_DECODER_REQUIRED false
+
+#define DEFAULT_CONTAINER "ogg"
+#define DEFAULT_AUDIO_ENCODER "vorbis"
+#define DEFAULT_VIDEO_ENCODER "theora"
+
+typedef enum {
+ INI_ITEM_TYPE_BOOL,
+ INI_ITEM_TYPE_INT,
+ INI_ITEM_TYPE_STRING,
+ INI_ITEM_TYPE_STRINGS
+} ini_item_type_e;
+
+static void __dump_item(const char *prefix_str, ini_item_type_e type, void *item)
+{
+ RET_IF(prefix_str == NULL, "prefix_str is NULL");
+
+ if (item == NULL)
+ return;
+
+ switch (type) {
+ case INI_ITEM_TYPE_BOOL:
+ LOG_INFO("- %-19s = %s", prefix_str, *(bool*)item ? "yes" : "no");
+ break;
+ case INI_ITEM_TYPE_INT:
+ LOG_INFO("- %-19s = %d", prefix_str, *(int*)item);
+ break;
+ case INI_ITEM_TYPE_STRING:
+ LOG_INFO("- %-19s = %s", prefix_str, (const char *)item);
+ break;
+ case INI_ITEM_TYPE_STRINGS: {
+ gchar *joined_str = g_strjoinv(" ", item);
+ LOG_INFO("- %-19s = %s", prefix_str, joined_str);
+ g_free(joined_str);
+ break;
+ }
+ default:
+ LOG_ERROR("not supported type[%d]", type);
+ break;
+ }
+}
+
+static void __dump_ini(mediaeditor_ini_s *ini)
+{
+ RET_IF(ini == NULL, "ini is NULL");
+
+ LOG_INFO("[%s]", INI_CATEGORY_GENERAL);
+ __dump_item(INI_ITEM_DOT_GENERATE, INI_ITEM_TYPE_BOOL, &ini->general.generate_dot);
+ __dump_item(INI_ITEM_DOT_PATH, INI_ITEM_TYPE_STRING, (void *)ini->general.dot_path);
+ __dump_item(INI_ITEM_GST_ARGS, INI_ITEM_TYPE_STRINGS, ini->general.gst_args);
+
+ LOG_INFO("[%s]", INI_CATEGORY_ENCODER);
+ __dump_item(INI_ITEM_ENCODER_CONTAINER, INI_ITEM_TYPE_STRING, (void *)ini->encoder.container);
+ __dump_item(INI_ITEM_AUDIO_HW_ENCODER, INI_ITEM_TYPE_STRING, (void *)ini->encoder.audio_hw_encoder);
+ __dump_item(INI_ITEM_VIDEO_HW_ENCODER, INI_ITEM_TYPE_STRING, (void *)ini->encoder.video_hw_encoder);
+
+ LOG_INFO("[%s]", INI_CATEGORY_DECODER);
+ __dump_item(INI_ITEM_AUDIO_HW_DECODER, INI_ITEM_TYPE_STRING, (void *)ini->decoder.audio_hw_decoder);
+ __dump_item(INI_ITEM_VIDEO_HW_DECODER, INI_ITEM_TYPE_STRING, (void *)ini->decoder.video_hw_decoder);
+
+ LOG_INFO("[%s]", INI_CATEGORY_RESOURCE_ACQUISITION);
+ __dump_item(INI_ITEM_RESOURCE_VIDEO_ENCODER, INI_ITEM_TYPE_STRING, (void *)ini->resource_acquisition.video_encoder);
+ __dump_item(INI_ITEM_RESOURCE_VIDEO_DECODER, INI_ITEM_TYPE_STRING, (void *)ini->resource_acquisition.video_decoder);
+}
+
+static const char* __get_delimiter(const char *ini_path)
+{
+ const char *delimiter = ",";
+
+ if (g_strrstr(ini_path, INI_ITEM_GST_ARGS))
+ delimiter = "|";
+
+ return delimiter;
+}
+
+static void __ini_read_list(dictionary *dict, const char *category, const char *item, gchar ***list)
+{
+ const char *str = NULL;
+ g_autofree gchar *path = NULL;
+ g_autofree gchar *strtmp = NULL;
+
+ if (dict == NULL)
+ return;
+
+ RET_IF(category == NULL, "category is NULL");
+ RET_IF(item == NULL, "item is NULL");
+ RET_IF(list == NULL, "list is NULL");
+
+ path = g_strconcat(category, ":", item, NULL);
+ str = iniparser_getstring(dict, path, NULL);
+ if (str && strlen(str) > 0) {
+ strtmp = g_strdup(str);
+ g_strstrip(strtmp);
+ *list = g_strsplit(strtmp, __get_delimiter((const char *)path), 10);
+ }
+
+ LOG_DEBUG("[%s] %s", path, strtmp ? strtmp : "none");
+}
+
+static const char* __ini_get_string(dictionary *dict, const char *category, const char *item, const char *default_value)
+{
+ g_autofree gchar *path = NULL;
+ const char *ret_val = NULL;
+
+ if (dict == NULL)
+ return default_value;
+
+ RET_VAL_IF(category == NULL, default_value, "category is NULL");
+ RET_VAL_IF(item == NULL, default_value, "item is NULL");
+
+ path = g_strconcat(category, ":", item, NULL);
+ ret_val = iniparser_getstring(dict, path, default_value);
+
+ if (ret_val != NULL && strlen(ret_val) == 0)
+ return default_value;
+
+ return ret_val;
+}
+
+static bool __ini_get_boolean(dictionary *dict, const char *category, const char *item, bool default_value)
+{
+ g_autofree gchar *path = NULL;
+ bool ret_val = false;
+
+ if (dict == NULL)
+ return default_value;
+ RET_VAL_IF(category == NULL, default_value, "category is NULL");
+ RET_VAL_IF(item == NULL, default_value, "item is NULL");
+
+ path = g_strconcat(category, ":", item, NULL);
+ ret_val = (bool)iniparser_getboolean(dict, path, default_value);
+
+ return ret_val;
+}
+
+int _load_ini(mediaeditor_s *editor)
+{
+ LOG_DEBUG("Enter [%p]", editor);
+ mediaeditor_ini_s *ini = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ memset(&editor->ini, 0, sizeof(mediaeditor_ini_s));
+ ini = &editor->ini;
+
+ ini->dict = iniparser_load(MEDIAEDITOR_INI_PATH);
+ if (!ini->dict)
+ LOG_WARNING("could not open ini[%s], use default values", MEDIAEDITOR_INI_PATH);
+
+ /* general */
+ ini->general.generate_dot = __ini_get_boolean(ini->dict, INI_CATEGORY_GENERAL, INI_ITEM_DOT_GENERATE, DEFAULT_GENERATE_DOT);
+ ini->general.dot_path = __ini_get_string(ini->dict, INI_CATEGORY_GENERAL, INI_ITEM_DOT_PATH, DEFAULT_DOT_PATH);
+ if (ini->general.generate_dot) {
+ LOG_INFO("dot file will be stored in [%s]", ini->general.dot_path);
+ g_setenv("GST_DEBUG_DUMP_DOT_DIR", ini->general.dot_path, FALSE);
+ }
+ __ini_read_list(ini->dict, INI_CATEGORY_GENERAL, INI_ITEM_GST_ARGS, &ini->general.gst_args);
+
+ /* encoder */
+ ini->encoder.container = __ini_get_string(ini->dict, INI_CATEGORY_ENCODER, INI_ITEM_ENCODER_CONTAINER, DEFAULT_CONTAINER);
+ ini->encoder.audio_hw_encoder = __ini_get_string(ini->dict, INI_CATEGORY_ENCODER, INI_ITEM_AUDIO_HW_ENCODER, NULL);
+ ini->encoder.video_hw_encoder = __ini_get_string(ini->dict, INI_CATEGORY_ENCODER, INI_ITEM_VIDEO_HW_ENCODER, NULL);
+
+ /* decoder */
+ ini->decoder.audio_hw_decoder = __ini_get_string(ini->dict, INI_CATEGORY_DECODER, INI_ITEM_AUDIO_HW_DECODER, NULL);
+ ini->decoder.video_hw_decoder = __ini_get_string(ini->dict, INI_CATEGORY_DECODER, INI_ITEM_VIDEO_HW_DECODER, NULL);
+
+ /* resource acqusition */
+ ini->resource_acquisition.video_encoder = __ini_get_boolean(ini->dict, INI_CATEGORY_RESOURCE_ACQUISITION,
+ INI_ITEM_RESOURCE_VIDEO_ENCODER, DEFAULT_RESOURCE_VIDEO_ENCODER_REQUIRED);
+ ini->resource_acquisition.video_decoder = __ini_get_boolean(ini->dict, INI_CATEGORY_RESOURCE_ACQUISITION,
+ INI_ITEM_RESOURCE_VIDEO_DECODER, DEFAULT_RESOURCE_VIDEO_DECODER_REQUIRED);
+
+ __dump_ini(ini);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+void _unload_ini(mediaeditor_s *editor)
+{
+ LOG_DEBUG("Enter [%p]", editor);
+
+ RET_IF(editor == NULL, "editor is NULL");
+ RET_IF(editor->ini.dict == NULL, "ini.dict is NULL");
+
+ g_strfreev(editor->ini.general.gst_args);
+ editor->ini.general.gst_args = NULL;
+
+ iniparser_freedict(editor->ini.dict);
+ LOG_DEBUG("ini instance[%p] is freed", editor->ini.dict);
+ editor->ini.dict = NULL;
+}
+
+bool _is_resource_required(mediaeditor_ini_s *ini)
+{
+ RET_VAL_IF(ini == NULL, false, "ini is NULL");
+
+ return (ini->resource_acquisition.video_encoder ||
+ ini->resource_acquisition.video_decoder);
+}
--- /dev/null
+/*
+ * 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 "media_editor.h"
+#include "media_editor_private.h"
+#include <limits.h>
+
+static void __print_layer_data(mediaeditor_layer_s *layer_data)
+{
+#ifdef PRINT_LAYER_DEBUG_INFO
+ RET_IF(layer_data == NULL, "layer_data is null");
+ LOG_DEBUG("layer_data[%p], layer[%p], id[%d]", layer_data, layer_data->layer, layer_data->id);
+#endif
+}
+
+static void __print_layer_priorities(GESTimeline *timeline)
+{
+#ifdef PRINT_LAYER_DEBUG_INFO
+ guint priority = 0;
+
+ RET_IF(timeline == NULL, "timeline is NULL");
+
+ for (GList *layer_iter = timeline->layers; layer_iter; layer_iter = layer_iter->next) {
+ GESLayer *layer = GES_LAYER(layer_iter->data);
+
+ priority = ges_layer_get_priority(layer);
+ LOG_DEBUG("layer[%p] layer priority[%d]", layer, priority);
+ }
+#endif
+}
+
+static gint __mediaeditor_comparefunc_find_layer(gconstpointer a, gconstpointer b)
+{
+ mediaeditor_layer_s *layer_data = (mediaeditor_layer_s *)a;
+ unsigned int *id = (unsigned int *)b;
+
+ if (layer_data == NULL || layer_data->id != *id)
+ return -1;
+
+ LOG_DEBUG("layer id[%d], layer[%p]", *id, layer_data->layer);
+
+ return 0;
+}
+
+static mediaeditor_layer_s *__mediaeditor_find_layer_data(GList *lists, unsigned int id)
+{
+ GList *list = NULL;
+
+ list = g_list_find_custom(lists, (gconstpointer)&id, __mediaeditor_comparefunc_find_layer);
+ RET_VAL_IF(list == NULL, NULL, "failed to find matched layer");
+
+ __print_layer_data(list->data);
+
+ return list->data;
+}
+
+static GESLayer *__mediaeditor_find_layer(GList *lists, unsigned int id)
+{
+ mediaeditor_layer_s *layer_data = NULL;
+
+ layer_data = __mediaeditor_find_layer_data(lists, id);
+ RET_VAL_IF(layer_data == NULL, NULL, "failed to find layer data");
+
+ return layer_data->layer;
+}
+
+static gint __mediaeditor_comparefunc_find_id(gconstpointer a, gconstpointer b)
+{
+ mediaeditor_layer_s *layer_data = (mediaeditor_layer_s *)a;
+ GESLayer *layer = (GESLayer *)b;
+
+ if (layer_data == NULL || layer_data->layer != layer)
+ return -1;
+
+ LOG_DEBUG("layer[%p], layer id[%d]", layer_data->layer, layer_data->id);
+
+ return 0;
+}
+
+static unsigned int __mediaeditor_find_layer_id(GList *lists, GESLayer *layer)
+{
+ mediaeditor_layer_s *layer_data = NULL;
+ GList *list = NULL;
+
+ list = g_list_find_custom(lists, (gconstpointer)layer, __mediaeditor_comparefunc_find_id);
+ RET_VAL_IF(list == NULL, INT_MAX, "failed to find matched layer");
+
+ layer_data = (mediaeditor_layer_s *)list->data;
+ RET_VAL_IF(layer_data == NULL, INT_MAX, "failed to find layer");
+
+ return layer_data->id;
+}
+
+static mediaeditor_layer_s * __mediaeditor_create_layer_data(GESLayer *layer, unsigned int id)
+{
+ mediaeditor_layer_s *data = NULL;
+
+ data = g_new0(mediaeditor_layer_s, 1);
+
+ data->layer = layer;
+ data->id = id;
+
+ return data;
+}
+
+static int __resync_layers(mediaeditor_s *editor, unsigned int layer_priority, bool *is_resynced)
+{
+ /* It should be done in GES but it's not curreltly. This is workaround. */
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ GESTimeline *timeline = editor->gst.timeline;
+ GESLayer *layer = NULL;
+ unsigned int layer_id = 0;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(is_resynced == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "is_resynced is NULL");
+ *is_resynced = false;
+
+ LOG_DEBUG("layer_priority[%d]", layer_priority);
+
+ GList *list = ges_timeline_get_layers(editor->gst.timeline);
+ if (list == NULL) {
+ LOG_DEBUG("There's no layers to resync");
+ return MEDIAEDITOR_ERROR_NONE;
+ }
+
+ ret = _mediaeditor_get_layer_id(editor, layer_priority + 1, &layer_id);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret , "failed to get layer id");
+
+ ret = _mediaeditor_get_layer(editor, layer_id, &layer);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ LOG_DEBUG("There's nothing to resync");
+ return MEDIAEDITOR_ERROR_NONE;
+ }
+ LOG_DEBUG("priority + 1[%d], layer id[%d], layer[%p]", layer_priority + 1, layer_id, layer);
+
+ /* ges_timeline_move_layer() resync priority internally. So we don't need to move layer anymore */
+ if (!ges_timeline_move_layer(timeline, layer, layer_priority)) {
+ LOG_DEBUG("failed to move layer");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ *is_resynced = true;
+ LOG_DEBUG("layer is resynced");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static void __invoke_layer_priority_changed_cb(mediaeditor_s *editor, mediaeditor_layer_info_s *layer_info, unsigned int size)
+{
+ RET_IF(editor == NULL, "editor is NULL");
+
+ if (editor->layer_priority_changed_cb.callback) {
+ LOG_DEBUG(">>> callback[%p] user_data[%p]", editor->layer_priority_changed_cb.callback, editor->layer_priority_changed_cb.user_data);
+ ((mediaeditor_layer_priority_changed_cb)(editor->layer_priority_changed_cb.callback))(layer_info, size, editor->layer_priority_changed_cb.user_data);
+ LOG_DEBUG("<<< end of the callback");
+ }
+}
+
+static int __create_layer_info(mediaeditor_s *editor, mediaeditor_layer_info_s **layer_info, unsigned int *size)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int lowest_priority = 0;
+ unsigned int layer_id = 0;
+ unsigned int number_of_layer = 0;
+ mediaeditor_layer_info_s *layer_info_tmp = NULL;
+
+ ret = _mediaeditor_get_layer_lowest_priority(editor, &lowest_priority);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to get lowest layer priority");
+
+ number_of_layer = lowest_priority + 1;
+ layer_info_tmp = (mediaeditor_layer_info_s *)malloc(sizeof(mediaeditor_layer_info_s) * number_of_layer);
+ RET_VAL_IF(layer_info_tmp == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to alloc layer info");
+
+ memset(layer_info_tmp, 0, sizeof(mediaeditor_layer_info_s) * number_of_layer);
+
+ for (unsigned int i = 0 ; i < number_of_layer ; i++) {
+ ret = _mediaeditor_get_layer_id(editor, i, &layer_id);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to get layer id");
+
+ layer_info_tmp[i].id = layer_id;
+ layer_info_tmp[i].priority = i;
+ }
+
+ *layer_info = layer_info_tmp;
+ *size = number_of_layer;
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static int __mediaeditor_create_layer_common(mediaeditor_s *editor, GESLayer *layer, unsigned int *layer_id)
+{
+ mediaeditor_layer_s *layer_data = NULL;
+
+ layer_data = __mediaeditor_create_layer_data(layer, editor->layer_id);
+ RET_VAL_IF(layer_data == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "layer_data is null");
+
+ __print_layer_data(layer_data);
+
+ editor->layers = g_list_append(editor->layers, layer_data);
+ RET_VAL_IF(editor->layers == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "list is null");
+
+ *layer_id = (editor->layer_id)++;
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_get_layer(mediaeditor_s *editor, unsigned int layer_id, GESLayer **layer)
+{
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(layer == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer is NULL");
+
+ *layer = __mediaeditor_find_layer(editor->layers, layer_id);
+ RET_VAL_IF(*layer == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid layer id");
+
+ LOG_DEBUG("layer[%p]", *layer);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_add_layer(mediaeditor_s *editor, unsigned int *layer_id, unsigned int *layer_priority)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ GESLayer *layer = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(layer_id == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_id is NULL");
+ RET_VAL_IF(layer_priority == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_priority is NULL");
+
+ layer = ges_timeline_append_layer(editor->gst.timeline);
+ RET_VAL_IF(layer == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to append layer");
+
+ *layer_priority = ges_layer_get_priority(layer);
+
+ LOG_DEBUG("added layer[%p], layer_priority[%d]", layer, *layer_priority);
+
+ ret = __mediaeditor_create_layer_common(editor, layer, layer_id);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create layer common");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_remove_layer(mediaeditor_s *editor, unsigned int layer_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ GESLayer *layer = NULL;
+ g_autofree mediaeditor_layer_s *layer_data = NULL;
+ unsigned int layer_priority = 0;
+ unsigned int number_of_layer = 0;
+ mediaeditor_layer_info_s *layer_info = NULL;
+ bool is_resynced = false;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ layer_data = __mediaeditor_find_layer_data(editor->layers, layer_id);
+ RET_VAL_IF(layer_data == NULL || layer_data->layer == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid layer id");
+ layer = layer_data->layer;
+
+ __print_layer_priorities(editor->gst.timeline);
+
+ layer_priority = ges_layer_get_priority(layer);
+ LOG_DEBUG("layer_priority to remove [%d]", layer_priority);
+
+ if (!ges_timeline_remove_layer(editor->gst.timeline, layer)) {
+ LOG_ERROR("failed to remove layer frome timeline");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ __print_layer_priorities(editor->gst.timeline);
+
+ editor->layers = g_list_remove(editor->layers, layer_data);
+
+ ret = __resync_layers(editor, layer_priority, &is_resynced);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to resync layer");
+
+ __print_layer_priorities(editor->gst.timeline);
+
+ if (is_resynced)
+ {
+ ret = __create_layer_info(editor, &layer_info, &number_of_layer);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create layer info");
+
+ __invoke_layer_priority_changed_cb(editor, layer_info, number_of_layer);
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_move_layer(mediaeditor_s *editor, unsigned int layer_id, unsigned int layer_priority)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ GESLayer *layer = NULL;
+ gboolean result = FALSE;
+ unsigned int current_priority = 0;
+ unsigned int number_of_layer = 0;
+ mediaeditor_layer_info_s *layer_info = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ ret = _mediaeditor_get_layer(editor, layer_id, &layer);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to get layer");
+
+ ret = _mediaeditor_get_layer_priority(editor, layer_id, ¤t_priority);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "invalid layer id");
+
+ if (current_priority == layer_priority) {
+ LOG_DEBUG("Nothing to move");
+ return MEDIAEDITOR_ERROR_NONE;
+ }
+
+ LOG_DEBUG("move layer ID[%d] -> layer priority[%d]", layer_id, layer_priority);
+
+ __print_layer_priorities(editor->gst.timeline);
+
+ result = ges_timeline_move_layer(editor->gst.timeline, layer, layer_priority);
+ RET_VAL_IF(result == FALSE, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to move layer");
+
+ __print_layer_priorities(editor->gst.timeline);
+
+ ret = __create_layer_info(editor, &layer_info, &number_of_layer);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create layer info");
+
+ __invoke_layer_priority_changed_cb(editor, layer_info, number_of_layer);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_activate_layer(mediaeditor_s *editor, unsigned int layer_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ GESLayer *layer = NULL;
+ GList *tracks = NULL;
+ gboolean result = FALSE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ tracks = ges_timeline_get_tracks(editor->gst.timeline);
+ RET_VAL_IF(tracks == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "tracks is NULL");
+
+ ret = _mediaeditor_get_layer(editor, layer_id, &layer);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to get layer");
+
+ result = ges_layer_set_active_for_tracks(layer, true, tracks);
+ RET_VAL_IF(result == FALSE, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to set active");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_deactivate_layer(mediaeditor_s *editor, unsigned int layer_id)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ GESLayer *layer = NULL;
+ GList *tracks = NULL;
+ gboolean result = FALSE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ tracks = ges_timeline_get_tracks(editor->gst.timeline);
+ RET_VAL_IF(tracks == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "tracks is NULL");
+
+ ret = _mediaeditor_get_layer(editor, layer_id, &layer);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to get layer");
+
+ result = ges_layer_set_active_for_tracks(layer, false, tracks);
+ RET_VAL_IF(result == FALSE, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to set active");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_get_layer_priority(mediaeditor_s *editor, unsigned int layer_id, unsigned int *layer_priority)
+{
+ GESLayer *layer = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is null");
+ RET_VAL_IF(layer_priority == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_priority is null");
+
+ layer = __mediaeditor_find_layer(editor->layers, layer_id);
+ RET_VAL_IF(layer == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "failed to get layer");
+
+ *layer_priority = ges_layer_get_priority(layer);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_get_layer_lowest_priority(mediaeditor_s *editor, unsigned int *layer_priority)
+{
+ unsigned int priority_max = 0;
+ unsigned int priority_tmp = 0;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is null");
+ RET_VAL_IF(layer_priority == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_priority is null");
+ RET_VAL_IF(editor->layers == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "there's no layer");
+
+ for (GList *layer_iter = editor->layers; layer_iter; layer_iter = layer_iter->next) {
+ mediaeditor_layer_s *layer_data = (mediaeditor_layer_s *)(layer_iter->data);
+
+ priority_tmp = ges_layer_get_priority(layer_data->layer);
+ if (priority_tmp > priority_max) {
+ priority_max = priority_tmp;
+ }
+ }
+
+ *layer_priority = priority_max;
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_get_layer_id(mediaeditor_s *editor, unsigned int layer_priority, unsigned int *layer_id)
+{
+ GESLayer *layer = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is null");
+ RET_VAL_IF(layer_id == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "layer_id is null");
+
+ LOG_DEBUG("layer priority[%d]", layer_priority);
+
+ layer = ges_timeline_get_layer(editor->gst.timeline, layer_priority);
+ RET_VAL_IF(layer == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invlid layer_priority");
+
+ *layer_id = __mediaeditor_find_layer_id(editor->layers, layer);
+ RET_VAL_IF(*layer_id == INT_MAX, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invlid layer_priority");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 "media_editor.h"
+#include "media_editor_private.h"
+#include <system_info.h>
+
+#define DEFAULT_DOT_FILE_NAME_PREFIX "mediaeditor"
+
+static const char* __state_str[] = {
+ [MEDIAEDITOR_STATE_IDLE] = "IDLE",
+ [MEDIAEDITOR_STATE_RENDERING] = "RENDERING",
+ [MEDIAEDITOR_STATE_PREVIEW] = "PREVIEW"
+};
+
+typedef struct _idle_userdata {
+ mediaeditor_s *editor;
+ idle_cb_type_e type;
+ union {
+ mediaeditor_state_e state;
+ mediaeditor_error_e error;
+ } new;
+} idle_userdata_s;
+
+static const char *__get_error_string(mediaeditor_error_e error)
+{
+ switch (error) {
+ case MEDIAEDITOR_ERROR_NONE:
+ return "NONE";
+ case MEDIAEDITOR_ERROR_PERMISSION_DENIED:
+ return "PERMISSION_DENIED";
+ case MEDIAEDITOR_ERROR_INVALID_PARAMETER:
+ return "INVALID_PARAMETER";
+ case MEDIAEDITOR_ERROR_INVALID_OPERATION:
+ return "INVALID_OPERATION";
+ case MEDIAEDITOR_ERROR_INVALID_STATE:
+ return "INVALID_STATE";
+ case MEDIAEDITOR_ERROR_RESOURCE_FAILED:
+ return "RESOURCE_FAILED";
+ case MEDIAEDITOR_ERROR_RESOURCE_CONFLICT:
+ return "RESOURCE_CONFLICT";
+ default:
+ LOG_ERROR_IF_REACHED("invalid error(0x%x)", error);
+ return "(invalid error)";
+ }
+}
+
+static int __create_encoding_profile(mediaeditor_s *editor, const char* path)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ GstEncodingContainerProfile *profile = NULL;
+ GstCaps *container_caps = NULL;
+ GstCaps *audio_caps = NULL;
+ GstCaps *video_caps = NULL;
+ g_autofree gchar *container_mime_type = NULL;
+ g_autofree gchar *audio_mime_type = NULL;
+ g_autofree gchar *video_mime_type = NULL;
+ g_autofree gchar *uri = NULL;
+
+ uri = gst_filename_to_uri(path, NULL);
+ RET_VAL_IF(uri == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "failed to get uri");
+ LOGI("uri : [%s]", uri);
+
+ LOG_INFO("Container : %s", editor->ini.encoder.container);
+ if (!strcmp(editor->ini.encoder.container, CONTAINER_TYPE_MPEG)) {
+ container_mime_type = g_strdup(CONTAINER_MIME_TYPE_MPEG);
+ audio_mime_type = g_strdup(AUDIO_ENCODER_MIME_TYPE_MPEG);
+ video_mime_type = g_strdup(VIDEO_ENCODER_MIME_TYPE_MPEG);
+ } else {
+ /* default is ogg */
+ container_mime_type = g_strdup(CONTAINER_MIME_TYPE_OGG);
+ audio_mime_type = g_strdup(AUDIO_ENCODER_MIME_TYPE_VORBIS);
+ video_mime_type = g_strdup(VIDEO_ENCODER_MIME_TYPE_THEORA);;
+ }
+
+ container_caps = gst_caps_from_string(container_mime_type);
+ RET_VAL_IF(container_caps == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to create caps");
+ profile = gst_encoding_container_profile_new(NULL, NULL, container_caps, NULL);
+ if (profile == NULL) {
+ LOG_ERROR("failed to create profile");
+ ret = MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ goto error_container;
+ }
+
+ audio_caps = gst_caps_from_string(audio_mime_type);
+ RET_VAL_IF(audio_caps == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to create audio_caps");
+ if (!gst_encoding_container_profile_add_profile(profile, (GstEncodingProfile *)gst_encoding_audio_profile_new(audio_caps, NULL, NULL, 0))) {
+ LOG_ERROR("failed to add audio prifile");
+ ret = MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ g_object_unref(profile);
+ goto error_audio_caps;
+ }
+
+ video_caps = gst_caps_from_string(video_mime_type);
+ RET_VAL_IF(video_caps == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to create video_caps");
+ if (!gst_encoding_container_profile_add_profile(profile, (GstEncodingProfile *)gst_encoding_video_profile_new(video_caps, NULL, NULL, 0))) {
+ LOG_ERROR("failed to add audio prifile");
+ ret = MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ g_object_unref(profile);
+ goto error_video_caps;
+ }
+
+ if (!ges_pipeline_set_render_settings(editor->gst.pipeline, uri, (GstEncodingProfile *)profile)) {
+ LOG_ERROR("failed to add audio prifile");
+ ret = MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ g_object_unref(profile);
+ }
+
+error_video_caps:
+ gst_caps_unref(video_caps);
+error_audio_caps:
+ gst_caps_unref(audio_caps);
+error_container:
+ gst_caps_unref(container_caps);
+
+ return ret;
+}
+
+static int __commit_audio_fade_effect(mediaeditor_s *editor)
+{
+ GESTrackElement *track_element = NULL;
+ GstControlSource *control_source = NULL;
+
+ LOG_DEBUG("Enter");
+
+ if (!editor->audio_fade_effects) {
+ LOG_DEBUG("No audio fade effect");
+ return MEDIAEDITOR_ERROR_NONE;
+ }
+
+ GList * tracks = ges_timeline_get_tracks(editor->gst.timeline);
+ for (GList *tmp = tracks; tmp; tmp = tmp->next) {
+ GList *trackelements = ges_track_get_elements(GES_TRACK(tmp->data));
+
+ if (GES_IS_AUDIO_URI_SOURCE(trackelements->data)) {
+ track_element = GES_TRACK_ELEMENT(trackelements->data);
+ break;
+ }
+
+ g_list_free_full(trackelements, gst_object_unref);
+ }
+ RET_VAL_IF(track_element == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to get track element");
+
+ control_source = GST_CONTROL_SOURCE(gst_interpolation_control_source_new());
+ g_object_set(G_OBJECT(control_source), "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
+
+ if (!gst_timed_value_control_source_set_from_list(GST_TIMED_VALUE_CONTROL_SOURCE(control_source), editor->audio_fade_effects)) {
+ LOG_ERROR("failed to set timed value control source");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ if (!ges_track_element_set_control_source(track_element, control_source, "volume", "direct-absolute")) {
+ LOG_ERROR("failed to set control source to track");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ LOG_DEBUG("Leave");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static void _generate_dot(mediaeditor_s *editor, const gchar *name)
+{
+ g_autofree gchar *dot_name = NULL;
+
+ RET_IF(editor == NULL, "editor is NULL");
+ RET_IF(editor->gst.pipeline == NULL, "pipeline is NULL");
+
+ LOG_INFO("Enter");
+
+ if (!name)
+ dot_name = g_strdup(DEFAULT_DOT_FILE_NAME_PREFIX);
+ else
+ dot_name = g_strconcat(DEFAULT_DOT_FILE_NAME_PREFIX, ".", name, NULL);
+
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(editor->gst.pipeline), GST_DEBUG_GRAPH_SHOW_ALL, dot_name);
+
+ LOG_INFO("dot file[%s] is generated", dot_name);
+}
+
+static bool __meet_gst_state(mediaeditor_state_e state, GstState gst_state)
+{
+ if ((state == MEDIAEDITOR_STATE_IDLE && gst_state == GST_STATE_READY) ||
+ ((state == MEDIAEDITOR_STATE_PREVIEW || state == MEDIAEDITOR_STATE_RENDERING) && gst_state == GST_STATE_PAUSED))
+ return true;
+
+ return false;
+}
+
+static void __invoke_state_changed_cb(mediaeditor_s *editor, mediaeditor_state_e old, mediaeditor_state_e new)
+{
+ RET_IF(editor == NULL, "editor is NULL");
+
+ LOG_INFO("state is changed [%s] -> [%s]", __state_str[old], __state_str[new]);
+
+ if (editor->state_changed_cb.callback) {
+ LOG_DEBUG(">>> callback[%p], user_data[%p]", editor->state_changed_cb.callback, editor->state_changed_cb.user_data);
+ ((mediaeditor_state_changed_cb)(editor->state_changed_cb.callback))(old, new, editor->state_changed_cb.user_data);
+ LOG_DEBUG("<<< end of the callback");
+ }
+
+ GENERATE_DOT(editor, "STATE_%s", __state_str[editor->state]);
+}
+
+void _invoke_error_cb(mediaeditor_s *editor, mediaeditor_error_e error)
+{
+ RET_IF(editor == NULL, "editor is NULL");
+
+ LOG_ERROR("error[0x%x, %s]", error, __get_error_string(error));
+
+ if (editor->error_cb.callback) {
+ LOG_DEBUG(">>> callback[%p], user_data[%p]", editor->error_cb.callback, editor->error_cb.user_data);
+ ((mediaeditor_error_cb)(editor->error_cb.callback))(error, editor->state, editor->error_cb.user_data);
+ LOG_DEBUG("<<< end of the callback");
+ }
+}
+
+static void __invoke_render_completed_cb(mediaeditor_s *editor)
+{
+ RET_IF(editor == NULL, "editor is NULL");
+
+ if (editor->render_completed_cb.callback) {
+ LOG_DEBUG(">>> callback[%p] user_data[%p]", editor->render_completed_cb.callback, editor->render_completed_cb.user_data);
+ ((mediaeditor_render_completed_cb)(editor->render_completed_cb.callback))(editor->render_completed_cb.user_data);
+ LOG_DEBUG("<<< end of the callback");
+
+ editor->render_completed_cb.callback = NULL;
+ editor->render_completed_cb.user_data = NULL;
+ }
+}
+
+static gboolean __bus_cb(GstBus *bus, GstMessage *message, gpointer *data)
+{
+ mediaeditor_s *editor = (mediaeditor_s *)data;
+ GError *err = NULL;
+ mediaeditor_error_e error = MEDIAEDITOR_ERROR_NONE;
+ GstState gst_state_old = GST_STATE_VOID_PENDING;
+ GstState gst_state_new = GST_STATE_VOID_PENDING;
+ GstState gst_state_pending = GST_STATE_VOID_PENDING;
+ gchar *state_transition_name = NULL;
+
+ RET_VAL_IF(editor == NULL, FALSE, "editor is NULL");
+
+ switch (GST_MESSAGE_TYPE(message)) {
+ case GST_MESSAGE_ERROR:
+ 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);
+
+ if (err->domain == GST_RESOURCE_ERROR)
+ error = MEDIAEDITOR_ERROR_RESOURCE_FAILED;
+ else
+ error = MEDIAEDITOR_ERROR_INVALID_OPERATION;
+
+ _invoke_error_cb(editor, error);
+
+ g_error_free(err);
+
+ break;
+ case GST_MESSAGE_STATE_CHANGED:
+ if (GST_MESSAGE_SRC(message) != GST_OBJECT(editor->gst.pipeline))
+ return TRUE;
+
+ gst_message_parse_state_changed(message, &gst_state_old, &gst_state_new, &gst_state_pending);
+ state_transition_name = g_strdup_printf("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));
+
+ LOG_INFO("GST_MESSAGE_STATE_CHANGED: %s", state_transition_name);
+ g_free(state_transition_name);
+
+ if (editor->pend_state == editor->state) {
+ LOG_DEBUG("pend_state[%s] is same with current state", __state_str[editor->pend_state]);
+ break;
+ }
+
+ LOG_INFO("editor state[%d], pend_state[%d]", editor->state, editor->pend_state);
+ if (__meet_gst_state(editor->pend_state, gst_state_new)) {
+ mediaeditor_state_e old_state = editor->state;
+ editor->state = editor->pend_state;
+ __invoke_state_changed_cb(editor, old_state, editor->state);
+ break;
+ }
+
+ break;
+ case GST_MESSAGE_EOS:
+ LOG_INFO("EOS reached");
+ int ret = _gst_pipeline_set_state(editor, GST_STATE_NULL);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, FALSE, "failed to set GST_STATE_NULL");
+
+ __invoke_render_completed_cb(editor);
+
+#ifndef TIZEN_TV
+ ret = _release_all_resources(editor);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to release all resources");
+#endif
+
+ editor->state = MEDIAEDITOR_STATE_IDLE;
+
+ return TRUE;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean __idle_cb(gpointer user_data)
+{
+ idle_userdata_s *data = (idle_userdata_s*)user_data;
+ mediaeditor_s *editor = NULL;
+
+ RET_VAL_IF(data == NULL, G_SOURCE_REMOVE, "userdata is NULL");
+ if (data->editor == NULL) {
+ LOG_ERROR("editor is NULL");
+ return G_SOURCE_REMOVE;
+ }
+
+ editor = data->editor;
+
+ switch (data->type) {
+ case IDLE_CB_TYPE_STATE: {
+ mediaeditor_state_e old_state;
+
+ g_mutex_lock(&editor->mutex);
+ g_mutex_lock(&editor->event_src_mutex);
+ editor->idle_cb_event_source_ids[data->type] = 0;
+ g_mutex_unlock(&editor->event_src_mutex);
+ old_state = editor->state;
+ editor->state = data->new.state;
+ g_mutex_unlock(&editor->mutex);
+
+ __invoke_state_changed_cb(editor, old_state, editor->state);
+ break;
+ }
+ case IDLE_CB_TYPE_ERROR:
+ g_mutex_lock(&editor->event_src_mutex);
+ editor->idle_cb_event_source_ids[data->type] = 0;
+ g_mutex_unlock(&editor->event_src_mutex);
+
+ _invoke_error_cb(editor, data->new.error);
+ break;
+ default:
+ LOG_ERROR_IF_REACHED("type(%d)", data->type);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void __post_state_cb_in_idle(mediaeditor_s *editor, mediaeditor_state_e new_state)
+{
+ idle_userdata_s *data = NULL;
+
+ RET_IF(editor == NULL, "editor is NULL");
+
+ if (editor->state == new_state)
+ return;
+
+ data = g_new0(idle_userdata_s, 1);
+ data->editor = editor;
+ data->type = IDLE_CB_TYPE_STATE;
+ data->new.state = new_state;
+
+ editor->pend_state = new_state;
+
+ g_mutex_lock(&editor->event_src_mutex);
+ editor->idle_cb_event_source_ids[data->type] = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, __idle_cb, data, g_free);
+ g_mutex_unlock(&editor->event_src_mutex);
+
+ LOG_DEBUG("state will be changed [%s] -> [%s]", __state_str[editor->state], __state_str[new_state]);
+}
+
+void _post_error_cb_in_idle(mediaeditor_s *editor, mediaeditor_error_e error)
+{
+ idle_userdata_s *data = NULL;
+
+ RET_IF(editor == NULL, "editor is NULL");
+
+ data = g_new0(idle_userdata_s, 1);
+ data->editor = editor;
+ data->type = IDLE_CB_TYPE_ERROR;
+ data->new.error = error;
+
+ g_mutex_lock(&editor->event_src_mutex);
+ editor->idle_cb_event_source_ids[data->type] = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, __idle_cb, data, g_free);
+ g_mutex_unlock(&editor->event_src_mutex);
+
+ LOG_DEBUG("error will occur [0x%x]", error);
+}
+
+void _remove_remained_event_sources(mediaeditor_s *editor)
+{
+ RET_IF(editor == NULL, "editor is NULL");
+
+ for (int i = IDLE_CB_TYPE_STATE; i < IDLE_CB_TYPE_NUM; i++) {
+ if (editor->idle_cb_event_source_ids[i] == 0)
+ continue;
+
+ g_source_remove(editor->idle_cb_event_source_ids[i]);
+ LOG_DEBUG("idle_cb_event_source_ids[%d] source id[%u]", i, editor->idle_cb_event_source_ids[i]);
+ editor->idle_cb_event_source_ids[i] = 0;
+ }
+}
+
+int _gst_init(mediaeditor_s *editor)
+{
+ static gboolean initialized = FALSE;
+ gboolean ret = FALSE;
+ char **argv = NULL;
+ gint argc = 1;
+ gchar **gst_args;
+ GError *err = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ if (initialized) {
+ LOG_ERROR("gstreamer is already initialized.");
+ return TRUE;
+ }
+
+ gst_args = editor->ini.general.gst_args;
+
+ if (gst_args)
+ argc += g_strv_length(gst_args);
+
+ argv = (char **)calloc(argc, sizeof(char*));
+ RET_VAL_IF(argv == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to calloc()");
+
+ argv[0] = g_strdup("capi-media-editor");
+
+ for (int i = 0 ; gst_args && gst_args[i]; ++i) {
+ if (argc <= i + 1) {
+ LOG_ERROR("need to check, prevent overrun");
+ break;
+ }
+ argv[i + 1] = gst_args[i];
+ LOG_DEBUG("[%s] is added", argv[i + 1]);
+ }
+
+ ret = gst_init_check(&argc, &argv, &err);
+
+ /* we need to free argv even if gst_init_check() is failed */
+ for (int i = 1 ; i < argc ; i++)
+ argv[i] = NULL;
+
+ SAFE_FREE(argv[0]);
+ SAFE_FREE(argv);
+
+ if (!ret) {
+ LOG_ERROR("Failed to initialize gstreamer; %s", err->message);
+ g_clear_error(&err);
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ /* Initialize the GStreamer Editing Services */
+ ges_init();
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _gst_pipeline_set_state(mediaeditor_s *editor, GstState state)
+{
+ GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(editor->gst.pipeline == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "pipeline is NULL");
+
+ ret = gst_element_set_state(GST_ELEMENT(editor->gst.pipeline), state);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ LOG_ERROR("failed to gst_element_set_state(), state[%s]", gst_element_state_get_name(state));
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+void _gst_pipeline_destroy(mediaeditor_s *editor)
+{
+ LOG_DEBUG("Enter");
+
+ RET_IF(editor == NULL, "editor is NULL");
+
+ if (editor->gst.bus) {
+ gst_object_unref(editor->gst.bus);
+ editor->gst.bus = NULL;
+ }
+ if (editor->gst.pipeline) {
+ gst_object_unref(G_OBJECT(editor->gst.pipeline));
+ editor->gst.pipeline = NULL;
+ }
+}
+
+static int __mediaeditor_set_bus_cb(mediaeditor_s *editor)
+{
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ editor->gst.bus = gst_pipeline_get_bus(GST_PIPELINE(editor->gst.pipeline));
+ RET_VAL_IF(editor->gst.bus == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to get bus from pipeline");
+
+ gst_bus_add_watch_full(editor->gst.bus, G_PRIORITY_DEFAULT, (GstBusFunc) __bus_cb, editor, NULL);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static bool __check_clip_added(mediaeditor_s *editor)
+{
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ return editor->clips != NULL;
+}
+
+int _mediaeditor_stop(mediaeditor_s *editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autoptr(GMutexLocker) locker = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ locker = g_mutex_locker_new(&editor->mutex);
+
+ RET_VAL_IF(editor->state == MEDIAEDITOR_STATE_IDLE, MEDIAEDITOR_ERROR_INVALID_STATE,
+ "the state should not be IDLE");
+
+ ret = _gst_pipeline_set_state(editor, GST_STATE_NULL);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to set GST_STATE_NULL");
+
+#ifndef TIZEN_TV
+ ret = _release_all_resources(editor);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to release all resources");
+#endif
+
+ __post_state_cb_in_idle(editor, MEDIAEDITOR_STATE_IDLE);
+
+ LOG_INFO("media editor[%p] is destroyed", editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_create_pipeline(mediaeditor_s *editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ editor->gst.pipeline = ges_pipeline_new();
+ RET_VAL_IF(editor->gst.pipeline == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to create ges pipeline");
+
+ if (editor->gst.timeline == NULL) {
+#ifdef TIZEN_MEDIAEDITOR_EXPERIMENTAL_AUDIO_TRACK_ONLY
+ LOG_DEBUG("Create timeline with audio track only");
+ GESTrack *tracka;
+ GESTimeline *timeline;
+
+ /* This is our main GESTimeline */
+ timeline = ges_timeline_new();
+
+ tracka = GES_TRACK(ges_audio_track_new());
+
+ if (!ges_timeline_add_track(timeline, tracka)) {
+ gst_object_unref(timeline);
+ timeline = NULL;
+ }
+ editor->gst.timeline = timeline;
+#else
+ LOG_DEBUG("Create timeline with audio, video track");
+ editor->gst.timeline = ges_timeline_new_audio_video();
+#endif
+ if (editor->gst.timeline == NULL) {
+ LOG_ERROR("failed to create ges timeline");
+ gst_object_unref(editor->gst.pipeline);
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+ }
+
+ if (!ges_pipeline_set_timeline(editor->gst.pipeline, editor->gst.timeline)) {
+ LOG_ERROR("failed to set ges timeline to ges pipeline");
+ gst_object_unref(editor->gst.pipeline);
+ gst_object_unref(editor->gst.timeline);
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ ret = __mediaeditor_set_bus_cb(editor);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to set bus callback");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_start_render(mediaeditor_s *editor, const char* path)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(path == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "path is NULL");
+ RET_VAL_IF(!__check_clip_added(editor), MEDIAEDITOR_ERROR_INVALID_OPERATION, "There's no clip to render");
+
+#ifndef TIZEN_TV
+ if (editor->ini.resource_acquisition.video_decoder) {
+ ret = _acquire_resource_for_type(editor, MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_DECODER);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to acquire video decoder resource");
+ }
+
+ if (editor->ini.resource_acquisition.video_encoder) {
+ ret = _acquire_resource_for_type(editor, MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_ENCODER);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to acquire video encoder resource");
+ }
+#endif
+
+ if(!ges_timeline_commit_sync(editor->gst.timeline)) {
+ LOG_ERROR("There's nothing to render");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ ret = __commit_audio_fade_effect(editor);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to commit audio fade effect");
+
+ ret = __create_encoding_profile(editor, path);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create encoding profile");
+
+ if (!ges_pipeline_set_mode(editor->gst.pipeline, GES_PIPELINE_MODE_SMART_RENDER)) {
+ LOG_ERROR("failed to set smart render pipeline mode");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ ret = _gst_pipeline_set_state(editor, GST_STATE_PLAYING);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to set GST_STATE_PLAYING");
+
+ editor->pend_state = MEDIAEDITOR_STATE_RENDERING;
+
+ GENERATE_DOT(editor, "%s", GST_ELEMENT_NAME(editor->gst.pipeline));
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_cancel_render(mediaeditor_s *editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ ret = _gst_pipeline_set_state(editor, GST_STATE_READY);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to set GST_STATE_READY");
+
+ __post_state_cb_in_idle(editor, MEDIAEDITOR_STATE_IDLE);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_start_preview(mediaeditor_s *editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(!__check_clip_added(editor), MEDIAEDITOR_ERROR_INVALID_OPERATION, "There's no clip to render");
+
+ if(!ges_timeline_commit_sync(editor->gst.timeline)) {
+ LOG_ERROR("There's nothing to render");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ if (!ges_pipeline_set_mode(editor->gst.pipeline, GES_PIPELINE_MODE_PREVIEW)) {
+ LOG_ERROR("failed to set preview pipeline mode");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ ret = _gst_pipeline_set_state(editor, GST_STATE_PLAYING);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to set GST_STATE_PLAYING");
+
+ editor->pend_state = MEDIAEDITOR_STATE_PREVIEW;
+
+ GENERATE_DOT(editor, "%s", GST_ELEMENT_NAME(editor->gst.pipeline));
+
+ LOG_ERROR("State[%d],[%d]", editor->state, editor->pend_state);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_stop_preview(mediaeditor_s *editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ ret = _gst_pipeline_set_state(editor, GST_STATE_READY);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to set GST_STATE_READY");
+
+ __post_state_cb_in_idle(editor, MEDIAEDITOR_STATE_IDLE);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _check_privilege(const char *path, bool file_exist, int mode)
+{
+ int ret = 0;
+
+ RET_VAL_IF(!path, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "path is null");
+
+ if (file_exist) {
+ ret = access(path, mode);
+ } else {
+ g_autofree gchar *dirname = g_path_get_dirname(path);
+ RET_VAL_IF(!dirname, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "invalid path");
+
+ ret = access(dirname, mode);
+ }
+
+ if (ret != 0) {
+ if (errno == EACCES || errno == EPERM) {
+ LOG_ERROR("fail to access path[%s]: permission denied", path);
+ return MEDIAEDITOR_ERROR_PERMISSION_DENIED;
+ } else {
+ LOG_ERROR("fail to access path[%s]: invalid path", path);
+ return MEDIAEDITOR_ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+ }
+
+int _check_feature(const char *feature)
+{
+ bool supported = false;
+
+ RET_VAL_IF(feature == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "feature is NULL");
+
+ if (system_info_get_platform_bool(feature, &supported) != SYSTEM_INFO_ERROR_NONE) {
+ LOG_ERROR("failed to system_info_get_platform_bool(), feature[%s]", feature);
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ if (!supported) {
+ LOG_ERROR("feature[%s] is not supported", feature);
+ return MEDIAEDITOR_ERROR_NOT_SUPPORTED;
+ }
+
+ LOG_INFO("feature[%s] is supported", feature);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 <media_editor.h>
+#include <media_editor_private.h>
+
+static void __project_loaded_cb(GESProject *project, GESTimeline *timeline, mediaeditor_s *editor)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ LOG_DEBUG("project[%p]:timeline[%p] is loaded", project, timeline);
+
+ g_signal_handlers_disconnect_by_func(project, (GCallback)__project_loaded_cb, editor);
+
+ ret = _mediaeditor_create_pipeline(editor);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ _invoke_error_cb(editor, ret);
+ return;
+ }
+
+ if (editor->project_loaded_cb.callback) {
+ LOG_DEBUG(">>> callback[%p], user_data[%p]", editor->project_loaded_cb.callback, editor->project_loaded_cb.user_data);
+ ((mediaeditor_project_loaded_cb)(editor->project_loaded_cb.callback))(editor->project_loaded_cb.user_data);
+ LOG_DEBUG("<<< end of the callback");
+ }
+}
+
+static int __connect_project_loaded_signal(mediaeditor_s *editor)
+{
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ g_signal_connect(editor->gst.project, "loaded", (GCallback)__project_loaded_cb, editor);
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static int __extract_timeline(mediaeditor_s *editor)
+{
+ GESTimeline *timeline = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ timeline = GES_TIMELINE(ges_asset_extract(GES_ASSET(editor->gst.project), NULL));
+ RET_VAL_IF(timeline == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to extract timeline from project");
+
+ editor->gst.timeline = timeline;
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+static int __mediaeditor_create_ges_project(mediaeditor_s *editor, gchar *uri)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ GESProject *project = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ project = ges_project_new(uri);
+ RET_VAL_IF(project == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to create project");
+
+ editor->gst.project = project;
+
+ ret = __extract_timeline(editor);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to load timeline");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_create_project(mediaeditor_s *editor, const char *path)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autofree gchar *uri = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(path == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "path is NULL");
+
+ uri = gst_filename_to_uri(path, NULL);
+ RET_VAL_IF(uri == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "failed to get uri");
+ LOGI("uri : [%s]", uri);
+
+ if (editor->gst.pipeline)
+ _gst_pipeline_destroy(editor);
+
+ ret = __mediaeditor_create_ges_project(editor, NULL);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create ges project");
+
+ ret = _mediaeditor_create_pipeline(editor);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create pipeline");
+
+ ret = _mediaeditor_save_project(editor, uri);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to save project");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_load_project(mediaeditor_s *editor, const char *path)
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ g_autofree gchar *uri = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(path == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "path is NULL");
+
+ uri = gst_filename_to_uri(path, NULL);
+ RET_VAL_IF(uri == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "failed to get uri");
+ LOGI("uri : [%s]", uri);
+
+ if (editor->gst.pipeline)
+ _gst_pipeline_destroy(editor);
+
+ if (editor->gst.project)
+ gst_object_unref(editor->gst.project);
+
+ ret = __mediaeditor_create_ges_project(editor, uri);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to create ges project");
+
+ ret = __connect_project_loaded_signal(editor);
+ RET_VAL_IF(ret != MEDIAEDITOR_ERROR_NONE, ret, "failed to connect signal");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _mediaeditor_save_project(mediaeditor_s *editor, const gchar *uri)
+{
+ GESAsset *formatter_asset = NULL;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+ RET_VAL_IF(uri == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "uri is NULL");
+
+ formatter_asset = ges_asset_request(GES_TYPE_FORMATTER, "ges", NULL);
+ RET_VAL_IF(formatter_asset == NULL, MEDIAEDITOR_ERROR_INVALID_OPERATION, "failed to request formatter");
+
+ if (!ges_project_save(editor->gst.project, editor->gst.timeline, uri, formatter_asset, TRUE, NULL)) {
+ LOGE("failed to save project");
+ return MEDIAEDITOR_ERROR_INVALID_OPERATION;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 "media_editor.h"
+#include "media_editor_private.h"
+
+#ifndef TIZEN_TV
+static int __resource_release_cb(mm_resource_manager_h mgr,
+ mm_resource_manager_res_h res, void *user_data)
+{
+ int ret = true;
+ mediaeditor_s *editor = (mediaeditor_s *)user_data;
+
+ RET_VAL_IF(editor == NULL, false, "editor is NULL");
+
+ editor->resource.release_cb_is_calling = true;
+
+ for (int i = 0; i < RESOURCE_TYPE_MAX; i++) {
+ if (editor->resource.res[i] == res) {
+ LOG_INFO("type[%d] resource was released by resource manager", i);
+ editor->resource.res[i] = NULL;
+ }
+ }
+
+ if (_mediaeditor_stop(editor) != MEDIAEDITOR_ERROR_NONE)
+ ret = false;
+
+ _post_error_cb_in_idle(editor, MEDIAEDITOR_ERROR_RESOURCE_CONFLICT);
+
+ editor->resource.release_cb_is_calling = false;
+
+ return ret;
+}
+
+static bool __is_valid_resource_type(mm_resource_manager_res_type_e type)
+{
+ if (type < MM_RESOURCE_MANAGER_RES_TYPE_VIDEO_DECODER || type >= RESOURCE_TYPE_MAX) {
+ LOG_ERROR("Type[%d] is a invalid resource type", type);
+ return false;
+ }
+ return true;
+}
+
+int _create_resource_manager(mediaeditor_s *editor)
+{
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ if (mm_resource_manager_create(MM_RESOURCE_MANAGER_APP_CLASS_MEDIA,
+ __resource_release_cb, editor,
+ &editor->resource.mgr) != MM_RESOURCE_MANAGER_ERROR_NONE) {
+ LOG_ERROR("Failed to init resource manager for media");
+ return MEDIAEDITOR_ERROR_RESOURCE_FAILED;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _acquire_resource_for_type(mediaeditor_s *editor, mm_resource_manager_res_type_e type)
+{
+ int ret = MM_RESOURCE_MANAGER_ERROR_NONE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ if (!editor->resource.mgr) {
+ LOG_DEBUG("There's no acquired hw encoder, decoder resources");
+ return MEDIAEDITOR_ERROR_NONE;
+ }
+
+ RET_VAL_IF(!__is_valid_resource_type(type), MEDIAEDITOR_ERROR_INVALID_PARAMETER,
+ "type is wrong");
+
+ if (editor->resource.res[type] != NULL) {
+ LOG_ERROR("type[%d] resource was already acquired", type);
+ return MEDIAEDITOR_ERROR_RESOURCE_FAILED;
+ }
+
+ LOG_DEBUG("mark for acquire type[%d] resource", type);
+ ret = mm_resource_manager_mark_for_acquire(editor->resource.mgr, type,
+ MM_RESOURCE_MANAGER_RES_VOLUME_FULL, &editor->resource.res[type]);
+ if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+ LOG_ERROR("failed to mark resource for acquire, ret[0x%x]", ret);
+ return MEDIAEDITOR_ERROR_RESOURCE_FAILED;
+ }
+
+ LOG_DEBUG("commit type[%d] resource", type);
+ ret = mm_resource_manager_commit(editor->resource.mgr);
+ if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+ LOG_ERROR("failed to commit of resource, ret([0x%x]", ret);
+ return MEDIAEDITOR_ERROR_RESOURCE_FAILED;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _release_all_resources(mediaeditor_s *editor)
+{
+ int ret = MM_RESOURCE_MANAGER_ERROR_NONE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ if (!editor->resource.mgr) {
+ LOG_DEBUG("There's no acquired hw encoder, decoder resources");
+ return MEDIAEDITOR_ERROR_NONE;
+ }
+
+ if(editor->resource.release_cb_is_calling) {
+ LOG_INFO("__resource_release_cb is calling, so skip");
+ return MEDIAEDITOR_ERROR_NONE;
+ }
+
+ ret = mm_resource_manager_mark_all_for_release(editor->resource.mgr);
+ if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+ LOG_ERROR("failed to mark all for release, ret[0x%x]", ret);
+ return MEDIAEDITOR_ERROR_RESOURCE_FAILED;
+ }
+ ret = mm_resource_manager_commit(editor->resource.mgr);
+ if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+ LOG_ERROR("failed to commit resource, ret[0x%x]", ret);
+ return MEDIAEDITOR_ERROR_RESOURCE_FAILED;
+ }
+ LOG_DEBUG("all resources were released by resource manager");
+
+ for (int i = 0; i < RESOURCE_TYPE_MAX; i++) {
+ editor->resource.need_to_acquire[i] = false;
+ editor->resource.res[i] = NULL;
+ }
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+
+int _destroy_resource_manager(mediaeditor_s *editor)
+{
+ int ret = MM_RESOURCE_MANAGER_ERROR_NONE;
+
+ RET_VAL_IF(editor == NULL, MEDIAEDITOR_ERROR_INVALID_PARAMETER, "editor is NULL");
+
+ if (!editor->resource.mgr) {
+ LOG_DEBUG("There's no acquired hw encoder, decoder resources");
+ return MEDIAEDITOR_ERROR_NONE;
+ }
+
+ ret = mm_resource_manager_destroy(editor->resource.mgr);
+ if (ret != MM_RESOURCE_MANAGER_ERROR_NONE) {
+ LOG_ERROR("failed to destroy resource manager, ret[0x%x]", ret);
+ return MEDIAEDITOR_ERROR_RESOURCE_FAILED;
+ }
+
+ editor->resource.mgr = NULL;
+ LOG_DEBUG("destroyed resource manager");
+
+ return MEDIAEDITOR_ERROR_NONE;
+}
+#endif
--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
+SET(fw_test "${fw_name}-test")
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(${fw_test} REQUIRED elementary evas ecore appcore-efl)
+FOREACH(flag ${${fw_test}_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ MESSAGE(${flag})
+ENDFOREACH()
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -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} ${fw_name} ${${fw_test}_LDFLAGS})
+ INSTALL(TARGETS ${src_name} DESTINATION bin)
+
+ENDFOREACH()
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <sys/time.h>
+#include <dlog.h>
+#include <Ecore.h>
+#include <Elementary.h>
+#include <appcore-efl.h>
+#include <media_editor.h>
+
+#define EXPORT_API __attribute__((__visibility__("default")))
+
+#ifdef PACKAGE
+#undef PACKAGE
+#endif
+#define PACKAGE "media_editor_test"
+
+#define GET_DISPLAY(x) (void*)(x)
+#define DEFAULT_FILE_PATH "/tmp"
+#define MAX_FILE_NAME_LENGTH 256
+#define MAX_FILE_PATH_LENGTH (MAX_FILE_NAME_LENGTH - 20)
+
+#define CHECK_MM_ERROR(expr) \
+do { \
+ int ret = 0; \
+ ret = expr; \
+ if (ret != 0) { \
+ g_print("[%s:%d] error code : %x \n", __func__, __LINE__, ret); \
+ return; \
+ } \
+} while (0)
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) \
+if (x) { \
+ g_free(x); \
+ x = NULL; \
+}
+#endif
+
+enum {
+ MENU_STATE_INIT,
+ MENU_STATE_MAIN,
+ MENU_STATE_NUM,
+};
+
+static void print_menu();
+static gboolean cmd_input(GIOChannel *channel, GIOCondition condition, gpointer data);
+static gboolean mode_change(gchar buf);
+static int app_create(void *data);
+static int app_terminate(void *data);
+
+typedef struct _appdata {
+ Evas_Object *win;
+ Evas_Object *eo;
+ Evas_Object *bg;
+ Evas_Object *rect;
+} appdata;
+
+struct appcore_ops ops = {
+ .create = app_create,
+ .terminate = app_terminate,
+};
+
+typedef struct _mediaeditor_handle {
+ mediaeditor_h mediaeditor;
+ int type;
+ int menu_state;
+} mediaeditor_handle_t;
+
+static struct timeval previous_time;
+static struct timeval current_time;
+static struct timeval result_time;
+
+appdata ad;
+GIOChannel *stdin_channel = NULL;
+static GTimer *timer = NULL;
+mediaeditor_h heditor = NULL;
+static mediaeditor_handle_t *hmediaeditor;
+
+static inline void flush_stdin()
+{
+ int ch;
+ while ((ch = getchar()) != EOF && ch != '\n');
+}
+
+static void render_completed_cb(void *user_data)
+{
+ g_print("Enter\n");
+}
+
+static void print_menu()
+{
+ switch (hmediaeditor->menu_state) {
+ case MENU_STATE_INIT:
+ g_print("\n\t=======================================\n");
+ g_print("\t MEDIAEDITOR_TESTSUITE\n");
+ g_print("\t=======================================\n");
+ g_print("\t '1' Start preview\n");
+ g_print("\t '2' Stop preview\n");
+ g_print("\t '3' Start render\n");
+ g_print("\t 'q' Exit\n");
+ g_print("\t=======================================\n");
+ break;
+ default:
+ g_print("\n\tunknow menu state !!\n");
+ return;
+ }
+
+ g_print("\tCommand >> ");
+
+ return;
+}
+
+static gboolean cmd_input(GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+ gchar *buf = NULL;
+ gsize read_size;
+ GError *g_error = NULL;
+
+ g_print("\n\tENTER\n");
+
+ g_io_channel_read_line(channel, &buf, &read_size, NULL, &g_error);
+ if (g_error) {
+ g_print("\n\tg_io_channel_read_chars error\n");
+ g_error_free(g_error);
+ g_error = NULL;
+ }
+
+ if (buf) {
+ g_strstrip(buf);
+
+ g_print("\n\tMenu Status : %d\n", hmediaeditor->menu_state);
+ switch (hmediaeditor->menu_state) {
+ case MENU_STATE_INIT:
+ mode_change(buf[0]);
+ break;
+ default:
+ break;
+ }
+
+ g_free(buf);
+ buf = NULL;
+
+ print_menu();
+ } else {
+ g_print("\n\tNo read input\n");
+ }
+
+ return TRUE;
+}
+
+static gboolean init_handle()
+{
+ hmediaeditor->menu_state = MENU_STATE_INIT;
+
+ return TRUE;
+}
+
+
+static void start_preview()
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+
+ ret = mediaeditor_add_layer(heditor, &layer_id, &layer_priority);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to add layer : error[0x%x]\n", ret);
+ return;
+ }
+
+ ret = mediaeditor_add_clip(heditor, "/tmp/test1.mp4", layer_priority, 0, G_TIME_SPAN_MILLISECOND * 10, 0, &clip_id);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to add clip : error[0x%x]\n", ret);
+ return;
+ }
+
+ ret = mediaeditor_set_display(heditor, MEDIAEDITOR_DISPLAY_TYPE_EVAS, GET_DISPLAY(ad.eo));
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to set display : error[0x%x]\n", ret);
+ return;
+ }
+
+ ret = mediaeditor_start_preview(heditor);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to start preview : error[0x%x]\n", ret);
+ return;
+ }
+}
+
+static void stop_preview()
+{
+ int ret = 0;
+
+ ret = mediaeditor_stop_preview(heditor);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to stop preview : error[0x%x]\n", ret);
+ return;
+ }
+}
+
+static void start_render()
+{
+ int ret = MEDIAEDITOR_ERROR_NONE;
+ unsigned int layer_id = 0;
+ unsigned int layer_priority = 0;
+ unsigned int clip_id = 0;
+
+ ret = mediaeditor_add_layer(heditor, &layer_id, &layer_priority);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to add layer : error[0x%x]\n", ret);
+ return;
+ }
+
+ ret = mediaeditor_add_clip(heditor, "/tmp/test1.mp4", layer_priority, 0, 100, 0, &clip_id);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to add clip : error[0x%x]\n", ret);
+ return;
+ }
+
+ g_print("Call render async\n");
+ ret = mediaeditor_start_render(heditor, "/tmp/out_render.mp4", render_completed_cb, heditor);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to start render : error[0x%x]\n", ret);
+ return;
+ }
+}
+
+static gboolean mode_change(gchar buf)
+{
+ switch (buf) {
+ case '1':
+ g_print("Start preview\n");
+ start_preview();
+ break;
+ case '2':
+ g_print("Stop preview\n");
+ stop_preview();
+ break;
+ case '3':
+ g_print("Start render\n");
+ start_render();
+ break;
+ case 'q':
+ g_print("\t Quit MediaEditor Testsuite!!\n");
+ elm_exit();
+ return FALSE;
+ default:
+ return FALSE;
+ }
+
+ gettimeofday(&previous_time, NULL);
+
+ g_timer_reset(timer);
+
+ gettimeofday(¤t_time, NULL);
+ timersub(¤t_time, &previous_time, &result_time);
+
+ g_print("\n\tElapsed time : %ld.%lds\n", result_time.tv_sec, result_time.tv_usec);
+
+ return TRUE;
+}
+
+static int app_create(void *data)
+{
+ appdata *app_data = data;
+ int w = 0;
+ int h = 0;
+ Evas_Object *win = NULL;
+ Evas_Object *eo = NULL;
+ Evas_Object *bg = NULL;
+ Evas_Object *rect = NULL;
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ if (app_data == NULL) {
+ g_print("\t\nappdata is NULL\n");
+ return 0;
+ }
+
+ /* use gl backend */
+ elm_config_accel_preference_set("opengl");
+
+ win = elm_win_add(NULL, "media_editor_test", ELM_WIN_BASIC);
+ if (win) {
+ elm_win_title_set(win, "media_editor_test");
+ elm_win_borderless_set(win, EINA_TRUE);
+ elm_win_screen_size_get(win, NULL, NULL, &w, &h);
+ g_print("\n\tscreen size %dx%d\n\n", w, h);
+ evas_object_resize(win, w, h);
+ elm_win_autodel_set(win, EINA_TRUE);
+ elm_win_alpha_set(win, EINA_TRUE);
+ } else {
+ g_print("\n\tfailed to get window\n\n");
+ return 1;
+ }
+
+ bg = elm_bg_add(win);
+ if (bg) {
+ elm_win_resize_object_add(win, bg);
+ evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_show(bg);
+ } else {
+ g_print("\n\tfailed to get elm bg\n\n");
+ return 1;
+ }
+
+ rect = evas_object_rectangle_add(evas_object_evas_get(win));
+ if (rect) {
+ evas_object_color_set(rect, 0, 0, 0, 0);
+ evas_object_render_op_set(rect, EVAS_RENDER_COPY);
+ } else {
+ g_print("\n\tfailed to get rectangle\n\n");
+ return 1;
+ }
+
+ elm_win_resize_object_add(win, rect);
+ evas_object_size_hint_weight_set(rect, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_show(rect);
+
+ /* Create evas image object for EVAS surface */
+ eo = evas_object_image_add(evas_object_evas_get(win));
+ evas_object_image_size_set(eo, w, h);
+ evas_object_image_fill_set(eo, 0, 0, w, h);
+ evas_object_resize(eo, w, h);
+ evas_object_show(eo);
+
+ elm_win_activate(win);
+ evas_object_show(win);
+
+ app_data->win = win;
+ app_data->eo = eo;
+
+ timer = g_timer_new();
+ g_timer_reset(timer);
+
+ init_handle();
+
+ g_print("create");
+ ret = mediaeditor_create(&heditor);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to create mediaeditor : error[0x%x]", ret);
+ return 1;
+ }
+
+ print_menu();
+
+ return 0;
+}
+
+static int app_terminate(void *data)
+{
+ appdata *app_data = data;
+ int ret = MEDIAEDITOR_ERROR_NONE;
+
+ if (app_data == NULL) {
+ g_print("\n\tappdata is NULL\n");
+ return 0;
+ }
+
+ if (timer) {
+ g_timer_stop(timer);
+ g_timer_destroy(timer);
+ timer = NULL;
+ }
+
+ ret = mediaeditor_destroy(heditor);
+ if (ret != MEDIAEDITOR_ERROR_NONE) {
+ g_print("failed to destroy : error[0x%x]", ret);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * This function is the example main function for media editor API.
+ * @return This function returns 0.
+ */
+int main(int argc, char **argv)
+{
+ int bret = 0;
+
+ hmediaeditor = (mediaeditor_handle_t *) g_malloc0(sizeof(mediaeditor_handle_t));
+
+ stdin_channel = g_io_channel_unix_new(fileno(stdin));/* read from stdin */
+ g_io_add_watch(stdin_channel, G_IO_IN, (GIOFunc)cmd_input, NULL);
+
+ memset(&ad, 0x0, sizeof(appdata));
+ ops.data = &ad;
+
+ bret = appcore_efl_main(PACKAGE, &argc, &argv, &ops);
+
+ g_print("\n\treturn appcore_efl : %d\n\n", bret);
+
+ g_free(hmediaeditor);
+ g_io_channel_unref(stdin_channel);
+
+ return bret;
+}