From a23e00489603bde524cc02294622db67c2c5d9bd Mon Sep 17 00:00:00 2001 From: Joungkook Seo Date: Mon, 13 Jul 2015 09:25:39 +0900 Subject: [PATCH] Add initial code for mediamuxer [1] Update the code according to the ACR review [2] Fixed review comment of patch set(#3) [3] Sync with 2.4 and Refine code (2nd) [4] Fixed review comment of patch set(#5) Change-Id: I82544b53d0057dec8c42640f8635a905cb70cf2d Signed-off-by: Joungkook Seo --- AUTHORS | 2 + CMakeLists.txt | 112 +++ LICENSE.APLv2 | 204 +++++ NOTICE | 3 + capi-mediamuxer.manifest | 5 + capi-mediamuxer.pc.in | 13 + doc/mediamuxer_doc.h | 60 ++ include/mediamuxer.h | 325 +++++++ include/mediamuxer_error.h | 124 +++ include/mediamuxer_ini.h | 85 ++ include/mediamuxer_port.h | 570 +++++++++++++ include/mediamuxer_private.h | 108 +++ include/mediamuxer_util.h | 96 +++ include/port_custom/mediamuxer_port_custom.h | 31 + include/port_ffmpeg/mediamuxer_port_ffmpeg.h | 31 + include/port_gst/mediamuxer_port_gst.h | 93 ++ packaging/capi-mediamuxer.spec | 80 ++ src/CMakeLists.txt | 4 + src/mediamuxer.c | 381 +++++++++ src/mediamuxer_ini.c | 240 ++++++ src/mediamuxer_port.c | 272 ++++++ src/port_custom/mediamuxer_port_custom.c | 69 ++ src/port_ffmpeg/mediamuxer_port_ffmpeg.c | 70 ++ src/port_gst/mediamuxer_port_gst.c | 1185 ++++++++++++++++++++++++++ test/CMakeLists.txt | 23 + test/mediamuxer_test.c | 734 ++++++++++++++++ 26 files changed, 4920 insertions(+) create mode 100755 AUTHORS create mode 100755 CMakeLists.txt create mode 100755 LICENSE.APLv2 create mode 100755 NOTICE create mode 100755 capi-mediamuxer.manifest create mode 100755 capi-mediamuxer.pc.in create mode 100755 doc/mediamuxer_doc.h create mode 100755 include/mediamuxer.h create mode 100755 include/mediamuxer_error.h create mode 100755 include/mediamuxer_ini.h create mode 100755 include/mediamuxer_port.h create mode 100755 include/mediamuxer_private.h create mode 100755 include/mediamuxer_util.h create mode 100755 include/port_custom/mediamuxer_port_custom.h create mode 100755 include/port_ffmpeg/mediamuxer_port_ffmpeg.h create mode 100755 include/port_gst/mediamuxer_port_gst.h create mode 100755 packaging/capi-mediamuxer.spec create mode 100755 src/CMakeLists.txt create mode 100644 src/mediamuxer.c create mode 100644 src/mediamuxer_ini.c create mode 100644 src/mediamuxer_port.c create mode 100644 src/port_custom/mediamuxer_port_custom.c create mode 100644 src/port_ffmpeg/mediamuxer_port_ffmpeg.c create mode 100644 src/port_gst/mediamuxer_port_gst.c create mode 100644 test/CMakeLists.txt create mode 100644 test/mediamuxer_test.c diff --git a/AUTHORS b/AUTHORS new file mode 100755 index 0000000..205b4f2 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Satheesan E N +Joungkook Seo diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..35a7cf0 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,112 @@ + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +SET(fw_name "capi-mediamuxer") + +PROJECT(${fw_name}) + +SET(CMAKE_INSTALL_PREFIX /usr) +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) + +SET(INC_DIR include) +SET(INC_PORT_GST_DIR include/port_gst) +SET(INC_PORT_CUSTOM_DIR include/port_custom) +SET(INC_PORT_FFMPEG_DIR include/port_ffmpeg) +INCLUDE_DIRECTORIES(${INC_DIR} ${INC_PORT_GST_DIR} ${INC_PORT_CUSTOM_DIR} ${INC_PORT_FFMPEG_DIR}) + +SET(dependents "dlog glib-2.0 mm-common capi-media-tool iniparser gstreamer-1.0 gstreamer-plugins-base-1.0 gstreamer-app-1.0") +SET(pc_dependents "capi-base-common capi-media-tool") + +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 "-I./include -I./include/headers ${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -Werror") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g") + +IF("${ARCH}" STREQUAL "arm") + ADD_DEFINITIONS("-DTARGET") +ENDIF("${ARCH}" STREQUAL "arm") + +ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"") +ADD_DEFINITIONS("-DTIZEN_DEBUG") + +SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}") + +AUX_SOURCE_DIRECTORY (src MAIN_SRC) +AUX_SOURCE_DIRECTORY (src/port_gst PORT_GST_SRC) +AUX_SOURCE_DIRECTORY (src/port_custom PORT_CUST_SRC) +AUX_SOURCE_DIRECTORY (src/port_ffmpeg PORT_FFMPEG_SRC) + +LIST (APPEND SOURCES + ${MAIN_SRC} + ${PORT_FFMPEG_SRC} + ${PORT_CUST_SRC} + ${PORT_GST_SRC} + ) + +ADD_LIBRARY(${fw_name} SHARED ${SOURCES}) + +TARGET_LINK_LIBRARIES(${fw_name} ${${fw_name}_LDFLAGS}) + +SET_TARGET_PROPERTIES(${fw_name} + PROPERTIES + VERSION ${FULLVER} + SOVERSION ${MAJORVER} + CLEAN_DIRECT_OUTPUT 1 +) + +INSTALL(TARGETS ${fw_name} DESTINATION ${LIB_INSTALL_DIR}) +INSTALL( + DIRECTORY ${INC_DIR}/ DESTINATION include/media + FILES_MATCHING + PATTERN "mediamuxer_*.h" EXCLUDE + PATTERN "${INC_DIR}/*.h" + ) + +SET(PC_NAME ${fw_name}) +SET(PC_REQUIRED ${pc_dependents}) +SET(PC_LDFLAGS -l${fw_name}) +SET(PC_CFLAGS -I\${includedir}/media) + +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/pkgconfig) + +ADD_SUBDIRECTORY(test) + +IF(UNIX) + +ADD_CUSTOM_TARGET (distclean @echo cleaning for source distribution) +ADD_CUSTOM_COMMAND( + DEPENDS clean + COMMENT "distribution clean" + COMMAND find + ARGS . + -not -name config.cmake -and \( + -name tester.c -or + -name Testing -or + -name CMakeFiles -or + -name cmake.depends -or + -name cmake.check_depends -or + -name CMakeCache.txt -or + -name cmake.check_cache -or + -name *.cmake -or + -name Makefile -or + -name core -or + -name core.* -or + -name gmon.out -or + -name install_manifest.txt -or + -name *.pc -or + -name *~ \) + | grep -v TC | xargs rm -rf + TARGET distclean + VERBATIM +) + +ENDIF(UNIX) + diff --git a/LICENSE.APLv2 b/LICENSE.APLv2 new file mode 100755 index 0000000..9c13a9b --- /dev/null +++ b/LICENSE.APLv2 @@ -0,0 +1,204 @@ +Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/NOTICE b/NOTICE new file mode 100755 index 0000000..ccdad52 --- /dev/null +++ b/NOTICE @@ -0,0 +1,3 @@ +Copyright (c) Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Apache License, Version 2. +Please, see the LICENSE file for Apache License terms and conditions. diff --git a/capi-mediamuxer.manifest b/capi-mediamuxer.manifest new file mode 100755 index 0000000..a76fdba --- /dev/null +++ b/capi-mediamuxer.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/capi-mediamuxer.pc.in b/capi-mediamuxer.pc.in new file mode 100755 index 0000000..2d56d71 --- /dev/null +++ b/capi-mediamuxer.pc.in @@ -0,0 +1,13 @@ +# 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} \ No newline at end of file diff --git a/doc/mediamuxer_doc.h b/doc/mediamuxer_doc.h new file mode 100755 index 0000000..d955b47 --- /dev/null +++ b/doc/mediamuxer_doc.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_DOC_H__ +#define __TIZEN_MEDIAMUXER_DOC_H__ + +/** + * @file mediamuxer_doc.h + * @brief This file contains high level documentation of the CAPI MEDIAMUXER. + */ + +/** + * @ingroup CAPI_MEDIA_FRAMEWORK + * @defgroup CAPI_MEDIAMUXER_MODULE Media Muxer + * @brief The @ref CAPI_MEDIAMUXER_MODULE APIs provides functions for muxing media data + * + * @section CAPI_MEDIAMUXER_MODULE_HEADER Required Header + * \#include + * + * @section CAPI_MEDIAMUXER_MODULE_OVERVIEW Overview + * + * MEDIAMUXER API set allows : + * The API set allows one to directly access media muxer on device. + * Application can create, add relevent media track(s) and write corresponding + * samples to get muxed media files. mediamuxer takes encoded media as input + * and gives muxed media in a compatable container format. + * + * Typical Call Flow of mediamuxer APIs is: + * mediamuxer_create() + * mediamuxer_set_data_sink() + * mediamuxer_add_track(1) + * mediamuxer_add_track(2) [add more tracks, if needed] + * mediamuxer_start() + * while() + * if (is_track(1)_data_available) + * mediamuxer_write_sample(track(1)), + * else + * mediamuxer_close_track(1) + * if (is_track(2)_data_available) + * mediamuxer_write_sample(track(2)) + * else + * mediamuxer_close_track(2) + * mediamuxer_stop() + * mediamuxer_destroy() + */ + +#endif /* __TIZEN_MEDIAMUXER_DOC_H__ */ diff --git a/include/mediamuxer.h b/include/mediamuxer.h new file mode 100755 index 0000000..0a382b1 --- /dev/null +++ b/include/mediamuxer.h @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_H__ +#define __TIZEN_MEDIAMUXER_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef TIZEN_ERROR_MEDIA_MUXER +#define TIZEN_ERROR_MEDIA_MUXER -0x05000000 +#endif + +/** +* @file mediamuxer.h +* @brief This file contains the capi media muxer API. +*/ + +/** +* @addtogroup CAPI_MEDIAMUXER_MODULE +* @ +*/ + +/** + * @brief Media Muxer handle type + * @since_tizen 3.0 + */ +typedef struct mediamuxer_s *mediamuxer_h; + +/** + * @brief Enumeration for media muxer state + * @since_tizen 3.0 + */ +typedef enum { + MEDIAMUXER_STATE_NONE, /**< The mediamuxer is not created */ + MEDIAMUXER_STATE_IDLE, /**< The mediamuxer is created, but not prepared */ + MEDIAMUXER_STATE_READY, /**< The mediamuxer is ready to mux media */ + MEDIAMUXER_STATE_MUXING, /**< The mediamuxer is muxing media */ + MEDIAMUXER_STATE_PAUSED /**< The mediamuxer is paused while muxing media */ +} mediamuxer_state_e; + +/** + * @brief Enumeration for media muxer error + * @since_tizen 3.0 + */ +typedef enum { + MEDIAMUXER_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */ + MEDIAMUXER_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, + MEDIAMUXER_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */ + MEDIAMUXER_ERROR_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION, /**< Invalid operation */ + MEDIAMUXER_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< Not supported */ + MEDIAMUXER_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED, /**< Permission denied */ + MEDIAMUXER_ERROR_INVALID_STATE = TIZEN_ERROR_MEDIA_MUXER | 0x01, /**< Invalid state */ + MEDIAMUXER_ERROR_INVALID_PATH = TIZEN_ERROR_MEDIA_MUXER | 0x02, /**< Invalid path */ + MEDIAMUXER_ERROR_RESOURCE_LIMIT = TIZEN_ERROR_MEDIA_MUXER | 0x03 /**< Resource limit */ +} mediamuxer_error_e; + +/** + * @brief Enumeration for media muxer output format + * @since_tizen 3.0 + */ +typedef enum { + MEDIAMUXER_CONTAINER_FORMAT_MP4 = MEDIA_FORMAT_CONTAINER_MP4, /**< The mediamuxer output format is MP4 container */ +} mediamuxer_output_format_e; + +/** + * @brief Called when error occurs in media muxer. + * @details Following error codes can be delivered. + * #MEDIAMUXER_ERROR_INVALID_OPERATION, + * #MEDIAMUXER_ERROR_NOT_SUPPORTED, + * #MEDIAMUXER_ERROR_INVALID_PATH, + * #MEDIAMUXER_ERROR_RESOURCE_LIMIT + * @since_tizen 3.0 + * @param[in] error The error that occurred in media muxer + * @param[in] user_data The user data passed from the code where + * mediamuxer_set_error_cb() was invoked + * This data will be accessible from @a mediamuxer_error_cb + * @pre Create media muxer handle by calling mediamuxer_create() function. + * @see mediamuxer_set_error_cb() + * @see mediamuxer_unset_error_cb() + */ +typedef void (*mediamuxer_error_cb)(mediamuxer_error_e error, void *user_data); + +/** + * @brief Creates a media muxer handle for muxing. + * @since_tizen 3.0 + * @param[out] muxer A new handle to media muxer + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @post The media muxer state will be #MEDIAMUXER_STATE_IDLE. + * @see mediamuxer_destroy() + */ +int mediamuxer_create(mediamuxer_h *muxer); + +/** + * @brief Sets the sink path of output stream. + * @since_tizen 3.0 + * @remarks The mediastorage privilege(http://tizen.org/privilege/mediastorage) should be added if any video/audio files are to be saved in the internal storage. + * @remarks The externalstorage privilege(http://tizen.org/privilege/externalstorage) should be added if any video/audio files are to be saved in the external storage. + * @param[in] muxer A new handle to media muxer + * @param[in] path The location of the output media file, such as the file path + This is the path at which the muxed file should be saved. + * @param[in] format The format of the output media file + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_STATE Invalid state + * @retval #MEDIAMUXER_ERROR_PERMISSION_DENIED Permission denied + * @retval #MEDIAMUXER_ERROR_INVALID_PATH Invalid path + * @pre The media muxer state will be #MEDIAMUXER_STATE_IDLE by calling mediamuxer_create + * @see #mediamuxer_output_format_e + */ +int mediamuxer_set_data_sink(mediamuxer_h muxer, char *path, mediamuxer_output_format_e format); + +/** + * @brief Adds the media track of interest to the muxer handle. + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @param[in] media_format The format of media muxer + * @param[out] track_index The index of the media track + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_STATE Invalid state + * @pre The media muxer state must be set to #MEDIAMUXER_STATE_IDLE. + * @see #media_format_h + * @see mediamuxer_create() + * @see mediamuxer_start() + * */ +int mediamuxer_add_track(mediamuxer_h muxer, media_format_h media_format, int *track_index); + +/** + * @brief Starts the media muxer. + * @remarks Initiates the necessary parameters, and keeps the muxer ready for writing data. + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_STATE Invalid state + * @pre The media muxer state must be set to #MEDIAMUXER_STATE_IDLE. + * @post The media muxer state will be #MEDIAMUXER_STATE_READY. + * @see mediamuxer_create() + * @see mediamuxer_stop() + * */ +int mediamuxer_start(mediamuxer_h muxer); + +/** + * @brief Writes the media packet of interest to the muxer handle. + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @param[in] track_index The index of the media track + * @param[in] inbuf The packet of media muxer + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_STATE Invalid state + * @pre The media muxer state must be set to #MEDIAMUXER_STATE_READY by calling mediamuxer_start() or + * set to #MEDIAMUXER_STATE_PAUSED by calling mediamuxer_pause(). + * @post The media muxer state will be #MEDIAMUXER_STATE_MUXING. + * @see mediamuxer_start() + * @see mediamuxer_close_track() + * @see mediamuxer_pause() + * @see #media_packet_h + * */ +int mediamuxer_write_sample(mediamuxer_h muxer, int track_index, media_packet_h inbuf); + +/** + * @brief Closes the track from further writing of data. + * @remarks For each added track, user needs to call this API to indicate the end of stream. + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @param[in] track_index the selected track index + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_STATE Invalid state + * @pre The media muxer state must be set to #MEDIAMUXER_STATE_MUXING. + * @see mediamuxer_write_sample() + * @see mediamuxer_pause() + * @see mediamuxer_stop() + * @see #mediamuxer_error_e + * */ +int mediamuxer_close_track(mediamuxer_h muxer, int track_index); + +/** + * @brief Pauses the media muxer. + * @remarks To temporarily disable writing data for muxing. This API pauses a playing muxer + If the prior state of the muxer is not in PLAYING, no action will be taken. + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_STATE Invalid state + * @pre The media muxer state must be set to #MEDIAMUXER_STATE_MUXING. + * @post The media muxer state will be #MEDIAMUXER_STATE_PAUSED. + * @see mediamuxer_write_sample() + * @see mediamuxer_resume() + * */ +int mediamuxer_pause(mediamuxer_h muxer); + +/** + * @brief Resumes the media muxer. + * @remarks Make it ready for any further writing. This API will resume a paused muxer. + If the prior state of the muxer is not playing, no action will be taken. + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_STATE Invalid state + * @pre The media muxer state must be set to #MEDIAMUXER_STATE_PAUSED. + * @post The media muxer state will be #MEDIAMUXER_STATE_MUXING. + * @see mediamuxer_pause() + * */ +int mediamuxer_resume(mediamuxer_h muxer); + +/** + * @brief Stops the media muxer. + * @remarks Unrefs the variables created after calling mediamuxer_start(). + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_STATE Invalid state + * @pre The media muxer state must be set to #MEDIAMUXER_STATE_MUXING by calling mediamuxer_start() or + * set to #MEDIAMUXER_STATE_PAUSED by calling mediamuxer_pause(). + * @post The media muxer state will be #MEDIAMUXER_STATE_IDLE. + * @see mediamuxer_write_sample() + * @see mediamuxer_pause() + * @see mediamuxer_destroy() + * */ +int mediamuxer_stop(mediamuxer_h muxer); + +/** + * @brief Removes the instance of media muxer and clear all its context memory. + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_STATE Invalid state + * @pre Create a media muxer handle by calling mediamuxer_create() function. + * @post The media muxer state will be #MEDIAMUXER_STATE_NONE. + * @see mediamuxer_create() + * */ +int mediamuxer_destroy(mediamuxer_h muxer); + +/** + * @brief Gets media muxer state. + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @param[out] state The media muxer sate + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIAMUXER_ERROR_INVALID_OPERATION Invalid operation + * @pre Create a media muxer handle by calling mediamuxer_create() function. + * @see #mediamuxer_state_e + * */ +int mediamuxer_get_state(mediamuxer_h muxer, mediamuxer_state_e *state); + +/** + * @brief Registers a error callback function to be invoked when an error occurs. + * @details Following error codes can be delivered. + * #MEDIAMUXER_ERROR_INVALID_OPERATION, + * #MEDIAMUXER_ERROR_NOT_SUPPORTED, + * #MEDIAMUXER_ERROR_INVALID_PATH, + * #MEDIAMUXER_ERROR_RESOURCE_LIMIT + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @param[in] callback Callback function pointer + * @param[in] user_data The user data passed from the code where + * mediamuxer_set_error_cb() was invoked + * This data will be accessible from @a mediamuxer_error_cb + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @pre Create a media muxer handle by calling mediamuxer_create() function. + * @post mediamuxer_error_cb() will be invoked. + * @see mediamuxer_unset_error_cb() + * @see mediamuxer_error_cb() + * */ +int mediamuxer_set_error_cb(mediamuxer_h muxer, mediamuxer_error_cb callback, void* user_data); + +/** + * @brief Unregisters the error callback function. + * @since_tizen 3.0 + * @param[in] muxer The media muxer handle + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIAMUXER_ERROR_NONE Successful + * @retval #MEDIAMUXER_ERROR_INVALID_PARAMETER Invalid parameter + * @see mediamuxer_error_cb() + * */ +int mediamuxer_unset_error_cb(mediamuxer_h muxer); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif +#endif /* __TIZEN_MEDIAMUXER_H__ */ diff --git a/include/mediamuxer_error.h b/include/mediamuxer_error.h new file mode 100755 index 0000000..bad4cc7 --- /dev/null +++ b/include/mediamuxer_error.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_ERROR_H__ +#define __TIZEN_MEDIAMUXER_ERROR_H__ + +#ifdef __cplusplus +extern "C" { +#endif +/**< Definition of number describing error group */ +#define MX_ERROR_CLASS 0x80000000 +/**< Category for describing common error group */ +#define MX_ERROR_COMMON_CLASS 0x80000100 +/**< Category for describing gst_port error group */ +#define MX_ERROR_GST_PORT_CLASS 0x80000200 +/**< Category for describing ffmpeg port error group */ +#define MX_ERROR_FFMPEG_PORT_CLASS 0x80000300 +/**< Category for describing custom error group */ +#define MX_ERROR_CUSTOM_PORT_CLASS 0x80000400 + +/* + MX_ERROR_CLASS + */ +/**< Unclassified error */ +#define MX_ERROR_UNKNOWN (MX_ERROR_CLASS | 0x00) +/**< Invalid argument */ +#define MX_ERROR_INVALID_ARGUMENT (MX_ERROR_CLASS | 0x01) +/**< Out of memory */ +#define MX_ERROR_OUT_OF_MEMORY (MX_ERROR_CLASS | 0x02) +/**< Out of storage */ +#define MX_ERROR_OUT_OF_STORAGE (MX_ERROR_CLASS | 0x03) +/**< Invalid handle */ +#define MX_ERROR_INVALID_HANDLE (MX_ERROR_CLASS | 0x04) +/**< Cannot find file */ +#define MX_ERROR_FILE_NOT_FOUND (MX_ERROR_CLASS | 0x05) +/**< Fail to read data from file */ +#define MX_ERROR_FILE_READ (MX_ERROR_CLASS | 0x06) +/**< Fail to write data to file */ +#define MX_ERROR_FILE_WRITE (MX_ERROR_CLASS | 0x07) +/**< End of file */ +#define MX_ERROR_END_OF_FILE (MX_ERROR_CLASS | 0x08) +/**< Not supported API*/ +#define MX_ERROR_NOT_SUPPORT_API (MX_ERROR_CLASS | 0x09) +/**< port regitstration failed error */ +#define MX_ERROR_PORT_REG_FAILED (MX_ERROR_CLASS | 0x0a) + +/* + MX_ERROR_COMMON_CLASS + */ +/**< Invalid argument */ +#define MX_ERROR_COMMON_INVALID_ARGUMENT (MX_ERROR_COMMON_CLASS | 1) +/**< Out of storage */ +#define MX_ERROR_COMMON_NO_FREE_SPACE (MX_ERROR_COMMON_CLASS | 2) +/**< Out of memory */ +#define MX_ERROR_COMMON_OUT_OF_MEMORY (MX_ERROR_COMMON_CLASS | 3) +/**< Unknown error */ +#define MX_ERROR_COMMON_UNKNOWN (MX_ERROR_COMMON_CLASS | 4) +/**< Invalid argument */ +#define MX_ERROR_COMMON_INVALID_ATTRTYPE (MX_ERROR_COMMON_CLASS | 5) +/**< Invalid permission */ +#define MX_ERROR_COMMON_INVALID_PERMISSION (MX_ERROR_COMMON_CLASS | 6) +/**< Out of array */ +#define MX_ERROR_COMMON_OUT_OF_ARRAY (MX_ERROR_COMMON_CLASS | 7) +/**< Out of value range*/ +#define MX_ERROR_COMMON_OUT_OF_RANGE (MX_ERROR_COMMON_CLASS | 8) +/**< Attribute doesn't exist. */ +#define MX_ERROR_COMMON_ATTR_NOT_EXIST (MX_ERROR_COMMON_CLASS | 9) + +/* + * MX_ERROR_GST_PORT_CLASS + */ +/**< GST Port instance is not initialized */ +#define MX_ERROR_GST_PORT_NOT_INITIALIZED (MX_ERROR_GST_PORT_CLASS | 0x01) + +/* + MX_ERROR_FFMPEG_PORT_CLASS + */ +/**< FFMPEG Port instance is not initialized */ +#define MX_ERROR_FFMPEG_PORT_NOT_INITIALIZED (MX_ERROR_FFMPEG_PORT_CLASS | 0x01) + +/* + MX_ERROR_CUSTOM_PORT_CLASS + */ +/**< CUSTOM Port instance is not initialized */ +#define MX_ERROR_CUSTOM_PORT_NOT_INITIALIZED MX_ERROR_CUSTOM_PORT_CLASS | 0x01) + +typedef enum { + MX_ERROR_NONE = 0, + MX_ERROR = -1, /**< muxer happens error */ + MX_MEMORY_ERROR = -2, /**< muxer memory is not enough */ + MX_PARAM_ERROR = -3, /**< muxer parameter is error */ + MX_INVALID_ARG = -4, /**< muxer has invalid arguments */ + MX_PERMISSION_DENIED = -5, + MX_INVALID_STATUS = -6, /**< muxer works at invalid status */ + MX_NOT_SUPPORTED = -7, /**< muxer can't support this specific video format */ + MX_INVALID_IN_BUF = -8, + MX_INVALID_OUT_BUF = -9, + MX_INTERNAL_ERROR = -10, + MX_HW_ERROR = -11, /**< muxer happens hardware error */ + MX_NOT_INITIALIZED = -12, + MX_INVALID_STREAM = -13, + MX_OUTPUT_BUFFER_EMPTY = -14, + MX_OUTPUT_BUFFER_OVERFLOW = -15,/**< muxer output buffer is overflow */ + MX_MEMORY_ALLOCED = -16, /**< muxer has got memory and can decode one frame */ + MX_COURRPTED_INI = -17, /**< value in the ini file is not valid */ +} mx_ret_e; + +#ifdef __cplusplus +} +#endif +#endif /* __TIZEN_MEDIAMUXER_ERROR_H__ */ diff --git a/include/mediamuxer_ini.h b/include/mediamuxer_ini.h new file mode 100755 index 0000000..b493d26 --- /dev/null +++ b/include/mediamuxer_ini.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_INI_H__ +#define __TIZEN_MEDIAMUXER_INI_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MEDIAMUXER_INI_DEFAULT_PATH "/usr/etc/mmfw_mediamuxer.ini" +#define MEDIAMUXER_INI_MAX_STRLEN 100 +#define DEFAULT_PORT "GST_PORT" + +/* NOTE : following content should be same with above default values */ +/* FIXIT : need smarter way to generate default ini file. */ +/* FIXIT : finally, it should be an external file */ +#define MEDIAMUXER_DEFAULT_INI \ + "\ +[general] \n\ +\n\ +;Add general config parameters here\n\ +\n\ +\n\ +\n\ +[port_in_use] \n\ +\n\ +;mediamuxer_port = GST_PORT \n\ +;mediamuxer_port = FFMPEG_PORT \n\ +;mediamuxer_port = CUSTOM_PORT \n\ +mediamuxer_port = GST_PORT \n\ +\n\ +[gst_port] \n\ +\n\ +;Add gst port specific config paramters here\n\ +\n\ +\n\ +[ffmpeg_port] \n\ +\n\ +;Add ffmpeg port specific config paramters here\n\ +\n\ +\n\ +[custom_port] \n\ +\n\ +;Add custom port specific config paramters here\n\ +\n\ +\n\ +\n\ +" + +typedef enum { + GST_PORT = 0, + FFMPEG_PORT, + CUSTOM_PORT, +} port_mode; + +/* @ mark means the item has tested */ +typedef struct __mx_ini { + port_mode port_type; + /* general */ + gchar port_name[MEDIAMUXER_INI_MAX_STRLEN]; +} mx_ini_t; + +int mx_ini_load(mx_ini_t *ini); + +#ifdef __cplusplus +} +#endif +#endif /*__TIZEN_MEDIAMUXER_INI_H__*/ diff --git a/include/mediamuxer_port.h b/include/mediamuxer_port.h new file mode 100755 index 0000000..191aaf7 --- /dev/null +++ b/include/mediamuxer_port.h @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_PORT_H__ +#define __TIZEN_MEDIAMUXER_PORT_H__ + +/*============================================================================= +| | +| INCLUDE FILES | +| | +==============================================================================*/ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + @addtogroup MEDIAMUXER + @{ + + @par + This part describes APIs used for playback of multimedia contents. + All multimedia contents are created by a media muxer through handle of + playback. In creating a muxer, it displays the muxer's status or information + by registering callback function. + + @par + In case of streaming playback, network has to be opend by using datanetwork + API. If proxy, cookies and the other attributes for streaming playback are + needed, set those attributes using mx_set_attribute() before create muxer. + + @par + The subtitle for local video playback is supported. Set "subtitle_uri" + attribute using mx_set_attribute() before the application creates the muxer. + Then the application could receive MMMessageParamType which includes + subtitle string and duration. + + @par + MediaMuxer can have 5 states, and each state can be changed by calling + described functions on "Figure1. State of MediaMuxer". + + @par + @image html muxer_state.jpg "Figure1. State of MediaMuxer" width=12cm + @image latex muxer_state.jpg "Figure1. State of MediaMuxer" width=12cm + + @par + Most of functions which change muxer state work as synchronous. But, + mx_start() should be used asynchronously. Both mx_pause() and mx__resume() + should also be used asynchronously in the case of streaming data. + So, application have to confirm the result of those APIs through message + callback function. + + @par + Note that "None" and Null" state could be reached from any state + by calling mx_destroy() and mx_unrealize(). + + @par +
+ + + + + + + + + + + + + + + + + + + + + + + + +
FUNCTIONPRE-STATEPOST-STATESYNC TYPE
mx_create()NONENULLSYNC
mx_destroy()NULLNONESYNC
mx_set_data_sink()NULLREADYSYNC
+ + @par + Following are the attributes supported in muxer which may be set after + initialization. Those are handled as a string. + + @par +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PROPERTYTYPEVALID TYPE
"profile_uri"stringN/A
"content_duration"intrange
"content_video_width"intrange
"content_video_height"intrange
"profile_user_param"dataN/A
"profile_play_count"intrange
"streaming_type"intrange
"streaming_udp_timeout"intrange
"streaming_user_agent"stringN/A
"streaming_wap_profile"stringN/A
"streaming_network_bandwidth"intrange
"streaming_cookie"stringN/A
"streaming_proxy_ip"stringN/A
"streaming_proxy_port"intrange
"subtitle_uri"stringN/A
+ + @par + Following attributes are supported for playing stream data. Those value can + be readable only and valid after starting playback. + Please use mm_fileinfo for local playback. + + @par +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PROPERTYTYPEVALID TYPE
"content_video_found"stringN/A
"content_video_codec"stringN/A
"content_video_track_num"intrange
"content_audio_found"stringN/A
"content_audio_codec"stringN/A
"content_audio_bitrate"intarray
"content_audio_channels"intrange
"content_audio_samplerate"intarray
"content_audio_track_num"intrange
"content_text_track_num"intrange
"tag_artist"stringN/A
"tag_title"stringN/A
"tag_album"stringN/A
"tag_genre"stringN/A
"tag_author"stringN/A
"tag_copyright"stringN/A
"tag_date"stringN/A
"tag_description"stringN/A
"tag_track_num"intrange
+ + */ + +/*=============================================================================| +| | +| GLOBAL DEFINITIONS AND DECLARATIONS | +| | +==============================================================================*/ +/** + * @brief Called when error occurs in media muxer. + * @details Following error codes can be delivered. + * #MEDIAMUXER_ERROR_INVALID_OPERATION, + * #MEDIAMUXER_ERROR_NOT_SUPPORTED, + * #MEDIAMUXER_ERROR_INVALID_PATH, + * #MEDIAMUXER_ERROR_RESOURCE_LIMIT + * @since_tizen 3.0 + * @param[in] error The error that occurred in media muxer + * @param[in] user_data The user data passed from the code where + * mediamuxer_set_error_cb() was invoked + * This data will be accessible from @a mediamuxer_error_cb + * @pre Create media muxer handle by calling mediamuxer_create() function. + * @see mediamuxer_set_error_cb() + * @see mediamuxer_unset_error_cb() + */ +typedef void (*mx_error_cb)(mediamuxer_error_e error, void *user_data); + +/** + * Attribute validity structure + */ +typedef struct _media_port_muxer_ops { + unsigned int n_size; + int (*init)(MMHandleType *pHandle); + /* Add new ops at the end of structure, no order change */ + int (*set_data_sink)(MMHandleType pHandle, char *uri, mediamuxer_output_format_e format); + int (*add_track)(MMHandleType pHandle, media_format_h media_format, int *track_index); + int (*start)(MMHandleType pHandle); + int (*write_sample)(MMHandleType pHandle, int track_index, media_packet_h inbuf); + int (*close_track)(MMHandleType pHandle, int track_index); + int (*pause)(MMHandleType pHandle); + int (*resume)(MMHandleType pHandle); + int (*stop)(MMHandleType pHandle); + int (*destroy)(MMHandleType pHandle); + int (*set_error_cb)(MMHandleType pHandle, mx_error_cb callback, void* user_data); +} media_port_muxer_ops; + +/*============================================================================= +| | +| GLOBAL FUNCTION PROTOTYPES | +| | +==============================================================================*/ + +/** + * This function creates a muxer object for parsing multimedia contents. \n + * The attributes of muxer are created to get/set some values with application. + * And, proper port is selected to do the actual parsing of the mdia. + * + * @param muxer [out] Handle of muxer + * @param op_uri uri at which muxed file should be stored + * @param format container format of the muxed data + * + * @return This function returns zero on success, or negative value with error + * code. Please refer 'mx_error.h' to know it in detail. + * + * @par Example + * @code + mx_create(&muxer, char *op_uri, mediamuxer_output_format_e format); + ... + mx_destroy(&muxer); + * @endcode + */ +int mx_create(MMHandleType *muxer); + +/** + * This function sets the input data source to parse. \n + * The source can be a local file or remote + * + * @param muxer Handle of muxer + * @param uri uri at which muxed file should be stored + * @param format container format of the muxed data + * + * @return This function returns zero on success, or negative value with error + * code. Please refer 'mx_error.h' to know it in detail. + * + * @par Example + * @code + if (mx_set_data_sink(muxer, uri, format) != MM_ERROR_NONE) + { + MX_E("failed to set the source \n"); + } + * @endcode + */ +int mx_set_data_sink(MMHandleType muxer, char *uri, mediamuxer_output_format_e format); + +/** + * This function releases muxer object and all resources which were created by + * mx_create(). And, muxer handle will also be destroyed. + * + * @param muxer [in] Handle of muxer + * + * @return This function returns zero on success, or negative value with error + * code. + * @see mx_destroy + * + * @par Example + * @code +if (mx_destroy(g_muxer) != MM_ERROR_NONE) +{ + MX_E("failed to destroy muxer\n"); +} + * @endcode + */ +int mx_destroy(MMHandleType muxer); + +/** + * This function starts/prepares the muxer object. \n + * For GST-port, this function creates necessary gst elemetns + * + * @param muxer [in] Handle of muxer + * + * @return This function returns zero on success, or negative value with error + code. + * @see mx_create + * + * @par Example + * @code +if (mx_start(g_muxer) != MX_ERROR_NONE) +{ + MX_E("failed to start muxer\n"); +} + * @endcode + */ +int mx_start(MMHandleType muxer); + +/** + * This function adds Audio/Vidoe/Subtitle track in muxer handle \n + * + * @param muxer [in] Handle of muxer + * media_format [in] media format of A/V/S + * track_index [out] index of the media track + * + * @return This function returns zero on success, or negative value with error + code. + * @see mx_create + * + * @par Example + * @code +if (mx_add_track(g_muxer,media_format,&track_index) != MX_ERROR_NONE) +{ + MX_E("failed to add track to muxer handle\n"); +} + * @endcode + */ +int mx_add_track(MMHandleType muxer, media_format_h media_format, int *track_index); + +/** + * This function writes Audio/Vidoe/Subtitle track to file using muxer handle \n + * + * @param muxer [in] Handle of muxer + * track_index index of the media track + * inbuf buffer to be muxed + * + * @return This function returns zero on success, or negative value with error + code. + * @see mx_write_sample + * + * @par Example + * @code +if (mx_write_sample(g_muxer,track_id, inbuf) != MX_ERROR_NONE) +{ + MX_E("failed to write sample\n"); +} + * @endcode + */ +int mx_write_sample(MMHandleType mediamuxer, int track_index, media_packet_h inbuf); + +/** + * This function close the selected track. \n + * For GST-port, this function send the eos to the specific appsrc element + * + * @param muxer [in] Handle of muxer + * @param track_index [in] selected track + * + * @return This function returns zero on success, or negative value with error + code. + * @see mx_stop + * + * @par Example + * @code +if (mx_close_track(g_muxer,1) != MX_ERROR_NONE) +{ + MX_E("failed to close audio track\n"); +} + * @endcode + */ +int mx_close_track(MMHandleType mediamuxer, int track_index); + +/** + * This function stops/un-prepares the muxer object. \n + * For GST-port, this function unrefs necessary gst elemetns + * + * @param muxer [in] Handle of muxer + * + * @return This function returns zero on success, or negative value with error + code. + * @see mx_stop + * + * @par Example + * @code +if (mx_stop(g_muxer) != MX_ERROR_NONE) +{ + MX_E("failed to stop muxer\n"); +} + * @endcode + */ +int mx_stop(MMHandleType mediamuxer); + +/** + * This function pauses the muxing operation. \n + * For GST-port, this function pauses the pipeline + * + * @param muxer [in] Handle of muxer + * + * @return This function returns zero on success, or negative value with error + code. + * @see mx_resume + * + * @par Example + * @code +if (mx_pause(g_muxer) != MX_ERROR_NONE) +{ + MX_E("failed to pause muxer\n"); +} + * @endcode + */ +int mx_pause(MMHandleType mediamuxer); + +/** + * This function resumes the muxing operation. \n + * For GST-port, this function sets the pipeline back to playing + * + * @param muxer [in] Handle of muxer + * + * @return This function returns zero on success, or negative value with error + code. + * @see mx_pause + * + * @par Example + * @code +if (mx_resume(g_muxer) != MX_ERROR_NONE) +{ + MX_E("failed to resume muxer\n"); +} + * @endcode + */ +int mx_resume(MMHandleType mediamuxer); + +/** + * This function is to set error call back function + * + * @param muxer [in] Handle of muxer + * @param callback [in] call back function pointer + * @param user_data [in] user specific data pointer + * + * @return This function returns zero on success, or negative value with error code. + */ +int mx_set_error_cb(MMHandleType muxer, mediamuxer_error_cb callback, void* user_data); + +#ifdef __cplusplus +} +#endif +#endif /* __TIZEN_MEDIAMUXER_PORT_H__ */ diff --git a/include/mediamuxer_private.h b/include/mediamuxer_private.h new file mode 100755 index 0000000..479d4e6 --- /dev/null +++ b/include/mediamuxer_private.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_PRIVATE_H__ +#define __TIZEN_MEDIAMUXER_PRIVATE_H__ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "TIZEN_N_MEDIAMUXER" + +#define MUXER_CHECK_CONDITION(condition,error,msg) \ + do \ + { \ + if (condition) {} else \ + { MX_E("[%s] %s(0x%08x)",__FUNCTION__, msg,error); return error;}; \ + }while(0) + +#define MUXER_INSTANCE_CHECK(muxer) \ + MUXER_CHECK_CONDITION(muxer != NULL, \ + MEDIAMUXER_ERROR_INVALID_PARAMETER,"MUXER_ERROR_INVALID_PARAMETER") + +#define MUXER_STATE_CHECK(muxer,expected_state) \ + MUXER_CHECK_CONDITION(muxer->state == expected_state, \ + MEDIAMUXER_ERROR_INVALID_STATE,"MUXER_ERROR_INVALID_STATE") + +#define MUXER_NULL_ARG_CHECK(arg) \ + MUXER_CHECK_CONDITION(arg != NULL,MEDIAMUXER_ERROR_INVALID_PARAMETER, \ + "MUXER_ERROR_INVALID_PARAMETER") + +/** + * @brief Enumerations of media muxer state + * @since_tizen 3.0 + */ + +int _convert_error_code(int code, char *func_name); + +typedef enum { + MEDIAMUXER_EVENT_TYPE_PREPARE, + MEDIAMUXER_EVENT_TYPE_COMPLETE, + MEDIAMUXER_EVENT_TYPE_INTERRUPT, + MEDIAMUXER_EVENT_TYPE_ERROR, + MEDIAMUXER_EVENT_TYPE_BUFFERING, + MEDIAMUXER_EVENT_TYPE_SUBTITLE, + MEDIAMUXER_EVENT_TYPE_CLOSED_CAPTION, + MEDIAMUXER_EVENT_TYPE_CAPTURE, + MEDIAMUXER_EVENT_TYPE_SEEK, + MEDIAMUXER_EVENT_TYPE_VIDEO_FRAME, + MEDIAMUXER_EVENT_TYPE_AUDIO_FRAME, + MEDIAMUXER_EVENT_TYPE_VIDEO_FRAME_RENDER_ERROR, + MEDIAMUXER_EVENT_TYPE_PD, + MEDIAMUXER_EVENT_TYPE_SUPPORTED_AUDIO_EFFECT, + MEDIAMUXER_EVENT_TYPE_SUPPORTED_AUDIO_EFFECT_PRESET, + MEDIAMUXER_EVENT_TYPE_MISSED_PLUGIN, + MEDIAMUXER_EVENT_TYPE_IMAGE_BUFFER, + MEDIAMUXER_EVENT_TYPE_OTHERS, + MEDIAMUXER_EVENT_TYPE_NUM +} mediamuxer_event_e; + +typedef struct _mediamuxer_s { + MMHandleType mx_handle; + const void *user_cb[MEDIAMUXER_EVENT_TYPE_NUM]; + void *user_data[MEDIAMUXER_EVENT_TYPE_NUM]; + void *display_handle; + int state; + bool is_set_pixmap_cb; + bool is_stopped; + bool is_display_visible; + bool is_progressive_download; + pthread_t prepare_async_thread; + mediamuxer_error_cb error_cb; + void* error_cb_userdata; + mediamuxer_state_e muxer_state; +} mediamuxer_s; + +typedef struct { + /* initialize values */ + media_port_muxer_ops *muxer_ops; + /* initialize values */ + mx_ini_t ini; + /* port specific handle */ + MMHandleType mxport_handle; +} mx_handle_t; + +#ifdef __cplusplus +} +#endif +#endif /* __TIZEN_MEDIAMUXER_PRIVATE_H__ */ diff --git a/include/mediamuxer_util.h b/include/mediamuxer_util.h new file mode 100755 index 0000000..95c1030 --- /dev/null +++ b/include/mediamuxer_util.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_UTIL_H__ +#define __TIZEN_MEDIAMUXER_UTIL_H__ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//#define PRINT_ON_CONSOLE +#ifdef PRINT_ON_CONSOLE +#include +#include +#define PRINT_F g_print +#define MX_FENTER(); PRINT_F("function:[%s] ENTER\n",__func__); +#define MX_FLEAVE(); PRINT_F("function [%s] LEAVE\n",__func__); +#define MX_C PRINT_F +#define MX_E PRINT_F +#define MX_W PRINT_F +#define MX_I PRINT_F +#define MX_L PRINT_F +#define MX_V PRINT_F +#else +#include +#include +#define MX_FENTER(); LOGI("function:[%s] ENTER\n",__func__); +#define MX_FLEAVE(); LOGI("function [%s] LEAVE\n",__func__); +#define MX_C LOGE /*MMF_DEBUG_LEVEL_0 */ +#define MX_E LOGE /*MMF_DEBUG_LEVEL_1 */ +#define MX_W LOGW /*MMF_DEBUG_LEVEL_2 */ +#define MX_I LOGI /*MMF_DEBUG_LEVEL_3 */ +#define MX_L LOGI /*MMF_DEBUG_LEVEL_4 */ +#define MX_V LOGV /*MMF_DEBUG_LEVEL_5 */ +#define MX_F LOGF /*MMF_DEBUG_LEVEL_6 */ +#endif + +/* general */ +#define MEDIAMUXER_FREEIF(x) \ + do \ + { \ + if ( x ) \ + g_free( x ); \ + x = NULL; \ + }while(0) + +#if 1 +#define MEDIAMUXER_FENTER(); MX_FENTER(); +#define MEDIAMUXER_FLEAVE(); MX_FLEAVE(); +#else +#define MEDIAMUXER_FENTER(); +#define MEDIAMUXER_FLEAVE(); +#endif + +#define MEDIAMUXER_CHECK_NULL( x_var ) \ + do \ + { \ + if ( ! x_var ) \ + { \ + MX_E("[%s] is NULL\n", #x_var ); \ + goto ERROR; \ + } \ + } while (0) +#define MEDIAMUXER_CHECK_SET_AND_PRINT( x_var, x_cond, ret, ret_val, err_text )\ + do \ + { \ + if ( x_var != x_cond ) \ + { \ + ret = ret_val; \ + MX_E("%s\n", #err_text ); \ + goto ERROR; \ + } \ + } while (0) +#ifdef __cplusplus +} +#endif +#endif /* __TIZEN_MEDIAMUXER_UTIL_H__ */ diff --git a/include/port_custom/mediamuxer_port_custom.h b/include/port_custom/mediamuxer_port_custom.h new file mode 100755 index 0000000..7739f91 --- /dev/null +++ b/include/port_custom/mediamuxer_port_custom.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_PORT_CUSTOM_H__ +#define __TIZEN_MEDIAMUXER_PORT_CUSTOM_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*Place holder */ + +#ifdef __cplusplus +} +#endif +#endif /* __TIZEN_MEDIAMUXER_PORT_CUSTOM_H__ */ diff --git a/include/port_ffmpeg/mediamuxer_port_ffmpeg.h b/include/port_ffmpeg/mediamuxer_port_ffmpeg.h new file mode 100755 index 0000000..2ae9320 --- /dev/null +++ b/include/port_ffmpeg/mediamuxer_port_ffmpeg.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_PORT_FFMPEG_H__ +#define __TIZEN_MEDIAMUXER_PORT_FFMPEG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*Place holder */ + +#ifdef __cplusplus +} +#endif +#endif /* __TIZEN_MEDIAMUXER_PORT_FFMPEG_H__ */ diff --git a/include/port_gst/mediamuxer_port_gst.h b/include/port_gst/mediamuxer_port_gst.h new file mode 100755 index 0000000..189fdb0 --- /dev/null +++ b/include/port_gst/mediamuxer_port_gst.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015 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_MEDIAMUXER_PORT_GST_H__ +#define __TIZEN_MEDIAMUXER_PORT_GST_H__ + +#include +#include +#include +#include +#include + +#define MEDIAMUXER_ELEMENT_SET_STATE( x_element, x_state ) \ + MX_I("setting state [%s:%d] to [%s]\n", #x_state, x_state, GST_ELEMENT_NAME( x_element ) ); \ + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state ( x_element, x_state) ) \ + { \ + MX_E("failed to set state %s to %s\n", #x_state, GST_ELEMENT_NAME( x_element )); \ + goto STATE_CHANGE_FAILED; \ + } + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + _GST_EVENT_TYPE_COMPLETE, + _GST_EVENT_TYPE_ERROR, + _GST_EVENT_TYPE_EOS, + _GST_EVENT_TYPE_NUM +} _gst_event_e; + +/* GST port Private data */ +typedef struct _mx_gst_track { + void *media_format; + char *caps; + int track_index; + int start_feed; + int stop_feed; +} mx_gst_track; + +typedef struct _mxgst_handle_t { + void *hmux; /**< mux handle */ + int state; /**< mx current state */ + bool is_prepared; + + mx_gst_track video_track; + mx_gst_track audio_track; + + mediamuxer_output_format_e muxed_format; + char *output_uri; + bool eos_flg; + guint bus_watch_id; + GstElement *pipeline; + GstElement *audio_appsrc; /* Input audio buffers to be muxed */ + GstElement *video_appsrc; /* Input video buffers to be muxed */ + GstElement *audioparse; + GstElement *videoparse; + GstElement *muxer; + GstElement *sink; /* sink for the muxed output */ + char *saveLocation; /* Save path for muxed data */ + void* user_cb[_GST_EVENT_TYPE_NUM]; /* for user cb */ + void* user_data[_GST_EVENT_TYPE_NUM]; +} mxgst_handle_t; + +/** + * @brief Called when the error has occured. + * @remarks It will be invoked when the error has occured. + * @since_tizen 3.0 + * @param[in] error_code The error code + * @param[in] user_data The user data passed from the callback registration function + * @pre It will be invoked when the error has occured if user register this callback using mediamuxer_set_error_cb(). + * @see mediamuxer_set_error_cb() + * @see mediamuxer_unset_error_cb() + */ +typedef void (*gst_error_cb)(mediamuxer_error_e error, void *user_data); + +#ifdef __cplusplus +} +#endif +#endif /* __TIZEN_MEDIAMUXER_PORT_GST_H__ */ diff --git a/packaging/capi-mediamuxer.spec b/packaging/capi-mediamuxer.spec new file mode 100755 index 0000000..9816fd8 --- /dev/null +++ b/packaging/capi-mediamuxer.spec @@ -0,0 +1,80 @@ +Name: capi-mediamuxer +Summary: A Media Muxer library in Tizen Native API +Version: 0.1.1 +Release: 1 +Group: Multimedia/API +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +BuildRequires: cmake +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(mm-common) +BuildRequires: pkgconfig(capi-base-common) +BuildRequires: pkgconfig(capi-media-tool) +BuildRequires: pkgconfig(libtbm) +BuildRequires: pkgconfig(gstreamer-1.0) +BuildRequires: pkgconfig(gstreamer-plugins-base-1.0) +BuildRequires: pkgconfig(gstreamer-video-1.0) +BuildRequires: pkgconfig(gstreamer-app-1.0) +BuildRequires: pkgconfig(iniparser) + +Requires(post): /sbin/ldconfig +Requires(post): libprivilege-control +Requires(postun): /sbin/ldconfig + +%description + +%package devel +Summary: Multimedia Framework Muxer Library (DEV) +Group: Multimedia/API +Requires: %{name} = %{version}-%{release} + +%description devel + +%prep +%setup -q + + +%build +%if 0%{?sec_build_binary_debug_enable} +export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE" +export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE" +export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE" +%endif +%ifarch %{arm} +export CFLAGS="$CFLAGS -DENABLE_FFMPEG_CODEC" +%endif + +MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'` +%cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DFULLVER=%{version} -DMAJORVER=${MAJORVER} + + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}%{_datadir}/license +mkdir -p %{buildroot}/usr/bin +cp test/mediamuxer_test %{buildroot}/usr/bin +cp LICENSE.APLv2 %{buildroot}%{_datadir}/license/%{name} + +%make_install + +%post +/sbin/ldconfig + +%postun -p /sbin/ldconfig + + +%files +%manifest capi-mediamuxer.manifest +%{_libdir}/libcapi-mediamuxer.so.* +%{_datadir}/license/%{name} +%{_bindir}/* + +%files devel +%{_includedir}/media/*.h +%{_libdir}/pkgconfig/*.pc +%{_libdir}/libcapi-mediamuxer.so + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100755 index 0000000..eb5446c --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,4 @@ +message(In src folder...) +add_subdirectory(port_gst) +add_subdirectory(port_ffmpeg) +add_subdirectory(port_custom) diff --git a/src/mediamuxer.c b/src/mediamuxer.c new file mode 100644 index 0000000..aa75887 --- /dev/null +++ b/src/mediamuxer.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2015 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef USE_TASK_QUEUE +#define USE_TASK_QUEUE +#endif + +/* +* Public Implementation +*/ +static gboolean _mediamuxer_error_cb(mediamuxer_error_e error, void *user_data); + +int mediamuxer_create(mediamuxer_h *muxer) +{ + MX_I("mediamuxer_create\n"); + mediamuxer_error_e ret = MM_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + + mediamuxer_s *handle; + handle = (mediamuxer_s *) malloc(sizeof(mediamuxer_s)); + if (handle != NULL) { + memset(handle, 0, sizeof(mediamuxer_s)); + handle->muxer_state = MEDIAMUXER_STATE_NONE; + } else { + MX_E("[CoreAPI][%s] MUXER_ERROR_OUT_OF_MEMORY(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_OUT_OF_MEMORY); + return MEDIAMUXER_ERROR_OUT_OF_MEMORY; + } + + ret = mx_create(&handle->mx_handle); + if (ret != MM_ERROR_NONE) { + MX_E("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + free(handle); + handle = NULL; + return MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + *muxer = (mediamuxer_h) handle; + handle->is_stopped = false; + MX_I("[CoreAPI][%s] new handle : %p", __FUNCTION__, *muxer); + + /* set callback */ + mx_set_error_cb(handle->mx_handle, (mediamuxer_error_cb)_mediamuxer_error_cb, handle); + handle->muxer_state = MEDIAMUXER_STATE_IDLE; + return MEDIAMUXER_ERROR_NONE; + } +} + +int mediamuxer_set_data_sink(mediamuxer_h muxer, char *path, mediamuxer_output_format_e format) +{ + MX_I("mediamuxer_set_data_sink\n"); + int ret = MEDIAMUXER_ERROR_NONE; + mediamuxer_s *handle; + MUXER_INSTANCE_CHECK(muxer); + handle = (mediamuxer_s *)(muxer); + + if (path == NULL) { + MX_I("Invalid uri"); + return MEDIAMUXER_ERROR_INVALID_PARAMETER; + } + + if (format != MEDIAMUXER_CONTAINER_FORMAT_MP4) { + MX_E("\n Unsupported format: %d \n", format); + return MEDIAMUXER_ERROR_INVALID_PARAMETER; + } + if (handle->muxer_state == MEDIAMUXER_STATE_IDLE) { + ret = mx_set_data_sink(handle->mx_handle, path, format); + if (ret != MEDIAMUXER_ERROR_NONE) { + MX_E("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + MX_I("[CoreAPI][%s] set_data_sink successful, handle : %p", + __FUNCTION__, handle); + } + } else { + MX_E("[CoreAPI][%s] MEDIAMUXER_ERROR_INVALID_STATE(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_STATE); + ret = MEDIAMUXER_ERROR_INVALID_STATE; + } + return ret; +} + +int mediamuxer_add_track(mediamuxer_h muxer, media_format_h media_format, int *track_index) +{ + MX_I("mediamuxer_add_track\n"); + mediamuxer_error_e ret = MEDIAMUXER_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle; + handle = (mediamuxer_s *)(muxer); + if (handle->muxer_state == MEDIAMUXER_STATE_IDLE){ + ret = mx_add_track(handle->mx_handle, media_format, track_index); + if (ret != MEDIAMUXER_ERROR_NONE) { + MX_E("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + MX_I("[CoreAPI][%s] add_track handle : %p", __FUNCTION__, + handle); + } + } else { + MX_E("[CoreAPI][%s] MEDIAMUXER_ERROR_INVALID_STATE(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_STATE); + ret = MEDIAMUXER_ERROR_INVALID_STATE; + } + return ret; +} + +int mediamuxer_start(mediamuxer_h muxer) +{ + MX_I("mediamuxer_start\n"); + int ret = MEDIAMUXER_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle = (mediamuxer_s *)(muxer); + if (handle->muxer_state == MEDIAMUXER_STATE_IDLE) { + ret = mx_start(handle->mx_handle); + if (ret != MEDIAMUXER_ERROR_NONE) { + MX_E("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + MX_I("[CoreAPI][%s] start successful, handle : %p", + __FUNCTION__, handle); + } + } else { + MX_E("[CoreAPI][%s] MEDIAMUXER_ERROR_INVALID_STATE(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_STATE); + return MEDIAMUXER_ERROR_INVALID_STATE; + } + handle->muxer_state = MEDIAMUXER_STATE_READY; + return ret; +} + +int mediamuxer_write_sample(mediamuxer_h muxer, int track_index, media_packet_h inbuf) +{ + MX_I("mediamuxer_write_sample\n"); + int ret = MEDIAMUXER_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle = (mediamuxer_s *)(muxer); + if (track_index < 0 || inbuf == NULL) { + return MEDIAMUXER_ERROR_INVALID_PARAMETER; + } + if (handle->muxer_state == MEDIAMUXER_STATE_READY + || handle->muxer_state == MEDIAMUXER_STATE_MUXING) { + ret = mx_write_sample(handle->mx_handle, track_index, inbuf); + if (ret != MEDIAMUXER_ERROR_NONE) { + MX_E + ("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + MX_I("[CoreAPI][%s] write_sample successful, handle : %p", + __FUNCTION__, handle); + } + } else { + MX_E("[CoreAPI][%s] MEDIAMUXER_ERROR_INVALID_STATE(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_STATE); + return MEDIAMUXER_ERROR_INVALID_STATE; + } + handle->muxer_state = MEDIAMUXER_STATE_MUXING; + return ret; +} + +int mediamuxer_close_track(mediamuxer_h muxer, int track_index) +{ + MX_I("mediamuxer_close_track\n"); + int ret = MEDIAMUXER_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle = (mediamuxer_s *)(muxer); + if (track_index < 0) { + return MEDIAMUXER_ERROR_INVALID_PARAMETER; + } + if (handle->muxer_state == MEDIAMUXER_STATE_MUXING || + handle->muxer_state == MEDIAMUXER_STATE_IDLE || + handle->muxer_state == MEDIAMUXER_STATE_READY) { + ret = mx_close_track(handle->mx_handle, track_index); + if (ret != MEDIAMUXER_ERROR_NONE) { + MX_E + ("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + MX_I("[CoreAPI][%s] close successful, handle : %p", + __FUNCTION__, handle); + } + } else { + MX_E("[CoreAPI][%s] MEDIAMUXER_ERROR_INVALID_STATE(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_STATE); + ret = MEDIAMUXER_ERROR_INVALID_STATE; + } + return ret; +} + +int mediamuxer_pause(mediamuxer_h muxer) +{ + MX_I("mediamuxer_pause\n"); + int ret = MEDIAMUXER_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle = (mediamuxer_s *)(muxer); + ret = mx_pause(handle->mx_handle); + if (handle->muxer_state == MEDIAMUXER_STATE_READY + || handle->muxer_state == MEDIAMUXER_STATE_MUXING) { + if (ret != MEDIAMUXER_ERROR_NONE) { + MX_E + ("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + MX_I("[CoreAPI][%s] pause successful, handle : %p", + __FUNCTION__, handle); + } + } else { + MX_E("[CoreAPI][%s] MEDIAMUXER_ERROR_INVALID_STATE(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_STATE); + return MEDIAMUXER_ERROR_INVALID_STATE; + } + handle->muxer_state = MEDIAMUXER_STATE_PAUSED; + return ret; +} + +int mediamuxer_resume(mediamuxer_h muxer) +{ + MX_I("mediamuxer_resume\n"); + int ret = MEDIAMUXER_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle = (mediamuxer_s *)(muxer); + if (handle->muxer_state == MEDIAMUXER_STATE_PAUSED) { + ret = mx_resume(handle->mx_handle); + if (ret != MEDIAMUXER_ERROR_NONE) { + MX_E + ("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + MX_I("[CoreAPI][%s] resume successful, handle : %p", + __FUNCTION__, handle); + } + } else { + MX_E("[CoreAPI][%s] MEDIAMUXER_ERROR_INVALID_STATE(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_STATE); + return MEDIAMUXER_ERROR_INVALID_STATE; + } + handle->muxer_state = MEDIAMUXER_STATE_MUXING; + return ret; +} + +int mediamuxer_stop(mediamuxer_h muxer) +{ + MX_I("mediamuxer_stop\n"); + int ret = MEDIAMUXER_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle = (mediamuxer_s *)(muxer); + if (handle->muxer_state == MEDIAMUXER_STATE_READY + || handle->muxer_state == MEDIAMUXER_STATE_MUXING + || handle->muxer_state == MEDIAMUXER_STATE_PAUSED) { + ret = mx_stop(handle->mx_handle); + if (ret != MEDIAMUXER_ERROR_NONE) { + MX_E + ("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + MX_I("[CoreAPI][%s] stop successful, handle : %p", + __FUNCTION__, handle); + } + } else { + MX_E("[CoreAPI][%s] MEDIAMUXER_ERROR_INVALID_STATE(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_STATE); + return MEDIAMUXER_ERROR_INVALID_STATE; + } + handle->muxer_state = MEDIAMUXER_STATE_IDLE; + return ret; +} + +int mediamuxer_destroy(mediamuxer_h muxer) +{ + MX_I("mediamuxer_destroy\n"); + int ret = MEDIAMUXER_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle; + handle = (mediamuxer_s *)(muxer); + if (handle->muxer_state == MEDIAMUXER_STATE_IDLE) { + ret = mx_destroy(handle->mx_handle); + if (ret != MEDIAMUXER_ERROR_NONE) { + MX_E("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } else { + MX_I("[CoreAPI][%s] destroy handle : %p", __FUNCTION__, + handle); + } + } else { + MX_E("[CoreAPI][%s] MEDIAMUXER_ERROR_INVALID_STATE(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_STATE); + return MEDIAMUXER_ERROR_INVALID_STATE; + } + handle->muxer_state = MEDIAMUXER_STATE_NONE; + return ret; +} + +int mediamuxer_get_state(mediamuxer_h muxer, mediamuxer_state_e *state) +{ + MX_I("mediamuxer_get_state\n"); + int ret = MEDIAMUXER_ERROR_NONE; + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle = (mediamuxer_s *)(muxer); + if (state != NULL) { + *state = handle->muxer_state; + } else { + MX_E("[CoreAPI][%s] MUXER_ERROR_INVALID_OPERATION(0x%08x)", + __FUNCTION__, MEDIAMUXER_ERROR_INVALID_OPERATION); + ret = MEDIAMUXER_ERROR_INVALID_OPERATION; + } + return ret; +} + +int mediamuxer_set_error_cb(mediamuxer_h muxer, mediamuxer_error_cb callback, void* user_data) +{ + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle; + handle = (mediamuxer_s *)(muxer); + + handle->error_cb = callback; + handle->error_cb_userdata = user_data; + + MX_I("set error_cb(%p)", callback); + + return MEDIAMUXER_ERROR_NONE; +} + +int mediamuxer_unset_error_cb(mediamuxer_h muxer) +{ + MUXER_INSTANCE_CHECK(muxer); + mediamuxer_s *handle; + handle = (mediamuxer_s *)(muxer); + + handle->error_cb = NULL; + handle->error_cb_userdata = NULL; + MX_I("mediamuxer_unset_error_cb\n"); + return MEDIAMUXER_ERROR_NONE; +} + +static gboolean _mediamuxer_error_cb(mediamuxer_error_e error, void *user_data) +{ + if (user_data == NULL) { + MX_I("_mediamuxer_error_cb: ERROR %d to report. But call back is not set\n",error); + return 0; + } + mediamuxer_s * handle = (mediamuxer_s *) user_data; + + if (handle->error_cb ) { + ((mediamuxer_error_cb)handle->error_cb)(error, handle->error_cb_userdata); + } + else { + MX_I("_mediamuxer_error_cb: ERROR %d to report. But call back is not set\n",error); + } + return 0; +} diff --git a/src/mediamuxer_ini.c b/src/mediamuxer_ini.c new file mode 100644 index 0000000..d829dd5 --- /dev/null +++ b/src/mediamuxer_ini.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2015 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 __MEDIAMUXER_INI_C__ +#define __MEDIAMUXER_INI_C__ + +#include +#include +#include +#include +#include +#include "iniparser.h" +#include +#include +#include + +/* macro */ +#define MEDIAMUXER_INI_GET_STRING( x_dict, x_item, x_ini, x_default ) \ + do \ + { \ + gchar* str = iniparser_getstring(x_dict, x_ini, x_default); \ + \ + if ( str && \ + ( strlen( str ) > 0 ) && \ + ( strlen( str ) < MEDIAMUXER_INI_MAX_STRLEN ) ) \ + { \ + strcpy ( x_item, str ); \ + } \ + else \ + { \ + strcpy ( x_item, x_default ); \ + } \ + }while(0) + +#define MEDIAMUXER_INI_GET_COLOR( x_dict, x_item, x_ini, x_default ) \ + do \ + { \ + gchar* str = iniparser_getstring(x_dict, x_ini, x_default); \ + \ + if ( str && \ + ( strlen( str ) > 0 ) && \ + ( strlen( str ) < MEDIAMUXER_INI_MAX_STRLEN ) ) \ + { \ + x_item = (guint) strtoul(str, NULL, 16); \ + } \ + else \ + { \ + x_item = (guint) strtoul(x_default, NULL, 16); \ + } \ + }while(0) + +/* x_ini is the list of index to set TRUE at x_list[index] */ +#define MEDIAMUXER_INI_GET_BOOLEAN_FROM_LIST( x_dict, x_list, \ + x_list_max, x_ini, x_default ) \ +do \ +{ \ + int index = 0; \ + const char *delimiters = " ,"; \ + char *usr_ptr = NULL; \ + char *token = NULL; \ + gchar temp_arr[MEDIAMUXER_INI_MAX_STRLEN] = {0}; \ + MMMEDIAMUXER_INI_GET_STRING( x_dict, temp_arr, x_ini, x_default); \ + token = strtok_r( temp_arr, delimiters, &usr_ptr ); \ + while (token) \ + { \ + index = atoi(token); \ + if (index < 0 || index > x_list_max -1) \ + { \ + MX_W("%d is not valid index\n", index); \ + } \ + else \ + { \ + x_list[index] = TRUE; \ + } \ + token = strtok_r( NULL, delimiters, &usr_ptr ); \ + } \ +}while(0) + +/* x_ini is the list of value to be set at x_list[index] */ +#define MEDIAMUXER_INI_GET_INT_FROM_LIST( x_dict, x_list, \ + x_list_max, x_ini, x_default ) \ +do \ +{ \ + int index = 0; \ + int value = 0; \ + const char *delimiters = " ,"; \ + char *usr_ptr = NULL; \ + char *token = NULL; \ + gchar temp_arr[MEDIAMUXER_INI_MAX_STRLEN] = {0}; \ + MMMEDIAMUXER_INI_GET_STRING(x_dict, temp_arr, x_ini, x_default); \ + token = strtok_r( temp_arr, delimiters, &usr_ptr ); \ + while (token) \ + { \ + if ( index > x_list_max -1) \ + { \ + MX_E("%d is not valid index\n", index); \ + break; \ + } \ + else \ + { \ + value = atoi(token); \ + x_list[index] = value; \ + index++; \ + } \ + token = strtok_r( NULL, delimiters, &usr_ptr ); \ + } \ +}while(0) + +/* internal functions, macros here */ +#ifdef MEDIAMUXER_DEFAULT_INI +static gboolean _generate_default_ini(void); +#endif + +static void _mx_ini_check_ini_status(void); + +int mx_ini_load(mx_ini_t *ini) +{ + dictionary *dict = NULL; + + _mx_ini_check_ini_status(); + + /* first, try to load existing ini file */ + dict = iniparser_load(MEDIAMUXER_INI_DEFAULT_PATH); + + /* if no file exists. create one with set of default values */ + if (!dict) { +#ifdef MEDIAMUXER_DEFAULT_INI + MX_L("No inifile found. muxer will create default inifile.\n"); + if (FALSE == _generate_default_ini()) { + MX_W("Creating default inifile failed. \ + MediaMuxer will use default values.\n"); + } else { + /* load default ini */ + dict = iniparser_load(MEDIAMUXER_INI_DEFAULT_PATH); + } +#else + MX_L("No ini file found. \n"); + return MM_ERROR_FILE_NOT_FOUND; +#endif + } + + /* get ini values */ + memset(ini, 0, sizeof(mx_ini_t)); + + if (dict) { /* if dict is available */ + /* general */ + MEDIAMUXER_INI_GET_STRING(dict, ini->port_name, + "port_in_use:mediamuxer_port", + DEFAULT_PORT); + } else { + /* if dict is not available just fill + the structure with default value */ + MX_W("failed to load ini. using hardcoded default\n"); + strncpy(ini->port_name, DEFAULT_PORT, + MEDIAMUXER_INI_MAX_STRLEN - 1); + } + + if (0 == strcmp(ini->port_name, "GST_PORT")) { + ini->port_type = GST_PORT; + } else if (0 == strcmp(ini->port_name, "FFMPEG_PORT")) { + ini->port_type = FFMPEG_PORT; + } else if (0 == strcmp(ini->port_name, "CUSTOM_PORT")) { + ini->port_type = CUSTOM_PORT; + } else { + MX_E("Invalid port is set to [%s] [%d]\n", ini->port_name, + ini->port_type); + goto ERROR; + } + MX_L("The port is set to [%s] [%d]\n", ini->port_name, ini->port_type); + + /* free dict as we got our own structure */ + iniparser_freedict(dict); + + /* dump structure */ + MX_L("muxer settings -----------------------------------\n"); + + /* general */ + MX_L("port_name: %s\n", ini->port_name); + MX_L("port_type : %d\n", ini->port_type); + + return MM_ERROR_NONE; +ERROR: + return MX_COURRPTED_INI; +} + +static void _mx_ini_check_ini_status(void) +{ + struct stat ini_buff; + + if (g_stat(MEDIAMUXER_INI_DEFAULT_PATH, &ini_buff) < 0) { + MX_W("failed to get muxer ini status\n"); + } else { + if (ini_buff.st_size < 5) { + MX_W("muxer.ini file size=%d, Corrupted! So, Removed\n", + (int)ini_buff.st_size); + + if (g_remove(MEDIAMUXER_INI_DEFAULT_PATH) == -1) + MX_E("failed to delete corrupted ini"); + } + } +} + +#ifdef MEDIAMUXER_DEFAULT_INI +static gboolean _generate_default_ini(void) +{ + FILE *fp = NULL; + gchar *default_ini = MEDIAMUXER_DEFAULT_INI; + + /* create new file */ + fp = fopen(MEDIAMUXER_INI_DEFAULT_PATH, "wt"); + + if (!fp) + return FALSE; + + /* writing default ini file */ + if (strlen(default_ini) != + fwrite(default_ini, 1, strlen(default_ini), fp)) { + fclose(fp); + return FALSE; + } + + fclose(fp); + return TRUE; +} +#endif + +#endif /* #ifdef _MEDIAMUXER_INI_C_ */ diff --git a/src/mediamuxer_port.c b/src/mediamuxer_port.c new file mode 100644 index 0000000..81bc5cc --- /dev/null +++ b/src/mediamuxer_port.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2015 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* function type */ +extern int gst_port_register(media_port_muxer_ops *pOps); +extern int ffmpeg_port_register(media_port_muxer_ops *pOps); +extern int custom_port_register(media_port_muxer_ops *pOps); + +/* + * Sequence of functions should be same as the port enumeration "port_mode" + * in mx_ini.h file + */ +typedef int (*register_port)(media_port_muxer_ops *); +register_port register_port_func[] = { + &gst_port_register, + &ffmpeg_port_register, + &custom_port_register +}; + +int mx_create(MMHandleType *muxer) +{ + int result = MX_ERROR_NONE; + media_port_muxer_ops *pOps = NULL; + mx_handle_t *new_muxer = NULL; + MEDIAMUXER_FENTER(); + new_muxer = (mx_handle_t *) g_malloc(sizeof(mx_handle_t)); + MEDIAMUXER_CHECK_NULL(new_muxer); + memset(new_muxer, 0, sizeof(mx_handle_t)); + + /* alloc ops structure */ + pOps = (media_port_muxer_ops *) g_malloc(sizeof(media_port_muxer_ops)); + MEDIAMUXER_CHECK_NULL(pOps); + + new_muxer->muxer_ops = pOps; + MX_I("mx_create allocating new_demuxer->demuxer_ops %p:\n", + new_muxer->muxer_ops); + pOps->n_size = sizeof(media_port_muxer_ops); + /* load ini files */ + result = mx_ini_load(&new_muxer->ini); + MEDIAMUXER_CHECK_SET_AND_PRINT(result, MX_ERROR_NONE, result, + MX_COURRPTED_INI, "can't load ini"); + + register_port_func[new_muxer->ini.port_type](pOps); + result = pOps->init(&new_muxer->mxport_handle); + MEDIAMUXER_CHECK_SET_AND_PRINT(result, MX_ERROR_NONE, result, + MX_NOT_INITIALIZED, "mx_create failed"); + *muxer = (MMHandleType) new_muxer; + MEDIAMUXER_FLEAVE(); + return result; +ERROR: + *muxer = (MMHandleType) 0; + if (pOps) + g_free(pOps); + if (new_muxer) + g_free(new_muxer); + MEDIAMUXER_FLEAVE(); + return result; +} + +int mx_set_data_sink(MMHandleType mediamuxer, char *uri, mediamuxer_output_format_e format) +{ + int ret = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *) mediamuxer; + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + ret = pOps->set_data_sink(mx_handle->mxport_handle, uri, format); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_INVALID_ARGUMENT; + MEDIAMUXER_FLEAVE(); + return ret; +} + +int mx_add_track(MMHandleType mediamuxer, media_format_h media_format, int *track_index) +{ + int ret = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *) mediamuxer; + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + ret = pOps->add_track(mx_handle->mxport_handle, media_format, track_index); + MEDIAMUXER_CHECK_SET_AND_PRINT(ret, MX_ERROR_NONE, ret, MX_ERROR, + "error while adding track"); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} + +int mx_start(MMHandleType mediamuxer) +{ + int ret = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *)(mediamuxer); + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + ret = pOps->start(mx_handle->mxport_handle); + MEDIAMUXER_CHECK_SET_AND_PRINT(ret, MX_ERROR_NONE, ret, MX_ERROR, + "error while starting"); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} + +int mx_write_sample(MMHandleType mediamuxer, int track_index, media_packet_h inbuf) +{ + int ret = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *) mediamuxer; + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + ret = pOps->write_sample(mx_handle->mxport_handle, track_index, inbuf); + MEDIAMUXER_CHECK_SET_AND_PRINT(ret, MX_ERROR_NONE, ret, MX_ERROR, + "error while writing sample"); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} + +int mx_close_track(MMHandleType mediamuxer, int track_index) +{ + int ret = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *)(mediamuxer); + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + ret = pOps->close_track(mx_handle->mxport_handle, track_index); + MEDIAMUXER_CHECK_SET_AND_PRINT(ret, MX_ERROR_NONE, ret, MX_ERROR, + "error while closing track"); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} + +int mx_pause(MMHandleType mediamuxer) +{ + int ret = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *)(mediamuxer); + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + ret = pOps->pause(mx_handle->mxport_handle); + MEDIAMUXER_CHECK_SET_AND_PRINT(ret, MX_ERROR_NONE, ret, MX_ERROR, + "error while pausing"); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} + +int mx_resume(MMHandleType mediamuxer) +{ + int ret = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *)(mediamuxer); + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + ret = pOps->resume(mx_handle->mxport_handle); + MEDIAMUXER_CHECK_SET_AND_PRINT(ret, MX_ERROR_NONE, ret, MX_ERROR, + "error while resuming"); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} + +int mx_stop(MMHandleType mediamuxer) +{ + int ret = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *)(mediamuxer); + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + ret = pOps->stop(mx_handle->mxport_handle); + MEDIAMUXER_CHECK_SET_AND_PRINT(ret, MX_ERROR_NONE, ret, MX_ERROR, + "error while destroying"); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} + +int mx_destroy(MMHandleType mediamuxer) +{ + int ret = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *) mediamuxer; + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + ret = pOps->destroy(mx_handle->mxport_handle); + MEDIAMUXER_CHECK_SET_AND_PRINT(ret, MX_ERROR_NONE, ret, MX_ERROR, + "error while destroying"); + + /* free mediamuxer structure */ + if (mx_handle) { + if (mx_handle->muxer_ops) { + MX_I("mx_destroy deallocating mx_handle->muxer_ops %p:\n", + mx_handle->muxer_ops); + g_free((void *)(mx_handle->muxer_ops)); + } + MX_I("mx_destroy deallocating mx_handle %p:\n", mx_handle); + g_free((void *)mx_handle); + mx_handle = NULL; + } +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} + +int mx_set_error_cb(MMHandleType muxer, + mediamuxer_error_cb callback, void* user_data) +{ + MEDIAMUXER_FENTER(); + int result = MX_ERROR_NONE; + mx_handle_t *mx_handle = (mx_handle_t *) muxer; + MEDIAMUXER_CHECK_NULL(mx_handle); + media_port_muxer_ops *pOps = mx_handle->muxer_ops; + MEDIAMUXER_CHECK_NULL(pOps); + result = pOps->set_error_cb(mx_handle->mxport_handle, callback,user_data); + MEDIAMUXER_CHECK_SET_AND_PRINT(result, MX_ERROR_NONE, result, + MX_ERROR, "error while setting error call back"); + MEDIAMUXER_FLEAVE(); + return result; +ERROR: + result = MX_ERROR_INVALID_ARGUMENT; + MEDIAMUXER_FLEAVE(); + return result; +} diff --git a/src/port_custom/mediamuxer_port_custom.c b/src/port_custom/mediamuxer_port_custom.c new file mode 100644 index 0000000..83ccafa --- /dev/null +++ b/src/port_custom/mediamuxer_port_custom.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015 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 +#include +#include +#include +#include + +static int custom_muxer_init(MMHandleType *pHandle); +static int custom_muxer_set_data_sink(MMHandleType pHandle, char *uri, + mediamuxer_output_format_e format); + +/*Media Muxer API common*/ +static media_port_muxer_ops def_mux_ops = { + .n_size = 0, + .init = custom_muxer_init, + .set_data_sink = custom_muxer_set_data_sink, +}; + +int custom_port_register(media_port_muxer_ops *pOps) +{ + int ret = MX_ERROR_NONE; + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(pOps); + + def_mux_ops.n_size = sizeof(def_mux_ops); + + memcpy((char *)pOps + sizeof(pOps->n_size), + (char *)&def_mux_ops + sizeof(def_mux_ops.n_size), + pOps->n_size - sizeof(pOps->n_size)); + + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_INVALID_ARGUMENT; + return ret; +} + +static int custom_muxer_init(MMHandleType *pHandle) +{ + int ret = MX_ERROR_NONE; + MEDIAMUXER_FENTER(); + MX_E("%s:exit: Not implemented\n", __func__); + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int custom_muxer_set_data_sink(MMHandleType pHandle, char *uri, + mediamuxer_output_format_e format) +{ + MEDIAMUXER_FENTER(); + MX_E("%s:exit: Not implemented\n", __func__); + MEDIAMUXER_FLEAVE(); + return 0; +} diff --git a/src/port_ffmpeg/mediamuxer_port_ffmpeg.c b/src/port_ffmpeg/mediamuxer_port_ffmpeg.c new file mode 100644 index 0000000..7857b7c --- /dev/null +++ b/src/port_ffmpeg/mediamuxer_port_ffmpeg.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015 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 +#include +#include +#include +#include + +static int ffmpeg_muxer_init(MMHandleType *pHandle); +static int ffmpeg_muxer_set_data_sink(MMHandleType pHandle, char *uri, + mediamuxer_output_format_e format); + +/*Media Muxer API common*/ +static media_port_muxer_ops def_mux_ops = { + .n_size = 0, + .init = ffmpeg_muxer_init, + .set_data_sink = ffmpeg_muxer_set_data_sink, +}; + +int ffmpeg_port_register(media_port_muxer_ops *pOps) +{ + int ret = MX_ERROR_NONE; + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(pOps); + + def_mux_ops.n_size = sizeof(def_mux_ops); + + memcpy((char *)pOps + sizeof(pOps->n_size), + (char *)&def_mux_ops + sizeof(def_mux_ops.n_size), + pOps->n_size - sizeof(pOps->n_size)); + + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_INVALID_ARGUMENT; + return ret; +} + +static int ffmpeg_muxer_init(MMHandleType *pHandle) +{ + int ret = MX_ERROR_NONE; + MEDIAMUXER_FENTER(); + MX_E("%s:exit: Not implemented\n", __func__); + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int ffmpeg_muxer_set_data_sink(MMHandleType pHandle, char *uri, + mediamuxer_output_format_e format) +{ + int ret = MX_ERROR_NONE; + MEDIAMUXER_FENTER(); + MX_E("%s:exit: Not implemented\n", __func__); + MEDIAMUXER_FLEAVE(); + return ret; +} diff --git a/src/port_gst/mediamuxer_port_gst.c b/src/port_gst/mediamuxer_port_gst.c new file mode 100644 index 0000000..98b2344 --- /dev/null +++ b/src/port_gst/mediamuxer_port_gst.c @@ -0,0 +1,1185 @@ +/* + * Copyright (c) 2015 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 +#include +#include +#include +#include + +#define EOS_POLL_PERIOD 1000 +#define WRITE_POLL_PERIOD 100 +/*#define SEND_FULL_CAPS_VIA_CODEC_DATA *//*For debug purpose*/ +#define ASYCHRONOUS_WRITE /*write sample is not blocking */ + +static int gst_muxer_init(MMHandleType *pHandle); +static int gst_muxer_set_data_sink(MMHandleType pHandle, char *uri, + mediamuxer_output_format_e format); +static int gst_muxer_add_track(MMHandleType pHandle, + media_format_h media_format, int *track_index); +static int gst_muxer_start(MMHandleType pHandle); +static int gst_muxer_write_sample(MMHandleType pHandle, int track_index, + media_packet_h inbuf); +static int gst_muxer_close_track(MMHandleType pHandle, int track_index); +static int gst_muxer_pause(MMHandleType pHandle); +static int gst_muxer_resume(MMHandleType pHandle); +static int gst_muxer_stop(MMHandleType pHandle); +static int gst_muxer_destroy(MMHandleType pHandle); +static int gst_set_error_cb(MMHandleType pHandle, + gst_error_cb callback, void* user_data); + +/*Media Muxer API common*/ +static media_port_muxer_ops def_mux_ops = { + .n_size = 0, + .init = gst_muxer_init, + .set_data_sink = gst_muxer_set_data_sink, + .add_track = gst_muxer_add_track, + .start = gst_muxer_start, + .write_sample = gst_muxer_write_sample, + .close_track = gst_muxer_close_track, + .pause = gst_muxer_pause, + .resume = gst_muxer_resume, + .stop = gst_muxer_stop, + .destroy = gst_muxer_destroy, + .set_error_cb = gst_set_error_cb, +}; + +int gst_port_register(media_port_muxer_ops *pOps) +{ + int ret = MX_ERROR_NONE; + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(pOps); + + def_mux_ops.n_size = sizeof(def_mux_ops); + + memcpy((char *)pOps + sizeof(pOps->n_size), + (char *)&def_mux_ops + sizeof(def_mux_ops.n_size), + pOps->n_size - sizeof(pOps->n_size)); + + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_INVALID_ARGUMENT; + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_init(MMHandleType *pHandle) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + mxgst_handle_t *new_mediamuxer = NULL; + + new_mediamuxer = (mxgst_handle_t *) g_malloc(sizeof(mxgst_handle_t)); + MX_I("GST_Muxer_Init allocating memory for new_mediamuxer: %p\n", new_mediamuxer); + if (!new_mediamuxer) { + MX_E("Cannot allocate memory for muxer \n"); + ret = MX_ERROR; + goto ERROR; + } + memset(new_mediamuxer, 0, sizeof(mxgst_handle_t)); + /* Set desired parameters */ + new_mediamuxer->output_uri = NULL; + new_mediamuxer->muxed_format = -1; + new_mediamuxer->video_track.media_format = NULL; + new_mediamuxer->audio_track.media_format = NULL; + new_mediamuxer->video_track.track_index = -1; + new_mediamuxer->audio_track.track_index = -1; + new_mediamuxer->video_track.start_feed = 1; + new_mediamuxer->audio_track.start_feed = 1; + new_mediamuxer->video_track.stop_feed = 0; + new_mediamuxer->audio_track.stop_feed = 0; + + MX_I("V.start_feed[%d],V.stop_feed[%d],A.start_feed[%d],A.stop_feed[%d]\n", + new_mediamuxer->video_track.start_feed, + new_mediamuxer->video_track.stop_feed, + new_mediamuxer->audio_track.start_feed, + new_mediamuxer->audio_track.stop_feed); + + new_mediamuxer->eos_flg = false; + *pHandle = (MMHandleType) new_mediamuxer; + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MX_E("%s: Not implemented\n", __func__); + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_set_data_sink(MMHandleType pHandle, + char *uri, mediamuxer_output_format_e format) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + MEDIAMUXER_CHECK_NULL(pHandle); + mxgst_handle_t *mx_handle_gst = (mxgst_handle_t *) pHandle; + + /* Set desired parameters */ + mx_handle_gst->output_uri = uri; + mx_handle_gst->muxed_format = format; + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MX_E("muxer handle already NULL, returning \n"); + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_add_track(MMHandleType pHandle, + media_format_h media_format, int *track_index) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + MEDIAMUXER_CHECK_NULL(pHandle); + mxgst_handle_t *mx_handle_gst = (mxgst_handle_t *) pHandle; + + media_format_mimetype_e mimetype = 0; + if (media_format_get_video_info(media_format, &mimetype, NULL, NULL, NULL, NULL) != + MEDIA_FORMAT_ERROR_INVALID_OPERATION) { + if (mimetype == MEDIA_FORMAT_H264_SP || + mimetype == MEDIA_FORMAT_H264_MP || + mimetype == MEDIA_FORMAT_H264_HP) { + if (mx_handle_gst->video_track.media_format == NULL) { + mx_handle_gst->video_track.media_format = (void *)media_format; + /* ToDo: track_index should be incremented accordingly for multiple tracks */ + mx_handle_gst->video_track.track_index = 1; + mx_handle_gst->video_track.caps = NULL; + *track_index = mx_handle_gst->video_track.track_index; + MX_I("Video track added successfully: %p \n", mx_handle_gst->video_track.media_format); + } else { + MX_E("muxer handle already have a video track, \ + add failed, as Multiple video tracks are not currently supported"); + ret = MX_ERROR_NOT_SUPPORT_API; + goto ERROR; + } + } + } else if (media_format_get_audio_info(media_format, &mimetype, NULL, NULL, NULL, NULL) != + MEDIA_FORMAT_ERROR_INVALID_OPERATION) { + if (mimetype == MEDIA_FORMAT_AAC || + mimetype == MEDIA_FORMAT_AAC_LC || + mimetype == MEDIA_FORMAT_AAC_HE || + mimetype == MEDIA_FORMAT_AAC_HE_PS) { + if (mx_handle_gst->audio_track.media_format == NULL) { + mx_handle_gst->audio_track.media_format = (void *)media_format; + /* ToDo: track_index should be incremented accordingly for multiple tracks */ + mx_handle_gst->audio_track.track_index = 2; + mx_handle_gst->audio_track.caps = NULL; + *track_index = mx_handle_gst->audio_track.track_index; + MX_I("Audio track added successfully: %p \n", mx_handle_gst->audio_track.media_format); + } else { + MX_E("muxer handle already have a audio track, add failed, \ + as Multiple audio tracks are not currently supported"); + ret = MX_ERROR_NOT_SUPPORT_API; + goto ERROR; + } + } else { + MX_E("Unsupported MIME Type\n"); + } + } else { + MX_E("Unsupported A/V MIME Type\n"); + } + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MX_E(" gst_muxer_add_track failed \n"); + MEDIAMUXER_FLEAVE(); + return ret; +} + +static gint __gst_handle_resource_error(mxgst_handle_t* gst_handle, int code) +{ + MEDIAMUXER_FENTER(); + gint trans_err = MEDIAMUXER_ERROR_NONE; + g_return_val_if_fail(gst_handle, MX_PARAM_ERROR); + + switch (code) { + /* ToDo: Add individual actions as and when needed */ + case GST_RESOURCE_ERROR_NO_SPACE_LEFT: + trans_err = MX_ERROR_COMMON_NO_FREE_SPACE; + break; + case GST_RESOURCE_ERROR_WRITE: + case GST_RESOURCE_ERROR_FAILED: + case GST_RESOURCE_ERROR_SEEK: + case GST_RESOURCE_ERROR_TOO_LAZY: + case GST_RESOURCE_ERROR_BUSY: + case GST_RESOURCE_ERROR_OPEN_WRITE: + case GST_RESOURCE_ERROR_OPEN_READ_WRITE: + case GST_RESOURCE_ERROR_CLOSE: + case GST_RESOURCE_ERROR_SYNC: + case GST_RESOURCE_ERROR_SETTINGS: + default: + trans_err = MX_INTERNAL_ERROR; + break; + } + MEDIAMUXER_FLEAVE(); + return trans_err; +} + +static gint __gst_handle_core_error(mxgst_handle_t* gst_handle, int code) +{ + MEDIAMUXER_FENTER(); + gint trans_err = MEDIAMUXER_ERROR_NONE; + + /* g_return_val_if_fail(core, MX_PARAM_ERROR); */ + switch (code) { + /* ToDo: Add individual actions as and when needed */ + case GST_CORE_ERROR_MISSING_PLUGIN: + case GST_CORE_ERROR_STATE_CHANGE: + case GST_CORE_ERROR_SEEK: + case GST_CORE_ERROR_NOT_IMPLEMENTED: + case GST_CORE_ERROR_FAILED: + case GST_CORE_ERROR_TOO_LAZY: + case GST_CORE_ERROR_PAD: + case GST_CORE_ERROR_THREAD: + case GST_CORE_ERROR_NEGOTIATION: + case GST_CORE_ERROR_EVENT: + case GST_CORE_ERROR_CAPS: + case GST_CORE_ERROR_TAG: + case GST_CORE_ERROR_CLOCK: + case GST_CORE_ERROR_DISABLED: + default: + trans_err = MX_INTERNAL_ERROR; + break; + } + MEDIAMUXER_FLEAVE(); + return trans_err; +} + +static gint __gst_handle_library_error(mxgst_handle_t* gst_handle, int code) +{ + MEDIAMUXER_FENTER(); + gint trans_err = MEDIAMUXER_ERROR_NONE; + g_return_val_if_fail(gst_handle, MX_PARAM_ERROR); + + switch (code) { + /* ToDo: Add individual actions as and when needed */ + case GST_LIBRARY_ERROR_FAILED: + case GST_LIBRARY_ERROR_TOO_LAZY: + case GST_LIBRARY_ERROR_INIT: + case GST_LIBRARY_ERROR_SHUTDOWN: + case GST_LIBRARY_ERROR_SETTINGS: + case GST_LIBRARY_ERROR_ENCODE: + default: + trans_err = MX_INTERNAL_ERROR; + break; + } + MEDIAMUXER_FLEAVE(); + return trans_err; +} + +static gboolean _mx_gst_bus_call(GstBus *bus, GstMessage *msg, gpointer data) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + mxgst_handle_t *gst_handle = (mxgst_handle_t*)data; + switch (GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_EOS: + MX_I("End of stream\n"); + break; + case GST_MESSAGE_ERROR: { + gchar *debug; + GError *error; + gst_message_parse_error(msg, &error, &debug); + if (!error) { + MX_E("GST error message parsing failed"); + break; + } + MX_E("Error: %s\n", error->message); + if (error) { + if (error->domain == GST_RESOURCE_ERROR) + ret = __gst_handle_resource_error(gst_handle, error->code); + else if (error->domain == GST_LIBRARY_ERROR) + ret = __gst_handle_library_error(gst_handle, error->code); + else if (error->domain == GST_CORE_ERROR) + ret = __gst_handle_core_error(gst_handle, error->code); + else + MX_E("Unexpected error has occured"); + /* ToDo: Update the user callback with ret... */ + return ret; + } + g_free(debug); + MX_E("Error: %s\n", error->message); + g_error_free(error); + } + break; + default: + MX_E("unhandled message: 0x%x", GST_MESSAGE_TYPE(msg)); + break; + } + MEDIAMUXER_FLEAVE(); + return TRUE; +} + +/* + * This signal callback triggers when appsrc needs mux_data. + * Here, we add an idle handler to the mainloop to start pushing data into the appsrc + */ +static void _audio_start_feed(GstElement *source, guint size, mxgst_handle_t *gst_handle) +{ + MX_I("\nAudio Start feeding...\n"); + gst_handle->audio_track.stop_feed = 0; + gst_handle->audio_track.start_feed = 1; +} + +static void _video_start_feed(GstElement *source, guint size, mxgst_handle_t *gst_handle) +{ + MX_I("\nVideo Start feeding cb...\n"); + gst_handle->video_track.stop_feed = 0; + gst_handle->video_track.start_feed = 1; +} + +/* + * This callback triggers when appsrc has enough data and we can stop sending. + * We remove the idle handler from the mainloop. + */ +static void _audio_stop_feed(GstElement *source, mxgst_handle_t *gst_handle) +{ + MX_I("\nAudio Stop feeding cb...\n"); + gst_handle->audio_track.stop_feed = 1; + gst_handle->audio_track.start_feed = 0; +} + +static void _video_stop_feed(GstElement *source, mxgst_handle_t *gst_handle) +{ + MX_I("\nVideo Stop feeding...\n"); + gst_handle->video_track.stop_feed = 1; + gst_handle->video_track.start_feed = 0; +} + +mx_ret_e _gst_create_pipeline(mxgst_handle_t *gst_handle) +{ + MEDIAMUXER_FENTER(); + gint ret = MX_ERROR_NONE; + GstBus *bus = NULL; + /* Note: Use a loop, if needed. GMainLoop *loop; */ + GstPad *audio_pad, *video_pad, *aud_src, *vid_src; + + /* Initialize GStreamer */ + /* Note: Replace the arguments of gst_init to pass the command line args to GStreamer. */ + gst_init(NULL, NULL); + + /* Create the empty pipeline */ + gst_handle->pipeline = gst_pipeline_new("Muxer Gst pipeline"); + + if (gst_handle->muxed_format == MEDIAMUXER_CONTAINER_FORMAT_MP4) { + gst_handle->muxer = gst_element_factory_make("qtmux", "media-muxer"); + /* gst_element_factory_make("mp4mux", "media-muxer"); */ + if (gst_handle->video_track.track_index == 1) { /* Video track */ + gst_handle->video_appsrc = gst_element_factory_make("appsrc", "video appsrc"); + gst_handle->videoparse = gst_element_factory_make("h264parse", "h264parse"); + } + if (gst_handle->audio_track.track_index == 2) { /* Audio track */ + gst_handle->audio_appsrc = gst_element_factory_make("appsrc", "audio appsrc"); + gst_handle->audioparse = gst_element_factory_make("aacparse", "aacparse"); + } + } else { + MX_E("Unsupported format. Currently suports only MPEG4"); + ret = MEDIAMUXER_ERROR_INVALID_PATH; + goto ERROR; + } + gst_handle->sink = gst_element_factory_make("filesink", "muxer filesink"); + + if (!gst_handle->pipeline || !gst_handle->muxer || !gst_handle->sink) { + MX_E("One element could not be created. Exiting.\n"); + ret = MEDIAMUXER_ERROR_RESOURCE_LIMIT; + goto ERROR; + } + + /* Build the pipeline */ + gst_bin_add_many(GST_BIN(gst_handle->pipeline), + gst_handle->muxer, + gst_handle->sink, + NULL); + + if (gst_handle->video_track.track_index == 1) { + /* video track */ + if (!gst_handle->video_appsrc || !gst_handle->videoparse) { + MX_E("One element (vparse) could not be created. Exiting.\n"); + ret = MEDIAMUXER_ERROR_RESOURCE_LIMIT; + goto ERROR; + } + gst_bin_add_many(GST_BIN(gst_handle->pipeline), + gst_handle->video_appsrc, + gst_handle->videoparse, + NULL); + /* Set video caps for corresponding src elements */ + g_object_set(gst_handle->video_appsrc, "caps", + gst_caps_from_string(gst_handle->video_track.caps), NULL); +#ifdef ASYCHRONOUS_WRITE + g_signal_connect(gst_handle->video_appsrc, "need-data", + G_CALLBACK(_video_start_feed), gst_handle); + g_signal_connect(gst_handle->video_appsrc, "enough-data", + G_CALLBACK(_video_stop_feed), gst_handle); +#else + g_object_set(gst_handle->video_appsrc, "block", TRUE, NULL); + gst_app_src_set_stream_type((GstAppSrc *)gst_handle->video_appsrc, + GST_APP_STREAM_TYPE_STREAM); +#endif + } + + if (gst_handle->audio_track.track_index == 2) { + /* Audio track */ + if (!gst_handle->audio_appsrc || !gst_handle->audioparse) { + MX_E("One element (aparse) could not be created. Exiting.\n"); + ret = MEDIAMUXER_ERROR_RESOURCE_LIMIT; + goto ERROR; + } + gst_bin_add_many(GST_BIN(gst_handle->pipeline), + gst_handle->audio_appsrc, + gst_handle->audioparse, + NULL); + /* Set audio caps for corresponding src elements */ + g_object_set(gst_handle->audio_appsrc, "caps", + gst_caps_from_string(gst_handle->audio_track.caps), NULL); +#ifdef ASYCHRONOUS_WRITE + g_signal_connect(gst_handle->audio_appsrc, "need-data", + G_CALLBACK(_audio_start_feed), gst_handle); + g_signal_connect(gst_handle->audio_appsrc, "enough-data", + G_CALLBACK(_audio_stop_feed), gst_handle); +#else + g_object_set(gst_handle->audio_appsrc, "block", TRUE, NULL); + gst_app_src_set_stream_type((GstAppSrc *)gst_handle->audio_appsrc, + GST_APP_STREAM_TYPE_STREAM); +#endif + } + + if (!gst_element_link(gst_handle->muxer, gst_handle->sink)) + MX_E("muxer-sink link failed"); + + if (gst_handle->video_track.track_index == 1) { + /* video track */ + gst_element_link(gst_handle->video_appsrc, gst_handle->videoparse); + /* Link videoparse to muxer_video_pad. Request for muxer A/V pads. */ + video_pad = gst_element_get_request_pad(gst_handle->muxer, "video_00"); + vid_src = gst_element_get_static_pad(gst_handle->videoparse, "src"); + if (gst_pad_link(vid_src, video_pad) != GST_PAD_LINK_OK) + MX_E("video parser to muxer link failed"); + } + + if (gst_handle->audio_track.track_index == 2) { + /* audio track */ + /* we add all elements into the pipeline */ + gst_element_link(gst_handle->audio_appsrc, gst_handle->audioparse); + /* Link audioparse to muxer_audio_pad. Request for muxer A/V pads. */ + audio_pad = gst_element_get_request_pad(gst_handle->muxer, "audio_00"); + aud_src = gst_element_get_static_pad(gst_handle->audioparse, "src"); + if (gst_pad_link(aud_src, audio_pad) != GST_PAD_LINK_OK) + MX_E("audio parser to muxer link failed"); + } + + MX_I("Output_uri= %s\n", gst_handle->output_uri); + g_object_set(GST_OBJECT(gst_handle->sink), "location", + gst_handle->output_uri, NULL); + + /* connect signals, bus watcher */ + bus = gst_pipeline_get_bus(GST_PIPELINE(gst_handle->pipeline)); + gst_handle->bus_watch_id = gst_bus_add_watch(bus, _mx_gst_bus_call, gst_handle); + gst_object_unref(bus); + + /* set pipeline state to PLAYING */ + MEDIAMUXER_ELEMENT_SET_STATE(GST_ELEMENT_CAST(gst_handle->pipeline), + GST_STATE_PLAYING); + return MX_ERROR_NONE; + +STATE_CHANGE_FAILED: +ERROR: + + if (gst_handle->pipeline) + gst_object_unref(GST_OBJECT(gst_handle->pipeline)); + + if (gst_handle->video_track.track_index == 1) { /* Video track */ + if (gst_handle->videoparse) + gst_object_unref(GST_OBJECT(gst_handle->videoparse)); + + if (gst_handle->video_appsrc) + gst_object_unref(GST_OBJECT(gst_handle->video_appsrc)); + } + + if (gst_handle->audio_track.track_index == 2) { /* audio track */ + if (gst_handle->audio_appsrc) + gst_object_unref(GST_OBJECT(gst_handle->audio_appsrc)); + if (gst_handle->audioparse) + gst_object_unref(GST_OBJECT(gst_handle->audioparse)); + } + + if (gst_handle->muxer) + gst_object_unref(GST_OBJECT(gst_handle->muxer)); + + if (gst_handle->sink) + gst_object_unref(GST_OBJECT(gst_handle->sink)); + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_start(MMHandleType pHandle) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + MEDIAMUXER_CHECK_NULL(pHandle); + mxgst_handle_t *new_mediamuxer = (mxgst_handle_t *) pHandle; + + MX_I("__gst_muxer_start adding elements to the pipeline:%p\n", new_mediamuxer); + ret = _gst_create_pipeline(new_mediamuxer); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MX_E("muxer handle NULL, returning \n"); + ret = MX_ERROR_INVALID_ARGUMENT; + MEDIAMUXER_FLEAVE(); + return ret; +} + +int __gst_codec_specific_caps(GstCaps *new_cap, + media_format_mimetype_e mimetype) +{ + MEDIAMUXER_FENTER(); + GValue val = G_VALUE_INIT; + switch (mimetype) { + /*video*/ + case MEDIA_FORMAT_H261: + break; + case MEDIA_FORMAT_H263: + break; + case MEDIA_FORMAT_H263P: + break; + case MEDIA_FORMAT_H264_SP: + break; + case MEDIA_FORMAT_H264_MP: + break; + case MEDIA_FORMAT_H264_HP: + break; + case MEDIA_FORMAT_MJPEG: + break; + case MEDIA_FORMAT_MPEG1: + break; + case MEDIA_FORMAT_MPEG2_SP: + break; + case MEDIA_FORMAT_MPEG2_MP: + break; + case MEDIA_FORMAT_MPEG2_HP: + break; + case MEDIA_FORMAT_MPEG4_SP: + break; + case MEDIA_FORMAT_MPEG4_ASP: + break; + case MEDIA_FORMAT_HEVC: + break; + case MEDIA_FORMAT_VP8: + break; + case MEDIA_FORMAT_VP9: + break; + case MEDIA_FORMAT_VC1: + break; + case MEDIA_FORMAT_I420: + break; + case MEDIA_FORMAT_NV12: + break; + case MEDIA_FORMAT_NV12T: + break; + case MEDIA_FORMAT_YV12: + break; + case MEDIA_FORMAT_NV21: + break; + case MEDIA_FORMAT_NV16: + break; + case MEDIA_FORMAT_YUYV: + break; + case MEDIA_FORMAT_UYVY: + break; + case MEDIA_FORMAT_422P: + break; + case MEDIA_FORMAT_RGB565: + break; + case MEDIA_FORMAT_RGB888: + break; + case MEDIA_FORMAT_RGBA: + break; + case MEDIA_FORMAT_ARGB: + break; + /*audio*/ + case MEDIA_FORMAT_L16: + break; + case MEDIA_FORMAT_ALAW: + break; + case MEDIA_FORMAT_ULAW: + break; + case MEDIA_FORMAT_AMR: + break; + case MEDIA_FORMAT_AMR_WB: + break; + case MEDIA_FORMAT_G729: + break; + case MEDIA_FORMAT_AAC: + g_value_init(&val, G_TYPE_INT); + g_value_set_int(&val, 4); + gst_caps_set_value(new_cap, "mpegversion", &val); + break; + case MEDIA_FORMAT_AAC_HE: + g_value_init(&val, G_TYPE_INT); + g_value_set_int(&val, 4); + gst_caps_set_value(new_cap, "mpegversion", &val); + break; + case MEDIA_FORMAT_AAC_HE_PS: + g_value_init(&val, G_TYPE_INT); + g_value_set_int(&val, 4); + gst_caps_set_value(new_cap, "mpegversion", &val); + break; + case MEDIA_FORMAT_MP3: + break; + case MEDIA_FORMAT_VORBIS: + break; + case MEDIA_FORMAT_PCM: + break; + case MEDIA_FORMAT_PCMA: + break; + case MEDIA_FORMAT_PCMU: + break; + default: + MX_E("Unknown media mimeype %d. Assuming H264\n", mimetype); + break; + } + MEDIAMUXER_FLEAVE(); + return 0; +} + + +int _gst_set_caps(MMHandleType pHandle, media_packet_h packet) +{ + MEDIAMUXER_FENTER(); + gint ret = MX_ERROR_NONE; + GstCaps *new_cap; + media_format_mimetype_e mimetype; + media_format_h format; + GValue val = G_VALUE_INIT; + int numerator; + int denominator = 1; + int channel; + int samplerate; + int bit; + int width; + int height; + int avg_bps; + int max_bps; + gchar *caps_string = NULL; + + mxgst_handle_t *gst_handle = (mxgst_handle_t *) pHandle; + media_format_type_e formattype; + char *codec_data; + unsigned int codec_data_size; + + if (media_packet_get_format(packet, &format)) { + MX_E("media_format_get_formati call failed \n"); + goto ERROR; + } + + if (media_format_get_type(format, &formattype)) { + MX_E("media_format_get_type failed\n"); + goto ERROR; + } + + switch (formattype) { + case MEDIA_FORMAT_AUDIO: + if (media_packet_get_codec_data(packet, + (void **)&codec_data, &codec_data_size)) { + MX_E("media_packet_get_codec_data call failed\n"); + ret = MX_ERROR_UNKNOWN; + break; + } + MX_I("extracted codec data is =%s size is %d\n", + codec_data, codec_data_size); + if (gst_handle->audio_track.caps == NULL || + g_strcmp0(codec_data, gst_handle->audio_track.caps) != 0) { + +#ifndef SEND_FULL_CAPS_VIA_CODEC_DATA + + if (media_format_get_audio_info(format, + &mimetype, &channel, &samplerate, + &bit, &avg_bps)) { + MX_E("media_format_get_audio_info call failed\n"); + ret = MX_ERROR_UNKNOWN; + break; + } + if (gst_handle->audio_track.caps == NULL) { + gst_handle->audio_track.caps = + (char *)g_malloc(codec_data_size); + if (gst_handle->audio_track.caps == NULL) { + MX_E("[%s][%d]memory allocation failed\n", + __func__, __LINE__); + ret = MX_ERROR_UNKNOWN; + break; + } + } + new_cap = gst_caps_from_string(codec_data); + if (__gst_codec_specific_caps(new_cap, mimetype)) { + MX_E("Setting Audio caps failed\n"); + gst_caps_unref(new_cap); + ret = MX_ERROR_UNKNOWN; + break; + } + caps_string = gst_caps_to_string(new_cap); + MX_I("New cap set by codec data is=%s\n", + caps_string); + if (caps_string) + g_free(caps_string); + g_object_set(gst_handle->audio_appsrc, + "caps", new_cap, NULL); + g_stpcpy(gst_handle->audio_track.caps, codec_data); +#else + /*Debugging purpose. The whole caps filter can be sent via codec_data*/ + new_cap = gst_caps_from_string(codec_data); + MX_I("codec cap is=%s\n", codec_data); + g_object_set(gst_handle->audio_appsrc, + "caps", new_cap, NULL); + if (gst_handle->audio_track.caps == NULL) { + gst_handle->audio_track.caps = + (char *)g_malloc(codec_data_size); + if (gst_handle->audio_track.caps == NULL) { + MX_E("[%s][%d] \ + memory allocation failed\n", + __func__, __LINE__); + gst_caps_unref(new_cap); + ret = MX_ERROR_UNKNOWN; + break; + } + } + g_stpcpy(gst_handle->audio_track.caps, codec_data); +#endif + gst_caps_unref(new_cap); + } + break; + case MEDIA_FORMAT_VIDEO: + if (media_packet_get_codec_data(packet, + (void **)&codec_data, &codec_data_size)) { + MX_E("media_packet_get_codec_data call failed\n"); + ret = MX_ERROR_UNKNOWN; + break; + } + MX_I("codec data is =%s size is %d\n", + codec_data, codec_data_size); + if (gst_handle->video_track.caps == NULL || + g_strcmp0(codec_data, gst_handle->video_track.caps) != 0) { + +#ifndef SEND_FULL_CAPS_VIA_CODEC_DATA + + if (media_format_get_video_info(format, + &mimetype, &width, &height, + &avg_bps, &max_bps)) { + MX_E("media_format_get_video_info call failed\n"); + ret = MX_ERROR_UNKNOWN; + break; + } + if (gst_handle->video_track.caps == NULL) { + gst_handle->video_track.caps = + (char *)g_malloc(codec_data_size); + if (gst_handle->video_track.caps == NULL) { + MX_E("[%s][%d] \ + memory allocation failed\n", + __func__, __LINE__); + ret = MX_ERROR_UNKNOWN; + break; + } + } + new_cap = gst_caps_from_string(codec_data); + MX_I("New cap set by codec data is=%s\n", + codec_data); + if (__gst_codec_specific_caps(new_cap, mimetype)) { + MX_E("Setting Audio caps failed\n"); + gst_caps_unref(new_cap); + ret = MX_ERROR_UNKNOWN; + break; + } + g_stpcpy(gst_handle->video_track.caps, codec_data); + + if (media_format_get_video_frame_rate(format, + &numerator)) { + MX_E("media_format_get_video_info call failed\n"); + } + g_value_init(&val, GST_TYPE_FRACTION); + gst_value_set_fraction(&val, numerator, denominator); + gst_caps_set_value(new_cap, "framerate", &val); + caps_string = gst_caps_to_string(new_cap); + MX_I("New cap set by codec data is=%s\n", + caps_string); + if (caps_string) + g_free(caps_string); + g_object_set(gst_handle->video_appsrc, "caps", + new_cap, NULL); +#else + /*Debugging purpose. The whole caps filter can be sent via codec_data*/ + media_packet_get_codec_data(packet, &codec_data, + &codec_data_size); + MX_I("extracted codec data is =%s\n", codec_data); + new_cap = gst_caps_from_string(codec_data); + MX_I("New cap is=%s\n", codec_data); + g_object_set(gst_handle->video_appsrc, "caps", + new_cap, NULL); + if (gst_handle->video_track.caps == NULL) { + gst_handle->video_track.caps = + (char *)g_malloc(codec_data_size); + if (gst_handle->video_track.caps == NULL) { + MX_E("[%s][%d] \ + memory allocation failed\n", + __func__, __LINE__); + gst_caps_unref(new_cap); + ret = MX_ERROR_UNKNOWN; + break; + } + } + g_stpcpy(gst_handle->video_track.caps, codec_data); +#endif + gst_caps_unref(new_cap); + } + break; + case MEDIA_FORMAT_CONTAINER: + case MEDIA_FORMAT_TEXT: + case MEDIA_FORMAT_UNKNOWN: + default: + MX_E("Unknown format type\n"); + } + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_UNKNOWN; + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int _gst_copy_media_packet_to_buf(media_packet_h out_pkt, + GstBuffer *buffer) +{ + MEDIAMUXER_FENTER(); + void *pkt_data; + uint64_t size; + unsigned char *data_ptr; + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(out_pkt); + /* GstMapInfo map; */ + int ret = MX_ERROR_NONE; + /* copy data*/ + media_packet_get_buffer_size(out_pkt, &size); + MX_I("Media packet Buffer capacity: %llu\n", size); + data_ptr = (unsigned char *) g_malloc(size); + if (!data_ptr) { + MX_E("Memory allocation failed in %s \n", __FUNCTION__); + ret = MX_MEMORY_ERROR; + goto ERROR; + } + + if (media_packet_get_buffer_data_ptr(out_pkt, &pkt_data)) { + MX_E("unable to get the buffer pointer \ + from media_packet_get_buffer_data_ptr\n"); + ret = MX_ERROR_UNKNOWN; + goto ERROR; + } + /*if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) { + MX_E("gst_buffer_map failed\n"); + ret = MX_ERROR_UNKNOWN; + goto ERROR; + }*/ + uint64_t info; + memcpy(data_ptr, (char *)pkt_data, size); + gst_buffer_insert_memory(buffer, -1, + gst_memory_new_wrapped(0, data_ptr, size, 0, + size, data_ptr, g_free)); + + if (media_packet_get_pts(out_pkt, &info)) { + MX_E("unable to get the pts\n"); + ret = MX_ERROR_UNKNOWN; + goto ERROR; + } + buffer->pts = info; + + if (media_packet_get_dts(out_pkt, &info)) { + MX_E("unable to get the dts\n"); + ret = MX_ERROR_UNKNOWN; + goto ERROR; + } + buffer->dts = info; + if (media_packet_get_duration(out_pkt, &info)) { + MX_E("unable to get the duration\n"); + ret = MX_ERROR_UNKNOWN; + goto ERROR; + } + buffer->duration = info; + /*TBD: set falgs is not available now in media_packet*/ + media_buffer_flags_e flags; + if (media_packet_get_flags(out_pkt, &flags)) { + MX_E("unable to get the buffer size\n"); + ret = MX_ERROR_UNKNOWN; + goto ERROR; + } + GST_BUFFER_FLAG_SET(buffer, flags); +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_write_sample(MMHandleType pHandle, int track_index, + media_packet_h inbuf) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + MEDIAMUXER_CHECK_NULL(pHandle); + mxgst_handle_t *gst_handle = (mxgst_handle_t *) pHandle; + + _gst_set_caps(pHandle, inbuf); + MX_I("\nTrack_index=%d", track_index); + + GstBuffer *gst_inbuf2 = NULL; + gst_inbuf2 = gst_buffer_new(); + /* ToDo: Add functionality to the following function */ + /* MX_I("\nBefore buff=%x", gst_inbuf2); */ + _gst_copy_media_packet_to_buf(inbuf, gst_inbuf2); + + if ((gst_handle->video_track.track_index != -1) && + (track_index == gst_handle->video_track.track_index)) { + MX_I("\n pushing video"); +#ifdef ASYCHRONOUS_WRITE + /*poll now to make it synchronous*/ + while (gst_handle->video_track.start_feed == 0) { + g_usleep(WRITE_POLL_PERIOD); + } + g_signal_emit_by_name(gst_handle->video_appsrc, "push-buffer", gst_inbuf2, &ret); +#else + ret = gst_app_src_push_buffer((GstAppSrc *)gst_handle->video_appsrc, gst_inbuf2); +#endif + MX_I("\n attempted a vid-buf push"); + if (ret != GST_FLOW_OK) { + /* We got some error, stop sending data */ + MX_E("--video appsrc push failed--"); + } + } else if ((gst_handle->audio_track.track_index != -1) && + (track_index == gst_handle->audio_track.track_index)) { + MX_I("\n pushing audio"); +#ifdef ASYCHRONOUS_WRITE + while (gst_handle->audio_track.start_feed == 0) { + g_usleep(WRITE_POLL_PERIOD); + } + g_signal_emit_by_name(gst_handle->audio_appsrc, "push-buffer", gst_inbuf2, &ret); +#else + ret = gst_app_src_push_buffer((GstAppSrc *)gst_handle->audio_appsrc, gst_inbuf2); +#endif + MX_I("\n attempted a aud-buf push"); + if (ret != GST_FLOW_OK) { + /* We got some error, stop sending data */ + MX_E("--audio appsrc push failed--"); + } + } else { + MX_E("\nUnsupported track index. Only 1/2 track index is vaild"); + ret = MX_ERROR_INVALID_ARGUMENT; + } + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_INVALID_ARGUMENT; + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_close_track(MMHandleType pHandle, int track_index) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + MEDIAMUXER_CHECK_NULL(pHandle); + mxgst_handle_t *gst_handle = (mxgst_handle_t *) pHandle; + + MX_I("__gst_muxer_stop setting eos to sources:%p\n", gst_handle); + if (gst_handle->pipeline!= NULL) { + if (gst_handle->audio_track.track_index == track_index) { + MX_I("\n-----EOS for audioappsrc-----\n"); + gst_app_src_end_of_stream((GstAppSrc *)(gst_handle->audio_appsrc)); + } else if (gst_handle->video_track.track_index == track_index) { + MX_I("\n-----EOS for videoappsrc-----\n"); + gst_app_src_end_of_stream((GstAppSrc *)(gst_handle->video_appsrc)); + } else { + MX_E("\nInvalid track Index[%d].\n", track_index); + goto ERROR; + } + } + if (gst_handle->audio_track.track_index == track_index) { + gst_handle->audio_track.media_format = NULL; + gst_handle->audio_track.track_index = -1; + } else if (gst_handle->video_track.track_index == track_index) { + gst_handle->video_track.media_format = NULL; + gst_handle->video_track.track_index = -1; + } + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_INVALID_ARGUMENT; + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_pause(MMHandleType pHandle) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + MEDIAMUXER_CHECK_NULL(pHandle); + mxgst_handle_t *gst_handle = (mxgst_handle_t *) pHandle; + + GstState state; + MX_I("gst_muxer_pause setting pipeline to pause"); + gst_element_get_state(gst_handle->pipeline, &state, NULL, GST_CLOCK_TIME_NONE); + if (state == GST_STATE_PLAYING) { + if (gst_element_set_state(gst_handle->pipeline, GST_STATE_PAUSED) == + GST_STATE_CHANGE_FAILURE) { + MX_I("Setting pipeline to pause failed"); + ret = MX_ERROR_INVALID_ARGUMENT; + } + } else { + MX_I("pipeline is not in playing, PAUSE is intended to pause a playing pipeline. \ + exiting with out state change"); + ret = MX_ERROR_INVALID_ARGUMENT; + } + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_INVALID_ARGUMENT; + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_resume(MMHandleType pHandle) +{ + MEDIAMUXER_FENTER(); + MEDIAMUXER_CHECK_NULL(pHandle); + int ret = MX_ERROR_NONE; + mxgst_handle_t *gst_handle = (mxgst_handle_t *) pHandle; + + MX_I("gst_muxer_resume setting pipeline to playing"); + if (gst_element_set_state(gst_handle->pipeline, GST_STATE_PLAYING) == + GST_STATE_CHANGE_FAILURE) { + MX_I("Setting pipeline to resume failed"); + ret = MX_ERROR_INVALID_ARGUMENT; + } + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_INVALID_ARGUMENT; + MEDIAMUXER_FLEAVE(); + return ret; +} + +mx_ret_e _gst_destroy_pipeline(mxgst_handle_t *gst_handle) +{ + gint ret = MX_ERROR_NONE; + MEDIAMUXER_FENTER(); + + /* Clean up nicely */ + gst_element_set_state(gst_handle->pipeline, GST_STATE_NULL); + + /* Free resources & set unused pointers to NULL */ + if (gst_handle->output_uri != NULL) { + gst_handle->output_uri = NULL; + } + + if (gst_handle->video_track.track_index == 1) { /* Video track */ + if (gst_handle->video_track.caps != NULL) { + g_free(gst_handle->video_track.caps); + } + + if (gst_handle->video_track.media_format != NULL) { + gst_handle->video_track.media_format = NULL; + } + } + + if (gst_handle->audio_track.track_index == 2) { /* audio track */ + if (gst_handle->audio_track.caps != NULL) { + g_free(gst_handle->audio_track.caps); + } + + if (gst_handle->audio_track.media_format != NULL) { + gst_handle->audio_track.media_format = NULL; + } + } + + if (gst_handle->pipeline) + gst_object_unref(GST_OBJECT(gst_handle->pipeline)); + + g_source_remove(gst_handle->bus_watch_id); + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_stop(MMHandleType pHandle) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + MEDIAMUXER_CHECK_NULL(pHandle); + mxgst_handle_t *gst_handle = (mxgst_handle_t *) pHandle; + + MX_I("__gst_muxer_stop setting eos to sources:%p\n", gst_handle); + ret = _gst_destroy_pipeline(gst_handle); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + ret = MX_ERROR_INVALID_ARGUMENT; + MEDIAMUXER_FLEAVE(); + return ret; +} + +static int gst_muxer_destroy(MMHandleType pHandle) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + MEDIAMUXER_CHECK_NULL(pHandle); + mxgst_handle_t *new_mediamuxer = (mxgst_handle_t *) pHandle; + + MX_I("__gst_muxer_destroy deallocating new_mediamuxer:%p\n", new_mediamuxer); + g_free(new_mediamuxer); + MEDIAMUXER_FLEAVE(); + return ret; +ERROR: + MX_E("muxer handle already NULL, returning \n"); + MEDIAMUXER_FLEAVE(); + return ret; +} + +int gst_set_error_cb(MMHandleType pHandle, gst_error_cb callback, void* user_data) +{ + MEDIAMUXER_FENTER(); + int ret = MX_ERROR_NONE; + MEDIAMUXER_CHECK_NULL(pHandle); + mxgst_handle_t *gst_handle = (mxgst_handle_t *) pHandle; + + if (!gst_handle) { + MX_E("fail invaild param\n"); + ret = MX_INVALID_ARG; + goto ERROR; + } + + if (gst_handle->user_cb[_GST_EVENT_TYPE_ERROR]) { + MX_E("Already set mediamuxer_error_cb\n"); + ret = MX_ERROR_INVALID_ARGUMENT; + goto ERROR; + } + else { + if (!callback) { + ret = MX_ERROR_INVALID_ARGUMENT; + goto ERROR; + } + } + + MX_I("Set event handler callback(cb = %p, data = %p)\n", callback, user_data); + gst_handle->user_cb[_GST_EVENT_TYPE_ERROR] = (gst_error_cb) callback; + gst_handle->user_data[_GST_EVENT_TYPE_ERROR] = user_data; + MEDIAMUXER_FLEAVE(); + return MX_ERROR_NONE; +ERROR: + MEDIAMUXER_FLEAVE(); + return ret; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..f0f24d2 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,23 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +SET(fw_test "${fw_name}-test") + +INCLUDE_DIRECTORIES(../include) +INCLUDE_DIRECTORIES(../include/headers) +link_directories(${CMAKE_SOURCE_DIR}/../) + +INCLUDE(FindPkgConfig) +FOREACH(flag ${${fw_test}_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + +aux_source_directory(. sources) + +FOREACH(src ${sources}) + GET_FILENAME_COMPONENT(src_name ${src} NAME_WE) + MESSAGE("${src_name}") + ADD_EXECUTABLE(${src_name} ${src}) + TARGET_LINK_LIBRARIES(${src_name} capi-mediamuxer ${${fw_test}_LDFLAGS}) +ENDFOREACH() + diff --git a/test/mediamuxer_test.c b/test/mediamuxer_test.c new file mode 100644 index 0000000..f5e57f6 --- /dev/null +++ b/test/mediamuxer_test.c @@ -0,0 +1,734 @@ +/* + * Copyright (c) 2015 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 FILES | +| | +==============================================================================*/ +#include +#include +#include +#include +#include + +#include +#include +#include "../include/mediamuxer_port.h" +#include +#include +#include +/* Read encoded medial files locally: encoded A/V files along with info & caps */ +#define H264_FILE video_data +#define H264_INFO video_extra_info +#define AAC_FILE audio_data +#define AAC_INFO audio_extra_info + +/*----------------------------------------------------------------------- +| GLOBAL VARIABLE DEFINITIONS: | +-----------------------------------------------------------------------*/ +#define MAX_STRING_LEN 100 +#define PACKAGE "mediamuxer_test" + +mediamuxer_h myMuxer = 0; +media_format_h media_format = NULL; +media_format_h media_format_a = NULL; + +bool aud_eos = 0; +bool vid_eos = 0; +char audio_extra_info[1000]; +char audio_data[1000]; +char video_extra_info[1000]; +char video_data[1000]; + +int test_mediamuxer_create() +{ + g_print("test_mediamuxer_create\n"); + g_print("%p", myMuxer); + + if (mediamuxer_create(&myMuxer) + != MEDIAMUXER_ERROR_NONE) { + g_print("mediamuxer create is failed\n"); + } + g_print("\n Muxer->mx_handle created successfully with address=%p", + (void *)((mediamuxer_s *) myMuxer)->mx_handle); + g_print("\n Muxer handle created successfully with address=%p", + myMuxer); + + return 0; +} + +int test_mediamuxer_set_data_sink() +{ + char *op_uri = "MyTest.mp4"; + + /* Set data source after creating */ + mediamuxer_set_data_sink(myMuxer, op_uri, MEDIAMUXER_CONTAINER_FORMAT_MP4); + return 0; +} + +int test_mediamuxer_destroy() +{ + int ret = 0; + g_print("test_mediamuxer_destroy\n"); + ret = mediamuxer_destroy(myMuxer); + myMuxer = NULL; + g_print("\nDestroy operation returned: %d\n", ret); + return ret; +} + +int test_mediamuxer_start() +{ + g_print("mediamuxer_start completed \n"); + mediamuxer_start(myMuxer); + return 0; +} + +int test_mediamuxer_add_track_video() +{ + int track_index_vid = -1; + media_format_mimetype_e mimetype; + int width; + int height; + int avg_bps; + int max_bps; + + g_print("test_mediamuxer_add_track_video\n"); + media_format_create(&media_format); + + /* MEDIA_FORMAT_H264_SP MEDIA_FORMAT_H264_MP MEDIA_FORMAT_H264_HP */ + media_format_set_video_mime(media_format, MEDIA_FORMAT_H264_SP); + + media_format_set_video_width(media_format, 640); + media_format_set_video_height(media_format, 480); + media_format_set_video_avg_bps(media_format, 10); + media_format_set_video_max_bps(media_format, 10); + + media_format_get_video_info(media_format, &mimetype, &width, &height, &avg_bps, &max_bps); + + g_print("\n Video Mime trying to set: %x %x\n", (int)(mimetype), (int)(MEDIA_FORMAT_H264_SP)); + g_print("\n Video param trying to set: (width, height, avg_bps, max_bps): %d %d %d %d \n", + width, height, avg_bps, max_bps); + + /* To add video track */ + mediamuxer_add_track(myMuxer, media_format, &track_index_vid); + g_print("audio track index returned is: %d", track_index_vid); + return 0; +} + +int test_mediamuxer_add_track_audio() +{ + int track_index_aud = -1; + media_format_mimetype_e mimetype; + int channel; + int samplerate; + int bit; + int avg_bps; + + g_print("test_mediamuxer_add_track\n"); + media_format_create(&media_format_a); + + /* MEDIA_FORMAT_AAC MEDIA_FORMAT_AAC_LC MEDIA_FORMAT_AAC_HE MEDIA_FORMAT_AAC_HE_PS */ + if (media_format_set_audio_mime(media_format_a, MEDIA_FORMAT_AAC) == MEDIA_FORMAT_ERROR_INVALID_OPERATION) + g_print("Problem during media_format_set_audio_mime operation"); + + if (media_format_set_audio_channel(media_format_a, 2) == MEDIA_FORMAT_ERROR_INVALID_OPERATION) + g_print("Problem during media_format_set_audio_channel operation"); + media_format_set_audio_samplerate(media_format_a, 44000); + media_format_set_audio_bit(media_format_a, 1); + media_format_set_audio_avg_bps(media_format_a, 10); + media_format_set_audio_aac_type(media_format_a, true); + + media_format_get_audio_info(media_format_a, &mimetype, &channel, &samplerate, &bit, &avg_bps); + + g_print("\n Audio Mime trying to set: %x %x\n", (int)(mimetype), (int)(MEDIA_FORMAT_AAC)); + g_print("\n Audio Param trying to set: (ch, samplert, bt, avg_bps) %d %d %d %d \n", + channel, samplerate, bit, avg_bps); + + /* To add audio track */ + mediamuxer_add_track(myMuxer, media_format_a, &track_index_aud); + g_print("track index returned is: %d", track_index_aud); + return 0; +} + +void app_err_cb(mediamuxer_error_e error, void *user_data) +{ + printf("Got Error %d from mediamuxer\n",error); +} + +int test_mediamuxer_set_error_cb() +{ + int ret = 0; + g_print("test_mediamuxer_set_error_cb\n"); + ret = mediamuxer_set_error_cb(myMuxer, app_err_cb, myMuxer); + return ret; +} + +void *_write_video_data() +{ + FILE *pvFile; + FILE *pvFileInfo; + unsigned int size; + unsigned int vsize; + unsigned int is_video_readable = 1; + unsigned int is_video_pts_readable; + unsigned int is_video_dts_readable; + unsigned int is_video_duration_readable; + unsigned int is_video_flag_readable; + unsigned int is_video_key_readable; + unsigned long long int pts_vid; + unsigned long long int dts_vid; + unsigned long long int duration_vid; + int flg_vid; + int *status = (int *)g_malloc(sizeof(int) * 1); + *status = -1; + int track_index_vid = 1; /* track_index=2 for video */ + int vcount = 0; + guint8 *ptr_vid; + int key_vid; + char *vid_caps; + int ret_scan; + media_packet_h vid_pkt; + media_format_h vidfmt; + unsigned int vcap_size; + + pvFile = fopen(H264_FILE, "rb"); + pvFileInfo = fopen(H264_INFO, "rt"); + + if (pvFile == NULL || pvFileInfo == NULL) { + g_print("\nOne of the files (info/data) cant be loaded...\n"); + return (void *)status; + } + + ret_scan = fscanf(pvFileInfo, "%d\n", &size); + vid_caps = (char *)malloc(size + 1); + ret_scan = fscanf(pvFileInfo, "%[^\n]s\n", vid_caps); + g_print("\nV_Caps = %s\n", vid_caps); + vcap_size = size + 1; + + if (!ret_scan) { /* ToDo: repeat the same for every scanf */ + g_print("\nscan failed"); + return (void *)status; + } + + while (is_video_readable == 1) { + /* Read encoded video data */ + is_video_readable = fscanf(pvFileInfo, "%d\n", &vsize); + is_video_pts_readable = fscanf(pvFileInfo, "%llu\n", &pts_vid); + is_video_dts_readable = fscanf(pvFileInfo, "%llu\n", &dts_vid); + is_video_duration_readable = fscanf(pvFileInfo, "%llu\n", &duration_vid); + is_video_flag_readable = fscanf(pvFileInfo, "%u\n", &flg_vid); + is_video_key_readable = fscanf(pvFileInfo, "%d\n", &key_vid); + + if (is_video_readable == 1 && is_video_pts_readable == 1 && is_video_dts_readable == 1 + && is_video_duration_readable == 1 && is_video_flag_readable == 1 + && is_video_key_readable == 1) { + g_print("\nv%d: ", ++vcount); + g_print("\nv_Size: %d, v_pts: %llu, v_dts: %llu v_duration: %llu, v_flag: %d", + vsize, pts_vid, dts_vid, duration_vid, key_vid); + ptr_vid = g_malloc(vsize); + g_assert(ptr_vid); + vsize = fread(ptr_vid, 1, vsize, pvFile); + + if (media_format_create(&vidfmt)) { + g_print("media_format_create failed\n"); + return (void *)status; + } + if (media_format_set_video_mime(vidfmt, MEDIA_FORMAT_H264_SP)) { + g_print("media_format_set_audio_mime failed\n"); + return (void *)status; + } + media_format_set_video_width(vidfmt, vsize / 2 + 1); + media_format_set_video_height(vidfmt, vsize / 2 + 1); + /*frame rate is came from the caps filter of demuxer*/ + if (media_format_set_video_frame_rate(vidfmt, 30)) { + g_print("media_format_set_video_frame_rate failed\n"); + return (void *)status; + } + + uint64_t ns; + guint8 *dptr; + + if (media_packet_create(vidfmt, NULL, NULL, &vid_pkt)) { + g_print("\ncreate v media packet failed tc\n"); + return (void *)status; + } + + if (media_packet_alloc(vid_pkt)) { + g_print(" v media packet alloc failed\n"); + return (void *)status; + } + media_packet_get_buffer_data_ptr(vid_pkt, (void **)&dptr); + media_packet_get_buffer_size(vid_pkt, &ns); + g_print("set v buf size as %d, data size=%d\n", (int)ns, vsize); + + memcpy((char *)dptr, ptr_vid, vsize); + if (media_packet_set_buffer_size(vid_pkt, vsize)) { + g_print("set v buf size failed\n"); + return (void *)status; + } + + + if (media_packet_get_buffer_size(vid_pkt, &ns)) { + g_print("unable to set the v buffer size actual =%d, fixed %d\n", size, (int)ns); + return (void *)status; + } + g_print(" fixed size %d\n", (int)ns); + + if (media_packet_set_pts(vid_pkt, pts_vid)) { + g_print("unable to set the pts\n"); + return (void *)status; + } + if (media_packet_set_dts(vid_pkt, dts_vid)) { + g_print("unable to set the pts\n"); + return (void *)status; + } + if (media_packet_set_duration(vid_pkt, duration_vid)) { + g_print("unable to set the pts\n"); + return (void *)status; + } + + if (media_packet_set_flags(vid_pkt, flg_vid)) { + g_print("unable to set the flag size\n"); + return (void *)status; + } + if (media_packet_set_codec_data(vid_pkt, vid_caps, vcap_size)) { + g_print("unable to set the flag size\n"); + return (void *)status; + } + + + g_print("V write sample call. packet add:%x\n", (unsigned int)vid_pkt); + mediamuxer_write_sample(myMuxer, track_index_vid, vid_pkt); + + media_packet_destroy(vid_pkt); + } else { + g_print("\nVideo while done in the test suite"); + mediamuxer_close_track(myMuxer, track_index_vid); + } + } + g_print("\n\n\n ******* Out of while loop ****** \n\n\n"); + fclose(pvFile); + fclose(pvFileInfo); + *status = 0; + return (void *)status; +} + +void *_write_audio_data() +{ + FILE *paFile; + FILE *paFileInfo; + unsigned int size; + unsigned int is_audio_readable = 1; + unsigned int is_audio_pts_readable; + unsigned int is_audio_dts_readable; + unsigned int is_audio_duration_readable; + unsigned int is_audio_flag_readable; + unsigned int is_audio_key_readable; + unsigned char *ptr1; + unsigned long long int pts; + unsigned long long int dts; + unsigned long long int duration; + int flg; + + int key; + int acount = 0; + int track_index_aud = 2; /* track_index=2 for audio */ + char *aud_caps; + int ret_scan; + media_packet_h aud_pkt; + media_format_h audfmt; + unsigned int acap_size; + int *status = (int *)g_malloc(sizeof(int) * 1); + *status = -1; + + paFileInfo = fopen(AAC_INFO, "rt"); + paFile = fopen(AAC_FILE, "rb"); + + if (paFile == NULL || paFileInfo == NULL) { + g_print("\nOne of the files (info/data) cant be loaded...\n"); + return (void *)status; + } + + ret_scan = fscanf(paFileInfo, "%d\n", &size); + aud_caps = (char *)malloc(1 + size); + ret_scan = fscanf(paFileInfo, "%[^\n]s\n", aud_caps); + g_print("\nA_Caps = %s\n", aud_caps); + acap_size = size + 1; + + if (!ret_scan) { /* ToDo: repeat the same for every scanf */ + g_print("\nscan failed"); + return (void *)status; + } + + while (is_audio_readable == 1) { + + /* Read encoded audio data */ + is_audio_readable = fscanf(paFileInfo, "%d\n", &size); + is_audio_pts_readable = fscanf(paFileInfo, "%llu\n", &pts); + is_audio_dts_readable = fscanf(paFileInfo, "%llu\n", &dts); + is_audio_duration_readable = fscanf(paFileInfo, "%llu\n", &duration); + is_audio_flag_readable = fscanf(paFileInfo, "%u\n", &flg); + is_audio_key_readable = fscanf(paFileInfo, "%d\n", &key); + + if (is_audio_readable == 1 && is_audio_pts_readable == 1 + && is_audio_dts_readable == 1 && is_audio_duration_readable == 1 + && is_audio_flag_readable == 1 && is_audio_key_readable == 1) { + g_print("\na%d: ", ++acount); + g_print("\nSize: %d, a_pts: %llu, a_dts: %llu, a_duration: %llu, a_flag:%d, a_key:%d", + size, pts, dts, duration, flg, key); + + if (media_format_create(&audfmt)) { + g_print("media_format_create failed\n"); + return (void *)status; + } + if (media_format_set_audio_mime(audfmt, MEDIA_FORMAT_AAC)) { + g_print("media_format_set_audio_mime failed\n"); + return (void *)status; + } + + ptr1 = g_malloc(size); + g_assert(ptr1); + + size = fread(ptr1, 1, size, paFile); + + /* To create media_pkt */ + uint64_t ns; + guint8 *dptr; + + if (media_packet_create(audfmt, NULL, NULL, &aud_pkt)) { + g_print("create audio media_packet failed\n"); + return (void *)status; + } + + if (media_packet_alloc(aud_pkt)) { + g_print("audio media_packet alloc failed\n"); + return (void *)status; + } + media_packet_get_buffer_data_ptr(aud_pkt, (void **)&dptr); + memcpy((char *)dptr, ptr1, size); + + if (media_packet_set_buffer_size(aud_pkt, (uint64_t)size)) { + g_print("audio set_buffer_size failed\n"); + return (void *)status; + } + + if (media_packet_get_buffer_size(aud_pkt, &ns)) { + g_print("unable to set the buffer size actual =%d, fixed %d\n", size, (int)&ns); + return (void *)status; + } + + g_print(" fixed size %d\n", (int)ns); + + if (media_packet_set_pts(aud_pkt, pts)) { + g_print("unable to set the pts\n"); + return (void *)status; + } + + if (media_packet_set_dts(aud_pkt, dts)) { + g_print("unable to set the pts\n"); + return (void *)status; + } + + if (media_packet_set_duration(aud_pkt, duration)) { + g_print("unable to set the pts\n"); + return (void *)status; + } + + if (media_packet_set_flags(aud_pkt, key)) { + g_print("unable to set the flag size\n"); + return (void *)status; + } + if (media_packet_set_codec_data(aud_pkt, aud_caps, acap_size)) { + g_print("unable to set the audio codec data e\n"); + return (void *)status; + } + + + g_print("A write sample call. packet add:%x\n", (unsigned int)aud_pkt); + mediamuxer_write_sample(myMuxer, track_index_aud, aud_pkt); + + media_packet_destroy(aud_pkt); + } else { + g_print("\nAudio while done in the test suite"); + mediamuxer_close_track(myMuxer, track_index_aud); + } + + } + g_print("\n\n\n ******* Out of while loop ****** \n\n\n"); + + fclose(paFile); + fclose(paFileInfo); + *status = 0; + return (void *)status; +} + + +int test_mediamuxer_write_sample() +{ + pthread_t thread[2]; + pthread_attr_t attr; + /* Initialize and set thread detached attribute */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + g_print("In main: creating thread for audio\n"); + pthread_create(&thread[0], &attr, _write_video_data, NULL); + pthread_create(&thread[1], &attr, _write_audio_data, NULL); + pthread_attr_destroy(&attr); + return 0; +} + +int test_mediamuxer_stop() +{ + g_print("test_mediamuxer_stop\n"); + mediamuxer_stop(myMuxer); + media_format_unref(media_format_a); + media_format_unref(media_format); + return 0; +} + +int test_mediamuxer_pause() +{ + g_print("test_mediamuxer_pause\n"); + mediamuxer_state_e state; + if (mediamuxer_get_state(myMuxer, &state) == MEDIAMUXER_ERROR_NONE) { + g_print("\nMediamuxer_state=%d",state); + if (state == MEDIAMUXER_STATE_MUXING) + mediamuxer_pause(myMuxer); + } + return 0; +} + +int test_mediamuxer_resume() +{ + g_print("test_mediamuxer_resume\n"); + mediamuxer_resume(myMuxer); + return 0; +} + +int test_mediamuxer_add_track() +{ + g_print("test_mediamuxer_add_track\n"); + return 0; +} + +void quit_testApp(void) +{ + /* To Do: Replace exit(0) with smooth exit */ + exit(0); +} + +enum { + CURRENT_STATUS_MAINMENU, + CURRENT_STATUS_AUDIO_FILENAME, + CURRENT_STATUS_AUDIO_INFONAME, + CURRENT_STATUS_VIDEO_FILENAME, + CURRENT_STATUS_VIDEO_INFONAME, +}; + +int g_menu_state = CURRENT_STATUS_MAINMENU; +static void display_sub_basic(); + +void reset_menu_state() +{ + g_menu_state = CURRENT_STATUS_MAINMENU; + return; +} + +static void input_filepath(char *filename) +{ + g_print("Opening file %s\n", filename); + return; +} + +void _interpret_main_menu(char *cmd) +{ + int len = strlen(cmd); + if (len == 1) { + if (strncmp(cmd, "c", 1) == 0) { + test_mediamuxer_create(); + } else if (strncmp(cmd, "o", 1) == 0) { + test_mediamuxer_set_data_sink(); + } else if (strncmp(cmd, "d", 1) == 0) { + test_mediamuxer_destroy(); + } else if (strncmp(cmd, "s", 1) == 0) { + test_mediamuxer_start(); + } else if (strncmp(cmd, "a", 1) == 0) { + g_menu_state = CURRENT_STATUS_AUDIO_FILENAME; + } else if (strncmp(cmd, "v", 1) == 0) { + g_menu_state = CURRENT_STATUS_VIDEO_FILENAME; + } else if (strncmp(cmd, "m", 1) == 0) { + test_mediamuxer_write_sample(); + } else if (strncmp(cmd, "e", 1) == 0) { + test_mediamuxer_stop(); + } else if (strncmp(cmd, "p", 1) == 0) { + test_mediamuxer_pause(); + } else if (strncmp(cmd, "r", 1) == 0) { + test_mediamuxer_resume(); + } else if (strncmp(cmd, "b", 1) == 0) { + test_mediamuxer_set_error_cb(); + } else if (strncmp(cmd, "q", 1) == 0) { + quit_testApp(); + } else { + g_print("unknown menu command. Please try again\n"); + } + } else { + g_print("unknown menu command. Please try again\n"); + } + + return; +} + +static void displaymenu(void) +{ + if (g_menu_state == CURRENT_STATUS_MAINMENU) { + display_sub_basic(); + } else if (g_menu_state == CURRENT_STATUS_AUDIO_FILENAME) { + g_print("*** input encoded audio_data path:\n"); + g_print("[This is the raw encoded audio file to be muxed]:"); + } else if (g_menu_state == CURRENT_STATUS_AUDIO_INFONAME) { + g_print("*** input encoded audio info (extra data) path\n"); + g_print("[This is the extra-information needed to mux."); + g_print("This includes gst-caps too]:"); + } else if (g_menu_state == CURRENT_STATUS_VIDEO_FILENAME) { + g_print("*** input encoded video path\n"); + g_print("[This is the raw encoded video file to be muxed]:"); + } else if (g_menu_state == CURRENT_STATUS_VIDEO_INFONAME) { + g_print("*** input encoded video info (extra data) path\n"); + g_print("[This is the extra-information needed to mux."); + g_print("This includes gst-caps too]:"); + } else { + g_print("*** unknown status.\n"); + exit(0); + } + g_print(" >>> "); +} + +gboolean timeout_menu_display(void *data) +{ + displaymenu(); + return FALSE; +} + +static void interpret(char *cmd) +{ + + switch (g_menu_state) { + case CURRENT_STATUS_MAINMENU: { + _interpret_main_menu(cmd); + break; + } + case CURRENT_STATUS_AUDIO_FILENAME: { + input_filepath(cmd); + strcpy(audio_data, cmd); + g_menu_state = CURRENT_STATUS_AUDIO_INFONAME; + break; + } + case CURRENT_STATUS_AUDIO_INFONAME: { + input_filepath(cmd); + strcpy(audio_extra_info, cmd); + test_mediamuxer_add_track_audio(); + g_menu_state = CURRENT_STATUS_MAINMENU; + + break; + } + case CURRENT_STATUS_VIDEO_FILENAME: { + input_filepath(cmd); + strcpy(video_data, cmd); + g_menu_state = CURRENT_STATUS_VIDEO_INFONAME; + break; + } + case CURRENT_STATUS_VIDEO_INFONAME: { + input_filepath(cmd); + strcpy(video_extra_info, cmd); + test_mediamuxer_add_track_video(); + g_menu_state = CURRENT_STATUS_MAINMENU; + break; + } + default: + break; + } + g_timeout_add(100, timeout_menu_display, 0); +} + +static void display_sub_basic() +{ + g_print("\n"); + g_print("==========================================================\n"); + g_print(" media muxer test\n"); + g_print("----------------------------------------------------------\n"); + g_print("c. Create \t"); + g_print("o. Set Data Sink \t"); + g_print("a. AddAudioTrack \t"); + g_print("v. AddVideoTrack \t"); + g_print("s. Start \t"); + g_print("m. StartMuxing \t"); + g_print("p. PauseMuxing \t"); + g_print("r. ResumeMuxing \t"); + g_print("b. set error callback \t"); + g_print("e. Stop (eos) \n"); + g_print("d. destroy \t"); + g_print("q. quit \t"); + g_print("\n"); + g_print("==========================================================\n"); +} + +/** + * This function is to execute command. + * + * @param channel [in] 1st parameter + * + * @return This function returns TRUE/FALSE + * @remark + * @see + */ +gboolean input(GIOChannel *channel) +{ + gchar buf[MAX_STRING_LEN]; + gsize read; + GError *error = NULL; + g_io_channel_read_chars(channel, buf, MAX_STRING_LEN, &read, &error); + buf[read] = '\0'; + g_strstrip(buf); + interpret(buf); + return TRUE; +} + +/** + * This function is the example main function for mediamuxer API. + * + * @param + * + * @return This function returns 0. + * @remark + * @see other functions + */ +int main(int argc, char *argv[]) +{ + GIOChannel *stdin_channel; + GMainLoop *loop = g_main_loop_new(NULL, 0); + stdin_channel = g_io_channel_unix_new(0); + g_io_channel_set_flags(stdin_channel, G_IO_FLAG_NONBLOCK, NULL); + g_io_add_watch(stdin_channel, G_IO_IN, (GIOFunc) input, NULL); + + displaymenu(); + /* g_print("RUN main loop\n"); */ + g_main_loop_run(loop); + g_print("STOP main loop\n"); + + g_main_loop_unref(loop); + return 0; +} -- 2.7.4