From: HyungKyu Song Date: Fri, 15 Feb 2013 15:15:22 +0000 (+0900) Subject: Tizen 2.0 Release X-Git-Tag: submit/tizen_2.0/20130215.191746^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fheads%2Ftizen_2.0;p=platform%2Fcore%2Fmultimedia%2Flibmm-player.git Tizen 2.0 Release --- diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..9c96f2e --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +JongHyuk Choi +YeJin Cho +YoungHwan An diff --git a/LICENSE.APLv2 b/LICENSE.APLv2 new file mode 100644 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/Makefile.am b/Makefile.am new file mode 100644 index 0000000..4234d3f --- /dev/null +++ b/Makefile.am @@ -0,0 +1,7 @@ +ACLOCAL_AMFLAGS='-I m4' +SUBDIRS = src + +pcfiles = mm-player.pc +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = $(pcfiles) +EXTRA_DIST = $(pcfiles) diff --git a/NOTICE b/NOTICE new file mode 100644 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/autogen.sh b/autogen.sh new file mode 100755 index 0000000..1bf3d9c --- /dev/null +++ b/autogen.sh @@ -0,0 +1,5 @@ +aclocal +libtoolize --copy -f +autoheader +autoconf +automake --add-missing --copy --foreign diff --git a/configure.ac b/configure.ac new file mode 100755 index 0000000..4070017 --- /dev/null +++ b/configure.ac @@ -0,0 +1,102 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.61) +AC_INIT([libmm-player],[0.0.1]) +AC_CONFIG_SRCDIR([src/mm_player_priv.c]) +AC_CONFIG_HEADER([config.h]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_LIBTOOL + +AC_FUNC_MMAP +AC_FUNC_REALLOC +AC_FUNC_SELECT_ARGTYPES +AC_FUNC_STAT +AC_FUNC_VPRINTF +AC_HEADER_STDBOOL +AC_HEADER_STDC +AC_HEADER_TIME +AC_PROG_GCC_TRADITIONAL + +# Checks for libraries. + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h stdlib.h string.h syslog.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_TYPE_INT64_T +AC_HEADER_TIME + +# Checks for library functions. +AC_FUNC_MALLOC +AC_FUNC_MEMCMP +AC_FUNC_STAT +AC_CHECK_FUNCS([gettimeofday memset strchr strstr]) + +PKG_CHECK_MODULES(MMTA, mm-ta) +AC_SUBST(MMTA_CFLAGS) +AC_SUBST(MMTA_LIBS) + +PKG_CHECK_MODULES(MMCOMMON,mm-common) +AC_SUBST(MMCOMMON_CFLAGS) +AC_SUBST(MMCOMMON_LIBS) + +PKG_CHECK_MODULES(MMSOUND,mm-sound) +AC_SUBST(MMSOUND_CFLAGS) +AC_SUBST(MMSOUND_LIBS) + +PKG_CHECK_MODULES(GLIB, glib-2.0) +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) + +PKG_CHECK_MODULES(GST, gstreamer-0.10 >= 0.10) +AC_SUBST(GST_CFLAGS) +AC_SUBST(GST_LIBS) + +PKG_CHECK_MODULES(GST_PLUGIN_BASE, gstreamer-plugins-base-0.10 >= 0.10) +AC_SUBST(GST_PLUGIN_BASE_CFLAGS) +AC_SUBST(GST_PLUGIN_BASE_LIBS) + +PKG_CHECK_MODULES(GST_INTERFACE, gstreamer-interfaces-0.10 >= 0.10) +AC_SUBST(GST_INTERFACE_CFLAGS) +AC_SUBST(GST_LIBS) + +PKG_CHECK_MODULES(GST_APP, gstreamer-app-0.10 >= 0.10) +AC_SUBST(GST_APP_CFLAGS) +AC_SUBST(GST_APP_LIBS) + +PKG_CHECK_MODULES(MMSESSION, mm-session) +AC_SUBST(MMSESSION_CFLAGS) +AC_SUBST(MMSESSION_LIBS) + +PKG_CHECK_MODULES(MMUTIL, mmutil-imgp) +AC_SUBST(MMUTIL_CFLAGS) +AC_SUBST(MMUTIL_LIBS) + +PKG_CHECK_MODULES(AUDIOSESSIONMGR, audio-session-mgr) +AC_SUBST(AUDIOSESSIONMGR_CFLAGS) +AC_SUBST(AUDIOSESSIONMGR_LIBS) + +PKG_CHECK_MODULES(ICU, icu-i18n) +AC_SUBST(ICU_CFLAGS) +AC_SUBST(ICU_LIBS) + +PKG_CHECK_MODULES(INIPARSER, iniparser) +AC_SUBST(INIPARSER_CFLAGS) +AC_SUBST(INIPARSER_LIBS) + +PKG_CHECK_MODULES(VCONF, vconf) +AC_SUBST(VCONF_CFLAGS) +AC_SUBST(VCONF_LIBS) + +AC_CONFIG_FILES([Makefile + src/Makefile + mm-player.pc +]) +AC_OUTPUT diff --git a/libmm-player.manifest b/libmm-player.manifest new file mode 100755 index 0000000..a76fdba --- /dev/null +++ b/libmm-player.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/mm-player.pc.in b/mm-player.pc.in new file mode 100644 index 0000000..6db989c --- /dev/null +++ b/mm-player.pc.in @@ -0,0 +1,11 @@ +prefix = @prefix@ +exec_prefix=@exec_prefix@ +libdir = @libdir@ +includedir = @includedir@ + +Name : mm-player +Description : Multimedia Framework Player Library +Requires : mm-common +Version : @VERSION@ +Libs : -L${libdir} -lmmfplayer +Cflags : -I${includedir}/mmf diff --git a/packaging/libmm-player.spec b/packaging/libmm-player.spec new file mode 100644 index 0000000..2a41c77 --- /dev/null +++ b/packaging/libmm-player.spec @@ -0,0 +1,77 @@ + +Name: libmm-player +Summary: Multimedia Framework Player Library +Version: 0.2.11 +Release: 1 +Group: System/Libraries +License: Apache License, Version 2.0 +URL: http://source.tizen.org +Source0: %{name}-%{version}.tar.gz +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig +BuildRequires: pkgconfig(mm-ta) +BuildRequires: pkgconfig(mm-common) +BuildRequires: pkgconfig(mm-sound) +BuildRequires: pkgconfig(gstreamer-0.10) +BuildRequires: pkgconfig(gstreamer-plugins-base-0.10) +BuildRequires: pkgconfig(gstreamer-interfaces-0.10) +BuildRequires: pkgconfig(gstreamer-app-0.10) +BuildRequires: pkgconfig(mm-session) +BuildRequires: pkgconfig(mmutil-imgp) +BuildRequires: pkgconfig(audio-session-mgr) +BuildRequires: pkgconfig(iniparser) +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(icu-i18n) + +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description + +%package devel +Summary: Multimedia Framework Player Library (DEV) +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel + +%prep +%setup -q + +%build + +./autogen.sh + +CFLAGS+=" -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" "; export CFLAGS +LDFLAGS+="-Wl,--rpath=%{_prefix}/lib -Wl,--hash-style=both -Wl,--as-needed"; export LDFLAGS + +CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS ./configure --prefix=%{_prefix} --disable-static + +# Call make instruction with smp support +make -j1 + +%install +rm -rf %{buildroot} +%make_install + +%clean +rm -rf %{buildroot} + + + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + + +%files +%manifest libmm-player.manifest +%defattr(-,root,root,-) +%{_libdir}/*.so.* + + +%files devel +%defattr(-,root,root,-) +%{_libdir}/*.so +%{_includedir}/mmf/*.h +%{_libdir}/pkgconfig/* + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..8e1523e --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,56 @@ +lib_LTLIBRARIES = libmmfplayer.la + +includelibmmfplayerdir = $(includedir)/mmf + +includelibmmfplayer_HEADERS = include/mm_player.h \ + include/mm_player_internal.h \ + include/mm_player_audioeffect.h + +libmmfplayer_la_SOURCES = mm_player.c \ + mm_player_priv.c \ + mm_player_ini.c \ + mm_player_utils.c \ + mm_player_asm.c \ + mm_player_attrs.c \ + mm_player_capture.c \ + mm_player_pd.c \ + mm_player_streaming.c \ + mm_player_audioeffect.c + +libmmfplayer_la_CFLAGS = -I$(srcdir)/include \ + $(MMCOMMON_CFLAGS) \ + $(MMTA_CFLAGS) \ + $(MMUTIL_CFLAGS) \ + $(GST_CFLAGS) \ + $(GST_INTERFACE_CFLAGS) \ + $(GST_APP_CFLAGS) \ + $(MMSESSION_CFLAGS) \ + $(MMSOUND_CFLAGS) \ + $(AUDIOSESSIONMGR_CFLAGS) \ + $(VCONF_CFLAGS) \ + $(ICU_CFLAGS) + +noinst_HEADERS = include/mm_player_utils.h \ + include/mm_player_ini.h \ + include/mm_player_priv.h \ + include/mm_player_asm.h \ + include/mm_player_attrs.h \ + include/mm_player_capture.h \ + include/mm_player_pd.h \ + include/mm_player_streaming.h + +libmmfplayer_la_LIBADD = $(GST_LIBS) \ + $(MMCOMMON_LIBS) \ + $(MMTA_LIBS) \ + $(MMUTIL_LIBS) \ + $(GST_INTERFACE_LIBS) \ + $(GST_APP_LIBS) \ + $(INIPARSER_LIBS) \ + $(MMSESSION_LIBS) \ + $(MMSOUND_LIBS) \ + $(AUDIOSESSIONMGR_LIBS) \ + $(VCONF_LIBS) \ + $(ICU_LIBS) + +libmmfplayer_la_CFLAGS += $(MMLOG_CFLAGS) -DMMF_LOG_OWNER=0x008 -DMMF_DEBUG_PREFIX=\"MMF-PLAYER\" -D_INTERNAL_SESSION_MANAGER_ +libmmfplayer_la_LIBADD += $(MMLOG_LIBS) diff --git a/src/include/mm_player.h b/src/include/mm_player.h new file mode 100644 index 0000000..4200347 --- /dev/null +++ b/src/include/mm_player.h @@ -0,0 +1,1556 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_H__ +#define __MM_PLAYER_H__ + + +/*=========================================================================================== +| | +| INCLUDE FILES | +| | +========================================================================================== */ + +#include + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + @addtogroup PLAYER + @{ + + @par + This part describes APIs used for playback of multimedia contents. + All multimedia contents are created by a media player through handle of playback. + In creating a player, it displays the player'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 mm_player_set_attribute() before create player. + + @par + The subtitle for local video playback is supported. Set "subtitle_uri" attribute + using mm_player_set_attribute() before the application creates the player. + Then the application could receive MMMessageParamType which includes subtitle string and duration. + + @par + Player can have 5 states, and each state can be changed by calling + described functions on "Figure1. State of Player". + + @par + @image html player_state.jpg "Figure1. State of Player" width=12cm + @image latex player_state.jpg "Figure1. State of Player" width=12cm + + @par + Most of functions which change player state work as synchronous. But, mm_player_start() should be used + asynchronously. Both mm_player_pause() and mm_player_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 mm_player_destroy() and mm_player_unrealize(). + + @par +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FUNCTIONPRE-STATEPOST-STATESYNC TYPE
mm_player_create()NONENULLSYNC
mm_player_destroy()NULLNONESYNC
mm_player_realize()NULLREADYSYNC
mm_player_unrealize()READYNULLSYNC
mm_player_start()READYPLAYINGASYNC
mm_player_stop()PLAYINGREADYSYNC
mm_player_pause()PLAYINGPAUSEDASYNC
mm_player_resume()PAUSEDPLAYINGASYNC
mm_player_set_message_callback()N/AN/ASYNC
mm_player_get_state()N/AN/ASYNC
mm_player_set_volume()N/AN/ASYNC
mm_player_get_volume()N/AN/ASYNC
mm_player_set_position()N/AN/ASYNC
mm_player_get_position()N/AN/ASYNC
mm_player_get_attribute()N/AN/ASYNC
mm_player_set_attribute()N/AN/ASYNC
+ + @par + Following are the attributes supported in player which may be set after initialization. \n + Those are handled as a string. + + @par +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PROPERTYTYPEVALID TYPE
"profile_uri"stringN/A
"content_duration"intrange
"content_video_width"intrange
"content_video_height"intrange
"display_evas_do_scaling"intrange
"display_evas_surface_sink"stringN/A
"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
"display_overlay"dataN/A
"display_rotation"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.\n + 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 | +| | +========================================================================================== */ + +/** + * MM_PLAYER_URI: + * + * uri to play (string) + * + */ +#define MM_PLAYER_CONTENT_URI "profile_uri" +/** + * MM_PLAYER_CONTENT_DURATION: + * + * get the duration (int) as millisecond, It's guaranteed after calling mm_player_start() or + * receiving MM_MESSAGE_BEGIN_OF_STREAM. + * + */ +#define MM_PLAYER_CONTENT_DURATION "content_duration" +/** + * MM_PLAYER_VIDEO_ROTATION + * + * can change video angle (int) + * @see MMDisplayRotationType + */ +#define MM_PLAYER_VIDEO_ROTATION "display_rotation" +/** + * MM_PLAYER_VIDEO_WIDTH: + * + * get the video width (int), It's guaranteed after calling mm_player_start() or + * receiving MM_MESSAGE_BEGIN_OF_STREAM. + * + */ +#define MM_PLAYER_VIDEO_WIDTH "content_video_width" +/** + * MM_PLAYER_VIDEO_HEIGHT: + * + * get the video height (int), It's guaranteed after calling mm_player_start() or + * receiving MM_MESSAGE_BEGIN_OF_STREAM. + * + */ +#define MM_PLAYER_VIDEO_HEIGHT "content_video_height" +/** + * MM_PLAYER_VIDEO_EVAS_SURFACE_DO_SCALING: + * + * set whether or not to scale frames size for evas surface. + * if TRUE, it scales down width, height size of frames with given size. + * if FALSE, it does not scale down any frames. + * + */ +#define MM_PLAYER_VIDEO_EVAS_SURFACE_DO_SCALING "display_evas_do_scaling" +/** + * MM_PLAYER_VIDEO_EVAS_SURFACE_SINK: + * + * get the video evas surface sink plugin name (string), It's guaranteed after calling mm_player_create() + * + */ +#define MM_PLAYER_VIDEO_EVAS_SURFACE_SINK "display_evas_surface_sink" +/** + * MM_PLAYER_MEM_SRC: + * + * set memory pointer to play (data) + * + */ +#define MM_PLAYER_MEMORY_SRC "profile_user_param" +/** + * MM_PLAYER_PLAYBACK_COUNT + * + * can set playback count (int), Default value is 1 and -1 is for infinity playing until releasing it. + * + */ +#define MM_PLAYER_PLAYBACK_COUNT "profile_play_count" +/** + * MM_PLAYER_SUBTITLE_URI + * + * set the subtitle path (string) + */ +#define MM_PLAYER_SUBTITLE_URI "subtitle_uri" +/** + * MM_PLAYER_STREAMING_TYPE + * + * set the streaming type (int) + * @see MMStreamingType + */ +#define MM_PLAYER_STREAMING_TYPE "streaming_type" +/** + * MM_PLAYER_STREAMING_UDP_TIMEOUT + * + * set the streaming udp timeout(int) + */ +#define MM_PLAYER_STREAMING_UDP_TIMEOUT "streaming_udp_timeout" +/** + * MM_PLAYER_STREAMING_USER_AGENT + * + * set the streaming user agent (string) + */ +#define MM_PLAYER_STREAMING_USER_AGENT "streaming_user_agent" +/** + * MM_PLAYER_STREAMING_WAP_PROFILE + * + * set the streaming wap profile (int) + */ +#define MM_PLAYER_STREAMING_WAP_PROFILE "streaming_wap_profile" +/** + * MM_PLAYER_STREAMING_NET_BANDWIDTH + * + * set the streaming network bandwidth (int) + */ +#define MM_PLAYER_STREAMING_NET_BANDWIDTH "streaming_network_bandwidth" +/** + * MM_PLAYER_STREAMING_COOKIE + * + * set the streaming cookie (int) + */ +#define MM_PLAYER_STREAMING_COOKIE "streaming_cookie" +/** + * MM_PLAYER_STREAMING_PROXY_IP + * + * set the streaming proxy ip (string) + */ +#define MM_PLAYER_STREAMING_PROXY_IP "streaming_proxy_ip" +/** + * MM_PLAYER_STREAMING_PROXY_PORT + * + * set the streaming proxy port (int) + */ +#define MM_PLAYER_STREAMING_PROXY_PORT "streaming_proxy_port" +/** + * MM_PLAYER_VIDEO_CODEC + * + * codec the video data is stored in (string) + */ +#define MM_PLAYER_VIDEO_CODEC "content_video_codec" +/** + * MM_PLAYER_VIDEO_TRACK_NUM + * + * track number inside a collection (int) + */ +#define MM_PLAYER_VIDEO_TRACK_NUM "content_video_track_num" +/** + * MM_PLAYER_AUDIO_CODEC + * + * codec the audio data is stored in (string) + */ +#define MM_PLAYER_AUDIO_CODEC "content_audio_codec" +/** + * MM_PLAYER_AUDIO_BITRATE + * + * set the streaming proxy port (int) + */ +#define MM_PLAYER_AUDIO_BITRATE "content_audio_bitrate" +/** + * MM_PLAYER_AUDIO_CHANNEL + * + * the number of audio channel (int) + */ +#define MM_PLAYER_AUDIO_CHANNEL "content_audio_channels" +/** + * MM_PLAYER_AUDIO_SAMPLERATE + * + * audio samplerate (int) + */ +#define MM_PLAYER_AUDIO_SAMPLERATE "content_audio_samplerate" +/** + * MM_PLAYER_AUDIO_TRACK_NUM + * + * track number inside a collection (int) + */ +#define MM_PLAYER_AUDIO_TRACK_NUM "content_audio_track_num" +/** + * MM_PLAYER_TEXT_TRACK_NUM + * + * track number inside a collection (int) + */ +#define MM_PLAYER_TEXT_TRACK_NUM "content_text_track_num" +/** + * MM_PLAYER_TAG_ARTIST + * + * person(s) responsible for the recording (string) + */ +#define MM_PLAYER_TAG_ARTIST "tag_artist" +/** + * MM_PLAYER_TAG_ARTIST + * + * title (string) + */ +#define MM_PLAYER_TAG_TITLE "tag_title" +/** + * MM_PLAYER_TAG_ARTIST + * + * album containing this data (string) + */ +#define MM_PLAYER_TAG_ALBUM "tag_album" +/** + * MM_PLAYER_TAG_ARTIST + * + * genre this data belongs to (string) + */ +#define MM_PLAYER_TAG_GENRE "tag_genre" +/** + * MM_PLAYER_TAG_ARTIST + * + * author (string) + */ +#define MM_PLAYER_TAG_AUTHOUR "tag_author" +/** + * MM_PLAYER_TAG_ARTIST + * + * copyright notice of the data (string) + */ +#define MM_PLAYER_TAG_COPYRIGHT "tag_copyright" +/** + * MM_PLAYER_TAG_ARTIST + * + * date the data was created (string) + */ +#define MM_PLAYER_TAG_DATE "tag_date" +/** + * MM_PLAYER_TAG_ARTIST + * + * short text describing the content of the data (string) + */ +#define MM_PLAYER_TAG_DESCRIPRION "tag_description" +/** + * MM_PLAYER_TAG_ARTIST + * + * track number inside a collection (int) + */ +#define MM_PLAYER_TAG_TRACK_NUM "tag_track_num" +/** + * MM_PLAYER_PD_MODE + * + * progressive download mode (int) + */ +#define MM_PLAYER_PD_MODE "pd_mode" + +/** + * Enumerations of player state. + */ +typedef enum { + MM_PLAYER_STATE_NULL, /**< Player is created, but not realized yet */ + MM_PLAYER_STATE_READY, /**< Player is ready to play media */ + MM_PLAYER_STATE_PLAYING, /**< Player is now playing media */ + MM_PLAYER_STATE_PAUSED, /**< Player is paused while playing media */ + MM_PLAYER_STATE_NONE, /**< Player is not created yet */ + MM_PLAYER_STATE_NUM, /**< Number of player states */ +} MMPlayerStateType; + +/** + * Enumerations of position formats. + * Used while invoking mm_player_get_position/mm_player_set_position APIs + */ +typedef enum { + MM_PLAYER_POS_FORMAT_TIME, /**< Format for time based */ + MM_PLAYER_POS_FORMAT_PERCENT, /**< Format for percentage */ + MM_PLAYER_POS_FORMAT_NUM, /**< Number of position formats */ +} MMPlayerPosFormatType; + +/** + * Enumeration for attribute values types. + */ +typedef enum { + MM_PLAYER_ATTRS_TYPE_INVALID = -1, /**< Type is invalid */ + MM_PLAYER_ATTRS_TYPE_INT, /**< Integer type */ + MM_PLAYER_ATTRS_TYPE_DOUBLE, /**< Double type */ + MM_PLAYER_ATTRS_TYPE_STRING, /**< UTF-8 String type */ + MM_PLAYER_ATTRS_TYPE_DATA, /**< Pointer type */ + MM_PLAYER_ATTRS_TYPE_ARRAY, /**< Array type */ + MM_PLAYER_ATTRS_TYPE_RANGE, /**< Range type */ + MM_PLAYER_ATTRS_TYPE_NUM, /**< Number of attribute type */ +} MMPlayerAttrsType; + +/** + * Enumeration for attribute validation type. + */ +typedef enum { + MM_PLAYER_ATTRS_VALID_TYPE_INVALID = -1, /**< Invalid validation type */ + MM_PLAYER_ATTRS_VALID_TYPE_NONE, /**< Do not check validity */ + MM_PLAYER_ATTRS_VALID_TYPE_INT_ARRAY, /**< validity checking type of integer array */ + MM_PLAYER_ATTRS_VALID_TYPE_INT_RANGE, /**< validity checking type of integer range */ + MM_PLAYER_ATTRS_VALID_TYPE_DOUBLE_ARRAY, /**< validity checking type of double array */ + MM_PLAYER_ATTRS_VALID_TYPE_DOUBLE_RANGE, /**< validity checking type of double range */ +} MMPlayerAttrsValidType; + +/** + * Enumeration for attribute access flag. + */ +typedef enum { + MM_PLAYER_ATTRS_FLAG_NONE = 0, /**< None flag is set */ + MM_PLAYER_ATTRS_FLAG_READABLE = 1 << 0, /**< Readable */ + MM_PLAYER_ATTRS_FLAG_WRITABLE = 1 << 1, /**< Writable */ + MM_PLAYER_ATTRS_FLAG_MODIFIED = 1 << 2, /**< Modified */ + + MM_PLAYER_ATTRS_FLAG_RW = MM_PLAYER_ATTRS_FLAG_READABLE | MM_PLAYER_ATTRS_FLAG_WRITABLE, /**< Readable and Writable */ +} MMPlayerAttrsFlag; + +/** + * Enumeration for progressive download + */ +typedef enum { + MM_PLAYER_PD_MODE_NONE, + MM_PLAYER_PD_MODE_URI, + MM_PLAYER_PD_MODE_FILE // not tested yet, because of no fixed scenario +}MMPlayerPDMode; + +/** + * Enumeration of track types + */ +typedef enum { + MM_PLAYER_TRACK_TYPE_AUDIO, + MM_PLAYER_TRACK_TYPE_VIDEO, + MM_PLAYER_TRACK_TYPE_TEXT +}MMPlayerTrackType; + +/** + * Attribute validity structure + */ +typedef struct { + MMPlayerAttrsType type; + MMPlayerAttrsValidType validity_type; + MMPlayerAttrsFlag flag; + /** + * a union that describes validity of the attribute. + * Only when type is 'MM_ATTRS_TYPE_INT' or 'MM_ATTRS_TYPE_DOUBLE', + * the attribute can have validity. + */ + union { + /** + * Validity structure for integer array. + */ + struct { + int *array; /**< a pointer of array */ + int count; /**< size of array */ + int d_val; + } int_array; + /** + * Validity structure for integer range. + */ + struct { + int min; /**< minimum range */ + int max; /**< maximum range */ + int d_val; + } int_range; + /** + * Validity structure for double array. + */ + struct { + double * array; /**< a pointer of array */ + int count; /**< size of array */ + double d_val; + } double_array; + /** + * Validity structure for double range. + */ + struct { + double min; /**< minimum range */ + double max; /**< maximum range */ + double d_val; + } double_range; + }; +} MMPlayerAttrsInfo; + +/** + * Volume type. + * + * @see mm_player_set_volume, mm_player_get_volume + */ +typedef struct { + float level[MM_VOLUME_CHANNEL_NUM]; /**< Relative volume factor for each channels */ +} MMPlayerVolumeType; + +/** + * Audio stream callback function type. + * + * @param stream [in] Reference pointer to audio frame data + * @param stream_size [in] Size of audio frame data + * @param user_param [in] User defined parameter which is passed when set + * audio stream callback + * + * @return This callback function have to return MM_ERROR_NONE. + */ +typedef bool (*mm_player_audio_stream_callback) (void *stream, int stream_size, void *user_param); + + +/*=========================================================================================== +| | +| GLOBAL FUNCTION PROTOTYPES | +| | +========================================================================================== */ + +/** + * This function creates a player object for playing multimedia contents. \n + * The attributes of player are created to get/set some values with application. \n + * And, mutex, gstreamer and other resources are initialized at this time. \n + * If player is created, the state will become MM_PLAYER_STATE_NULL. + * + * @param player [out] Handle of player + * + * @return This function returns zero on success, or negative value with error code. \n + * Please refer 'mm_error.h' to know it in detail. + * @pre None + * @post MM_PLAYER_STATE_NULL + * @see mm_player_destroy + * @remark You can create multiple handles on a context at the same time. \n + * However, player cannot guarantee proper operation because of limitation of resources, \n + * such as audio device or display device. + * + * @par Example + * @code +char *g_err_attr_name = NULL; + +if (mm_player_create(&g_player) != MM_ERROR_NONE) +{ + printf("failed to create player\n"); +} + +if (mm_player_set_attribute(g_player, + &g_err_attr_name, + "profile_uri", filename, strlen(filename), + "display_overlay", (void*)&g_win.xid, sizeof(g_win.xid), + NULL) != MM_ERROR_NONE) +{ + printf("failed to set %s attribute\n", g_err_attr_name); + free(g_err_attr_name); +} + +mm_player_set_message_callback(g_player, msg_callback, (void*)g_player); + * @endcode + */ +int mm_player_create(MMHandleType *player); + +/** + * This function releases player object and all resources which were created by mm_player_create(). \n + * And, player handle will also be destroyed. + * + * @param player [in] Handle of player + * + * @return This function returns zero on success, or negative value with error code. + * @pre Player state may be MM_PLAYER_STATE_NULL. \n + * But, it can be called in any state. + * @post Because handle is released, there is no any state. + * @see mm_player_create + * @remark This method can be called with a valid player handle from any state to \n + * completely shutdown the player operation. + * + * @par Example + * @code +if (mm_player_destroy(g_player) != MM_ERROR_NONE) +{ + printf("failed to destroy player\n"); +} + * @endcode + */ +int mm_player_destroy(MMHandleType player); + +/** + * This function parses uri and makes gstreamer pipeline by uri scheme. \n + * So, uri should be set before realizing with mm_player_set_attribute(). \n + * + * @param player [in] Handle of player + * + * @return This function returns zero on success, or negative value with error code. + * + * @pre Player state should be MM_PLAYER_STATE_NULL. + * @post Player state will be MM_PLAYER_STATE_READY. + * @see mm_player_unrealize + * @remark None + * @par Example + * @code +if (mm_player_realize(g_player) != MM_ERROR_NONE) +{ + printf("failed to realize player\n"); +} + * @endcode + */ +int mm_player_realize(MMHandleType player) ; + +/** + * This function uninitializes player object. So, resources and allocated memory \n + * will be freed. And, gstreamer pipeline is also destroyed. So, if you want to play \n + * other contents, player should be created again after destruction or realized with new uri. + * + * @param player [in] Handle of player + * + * @return This function returns zero on success, or negative value with error code. + * @pre Player state may be MM_PLAYER_STATE_READY to unrealize. \n + * But, it can be called in any state. + * @post Player state will be MM_PLAYER_STATE_NULL. + * @see mm_player_realize + * @remark This method can be called with a valid player handle from any state. + * + * @par Example + * @code +if (mm_player_unrealize(g_player) != MM_ERROR_NONE) +{ + printf("failed to unrealize player\n"); +} + * @endcode + */ +int mm_player_unrealize(MMHandleType player); + +/** + * This function is to get current state of player. \n + * Application have to check current state before doing some action. + * + * @param player [in] Handle of player + * @param state [out] current state of player on success + * + * @return This function returns zero on success, or negative value with error code. + * + * @see MMPlayerStateType + * @remark None + * @par Example + * @code +if (mm_player_get_state(g_player, &state) != MM_ERROR_NONE) +{ + printf("failed to get state\n"); +} + * @endcode + */ +int mm_player_get_state(MMHandleType player, MMPlayerStateType *state); + +/** + * This function is to set relative volume of player. \n + * So, It controls logical volume value. \n + * But, if developer want to change system volume, mm sound api should be used. + * + * @param player [in] Handle of player + * @param volume [in] Volume factor of each channel + * + * @return This function returns zero on success, or negative value with error code. + * @see MMPlayerVolumeType, mm_player_get_volume + * @remark The range of factor range is from 0 to 1.0. (1.0 = 100%) And, default value is 1.0. + * @par Example + * @code +MMPlayerVolumeType volume; +int i = 0; + +for (i = 0; i < MM_VOLUME_CHANNEL_NUM; i++) + volume.level[i] = MM_VOLUME_LEVEL_MAX; + +if (mm_player_set_volume(g_player, &volume) != MM_ERROR_NONE) +{ + printf("failed to set volume\n"); +} + * @endcode + */ +int mm_player_set_volume(MMHandleType player, MMPlayerVolumeType *volume); + +/** + * This function is to get current volume factor of player. + * + * @param player [in] Handle of player. + * @param volume [out] Volume factor of each channel. + * + * @return This function returns zero on success, or negative value with error code. + * + * @see MMPlayerVolumeType, mm_player_set_volume + * @remark None + * @par Example + * @code +MMPlayerVolumeType volume; +int i; + +if (mm_player_get_volume(g_player, &volume) != MM_ERROR_NONE) +{ + printf("failed to get volume\n"); +} + +for (i = 0; i < MM_VOLUME_CHANNEL_NUM; i++) + printf("channel[%d] = %d \n", i, volume.level[i]); + * @endcode + */ +int mm_player_get_volume(MMHandleType player, MMPlayerVolumeType *volume); + +/** + * This function is to start playing media contents. Demux(parser), codec and related plugins are decided \n + * at this time. And, MM_MESSAGE_BEGIN_OF_STREAM will be posted through callback function registered \n + * by mm_player_set_message_callback(). + * + * @param player [in] Handle of player + * + * @return This function returns zero on success, or negative value with error code. + * @remark + * + * @pre Player state may be MM_PLAYER_STATE_READY. + * @post Player state will be MM_PLAYER_STATE_PLAYING. + * @see mm_player_stop + * @remark None + * @par Example + * @code +if (mm_player_start(g_player) != MM_ERROR_NONE) +{ + printf("failed to start player\n"); +} + * @endcode + */ +int mm_player_start(MMHandleType player); + +/** + * This function is to stop playing media contents and it's different with pause. \n + * If mm_player_start() is called after this, content will be started again from the beginning. \n + * So, it can be used to close current playback. + * + * @param player [in] Handle of player + * + * @return This function returns zero on success, or negative value with error code. + * + * @pre Player state may be MM_PLAYER_STATE_PLAYING. + * @post Player state will be MM_PLAYER_STATE_READY. + * @see mm_player_start + * @remark None + * @par Example + * @code +if (mm_player_stop(g_player) != MM_ERROR_NONE) +{ + printf("failed to stop player\n"); +} + * @endcode + */ +int mm_player_stop(MMHandleType player); + +/** + * This function is to pause playing media contents. + * + * @param player [in] Handle of player. + * + * @return This function returns zero on success, or negative value with error code. + * + * @pre Player state may be MM_PLAYER_STATE_PLAYING. + * @post Player state will be MM_PLAYER_STATE_PAUSED. + * @see mm_player_resume + * @remark None + * @par Example + * @code +if (mm_player_pause(g_player) != MM_ERROR_NONE) +{ + printf("failed to pause player\n"); +} + * @endcode + */ +int mm_player_pause(MMHandleType player); + +/** + * This function is to resume paused media contents. + * + * @param player [in] Handle of player. + * + * @return This function returns zero on success, or negative value with error code. + * + * @pre Player state may be MM_PLAYER_STATE_PAUSED. + * @post Player state will be MM_PLAYER_STATE_PLAYING. + * @see mm_player_pause + * @remark None + * @par Example + * @code +if (mm_player_resume(g_player) != MM_ERROR_NONE) +{ + printf("failed to resume player\n"); +} + * @endcode + */ +int mm_player_resume(MMHandleType player); + +/** + * This function is to set the position for playback. \n + * So, it can be seeked to requested position. \n + * + * @param player [in] Handle of player + * @param format [in] Format of position. + * @param pos [in] Position for playback + * + * @return This function returns zero on success, or negative value with error code. + * @see MMPlayerPosFormatType, mm_player_get_position + * @remark the unit of time-based format is millisecond and other case is percent. + * @par Example + * @code +int position = 1000; //1sec + +if (mm_player_set_position(g_player, MM_PLAYER_POS_FORMAT_TIME, position) != MM_ERROR_NONE) +{ + g_print("failed to set position\n"); +} + * @endcode + */ +int mm_player_set_position(MMHandleType player, MMPlayerPosFormatType format, int pos); + +/** + * This function is to get current position of playback content. + * + * @param player [in] Handle of player. + * @param format [in] Format of position. + * @param pos [out] contains current position on success or zero in case of failure. + * + * @return This function returns zero on success, or negative value with errors + * @see MMPlayerPosFormatType, mm_player_set_position + * @remark the unit of time-based format is millisecond and other case is percent. + * @par Example + * @code +int position = 0; +int duration = 0; + +mm_player_get_position(g_player, MM_PLAYER_POS_FORMAT_TIME, &position); + +mm_player_get_attribute(g_player, &g_err_name, "content_duration", &duration, NULL); + +printf("pos: [%d/%d] msec\n", position, duration); + * @endcode + */ +int mm_player_get_position(MMHandleType player, MMPlayerPosFormatType format, int *pos); + +/** + * This function is to get current buffer position of playback content. + * + * @param player [in] Handle of player. + * @param format [in] Format of position. + * @param start_pos [out] contains buffer start position on success or zero in case of failure. + * @param stop_pos [out] contains buffer current position on success or zero in case of failure. + * + * @return This function returns zero on success, or negative value with errors + * @see MMPlayerPosFormatType, mm_player_set_position + * @remark the unit of time-based format is millisecond and other case is percent. + * @par Example + * @code +int start_pos = 0, stop_pos = 0; + +mm_player_get_buffer_position(g_player, MM_PLAYER_POS_FORMAT_PERCENT, &start_pos, &stop_pos ); + +printf("buffer position: [%d] ~ [%d] \%\n", start_pos, stop_pos ); + * @endcode + */ +int mm_player_get_buffer_position(MMHandleType player, MMPlayerPosFormatType format, int *start_pos, int *stop_pos); + +/** + * This function is to activate the section repeat. If it's set, selected section will be played \n + * continually before deactivating it by mm_player_deactivate_section_repeat(). \n + * The unit for setting is millisecond. + * + * @param player [in] Handle of player. + * @param start_pos [in] start position. + * @param end_pos [in] end position. + * + * @return This function returns zero on success, or negative value with error code. + * @see mm_player_deactivate_section_repeat + * @remark None + * @par Example + * @code +int position; +int endtime = 4000; //msec + +mm_player_get_position(g_player, MM_PLAYER_POS_FORMAT_TIME, &position); + +mm_player_activate_section_repeat(g_player, position, position+endtime); + * @endcode + */ +int mm_player_activate_section_repeat(MMHandleType player, int start_pos, int end_pos); + +/** + * This function is to deactivate the section repeat. + * + * @param player [in] Handle of player. + * + * @return This function returns zero on success, or negative value with error code. + * @see mm_player_activate_section_repeat + * @remark None + * @par Example + * @code +if ( mm_player_deactivate_section_repeat(g_player) != MM_ERROR_NONE) +{ + printf("failed to deactivate section repeat\n"); +} + * @endcode + */ +int mm_player_deactivate_section_repeat(MMHandleType player); + +/** + * This function sets callback function for receiving messages from player. + * So, player can notify warning, error and normal cases to application. + * + * @param player [in] Handle of player. + * @param callback [in] Message callback function. + * @param user_param [in] User parameter which is passed to callback function. + * + * @return This function returns zero on success, or negative value with error code. + * @see MMMessageCallback + * @remark None + * @par Example + * @code +int msg_callback(int message, MMMessageParamType *param, void *user_param) +{ + switch (message) + { + case MM_MESSAGE_ERROR: + //do something + break; + + case MM_MESSAGE_END_OF_STREAM: + //do something + break; + + case MM_MESSAGE_STATE_CHANGED: + //do something + break; + + case MM_MESSAGE_BEGIN_OF_STREAM: + //do something + break; + + default: + break; + } + return TRUE; +} + +mm_player_set_message_callback(g_player, msg_callback, (void*)g_player); + * @endcode + */ +int mm_player_set_message_callback(MMHandleType player, MMMessageCallback callback, void *user_param); + +/** + * This function set callback function for receiving audio stream from player. \n + * So, application can get raw audio data and modify it. \n + * But, if callback don't return or holds it for long time, performance can be deteriorated. \n + * It's only supported when audio stream is included in file. \n + * So, if there is video stream or DRM content, it can't be used. + * + * @param player [in] Handle of player. + * @param callback [in] Audio stream callback function. + * @param user_param [in] User parameter. + * + * @return This function returns zero on success, or negative value with error + * code. + * @see mm_player_audio_stream_callback + * @remark It can be used for audio playback only. + * @par Example + * @code +bool audio_callback(void *stream, int stream_size, void *user_param) +{ + printf("audio stream callback\n"); + return TRUE; +} +mm_player_set_audio_stream_callback(g_player, audio_callback, NULL); + * @endcode + */ + int mm_player_set_audio_stream_callback(MMHandleType player, mm_player_audio_stream_callback callback, void *user_param); + +/** + * This function is to mute volume of player + * + * @param player [in] Handle of player + * @param mute [in] Mute(1) or not mute(0) + * + * @return This function returns zero on success, or negative value with error code + * @see mm_player_get_mute + * @remark None + * @par Example + * @code +if (mm_player_set_mute(g_player, TRUE) != MM_ERROR_NONE) +{ + printf("failed to set mute\n"); +} + * @endcode + */ +int mm_player_set_mute(MMHandleType player, int mute); + +/** + * This function is to get mute value of player + * + * @param player [in] Handle of player + * @param mute [out] Sound is muted + * + * @return This function returns zero on success, or negative value with error code + * @see mm_player_set_mute + * @remark None + * @par Example + * @code +int mute; + +if (mm_player_get_mute(g_player, &mute) != MM_ERROR_NONE) +{ + printf("failed to get mute\n"); +} + +printf("mute status:%d\n", mute); + * @endcode + */ +int mm_player_get_mute(MMHandleType player, int *mute); + +/** + * This function is to adjust subtitle postion. So, subtitle can show at the adjusted position. \n + * If pos is negative, subtitle will be displayed previous time, the other hand forward time. \n + * + * @param player [in] Handle of player + * @param pos [in] postion to be adjusted + * + * @return This function returns zero on success, or negative value with error + * code + * @see mm_player_adjust_subtitle_position + * @remark None + * @par Example + * @code +int pos; + +pos = 5000; +if (mm_player_adjust_subtitle_position(g_player, MM_PLAYER_POS_FORMAT_TIME, pos) != MM_ERROR_NONE) +{ + printf("failed to adjust subtitle postion.\n"); +} + * @endcode + */ + +int mm_player_adjust_subtitle_position(MMHandleType player, MMPlayerPosFormatType format, int pos); + +/** + * This function is to set subtitle silent status. So, subtitle can show or hide during playback \n + * by this value. But, one subtitle file should be set with "subtitle_uri" attribute before calling mm_player_realize(); \n + * Player FW parses subtitle file and send text data including timestamp to application \n + * through message callback with MM_MESSAGE_UPDATE_SUBTITLE will be. \n + * So, application have to render it. And, subtitle can be supported only in a seprate file. \n + * So, it's not supported for embedded case. + * + * @param player [in] Handle of player + * @param silent [in] silent(integer value except 0) or not silent(0) + * + * @return This function returns zero on success, or negative value with error + * code + * @see mm_player_get_subtitle_silent, MM_MESSAGE_UPDATE_SUBTITLE + * @remark None + * @par Example + * @code +mm_player_set_attribute(g_player, + &g_err_name, + "subtitle_uri", g_subtitle_uri, strlen(g_subtitle_uri), + NULL + ); + +if (mm_player_set_subtitle_silent(g_player, TRUE) != MM_ERROR_NONE) +{ + printf("failed to set subtitle silent\n"); +} + * @endcode + */ +int mm_player_set_subtitle_silent(MMHandleType player, int silent); + +/** + * This function is to get silent status of subtitle. + * + * @param player [in] Handle of player + * @param silent [out] subtitle silent property + * + * @return This function returns zero on success, or negative value with error + * code + * @see mm_player_set_subtitle_silent, MM_MESSAGE_UPDATE_SUBTITLE + * @remark None + * @par Example + * @code +int silent = FALSE; + +if (mm_player_get_subtitle_silent(g_player, &silent) != MM_ERROR_NONE) +{ + printf("failed to set subtitle silent\n"); +} + * @endcode + */ +int mm_player_get_subtitle_silent(MMHandleType player, int *silent); + +/** + * This function is to set attributes into player. Multiple attributes can be set simultaneously. \n + * If one of attribute fails, this function will stop at the point and let you know the name which is failed. \n + * + * @param player [in] Handle of player. + * @param err_attr_name [out] Name of attribute which is failed to set + * @param first_attribute_name [in] Name of the first attribute to set + * @param ... [in] Value for the first attribute, followed optionally by more name/value pairs, terminated by NULL. + * But, in the case of data or string type, it should be name/value/size. + * + * @return This function returns zero on success, or negative value with error code. + * + * @see mm_player_get_attribute + * @remark This function must be terminated by NULL argument. + * And, if this function is failed, err_attr_name param must be free. + * @par Example + * @code +char *g_err_attr_name = NULL; + +if (mm_player_set_attribute(g_player, + &g_err_attr_name, + "profile_uri", filename, strlen(filename), + "profile_play_count", count, + NULL) != MM_ERROR_NONE) +{ + printf("failed to set %s attribute\n", g_err_attr_name); + free(g_err_attr_name); +} + + * @endcode + */ +int mm_player_set_attribute(MMHandleType player, char **err_attr_name, const char *first_attribute_name, ...)G_GNUC_NULL_TERMINATED; + +/** + * This function is to get attributes from player. Multiple attributes can be got simultaneously. + * + * @param player [in] Handle of player. + * @param err_attr_name [out] Name of attribute which is failed to get + * @param first_attribute_name [in] Name of the first attribute to get + * @param ... [out] Value for the first attribute, followed optionally by more name/value pairs, terminated by NULL. + * But, in the case of data or string type, it should be name/value/size. + * + * @return This function returns zero on success, or negative value with error + * code. + * @see mm_player_set_attribute + * @remark This function must be terminated by NULL argument. + * And, if this function is failed, err_attr_name param must be free. + * @par Example + * @code +char *g_err_attr_name = NULL; + +if (mm_player_get_attribute(g_player, &g_err_attr_name, "content_duration", &duration, NULL) != MM_ERROR_NONE) +{ + printf("failed to set %s attribute\n", g_err_attr_name); + free(g_err_attr_name); +} + * @endcode + */ +int mm_player_get_attribute(MMHandleType player, char **err_attr_name, const char *first_attribute_name, ...)G_GNUC_NULL_TERMINATED; + +/** + * This function is to get detail information of attribute. + * + * @param player [in] Handle of player. + * @param attribute_name [in] Name of the attribute to get + * @param info [out] Attribute infomation + * + * @return This function returns zero on success, or negative value with error + * code. + * + * @see mm_player_set_attribute, mm_player_get_attribute + * @remark None + * @par Example + * @code +if (mm_player_get_attribute_info (g_player, "display_method", &method_info) != MM_ERROR_NONE) +{ + printf("failed to get info\n"); +} + +printf("type:%d \n", method_info.type); //int, double.. +printf("flag:%d \n", method_info.flag); //readable, writable.. +printf("validity type:%d \n", method_info.validity_type); //range, array.. + +if (method_info. validity_type == MM_PLAYER_ATTRS_VALID_TYPE_INT_RANGE) +{ + printf("range min:%d\n", method_info.int_range.min); + printf("range max:%d\n", method_info.int_range.max); +} + * @endcode + */ +int mm_player_get_attribute_info(MMHandleType player, const char *attribute_name, MMPlayerAttrsInfo *info); + +/** + * This function is to get download position and total size of progressive download + * + * @param player [in] Handle of player. + * @param current_pos [in] Download position currently (bytes) + * @param total_size [in] Total size of file (bytes) + * + * @return This function returns zero on success, or negative value with error code. + * + * @see + * @remark + * @par Example + * @code +guint64 current_pos = 0LLU; +guint64 total_size = 0LLU; + +if (mm_player_get_pd_status(g_player, ¤t_pos, &total_size, NULL) != MM_ERROR_NONE) +{ + printf("current download pos = %llu, total size = %llu\n", current_pos, total_size); +} + * @endcode + */ +int mm_player_get_pd_status(MMHandleType player, guint64 *current_pos, guint64 *total_size); + +/** + * This function sets callback function for receiving messages of PD downloader. + * + * @param player [in] Handle of player. + * @param callback [in] Message callback function. + * @param user_param [in] User parameter which is passed to callback function. + * + * @return This function returns zero on success, or negative value with error code. + * @see + * @remark None + * @par Example + * @code +int msg_callback(int message, MMMessageParamType *param, void *user_param) +{ + switch (message) + { + case MM_MESSAGE_PD_DOWNLOADER_START: + printf("Progressive download is started...\n"); + break; + case MM_MESSAGE_PD_DOWNLOADER_END: + printf("Progressive download is ended...\n"); + break; + default: + break; + } + return TRUE; +} + +mm_player_set_pd_message_callback(g_player, msg_callback, NULL); + * @endcode + */ +int mm_player_set_pd_message_callback(MMHandleType player, MMMessageCallback callback, void *user_param); + +/** + * This function is to get the track count + * + * @param player [in] handle of player. + * @param track_type [in] type of the track type + * @param info [out] the count of the track + * + * @return This function returns zero on success, or negative value with error + * code. + * + * @see + * @remark None + * @par Example + * @code +gint audio_count = 0; + +if (mm_player_get_track_count (g_player, MM_PLAYER_TRACK_TYPE_AUDIO, &audio_count) != MM_ERROR_NONE) +{ + printf("failed to get audio track count\n"); +} + +printf("audio track count : %d \n", audio_count); + * @endcode + */ +int mm_player_get_track_count(MMHandleType player, MMPlayerTrackType track_type, int *count); + + +/** + @} + */ + +#ifdef __cplusplus + } +#endif + +#endif /* __MM_PLAYER_H__ */ diff --git a/src/include/mm_player_asm.h b/src/include/mm_player_asm.h new file mode 100644 index 0000000..cd9197f --- /dev/null +++ b/src/include/mm_player_asm.h @@ -0,0 +1,55 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_ASM_H__ +#define __MM_PLAYER_ASM_H__ + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int handle; + int pid; + int by_asm_cb; + int event_src; + ASM_sound_states_t state; + ASM_sound_events_t event; +} MMPlayerASM; + +/* returns allocated handle */ +gint _mmplayer_asm_register(MMPlayerASM* sm, ASM_sound_cb_t callback, void* param); +gint _mmplayer_asm_deregister(MMPlayerASM* sm); +gint _mmplayer_asm_set_state(MMHandleType player, ASM_sound_states_t state); + +#ifdef __cplusplus +} +#endif + +#endif /* __MM_PLAYER_ASM_H__ */ diff --git a/src/include/mm_player_attrs.h b/src/include/mm_player_attrs.h new file mode 100644 index 0000000..846bbba --- /dev/null +++ b/src/include/mm_player_attrs.h @@ -0,0 +1,92 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_ATTRS_H__ +#define __MM_PLAYER_ATTRS_H__ + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * This function set values of attributes. + * + * @param[in] handle Handle of player. + * @param[in] err_atr_name Name of attribute that is failed (NULL can be set if it's not require to check. ) + * @param[in] attribute_name Name of the first attribute to set + * @param[in] args_list List of attributes and values + * @return This function returns zero on success, or negative value with error code. + * @remarks + * @see _mmplayer_get_attribute() + * + */ +int _mmplayer_set_attribute(MMHandleType handle, char **err_atr_name, const char *attribute_name, va_list args_list); +/** + * This function get values of attributes. + * + * @param[in] handle Handle of player. + * @param[in] err_atr_name Name of attribute that is failed (NULL can be set if it's not require to check. ) + * @param[in] attribute_name Name of the first attribute to set + * @param[in] args_list List of attributes and values + * @return This function returns zero on success, or negative value with error code. + * @remarks + * @see _mmplayer_set_attribute() + * + */ +int _mmplayer_get_attribute(MMHandleType handle, char **err_atr_name, const char *attribute_name, va_list args_list); +/** + * This function get configuration values of attribute. + * + * @param[in] handle Handle of player. + * @param[in] attribute_name Name of the first attribute to set + * @param[in] info Configuration values + * @return This function returns zero on success, or negative value with error code. + * @remarks + * @see + * + */ +int _mmplayer_get_attribute_info(MMHandleType handle, const char *attribute_name, MMPlayerAttrsInfo *info); +/** + * This function allocates structure of attributes and sets initial values. + * + * @param[in] handle Handle of player. + * @return This function returns allocated structure of attributes. + * @remarks + * @see _mmplayer_deconstruct_attribute() + * + */ +MMHandleType _mmplayer_construct_attribute(MMHandleType handle); +/** + * This function release allocated attributes. + * + * @param[in] handle Handle of player. + * @return This function returns true on success or false on failure. + * @remarks + * @see _mmplayer_construct_attribute() + * + */ +bool _mmplayer_deconstruct_attribute(MMHandleType handle); + +#ifdef __cplusplus + } +#endif + +#endif /* __MM_PLAYER_ATTRS_H__ */ diff --git a/src/include/mm_player_audioeffect.h b/src/include/mm_player_audioeffect.h new file mode 100644 index 0000000..fc03392 --- /dev/null +++ b/src/include/mm_player_audioeffect.h @@ -0,0 +1,348 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , + * Seungbae Shin , YoungHwan An + * + * 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 __MM_PLAYER_AUDIOEFFECT_H__ +#define __MM_PLAYER_AUDIOEFFECT_H__ + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#define MM_AUDIO_EFFECT_EQ_BAND_NUM_MAX 10 +#define MM_AUDIO_EFFECT_CUSTOM_LEVEL_INIT 0 + +/** + @addtogroup PLAYER_INTERNAL + +*/ + +/** + * Enumerations of Audio Effect Preset Type + */ +typedef enum { + MM_AUDIO_EFFECT_PRESET_AUTO = 0, /**< Preset type Auto */ + MM_AUDIO_EFFECT_PRESET_NORMAL, /**< Preset type Normal */ + MM_AUDIO_EFFECT_PRESET_POP, /**< Preset type Pop */ + MM_AUDIO_EFFECT_PRESET_ROCK, /**< Preset type Rock */ + MM_AUDIO_EFFECT_PRESET_DANCE, /**< Preset type Dance */ + MM_AUDIO_EFFECT_PRESET_JAZZ, /**< Preset type Jazz */ + MM_AUDIO_EFFECT_PRESET_CLASSIC, /**< Preset type Classic */ + MM_AUDIO_EFFECT_PRESET_VOCAL, /**< Preset type Vocal */ + MM_AUDIO_EFFECT_PRESET_BASS_BOOST, /**< Preset type Bass Boost */ + MM_AUDIO_EFFECT_PRESET_TREBLE_BOOST, /**< Preset type Treble Boost */ + MM_AUDIO_EFFECT_PRESET_MTHEATER, /**< Preset type MTheater */ + MM_AUDIO_EFFECT_PRESET_EXT, /**< Preset type Externalization */ + MM_AUDIO_EFFECT_PRESET_CAFE, /**< Preset type Cafe */ + MM_AUDIO_EFFECT_PRESET_CONCERT_HALL, /**< Preset type Concert Hall */ + MM_AUDIO_EFFECT_PRESET_VOICE, /**< Preset type Voice */ + MM_AUDIO_EFFECT_PRESET_MOVIE, /**< Preset type Movie */ + MM_AUDIO_EFFECT_PRESET_VIRT51, /**< Preset type Virtual 5.1 */ + MM_AUDIO_EFFECT_PRESET_HIPHOP, /**< Preset type HipHop */ + MM_AUDIO_EFFECT_PRESET_RNB, /**< Preset type R&B */ + MM_AUDIO_EFFECT_PRESET_FLAT, /**< Preset type Flat */ + MM_AUDIO_EFFECT_PRESET_NUM, /**< Number of Preset type */ +} MMAudioEffectPresetType; + +/** + * Enumerations of Audio Effect Custom Type + */ +typedef enum { + MM_AUDIO_EFFECT_CUSTOM_EQ = 0, /**< Custom type Equalizer */ + MM_AUDIO_EFFECT_CUSTOM_3D, /**< Custom type 3D */ + MM_AUDIO_EFFECT_CUSTOM_BASS, /**< Custom type Bass */ + MM_AUDIO_EFFECT_CUSTOM_ROOM_SIZE, /**< Custom type Room Size */ + MM_AUDIO_EFFECT_CUSTOM_REVERB_LEVEL, /**< Custom type Reverb Level */ + MM_AUDIO_EFFECT_CUSTOM_CLARITY, /**< Custom type Clarity */ + MM_AUDIO_EFFECT_CUSTOM_NUM, /**< Number of Custom type */ +} MMAudioEffectCustomType; + +/** + * Enumerations of Audio Effect Type + */ +typedef enum { + MM_AUDIO_EFFECT_TYPE_NONE, + MM_AUDIO_EFFECT_TYPE_PRESET, + MM_AUDIO_EFFECT_TYPE_CUSTOM, +} MMAudioEffectType; + + +/** + * Enumerations of Output Mode + */ +typedef enum { + MM_AUDIO_EFFECT_OUTPUT_SPK, /**< Speaker out */ + MM_AUDIO_EFFECT_OUTPUT_EAR /**< Earjack out */ +} MMAudioEffectOutputMode; + + +/** + * Structure of AudioEffectInfo + */ +typedef struct { + MMAudioEffectType effect_type; /**< effect type, (NONE,PRESET,CUSTOM)*/ + MMAudioEffectPresetType preset; /**< for preset type*/ + int *custom_ext_level_for_plugin; /**< for custom type, level value list of Extension effects*/ + int custom_eq_level[MM_AUDIO_EFFECT_EQ_BAND_NUM_MAX]; /**< for custom type, EQ info*/ + int custom_ext_level[MM_AUDIO_EFFECT_CUSTOM_NUM-1]; /**< for custom type, Extension effect info*/ +} MMAudioEffectInfo; + + +/** + * @brief Called to get each supported audio effect. + * + * @param effect_type [in] Type of effect (preset effect or custom effect). + * @param effect [in] Supported audio effect. + * @param user_data [in] Pointer of user data. + * + * @return True to continue with the next iteration of the loop, False to break out of the loop. + * @see mm_player_get_foreach_present_supported_effect_type() + */ +typedef bool (*mmplayer_supported_audio_effect_cb) (int effect_type, int type, void *user_data); + +/** + * This function is to get supported effect type. + * + * @param hplayer [in] Handle of player. + * @param effect_type [in] Type of effect. + * @param foreach_cb [in] Callback function to be passed the result. + * @param user_data [in] Pointer of user data. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_get_foreach_present_supported_effect_type(MMHandleType player, MMAudioEffectType effect_type, mmplayer_supported_audio_effect_cb foreach_cb, void *user_data); + +/** + * This function is to bypass audio effect. + * + * @param hplayer [in] Handle of player. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_audio_effect_bypass (MMHandleType hplayer); + +/** + * This function is to apply preset effect. + * + * @param hplayer [in] Handle of player. + * @param type [in] Preset type effect. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see MMAudioEffectPresetType + * @since + */ +int mm_player_audio_effect_preset_apply(MMHandleType hplayer, MMAudioEffectPresetType type); + +/** + * This function is to apply custom effect(Equalizer and Extension effects). + * + * @param hplayer [in] Handle of player. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_audio_effect_custom_apply(MMHandleType hplayer); + +/** + * This function is to clear Equalizer custom effect. + * + * @param hplayer [in] Handle of player. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_audio_effect_custom_clear_eq_all(MMHandleType hplayer); + +/** + * This function is to clear Extension custom effects. + * + * @param hplayer [in] Handle of player. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_audio_effect_custom_clear_ext_all(MMHandleType hplayer); + +/** + * This function is to get the number of equalizer bands. + * + * @param hplayer [in] Handle of player. + * @param bands [out] The number of bands. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_audio_effect_custom_get_eq_bands_number(MMHandleType hplayer, int *bands); + +/** + * This function is to get width of equalizer band of each index. + * + * @param hplayer [in] Handle of player. + * @param band_idx [in] Index of band. + * @param width [out] Value of width. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_audio_effect_custom_get_eq_bands_width(MMHandleType hplayer, int band_idx, int *width); + +/** + * This function is to get frequency of equalizer band of each index. + * + * @param hplayer [in] Handle of player. + * @param band_idx [in] Index of band. + * @param freq [out] Value of frequency. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_audio_effect_custom_get_eq_bands_freq(MMHandleType hplayer, int band_idx, int *freq); + +/** + * This function is to get the level of the custom effect. + * + * @param hplayer [in] Handle of player. + * @param type [in] Custom type effect. + * @param eq_index [in] Equalizer band index. This parameter is available only when the type is MM_AUDIO_EFFECT_CUSTOM_EQ. + * @param level [out] The level of the custom effect. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see MMAudioEffectCustomType + * @since + */ +int mm_player_audio_effect_custom_get_level(MMHandleType hplayer, MMAudioEffectCustomType type, int eq_index, int *level); + +/** + * This function is to get range of the level of the custom effect. + * + * @param hplayer [in] Handle of player. + * @param type [in] Custom type effect. + * @param min [out] Minimal value of level. + * @param max [out] Maximum value of level. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see MMAudioEffectCustomType + * @since + */ +int mm_player_audio_effect_custom_get_level_range(MMHandleType hplayer, MMAudioEffectCustomType type, int *min, int *max); + +/** + * This function is to set the level of the custom effect. + * + * @param hplayer [in] Handle of player. + * @param type [in] Custom type effect. + * @param eq_index [in] Equalizer band index. This parameter is available only when the type is MM_AUDIO_EFFECT_CUSTOM_EQ. + * @param level [in] The level of the custom effect. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see MMAudioEffectCustomType + * @since + */ +int mm_player_audio_effect_custom_set_level(MMHandleType hplayer, MMAudioEffectCustomType effect_custom_type, int eq_index, int level); + +/** + * This function is to set the bands level of equalizer custom effect using input list. + * + * @param hplayer [in] Handle of player. + * @param level_list [in] list of bands level of equalizer custom audio_effect want to set. + * @param size [in] size of level_list. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_audio_effect_custom_set_level_eq_from_list(MMHandleType hplayer, int *level_list, int size); + +/** + * This function is to decide if the preset type effect is supported or not + * + * @param hplayer [in] Handle of player. + * @param effect [in] Preset type effect. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_is_supported_preset_effect_type(MMHandleType hplayer, MMAudioEffectPresetType effect); + +/** + * This function is to decide if the custom type effect is supported or not + * + * @param hplayer [in] Handle of player. + * @param effect [in] Custom type effect. + * + * @return This function returns zero on success, or negative value with error code. + * + * @remark + * @see + * @since + */ +int mm_player_is_supported_custom_effect_type(MMHandleType hplayer, MMAudioEffectCustomType effect); + +/** + @} + */ + +#ifdef __cplusplus + } +#endif + +#endif /* __MM_PLAYER_AUDIOEFFECT_H__ */ diff --git a/src/include/mm_player_capture.h b/src/include/mm_player_capture.h new file mode 100755 index 0000000..b4349e8 --- /dev/null +++ b/src/include/mm_player_capture.h @@ -0,0 +1,73 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_CAPTURE_H__ +#define __MM_PLAYER_CAPTURE_H__ + +/*======================================================================================= +| INCLUDE FILES | +========================================================================================*/ +#include +#include "mm_player_priv.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/*======================================================================================= +| GLOBAL FUNCTION PROTOTYPES | +========================================================================================*/ +/** + * This function is to initialize video capture + * + * @param[in] handle Handle of player. + * @return This function returns zero on success, or negative value with errors. + * @remarks + * @see + * + */ +int _mmplayer_initialize_video_capture(mm_player_t* player); +/** + * This function is to release video capture + * + * @param[in] handle Handle of player. + * @return This function returns zero on success, or negative value with errors. + * @remarks + * @see + * + */ +int _mmplayer_release_video_capture(mm_player_t* player); +/** + * This function is to get video snapshot during playback. + * + * @param[in] handle Handle of player. + * @return This function returns zero on success, or negative value with errors. + * @remarks + * @see + * + */ +int _mmplayer_do_video_capture(MMHandleType hplayer); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/include/mm_player_ini.h b/src/include/mm_player_ini.h new file mode 100644 index 0000000..c8c095e --- /dev/null +++ b/src/include/mm_player_ini.h @@ -0,0 +1,256 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_INI_H__ +#define __MM_PLAYER_INI_H__ + +#include +#include +#include "mm_player_audioeffect.h" + +#ifdef __cplusplus + extern "C" { +#endif + + +#define MM_PLAYER_INI_DEFAULT_PATH "/usr/etc/mmfw_player.ini" +#define MM_PLAYER_INI_DEFAULT_AUDIOEFFECT_PATH "/usr/etc/mmfw_player_audio_effect.ini" + +#define PLAYER_INI() mm_player_ini_get_structure() + +#define PLAYER_INI_MAX_STRLEN 100 +#define PLAYER_INI_MAX_PARAM_STRLEN 256 + +/* NOTE : MMPlayer has no initalizing API for library itself + * so we cannot decide when those ini values to be released. + * this is the reason of all string items are static array. + * make it do with malloc when MMPlayerInitialize() API created + * before that time, we should be careful with size limitation + * of each string item. + */ + +/* @ mark means the item has tested */ +typedef struct __mm_player_ini +{ + /* general */ + gboolean use_decodebin; // @ + gchar videosink_element_x[PLAYER_INI_MAX_STRLEN]; + gchar videosink_element_evas[PLAYER_INI_MAX_STRLEN]; + gchar videosink_element_fake[PLAYER_INI_MAX_STRLEN]; + gchar name_of_audiosink[PLAYER_INI_MAX_STRLEN]; // @ + gchar name_of_drmsrc[PLAYER_INI_MAX_STRLEN]; // @ + gchar name_of_video_converter[PLAYER_INI_MAX_STRLEN]; + gboolean skip_rescan; // @ + gboolean generate_dot; // @ + gboolean provide_clock; // @ + gint live_state_change_timeout; // @ + gint localplayback_state_change_timeout; // @ + gint delay_before_repeat; + gint eos_delay; // @ + gboolean multiple_codec_supported; + + gchar gst_param[5][PLAYER_INI_MAX_PARAM_STRLEN]; // @ + gchar exclude_element_keyword[10][PLAYER_INI_MAX_STRLEN]; + gboolean async_start; + gboolean disable_segtrap; + + /* audio effect */ + gchar name_of_audio_effect[PLAYER_INI_MAX_STRLEN]; + + gboolean use_audio_effect_preset; + gboolean audio_effect_preset_list[MM_AUDIO_EFFECT_PRESET_NUM]; + gboolean audio_effect_preset_earphone_only_list[MM_AUDIO_EFFECT_PRESET_NUM]; + + gboolean use_audio_effect_custom; + gboolean audio_effect_custom_list[MM_AUDIO_EFFECT_CUSTOM_NUM]; + gboolean audio_effect_custom_earphone_only_list[MM_AUDIO_EFFECT_CUSTOM_NUM]; + gint audio_effect_custom_eq_band_num; + gint audio_effect_custom_eq_band_width[MM_AUDIO_EFFECT_EQ_BAND_NUM_MAX]; + gint audio_effect_custom_eq_band_freq[MM_AUDIO_EFFECT_EQ_BAND_NUM_MAX]; + gint audio_effect_custom_ext_num; + gint audio_effect_custom_min_level_list[MM_AUDIO_EFFECT_CUSTOM_NUM]; + gint audio_effect_custom_max_level_list[MM_AUDIO_EFFECT_CUSTOM_NUM]; + + /* http streaming */ + gchar name_of_httpsrc[PLAYER_INI_MAX_STRLEN]; // @ + gchar http_file_buffer_path[PLAYER_INI_MAX_STRLEN]; + gdouble http_buffering_limit; + guint http_max_size_bytes; + gdouble http_buffering_time; + guint http_timeout; + + /* rtsp streaming */ + gchar name_of_rtspsrc[PLAYER_INI_MAX_STRLEN]; // @ + guint rtsp_buffering_time; + guint rtsp_rebuffering_time; + gboolean rtsp_do_typefinding; + gboolean rtsp_error_concealment; /* testing purpose */ +} mm_player_ini_t; + +/* default values if each values are not specified in inifile */ +/* general */ +#define DEFAULT_USE_DECODEBIN FALSE +#define DEFAULT_AUDIO_EFFECT_ELEMENT "" +#define DEFAULT_USE_AUDIO_EFFECT_PRESET FALSE +#define DEFAULT_AUDIO_EFFECT_PRESET_LIST "" +#define DEFAULT_AUDIO_EFFECT_PRESET_LIST_EARPHONE_ONLY "" +#define DEFAULT_USE_AUDIO_EFFECT_CUSTOM FALSE +#define DEFAULT_AUDIO_EFFECT_CUSTOM_LIST "" +#define DEFAULT_AUDIO_EFFECT_CUSTOM_LIST_EARPHONE_ONLY "" +#define DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_NUM 0 +#define DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_WIDTH "" +#define DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_FREQ "" +#define DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_MIN 0 +#define DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_MAX 0 +#define DEFAULT_AUDIO_EFFECT_CUSTOM_EXT_NUM 0 +#define DEFAULT_USE_SINK_HANDLER TRUE +#define DEFAULT_SKIP_RESCAN TRUE +#define DEFAULT_GENERATE_DOT FALSE +#define DEFAULT_PROVIDE_CLOCK TRUE +#define DEFAULT_DELAY_BEFORE_REPEAT 50 /* msec */ +#define DEFAULT_EOS_DELAY 150 /* msec */ +#define DEFAULT_DRMSRC "drmsrc" +#define DEFAULT_VIDEOSINK_X "xvimagesink" +#define DEFAULT_VIDEOSINK_EVAS "evasimagesink" +#define DEFAULT_VIDEOSINK_FAKE "fakesink" +#define DEFAULT_AUDIOSINK "avsysaudiosink" +#define DEFAULT_GST_PARAM "" +#define DEFAULT_EXCLUDE_KEYWORD "" +#define DEFAULT_ASYNC_START TRUE +#define DEFAULT_DISABLE_SEGTRAP TRUE +#define DEFAULT_VIDEO_CONVERTER "" +#define DEFAULT_MULTIPLE_CODEC_SUPPORTED TRUE +#define DEFAULT_LIVE_STATE_CHANGE_TIMEOUT 30 /* sec */ +#define DEFAULT_LOCALPLAYBACK_STATE_CHANGE_TIMEOUT 10 /* sec */ +/* http streaming */ +#define DEFAULT_HTTPSRC "souphttpsrc" +#define DEFAULT_HTTP_FILE_BUFFER_PATH "" +#define DEFAULT_HTTP_BUFFERING_LIMIT 99.0 /* percent */ +#define DEFAULT_HTTP_MAX_SIZE_BYTES 1048576 /* bytes : 1 MBytes */ +#define DEFAULT_HTTP_BUFFERING_TIME 3.0 /* sec */ +#define DEFAULT_HTTP_TIMEOUT 30 /* sec */ +/* rtsp streaming */ +#define DEFAULT_RTSPSRC "secrtspsrc" +#define DEFAULT_RTSP_BUFFERING 5000 /* msec */ +#define DEFAULT_RTSP_REBUFFERING 15000 /* msec */ +#define DEFAULT_RTSP_DO_TYPEFINDING FALSE +#define DEFAULT_RTSP_ERROR_CONCEALMENT TRUE + +/* 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 MM_PLAYER_DEFAULT_INI \ +"\ +[general] \n\ +\n\ +; if disabled typefind element will used directely \n\ +use decodebin = no ; async state change problem exist \n\ +\n\ +use sink handler = yes \n\ +\n\ +disable segtrap = yes ; same effect with --gst-disable-segtrap \n\ +\n\ +; set default video sink but, it can be replaced with others selected by application\n\ +; 0:v4l2sink, 1:ximagesink, 2:xvimagesink, 3:fakesink 4:evasimagesink 5:glimagesink\n\ +videosink element = 2 \n\ +\n\ +video converter element = \n\ +\n\ +audiosink element = avsysaudiosink \n\ +\n\ +drmsrc element = drmsrc \n\ +\n\ +; if yes. gstreamer will not update registry \n\ +skip rescan = yes \n\ +\n\ +delay before repeat = 50 ; msec\n\ +\n\ +; comma separated list of tocken which elemnts has it in it's name will not be used \n\ +element exclude keyword = \n\ +\n\ +async start = yes \n\ +\n\ +multiple codec supported = yes \n\ +\n\ +; parameters for initializing gstreamer \n\ +gstparam1 = \n\ +gstparam2 = \n\ +gstparam3 = \n\ +gstparam4 = \n\ +gstparam5 = \n\ +\n\ +; generating dot file representing pipeline state \n\ +generate dot = no \n\ +\n\ +; parameter for clock provide in audiosink \n\ +provide clock = yes \n\ +\n\ +; allowed timeout for changing pipeline state \n\ +live state change timeout = 30 ; sec \n\ +localplayback state change timeout = 4 ; sec \n\ +\n\ +; delay in msec for sending EOS \n\ +eos delay = 150 ; msec \n\ +\n\ +\n\ +[http streaming] \n\ +\n\ +httppsrc element = souphttpsrc \n\ +\n\ +; if set, use file or not use memory for buffering\n\ +http file buffer path = /opt/media\n\ +\n\ +http buffering limit = 99 ; percent\n\ +\n\ +http max size bytes = 1048576 ; bytes\n\ +\n\ +http buffering time = 3.0 \n\ +\n\ +http timeout = 30 ; sec \n\ +\n\ +\n\ +[rtsp streaming] \n\ +\n\ +rtspsrc element = secrtspsrc \n\ +\n\ +rtsp buffering time = 5000; msec \n\ +\n\ +rtsp rebuffering time = 15000; msec \n\ +\n\ +rtsp do typefinding = no; if no, caps on rtspsrc:src pad will be used for autoplugging \n\ +\n\ +rtsp error concealment = yes \n\ +\n\ +\n\ +" + +int +mm_player_ini_load(void); + +mm_player_ini_t* +mm_player_ini_get_structure(void); + + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/include/mm_player_internal.h b/src/include/mm_player_internal.h new file mode 100644 index 0000000..bf8b4c0 --- /dev/null +++ b/src/include/mm_player_internal.h @@ -0,0 +1,339 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_INTERNAL_H__ +#define __MM_PLAYER_INTERNAL_H__ + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + @addtogroup PLAYER-INTERNAL + @{ + + @par +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PROPERTYTYPEVALID TYPEDEFAULT VALUE
"display_roi_x"intrange0
"display_roi_y"intrange0
"display_roi_width"intrange640
"display_roi_height"intrange480
"display_method"intrangeMM_DISPLAY_METHOD_LETTER_BOX
"sound_volume_type"intrangeMM_SOUND_VOLUME_TYPE_CALL
"sound_route"intrangeMM_AUDIOROUTE_USE_EXTERNAL_SETTING
"sound_stop_when_unplugged"intrange
+ +*/ + +/* + * Enumerations of video colorspace + */ +typedef enum { + MM_PLAYER_COLORSPACE_I420 = 0, /**< I420 format - planer */ + MM_PLAYER_COLORSPACE_RGB888, /**< RGB888 pixel format */ + MM_PLAYER_COLORSPACE_NV12_TILED, /**< Customized color format in s5pc110 */ +}MMPlayerVideoColorspace; + +typedef struct +{ + unsigned char *data; /* capture image buffer */ + int size; /* capture image size */ + MMPlayerVideoColorspace fmt; /* color space type */ +} MMPlayerVideoCapture; + +/** + * Buffer need data callback function type. + * + * @param size [in] size required for the buffer playback + * @param user_param [in] User defined parameter which is passed when set + * to need data callback + * + * @return This callback function have to return MM_ERROR_NONE. + */ +typedef bool (*mm_player_buffer_need_data_callback) (unsigned int size, void *user_param); + +/** + * Buffer enough data callback function type. + * + * @param user_param [in] User defined parameter which is passed when set + * to enough data callback + * + * @return This callback function have to return MM_ERROR_NONE. + */ +typedef bool (*mm_player_buffer_enough_data_callback) (void *user_param); + +/** + * Buffer seek data callback function type. + * + * @param offset [in] offset for the buffer playback + * @param user_param [in] User defined parameter which is passed when set + * to seek data callback + * + * @return This callback function have to return MM_ERROR_NONE. + */ +typedef bool (*mm_player_buffer_seek_data_callback) (unsigned long long offset, void *user_param); + +/** + * Video stream callback function type. + * + * @param stream [in] Reference pointer to video frame data + * @param stream_size [in] Size of video frame data + * @param user_param [in] User defined parameter which is passed when set + * video stream callback + * @param width [in] width of video frame + * @param height [in] height of video frame + * + * @return This callback function have to return MM_ERROR_NONE. + */ +typedef bool (*mm_player_video_stream_callback) (void *stream, int stream_size, void *user_param, int width, int height); + +/** + * Audio stream callback function type. + * + * @param stream [in] Reference pointer to audio frame data + * @param stream_size [in] Size of audio frame data + * @param user_param [in] User defined parameter which is passed when set + * audio stream callback + * + * @return This callback function have to return MM_ERROR_NONE. + */ +typedef bool (*mm_player_video_capture_callback) (void *stream, int stream_size, void *user_param); + +/** + * Video frame render error callback function type. + * + * @param error_id [in] cause of error + * @param user_param [in] User defined parameter which is passed when set + * video frame render error callback + * + * @return This callback function have to return MM_ERROR_NONE. + */ +typedef bool (*mm_player_video_frame_render_error_callback) (void *error_id, void *user_param); + +/** + * This function is to set play speed for playback. + * + * @param player [in] Handle of player. + * @param ratio [in] Speed for playback. + * + * @return This function returns zero on success, or negative value with error + * code + * @remark The current supported range is from -64x to 64x. + * But, the quailty is dependent on codec performance. + * And, the sound is muted under normal speed and more than double speed. + * @see + * @since + */ +int mm_player_set_play_speed(MMHandleType player, float rate); + +/** + * This function set callback function for receiving video stream from player. + * + * @param player [in] Handle of player. + * @param callback [in] Video stream callback function. + * @param user_param [in] User parameter. + * + * @return This function returns zero on success, or negative value with error + * code. + * @remark + * @see mm_player_video_stream_callback mm_player_set_audio_stream_callback + * @since + */ +int mm_player_set_video_stream_callback(MMHandleType player, mm_player_video_stream_callback callback, void *user_param); + +/** + * This function set callback function for rendering error information of video render plug-in. + * + * @param player [in] Handle of player. + * @param callback [in] Frame render error callback function. + * @param user_param [in] User parameter which is passed to callback function. + * + * @return This function returns zero on success, or negative value with error code. + * @see + * @remark None + * @par Example + * @code + * @endcode + */ +int mm_player_set_video_frame_render_error_callback(MMHandleType player, mm_player_video_frame_render_error_callback callback, void *user_param); + +/** + * This function set callback function for receiving audio stream from player. + * + * @param player [in] Handle of player. + * @param callback [in] Audio buffer callback function. + * @param user_param [in] User parameter. + * + * @return This function returns zero on success, or negative value with error + * code. + * @remark It can be used for audio playback only. + * @see mm_player_audio_stream_callback + * @since + */ +int mm_player_set_audio_buffer_callback(MMHandleType player, mm_player_audio_stream_callback callback, void *user_param); + +/** + * This function is to capture video frame. + * + * @param player [in] Handle of player. + * + * @return This function returns zero on success, or negative value with error + * code. + * + * @remark Captured buffer is sent asynchronously through message callback with MM_MESSAGE_VIDEO_CAPTURED. + * And, application should free the captured buffer directly. + * @see MM_MESSAGE_VIDEO_CAPTURED + * @since + */ +int mm_player_do_video_capture(MMHandleType player); + +/** + * This function set callback function for receiving need data message from player. + * + * @param player [in] Handle of player. + * @param callback [in] Need data callback function. + * @param user_param [in] User parameter. + * + * @return This function returns zero on success, or negative value with error + * code. + * @remark + * @see mm_player_set_buffer_enough_data_callback + * @since + */ +int mm_player_set_buffer_need_data_callback(MMHandleType player, mm_player_buffer_need_data_callback callback , void *user_param); + +/** + * This function set callback function for receiving enough data message from player. + * + * @param player [in] Handle of player. + * @param callback [in] Enough data callback function. + * @param user_param [in] User parameter. + * + * @return This function returns zero on success, or negative value with error + * code. + * @remark + * @see mm_player_set_buffer_need_data_callback + * @since + */ +int mm_player_set_buffer_enough_data_callback(MMHandleType player, mm_player_buffer_enough_data_callback callback, void *user_param); + +/** + * This function set callback function for receiving seek data message from player. + * + * @param player [in] Handle of player. + * @param callback [in] Seek data callback function. + * @param user_param [in] User parameter. + * + * @return This function returns zero on success, or negative value with error + * code. + * @remark + * @see + * @since + */ +int mm_player_set_buffer_seek_data_callback(MMHandleType player, mm_player_buffer_seek_data_callback callback, void *user_param); + +/** + * This function set callback function for putting data into player. + * + * @param player [in] Handle of player. + * @param buf [in] data to push into player + * @param size [in] buffer size to push + * + * @return This function returns zero on success, or negative value with error + * code. + * @remark + * @see + * @since + */ +int mm_player_push_buffer(MMHandleType player, unsigned char *buf, int size); + +/** + * This function changes the previous videosink plugin for a new one + * + * @param player [in] Handle of player. + * @param display_surface_type [in] display surface type to set + * @param display_overlay [in] display overlay to set + * + * @return This function returns zero on success, or negative value with error + * code. + * @remark Not supported + * @see + * @since + */ +int mm_player_change_videosink(MMHandleType player, MMDisplaySurfaceType display_surface_type, void *display_overlay); + +/** + @} + */ + +#ifdef __cplusplus + } +#endif + +#endif /* __MM_PLAYER_INTERNAL_H__ */ diff --git a/src/include/mm_player_pd.h b/src/include/mm_player_pd.h new file mode 100644 index 0000000..163f65c --- /dev/null +++ b/src/include/mm_player_pd.h @@ -0,0 +1,130 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , naveen cherukuri , + * YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_PD_H__ +#define __MM_PLAYER_PD_H__ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct +{ + gchar *path_read_from; // path for download and playback + gchar *location_to_save; // path for saving to local + gint64 total_size; // size of file to download (bytes) + + GstElement *playback_pipeline_src; // src element of playback pipeline + GstElement *downloader_pipeline; + GstElement *downloader_src; + GstElement *downloader_queue; + GstElement *downloader_sink; +}mm_player_pd_t; + +/** + * This function allocates handle of progressive download. + * + * @return This function returns allocated handle. + * @remarks + * @see _mmplayer_destroy_pd_downloader() + * + */ +mm_player_pd_t * _mmplayer_create_pd_downloader (); +/** + * This function destroy progressive download. + * + * @param[in] handle Handle of player. + * @return This function returns true on success, or false on failure. + * @remarks + * @see _mmplayer_create_pd_downloader() + * + */ +gboolean _mmplayer_destroy_pd_downloader (MMHandleType handle); +/** + * This function realize progressive download. + * + * @param[in] handle Handle of player. + * @param[in] src_uri path to download. + * @param[in] dst_uri path to save in local system. + * @param[in] pushsrc source element of playback pipeline + * @return This function returns true on success, or false on failure. + * @remarks + * @see + * + */ +gboolean _mmplayer_realize_pd_downloader (MMHandleType handle, gchar *src_uri, gchar *dst_uri, GstElement *pushsrc); +/** + * This function unrealize progressive download. + * + * @param[in] handle Handle of player. + * @return This function returns true on success, or false on failure. + * @remarks + * @see _mmplayer_realize_pd_downloader() + * + */ +gboolean _mmplayer_unrealize_pd_downloader (MMHandleType handle); +/** + * This function start progressive download. + * + * @param[in] handle Handle of player. + * @return This function returns true on success, or false on failure. + * @remarks + * @see + * + */ +gboolean _mmplayer_start_pd_downloader (MMHandleType handle); +/** + * This function get pd current status. + * + * @param[in] handle Handle of player. + * @param[in] current_pos current downloaded size + * @param[in] total_size total file size to download + * @return This function returns true on success, or false on failure. + * @remarks + * @see + * + */ +gboolean _mmplayer_get_pd_downloader_status(MMHandleType handle, guint64 *current_pos, guint64 *total_size); +/** + * This function set message callback of PD downloader. + * + * @param[in] handle Handle of player. + * @param[in] MMMessageCallback Message callback function + * @param[in] user_param User parameter which is passed to callback function. + * @return This function returns true on success, or false on failure. + * @remarks + * @see + * + */ +gint _mm_player_set_pd_downloader_message_cb(MMHandleType player, MMMessageCallback callback, gpointer user_param); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/include/mm_player_priv.h b/src/include/mm_player_priv.h new file mode 100644 index 0000000..08f7ba2 --- /dev/null +++ b/src/include/mm_player_priv.h @@ -0,0 +1,669 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_PRIV_H__ +#define __MM_PLAYER_PRIV_H__ + +/*=========================================================================================== +| | +| INCLUDE FILES | +| | +========================================================================================== */ +#include +#include +#include +#include +#include +#include + +#include "mm_player.h" +#include "mm_player_internal.h" +#include "mm_player_audioeffect.h" +#include "mm_message.h" +#include "mm_player_utils.h" +#include "mm_player_asm.h" +#include "mm_player_pd.h" +#include "mm_player_streaming.h" + +/*=========================================================================================== +| | +| GLOBAL DEFINITIONS AND DECLARATIONS FOR MODULE | +| | +========================================================================================== */ + +/*--------------------------------------------------------------------------- +| GLOBAL #defines: | +---------------------------------------------------------------------------*/ + +#define MM_PLAYER_IMGB_MPLANE_MAX 4 +#define MM_PLAYER_STREAM_COUNT_MAX 3 + +#define MM_PLAYER_CAST(x_player) ((mm_player_t *)(x_player)) +/** + * @x_player: MMHandleType of player + * + * Get the PD downloader of this player. + */ +#define MM_PLAYER_GET_PD(x_player) (MM_PLAYER_CAST(x_player)->pd_downloader) +/** + * @x_player: MMHandleType of player + * + * Get the attributes handle of this player. + */ +#define MM_PLAYER_GET_ATTRS(x_player) (MM_PLAYER_CAST(x_player)->attrs) + +/*--------------------------------------------------------------------------- +| GLOBAL CONSTANT DEFINITIONS: | +---------------------------------------------------------------------------*/ +enum tag_info +{ + TAG_AUDIO_CODEC = 0x0001, + TAG_VIDEO_CODEC = 0x0002, + TAG_ARTIST = 0x0004, + TAG_TITLE = 0x0008, + TAG_ALBUM = 0x0010, + TAG_GENRE = 0x0020, + TAG_COPYRIGHT = 0x0040, + TAG_DATE = 0x0080, + TAG_DESCRIPTION = 0x0100, + TAG_TRACK_NUMBER = 0x0200 +}; + +/* async mode makes trouble. alsasink sometimes fails to pause. */ +enum alassink_sync +{ + ALSASINK_SYNC, + ALSASINK_ASYNC +}; + + +/** + * Enumerations of Player Mode + */ +enum MMPlayerMode { + MM_PLAYER_MODE_NONE, /**< Player mode None */ + MM_PLAYER_MODE_MIDI, /**< Player mode Midi */ + MM_PLAYER_MODE_GST, /**< Player mode Gstreamer */ +}; + + +/** + * Enumerations of Player Uri type + */ +enum MMPlayerUriType { + MM_PLAYER_URI_TYPE_NONE, /**< Player URI type None */ + MM_PLAYER_URI_TYPE_URL_RTSP, /**< Player URI type RTSP */ + MM_PLAYER_URI_TYPE_URL_HTTP,/**< Player URI type HTTP */ + MM_PLAYER_URI_TYPE_URL_MMS,/**< Player URI type MMS */ + MM_PLAYER_URI_TYPE_MEM, /**< Player URI type Mem */ + MM_PLAYER_URI_TYPE_FILE, /**< Player URI type File */ + MM_PLAYER_URI_TYPE_URL, /**< Player URI type URL */ + MM_PLAYER_URI_TYPE_BUFF, /**< Player URI type Buffer */ + MM_PLAYER_URI_TYPE_HLS, /**< Player URI type http live streaming */ + MM_PLAYER_URI_TYPE_TEMP, /**< Player URI type Temp */ +}; + +typedef enum _MissingCodec +{ + MISSING_PLUGIN_NONE = 0x00, + MISSING_PLUGIN_AUDIO = 0x01, + MISSING_PLUGIN_VIDEO = 0x02 +}MissingCodec; + + +typedef enum _FoundCodec +{ + FOUND_PLUGIN_NONE = 0x00, + FOUND_PLUGIN_AUDIO = 0x01, + FOUND_PLUGIN_VIDEO = 0x02 +}FoundCodec; + +/* main pipeline's element id */ +enum MainElementID +{ + MMPLAYER_M_PIPE = 0, /* NOTE : MMPLAYER_M_PIPE should be zero */ + MMPLAYER_M_SRC, + MMPLAYER_M_SUBSRC, + + /* it could be a decodebin or could be a typefind. depends on player ini */ + MMPLAYER_M_AUTOPLUG, + + /* NOTE : we need two fakesink to autoplug without decodebin. + * first one will hold whole pipeline state. and second one will hold state of + * a sink-decodebin for an elementary stream. no metter if there's more then one + * elementary streams because MSL reuse it. + */ + MMPLAYER_M_SRC_FAKESINK, + MMPLAYER_M_SRC_2ND_FAKESINK, + + /* streaming plugin */ + MMPLAYER_M_S_BUFFER, + MMPLAYER_M_S_ADEC, + MMPLAYER_M_S_VDEC, + + /* FIXIT : if there's really no usage for following IDs. remove it */ + MMPLAYER_M_DEC1, + MMPLAYER_M_DEC2, + MMPLAYER_M_Q1, + MMPLAYER_M_Q2, + MMPLAYER_M_DEMUX, + MMPLAYER_M_SUBPARSE, + MMPLAYER_M_NUM +}; + +/* audio pipeline's element id */ +enum AudioElementID +{ + MMPLAYER_A_BIN = 0, /* NOTE : MMPLAYER_A_BIN should be zero */ + MMPLAYER_A_TP, + MMPLAYER_A_CONV, + MMPLAYER_A_VOL, + MMPLAYER_A_FILTER, + MMPLAYER_A_CAPS_DEFAULT, + MMPLAYER_A_SINK, + MMPLAYER_A_RESAMPLER, + MMPLAYER_A_NUM +}; + +/* video pipeline's element id */ +enum VideoElementID +{ + MMPLAYER_V_BIN = 0, /* NOTE : MMPLAYER_V_BIN should be zero */ + MMPLAYER_V_FLIP, + MMPLAYER_V_CONV, + MMPLAYER_V_SCALE, + MMPLAYER_V_CAPS, + MMPLAYER_V_SINK, + MMPLAYER_V_NUM +}; + +/* text pipeline's element id */ +enum TextElementID +{ + MMPLAYER_T_BIN = 0, /* NOTE : MMPLAYER_V_BIN should be zero */ + MMPLAYER_T_TEXT_QUEUE, + MMPLAYER_T_VIDEO_QUEUE, + MMPLAYER_T_VIDEO_CONVERTER, + MMPLAYER_T_OVERLAY, + MMPLAYER_T_SINK, + MMPLAYER_T_NUM +}; + +/* midi main pipeline's element id */ +enum MidiElementID +{ + MMPLAYER_MIDI_PIPE, + MMPLAYER_MIDI_PLAYER, + MMPLAYER_MIDI_NUM +}; + +enum PlayerCommandState +{ + MMPLAYER_COMMAND_NONE, + MMPLAYER_COMMAND_CREATE, + MMPLAYER_COMMAND_DESTROY, + MMPLAYER_COMMAND_UNREALIZE, + MMPLAYER_COMMAND_START, + MMPLAYER_COMMAND_REALIZE, + MMPLAYER_COMMAND_STOP, + MMPLAYER_COMMAND_PAUSE, + MMPLAYER_COMMAND_RESUME, + MMPLAYER_COMMAND_NUM +}; + +/* Note : StreamingSrcError is error enum for streaming source which post error message + * using custom message made by itself. The enum value must start with zero, + * because the streaming source(secrtspsrc) also does. + */ +enum StreamingSrcError +{ + MMPLAYER_STREAMING_ERROR_NONE = 0, + MMPLAYER_STREAMING_ERROR_UNSUPPORTED_AUDIO, + MMPLAYER_STREAMING_ERROR_UNSUPPORTED_VIDEO, + MMPLAYER_STREAMING_ERROR_CONNECTION_FAIL, + MMPLAYER_STREAMING_ERROR_DNS_FAIL, + MMPLAYER_STREAMING_ERROR_SERVER_DISCONNECTED, + MMPLAYER_STREAMING_ERROR_BAD_SERVER, + MMPLAYER_STREAMING_ERROR_INVALID_PROTOCOL, + MMPLAYER_STREAMING_ERROR_INVALID_URL, + MMPLAYER_STREAMING_ERROR_UNEXPECTED_MSG, + MMPLAYER_STREAMING_ERROR_OUT_OF_MEMORIES, + MMPLAYER_STREAMING_ERROR_RTSP_TIMEOUT, + MMPLAYER_STREAMING_ERROR_BAD_REQUEST, + MMPLAYER_STREAMING_ERROR_NOT_AUTHORIZED, + MMPLAYER_STREAMING_ERROR_PAYMENT_REQUIRED, + MMPLAYER_STREAMING_ERROR_FORBIDDEN, + MMPLAYER_STREAMING_ERROR_CONTENT_NOT_FOUND, + MMPLAYER_STREAMING_ERROR_METHOD_NOT_ALLOWED, + MMPLAYER_STREAMING_ERROR_NOT_ACCEPTABLE, + MMPLAYER_STREAMING_ERROR_PROXY_AUTHENTICATION_REQUIRED, + MMPLAYER_STREAMING_ERROR_SERVER_TIMEOUT, + MMPLAYER_STREAMING_ERROR_GONE, + MMPLAYER_STREAMING_ERROR_LENGTH_REQUIRED, + MMPLAYER_STREAMING_ERROR_PRECONDITION_FAILED, + MMPLAYER_STREAMING_ERROR_REQUEST_ENTITY_TOO_LARGE, + MMPLAYER_STREAMING_ERROR_REQUEST_URI_TOO_LARGE, + MMPLAYER_STREAMING_ERROR_UNSUPPORTED_MEDIA_TYPE, + MMPLAYER_STREAMING_ERROR_PARAMETER_NOT_UNDERSTOOD, + MMPLAYER_STREAMING_ERROR_CONFERENCE_NOT_FOUND, + MMPLAYER_STREAMING_ERROR_NOT_ENOUGH_BANDWIDTH, + MMPLAYER_STREAMING_ERROR_NO_SESSION_ID, + MMPLAYER_STREAMING_ERROR_METHOD_NOT_VALID_IN_THIS_STATE, + MMPLAYER_STREAMING_ERROR_HEADER_FIELD_NOT_VALID_FOR_SOURCE, + MMPLAYER_STREAMING_ERROR_INVALID_RANGE, + MMPLAYER_STREAMING_ERROR_PARAMETER_IS_READONLY, + MMPLAYER_STREAMING_ERROR_AGGREGATE_OP_NOT_ALLOWED, + MMPLAYER_STREAMING_ERROR_ONLY_AGGREGATE_OP_ALLOWED, + MMPLAYER_STREAMING_ERROR_BAD_TRANSPORT, + MMPLAYER_STREAMING_ERROR_DESTINATION_UNREACHABLE, + MMPLAYER_STREAMING_ERROR_INTERNAL_SERVER_ERROR, + MMPLAYER_STREAMING_ERROR_NOT_IMPLEMENTED, + MMPLAYER_STREAMING_ERROR_BAD_GATEWAY, + MMPLAYER_STREAMING_ERROR_SERVICE_UNAVAILABLE, + MMPLAYER_STREAMING_ERROR_GATEWAY_TIME_OUT , + MMPLAYER_STREAMING_ERROR_RTSP_VERSION_NOT_SUPPORTED, + MMPLAYER_STREAMING_ERROR_OPTION_NOT_SUPPORTED, +}; + + +/*--------------------------------------------------------------------------- +| GLOBAL DATA TYPE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +typedef struct +{ + int id; + GstElement *gst; +} MMPlayerGstElement; + +typedef struct +{ + GstTagList *tag_list; + MMPlayerGstElement *mainbin; + MMPlayerGstElement *audiobin; + MMPlayerGstElement *videobin; + MMPlayerGstElement *textbin; +} MMPlayerGstPipelineInfo; + +typedef struct +{ + char device[MAX_SOUND_DEVICE_LEN]; + float volume; + int mute; + int bluetooth; /* enable/disable */ +} MMPlayerSoundInfo; + +typedef struct { + char *buf; + int len; + int offset; + +} tBuffer; /* FIXIT : choose better name */ + +typedef struct { + int uri_type; + int play_mode; + void *mem; + int mem_size; + char uri[MM_MAX_URL_LEN]; + char urgent[MM_MAX_FILENAME_LEN]; +} MMPlayerParseProfile; + +typedef struct { + bool is_pending; + MMPlayerPosFormatType format; + unsigned long pos; +}MMPlayerPendingSeek; + +typedef struct { + GObject* obj; + gulong sig; +} MMPlayerSignalItem; + +/* image buffer definition *************************************************** + + +------------------------------------------+ --- + | | ^ + | a[], p[] | | + | +---------------------------+ --- | | + | | | ^ | | + | |<---------- w[] ---------->| | | | + | | | | | | + | | | | + | | | h[] | e[] + | | | | + | | | | | | + | | | | | | + | | | v | | + | +---------------------------+ --- | | + | | v + +------------------------------------------+ --- + + |<----------------- s[] ------------------>| +*/ +typedef struct +{ + /* width of each image plane */ + int w[MM_PLAYER_IMGB_MPLANE_MAX]; + /* height of each image plane */ + int h[MM_PLAYER_IMGB_MPLANE_MAX]; + /* stride of each image plane */ + int s[MM_PLAYER_IMGB_MPLANE_MAX]; + /* elevation of each image plane */ + int e[MM_PLAYER_IMGB_MPLANE_MAX]; + /* user space address of each image plane */ + void *a[MM_PLAYER_IMGB_MPLANE_MAX]; + /* physical address of each image plane, if needs */ + void *p[MM_PLAYER_IMGB_MPLANE_MAX]; + /* color space type of image */ + int cs; + /* left postion, if needs */ + int x; + /* top position, if needs */ + int y; + /* to align memory */ + int __dummy2; + /* arbitrary data */ + int data[16]; + + /* dmabuf fd */ + int fd[MM_PLAYER_IMGB_MPLANE_MAX]; + + /* flag for buffer share */ + int buf_share_method; + + /*y, cbcr size for bookmark */ + int y_size; + int uv_size; +} MMPlayerMPlaneImage; + +typedef struct { + /* STATE */ + int state; // player current state + int prev_state; // player previous state + int pending_state; // player state which is going to now + int target_state; // player state which user want to go to + guint state_change_timeout; + + gboolean section_repeat; + gint section_repeat_start; + gint section_repeat_end; + + gchar *album_art; + + int cmd; + + /* command lock */ + GMutex* cmd_lock; + + /* repeat thread lock */ + GCond* repeat_thread_cond; + GMutex* repeat_thread_mutex; + GThread* repeat_thread; + gboolean repeat_thread_exit; + + /* capture thread */ + GThread* capture_thread; + gboolean capture_thread_exit; + GCond* capture_thread_cond; + GMutex* capture_thread_mutex; + MMPlayerVideoCapture capture; + MMPlayerVideoColorspace video_cs; + MMPlayerMPlaneImage captured; + + /* fakesink handling lock */ + GMutex* fsink_lock; + + /* player attributes */ + MMHandleType attrs; + + /* message callback */ + MMMessageCallback msg_cb; + void* msg_cb_param; + GMutex* msg_cb_lock; + + /* progressive download */ + mm_player_pd_t *pd_downloader; + gchar *pd_file_save_path; + MMPlayerPDMode pd_mode; + + /* streaming player */ + mm_player_streaming_t *streamer; + + /* gstreamer pipeline */ + MMPlayerGstPipelineInfo *pipeline; + gboolean pipeline_is_constructed; + + /* buffering support cbs*/ + mm_player_buffer_need_data_callback need_data_cb; + mm_player_buffer_enough_data_callback enough_data_cb; + mm_player_buffer_seek_data_callback seek_data_cb; + + void* buffer_cb_user_param; + + /* video stream callback */ + mm_player_video_stream_callback video_stream_cb; + void* video_stream_cb_user_param; + int use_video_stream; + + /* audio stram callback */ + mm_player_audio_stream_callback audio_stream_cb; + void* audio_stream_cb_user_param; + + /* audio buffer callback */ + mm_player_audio_stream_callback audio_buffer_cb; + void* audio_buffer_cb_user_param; + + /* video capture callback*/ + gulong video_capture_cb_probe_id; + + /* video frame render error callback */ + mm_player_video_frame_render_error_callback video_frame_render_error_cb; + void* video_frame_render_error_cb_user_param; + + /* sound info */ + MMPlayerSoundInfo sound; + + /* type string */ + gchar *type; + + /* video stream caps parsed by demuxer */ + GstCaps* v_stream_caps; + + /* audio effect infomation */ + MMAudioEffectInfo audio_effect_info; + gboolean bypass_audio_effect; + + gulong audio_cb_probe_id; + + /* for appsrc */ + tBuffer mem_buf; + + /* content profile */ + MMPlayerParseProfile profile; + + /* streaming service type */ + MMStreamingType streaming_type; + + /* autoplugging */ + GList* factories; + gboolean have_dynamic_pad; + GList* parsers; // list of linked parser name + gboolean no_more_pad; + gint num_dynamic_pad; + gboolean has_many_types; + + /* progress callback timer */ + /* FIXIT : since duplicated functionality with get_position + * this function will be deprecated after fixing all application + * which are using it. + */ + guint progress_timer; + + /* timer for sending delayed EOS */ + guint eos_timer; + + /* last point (msec) that player is paused or seeking */ + gint64 last_position; + + /* duration */ + gint64 duration; + + /* data size of http streaming */ + guint64 http_content_size; + + /* last error */ + gchar last_error_msg[1024]; /* FIXIT : should it be dynamic ? */ + + gint videodec_linked; + gint audiodec_linked; + gint videosink_linked; + gint audiosink_linked; + gint textsink_linked; + + /* missing plugin during autoplugging */ + MissingCodec not_supported_codec; + + /*unlinked audio/video mime type */ + gchar *unlinked_video_mime; + gchar *unlinked_audio_mime; + gchar *unlinked_demuxer_mime; + + /* found codec during autoplugging */ + FoundCodec can_support_codec; + + gboolean not_found_demuxer; + + /* support seek even though player is not start */ + MMPlayerPendingSeek pending_seek; + + gboolean doing_seek; + + /* prevent to post msg over and over */ + gboolean posted_msg; + + /* list of sink elements */ + GList* sink_elements; + + /* signal notifiers */ + GList* signals; + guint bus_watcher; + + /* NOTE : if sink elements receive flush start event then it's state will be lost. + * this can happen when doing buffering in streaming pipeline since all control operation + * (play/pause/resume/seek) is requiring server interaction. during 'state lost' situation + * _set_state will not work correctely and state transition message will not posted to our + * gst_callback. + * So. we need to do some special care on the situation. + */ + gboolean state_lost; + + gboolean need_update_content_attrs; + gboolean need_update_content_dur; + + gboolean is_sound_extraction; + + gdouble playback_rate; + /* player state resumed by fast rewind */ + gboolean resumed_by_rewind; + + gboolean is_nv12_tiled; + + MMPlayerASM sm; + + gboolean is_subtitle_off; + + /* contents bitrate for buffering management */ + guint bitrate[MM_PLAYER_STREAM_COUNT_MAX]; + guint total_bitrate; + guint updated_bitrate_count; + guint maximum_bitrate[MM_PLAYER_STREAM_COUNT_MAX]; + guint total_maximum_bitrate; + guint updated_maximum_bitrate_count; + + /* prevent it from posting duplicatly*/ + gboolean sent_bos; + + /* timeout source for lazy pause */ + guint lazy_pause_event_id; + + gboolean keep_detecting_vcodec; + + gboolean play_subtitle; + gboolean use_textoverlay; + + /* PD downloader message callback and param */ + MMMessageCallback pd_msg_cb; + void* pd_msg_cb_param; +} mm_player_t; + +/*=========================================================================================== +| | +| GLOBAL FUNCTION PROTOTYPES | +| | +========================================================================================== */ +#ifdef __cplusplus + extern "C" { +#endif + +int _mmplayer_create_player(MMHandleType hplayer); +int _mmplayer_destroy(MMHandleType hplayer); +int _mmplayer_realize(MMHandleType hplayer); +int _mmplayer_unrealize(MMHandleType hplayer); +int _mmplayer_get_state(MMHandleType hplayer, int* pstate); +int _mmplayer_set_volume(MMHandleType hplayer, MMPlayerVolumeType volume); +int _mmplayer_get_volume(MMHandleType hplayer, MMPlayerVolumeType *volume); +int _mmplayer_set_mute(MMHandleType hplayer, int mute); +int _mmplayer_get_mute(MMHandleType hplayer, int* pmute); +int _mmplayer_start(MMHandleType hplayer); +int _mmplayer_stop(MMHandleType hplayer); +int _mmplayer_pause(MMHandleType hplayer); +int _mmplayer_resume(MMHandleType hplayer); +int _mmplayer_set_position(MMHandleType hplayer, int format, int pos); +int _mmplayer_get_position(MMHandleType hplayer, int format, unsigned long *pos); +int _mmplayer_adjust_subtitle_postion(MMHandleType hplayer, int format, int pos); +int _mmplayer_activate_section_repeat(MMHandleType hplayer, unsigned long start, unsigned long end); +int _mmplayer_deactivate_section_repeat(MMHandleType hplayer); +int _mmplayer_push_buffer(MMHandleType hplayer, unsigned char *buf, int size); +int _mmplayer_set_buffer_need_data_cb(MMHandleType hplayer,mm_player_buffer_need_data_callback callback, void *user_param); +int _mmplayer_set_buffer_enough_data_cb(MMHandleType hplayer,mm_player_buffer_enough_data_callback callback, void *user_param); +int _mmplayer_set_buffer_seek_data_cb(MMHandleType hplayer,mm_player_buffer_seek_data_callback callback, void *user_param); +int _mmplayer_set_playspeed(MMHandleType hplayer, gdouble rate); +int _mmplayer_set_message_callback(MMHandleType hplayer, MMMessageCallback callback, void *user_param); +int _mmplayer_set_videostream_cb(MMHandleType hplayer,mm_player_video_stream_callback callback, void *user_param); +int _mmplayer_set_audiostream_cb(MMHandleType hplayer,mm_player_audio_stream_callback callback, void *user_param); +int _mmplayer_set_videoframe_render_error_cb(MMHandleType hplayer, mm_player_video_frame_render_error_callback callback, void *user_param); +int _mmplayer_set_subtitle_silent (MMHandleType hplayer, int silent); +int _mmplayer_get_subtitle_silent (MMHandleType hplayer, int* silent); +int _mmplayer_get_buffer_position(MMHandleType hplayer, int format, unsigned long* start_pos, unsigned long* stop_pos); +gboolean _mmplayer_update_content_attrs(mm_player_t* player); +/* test API for tuning audio gain. this API should be + * deprecated before the day of final release + */ +int _mmplayer_set_volume_tune(MMHandleType hplayer, MMPlayerVolumeType volume); +int _mmplayer_update_video_param(mm_player_t* player); +int _mmplayer_set_audiobuffer_cb(MMHandleType hplayer, mm_player_audio_stream_callback callback, void *user_param); + +#ifdef __cplusplus + } +#endif + +#endif /* __MM_PLAYER_PRIV_H__ */ diff --git a/src/include/mm_player_streaming.h b/src/include/mm_player_streaming.h new file mode 100644 index 0000000..8766bb3 --- /dev/null +++ b/src/include/mm_player_streaming.h @@ -0,0 +1,75 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_STREAMING_H__ +#define __MM_PLAYER_STREAMING_H__ + +#include +#include +#include +#include "mm_debug.h" + +#define MAX_FILE_BUFFER_NAME_LEN 256 + +#define MIN_BUFFER_PERCENT 0.0 +#define MAX_BUFFER_PERCENT 100.0 +#define MIN_BUFFERING_TIME 2.0 +#define MAX_BUFFERING_TIME 10.0 + +#define DEFAULT_BUFFER_SIZE 4194304 // 4 MBytes +#define DEFAULT_BUFFER_LOW_PERCENT 1.0 // 1% +#define DEFAULT_BUFFER_HIGH_PERCENT 99.0 // 15% +#define DEFAULT_BUFFERING_TIME 3.0 // about 3sec + +#define DEFAULT_FILE_BUFFER_PATH "/opt/media" + +#define STREAMING_USE_FILE_BUFFER +#define STREAMING_USE_MEMORY_BUFFER + +typedef struct +{ + GstElement *buffer; /* buffering element of playback pipeline */ + + gboolean is_buffering; + gint buffering_percent; + + gboolean need_update; + guint buffer_size; + gdouble buffer_high_percent; + gdouble buffer_low_percent; + gdouble buffering_time; + guint buffer_max_bitrate; + guint buffer_avg_bitrate; +}mm_player_streaming_t; + + +mm_player_streaming_t *__mm_player_streaming_create (); +void __mm_player_streaming_initialize (mm_player_streaming_t* streaming_player); +void __mm_player_streaming_deinitialize (mm_player_streaming_t* streaming_player); +void __mm_player_streaming_destroy(mm_player_streaming_t* streaming_player); + +void __mm_player_streaming_set_buffer(mm_player_streaming_t* streaming_player, GstElement * buffer, + gboolean use_buffering, guint buffer_size, gdouble low_percent, gdouble high_percent, gdouble buffering_time, + gboolean use_file, gchar * file_path, guint64 content_size); +void __mm_player_streaming_set_content_bitrate(mm_player_streaming_t* streaming_player, guint max_bitrate, guint avg_bitrate); +void __mm_player_streaming_buffering (mm_player_streaming_t* streaming_player , GstMessage *buffering_msg); + +#endif diff --git a/src/include/mm_player_utils.h b/src/include/mm_player_utils.h new file mode 100644 index 0000000..624df44 --- /dev/null +++ b/src/include/mm_player_utils.h @@ -0,0 +1,278 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MMF_PLAYER_UTILS_H__ +#define __MMF_PLAYER_UTILS_H__ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* general */ +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +#define MMPLAYER_MAX_INT (2147483647) + +#define MMPLAYER_FREEIF(x) \ +if ( x ) \ + g_free( x ); \ +x = NULL; + +#define MMPLAYER_CMD_LOCK(x_player) \ +do \ +{ \ + GMutex* cmd_lock = ((mm_player_t *)x_player)->cmd_lock; \ + if (cmd_lock) \ + g_mutex_lock(cmd_lock); \ + else \ + { \ + debug_log("don't get command lock"); \ + return MM_ERROR_PLAYER_NOT_INITIALIZED; \ + } \ +} while (0); + +#define MMPLAYER_CMD_UNLOCK(x_player) g_mutex_unlock( ((mm_player_t*)x_player)->cmd_lock ) + +#define MMPLAYER_MSG_POST_LOCK(x_player) g_mutex_lock( ((mm_player_t*)x_player)->msg_cb_lock ) +#define MMPLAYER_MSG_POST_UNLOCK(x_player) g_mutex_unlock( ((mm_player_t*)x_player)->msg_cb_lock ) + +#define MMPLAYER_GET_ATTRS(x_player) ((mm_player_t*)x_player)->attrs + +/* sbs : for bluetooth */ +#define MAX_SOUND_DEVICE_LEN 18 + +/* element linking */ +#ifdef GST_EXT_PAD_LINK_UNCHECKED +#define GST_ELEMENT_LINK_FILTERED gst_element_link_filtered_unchecked +#define GST_ELEMENT_LINK_MANY gst_element_link_many_unchecked +#define GST_ELEMENT_LINK gst_element_link_unchecked +#define GST_ELEMENT_LINK_PADS gst_element_link_pads_unchecked +#define GST_PAD_LINK gst_pad_link_unchecked +#else +#define GST_ELEMENT_LINK_FILTERED gst_element_link_filtered +#define GST_ELEMENT_LINK_MANY gst_element_link_many +#define GST_ELEMENT_LINK gst_element_link +#define GST_ELEMENT_UNLINK gst_element_unlink +#define GST_ELEMENT_LINK_PADS gst_element_link_pads +#define GST_PAD_LINK gst_pad_link +#endif + +/* debug caps string */ +#define MMPLAYER_LOG_GST_CAPS_TYPE(x_caps) \ +do \ +{ \ + gchar* caps_type = NULL; \ + caps_type = gst_caps_to_string(x_caps); \ + debug_log ("caps: %s\n", caps_type ); \ + MMPLAYER_FREEIF (caps_type) \ +} while (0); + +/* message posting */ +#define MMPLAYER_POST_MSG( x_player, x_msgtype, x_msg_param ) \ +debug_log("posting %s to application\n", #x_msgtype); \ +__mmplayer_post_message(x_player, x_msgtype, x_msg_param); + +/* setting player state */ +#define MMPLAYER_SET_STATE( x_player, x_state ) \ +debug_log("setting state machine to %d\n", x_state); \ +__mmplayer_set_state(x_player, x_state); + + +#define MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( x_player, x_command ) \ +debug_log("checking player state before doing %s\n", #x_command); \ +switch ( __mmplayer_check_state(x_player, x_command) ) \ +{ \ + case MM_ERROR_PLAYER_INVALID_STATE: \ + return MM_ERROR_PLAYER_INVALID_STATE; \ + break; \ + /* NOTE : for robustness of player. we won't treat it as an error */ \ + case MM_ERROR_PLAYER_NO_OP: \ + return MM_ERROR_NONE; \ + break; \ + case MM_ERROR_PLAYER_DOING_SEEK: \ + return MM_ERROR_PLAYER_DOING_SEEK; \ + default: \ + break; \ +} + +/* setting element state */ +#define MMPLAYER_ELEMENT_SET_STATE( x_element, x_state ) \ +debug_log("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) ) \ +{ \ + debug_error("failed to set state %s to %s\n", #x_state, GST_ELEMENT_NAME( x_element )); \ + goto STATE_CHANGE_FAILED; \ +} + +#define MMPLAYER_CHECK_NULL( x_var ) \ +if ( ! x_var ) \ +{ \ + debug_error("[%s] is NULL\n", #x_var ); \ + goto ERROR; \ +} + +#define MMPLAYER_CHECK_CMD_IF_EXIT( x_player ) \ +if ( x_player->cmd == MMPLAYER_COMMAND_UNREALIZE || x_player->cmd == MMPLAYER_COMMAND_DESTROY ) \ +{ \ + debug_log("it's exit state...\n");\ + goto ERROR; \ +} +/* volume */ +/* +|----|-------|-------|-------|-------| +|Res. | HFK(7) | BT(7) | E.J(7) | SPK(7) | +|----|-------|-------|-------|-------| +*/ + +/* 090424 Fix me : Currently volume is 0~9, so bt volume can be only 0.0 ~ 0.9 */ +#define GET_VOLUME_BT(volume) (volume/10.) + +#if 0 +#define GET_VOLUME_SPK(volume) ((volume) & 0x7F) +#define GET_VOLUME_EARJACK(volume) ((volume >> 7)& 0x7F) +#define GET_VOLUME_HFK(volume) ((volume >> 21) & 0x7F) + +#define SET_VOLUME_SPK(volume,input) (volume |= (input &0x7F)) +#define SET_VOLUME_EARJACK(volume,input) (volume |= ((input & 0x7F)<<7)) +#define SET_VOLUME_BT(volume,input) (volume |= ((input & 0x7F)<<14)) +#define SET_VOLUME_HFK(volume,input) (volume |= ((input & 0x7F)<<21)) +#endif + + +/* pad probe for pipeilne debugging */ +gboolean __util_gst_pad_probe(GstPad *pad, GstBuffer *buffer, gpointer u_data); + +#define MM_PROBE_DEFAULT (0) +#define MM_PROBE_TIMESTAMP (1) +#define MM_PROBE_BUFFERSIZE (1 << 1) +#define MM_PROBE_CAPS (1 << 2) +#define MM_PROBE_BUFFER_DURATION (1 << 3) +#define MM_PROBE_DROP_BUFFER (1 << 4) +#define MM_PROBE_CLOCK_TIME (1 << 5) +/* ... add more */ + +/* messages are treated as warnings bcz those code should not be checked in. + * and no error handling will supported for same manner. + */ +#define MMPLAYER_ADD_PROBE(x_pad, x_flag) \ +debug_warning("adding pad probe\n"); \ +if ( ! gst_pad_add_buffer_probe(x_pad, \ + G_CALLBACK(__util_gst_pad_probe), \ + (gpointer)x_flag) ) \ +{ \ + debug_error("failed to add pad probe\n"); \ +} + + +/* generating dot */ +#define MMPLAYER_GENERATE_DOT_IF_ENABLED( x_player, x_name ) \ +if ( PLAYER_INI()->generate_dot ) \ +{ \ + debug_log("generating dot file(%s)\n", #x_name); \ + GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (player->pipeline->mainbin[MMPLAYER_M_PIPE].gst), \ + GST_DEBUG_GRAPH_SHOW_ALL, x_name); \ +} + +/* signal manipulation */ +#define MMPLAYER_SIGNAL_CONNECT( x_player, x_object, x_signal, x_callback, x_arg ) \ +do \ +{ \ + MMPlayerSignalItem* item = NULL; \ + item = (MMPlayerSignalItem*) g_malloc( sizeof (MMPlayerSignalItem) ); \ + if ( ! item ) \ + { \ + debug_error("cannot connect signal [%s]\n", x_signal ); \ + } \ + else \ + { \ + item->obj = G_OBJECT( x_object ); \ + item->sig = g_signal_connect( G_OBJECT(x_object), x_signal, \ + x_callback, x_arg ); \ + x_player->signals = g_list_append(x_player->signals, item); \ + } \ +} while ( 0 ); + + +/* state */ +#define MMPLAYER_PREV_STATE(x_player) ((mm_player_t*)x_player)->prev_state +#define MMPLAYER_CURRENT_STATE(x_player) ((mm_player_t*)x_player)->state +#define MMPLAYER_PENDING_STATE(x_player) ((mm_player_t*)x_player)->pending_state +#define MMPLAYER_TARGET_STATE(x_player) ((mm_player_t*)x_player)->target_state +#define MMPLAYER_STATE_GET_NAME(state) __get_state_name(state) + +#define MMPLAYER_PRINT_STATE(x_player) \ +debug_log("-----------------------PLAYER STATE-------------------------\n"); \ +debug_log(" prev %s, current %s, pending %s, target %s \n", \ + MMPLAYER_STATE_GET_NAME(MMPLAYER_PREV_STATE(x_player)), \ + MMPLAYER_STATE_GET_NAME(MMPLAYER_CURRENT_STATE(x_player)), \ + MMPLAYER_STATE_GET_NAME(MMPLAYER_PENDING_STATE(x_player)), \ + MMPLAYER_STATE_GET_NAME(MMPLAYER_TARGET_STATE(x_player))); \ +debug_log("------------------------------------------------------------\n"); + + +#define MMPLAYER_STATE_CHANGE_TIMEOUT(x_player ) ((mm_player_t*)x_player)->state_change_timeout + +/* streaming */ +#define MMPLAYER_IS_STREAMING(x_player) __is_streaming(x_player) +#define MMPLAYER_IS_RTSP_STREAMING(x_player) __is_rtsp_streaming(x_player) +#define MMPLAYER_IS_HTTP_STREAMING(x_player) __is_http_streaming(x_player) +#define MMPLAYER_IS_HTTP_PD(x_player) __is_http_progressive_down(x_player) +#define MMPLAYER_IS_HTTP_LIVE_STREAMING(x_player) __is_http_live_streaming(x_player) +#define MMPLAYER_IS_LIVE_STREAMING(x_player) __is_live_streaming(x_player) + +/* etc */ +#define MMF_PLAYER_FILE_BACKUP_PATH "/tmp/media_temp." +#define MMPLAYER_PT_IS_AUDIO( x_pt ) ( strstr(x_pt, "_97") || strstr(x_pt, "audio") ) +#define MMPLAYER_PT_IS_VIDEO( x_pt ) ( strstr(x_pt, "_96") || strstr(x_pt, "video") ) + +bool util_is_sdp_file ( const char *path ); +int64_t uti_get_time ( void ); +int util_get_rank_increase ( const char *factory_class ); +int util_factory_rank_compare(GstPluginFeature *f1, GstPluginFeature *f2); // @ + + +bool util_exist_file_path(const char *file_path); +bool util_write_file_backup(const char *backup_path, char *data_ptr, int data_size); +bool util_remove_file_backup(const char *backup_path); /* For Midi Player */ + +int util_is_midi_type_by_mem(void *mem, int size); +int util_is_midi_type_by_file(const char *file_path); + +char** util_get_cookie_list ( const char *cookies ); +bool util_check_valid_url ( const char *proxy ); + +char* util_get_charset(const char *file_path); + +#ifdef __cplusplus + } +#endif + +#endif /* __MMF_PLAYER_UTILS_H__ */ + diff --git a/src/mm_player.c b/src/mm_player.c new file mode 100644 index 0000000..e50fefb --- /dev/null +++ b/src/mm_player.c @@ -0,0 +1,815 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 "mm_player.h" +#include "mm_player_priv.h" +#include "mm_player_attrs.h" +#include "mm_player_utils.h" +#include "mm_player_ini.h" +#include "mm_debug.h" +#include "mm_player_capture.h" + +int mm_player_create(MMHandleType *player) +{ + int result = MM_ERROR_NONE; + mm_player_t* new_player = NULL; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + if (!g_thread_supported ()) + g_thread_init (NULL); + + MMTA_INIT(); + + __ta__("mm_player_ini_load", + result = mm_player_ini_load(); + ) + if(result != MM_ERROR_NONE) + return result; + + __ta__("mm_player_audio_effect_ini_load", + result = mm_player_audio_effect_ini_load(); + ) + if(result != MM_ERROR_NONE) + return result; + + /* alloc player structure */ + new_player = g_malloc(sizeof(mm_player_t)); + if ( ! new_player ) + { + debug_critical("Cannot allocate memory for player\n"); + goto ERROR; + } + memset(new_player, 0, sizeof(mm_player_t)); + + /* create player lock */ + new_player->cmd_lock = g_mutex_new(); + + if ( ! new_player->cmd_lock ) + { + debug_critical("failed to create player lock\n"); + goto ERROR; + } + + /* create msg callback lock */ + new_player->msg_cb_lock = g_mutex_new(); + + if ( ! new_player->msg_cb_lock ) + { + debug_critical("failed to create msg cb lock\n"); + goto ERROR; + } + __ta__("[KPI] create media player service", + result = _mmplayer_create_player((MMHandleType)new_player); + ) + + if(result != MM_ERROR_NONE) + goto ERROR; + + *player = (MMHandleType)new_player; + + return result; + +ERROR: + + if ( new_player ) + { + if (new_player->cmd_lock) + { + g_mutex_free(new_player->cmd_lock); + new_player->cmd_lock = NULL; + } + + _mmplayer_destroy( (MMHandleType)new_player ); + MMPLAYER_FREEIF( new_player ); + } + + *player = (MMHandleType)0; + return MM_ERROR_PLAYER_NO_FREE_SPACE; // are you sure? +} + +int mm_player_destroy(MMHandleType player) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + __ta__("[KPI] destroy media player service", + result = _mmplayer_destroy(player); + ) + + MMPLAYER_CMD_UNLOCK( player ); + + if (((mm_player_t*)player)->cmd_lock) + { + g_mutex_free(((mm_player_t*)player)->cmd_lock); + ((mm_player_t*)player)->cmd_lock = NULL; + } + + memset( (mm_player_t*)player, 0x00, sizeof(mm_player_t) ); + + /* free player */ + g_free( (void*)player ); + + MMTA_ACUM_ITEM_SHOW_RESULT_TO(MMTA_SHOW_FILE); + + MMTA_RELEASE(); + + return result; +} + + +int mm_player_realize(MMHandleType player) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + __ta__("[KPI] initialize media player service", + result = _mmplayer_realize(player); + ) + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_unrealize(MMHandleType player) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + __ta__("[KPI] cleanup media player service", + result = _mmplayer_unrealize(player); + ) + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_message_callback(MMHandleType player, MMMessageCallback callback, void *user_param) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_message_callback(player, callback, user_param); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + +int mm_player_set_pd_message_callback(MMHandleType player, MMMessageCallback callback, void *user_param) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + result = _mm_player_set_pd_downloader_message_cb(player, callback, user_param); + + return result; +} + +int mm_player_set_audio_stream_callback(MMHandleType player, mm_player_audio_stream_callback callback, void *user_param) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_audiostream_cb(player, callback, user_param); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_audio_buffer_callback(MMHandleType player, mm_player_audio_stream_callback callback, void *user_param) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_audiobuffer_cb(player, callback, user_param); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + +int mm_player_set_video_stream_callback(MMHandleType player, mm_player_video_stream_callback callback, void *user_param) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_videostream_cb(player, callback, user_param); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + +int mm_player_set_video_frame_render_error_callback(MMHandleType player, mm_player_video_frame_render_error_callback callback, void *user_param) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_videoframe_render_error_cb(player, callback, user_param); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + +int mm_player_do_video_capture(MMHandleType player) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_do_video_capture(player); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + +int mm_player_set_buffer_need_data_callback(MMHandleType player, mm_player_buffer_need_data_callback callback, void * user_param) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_buffer_need_data_cb(player, callback, user_param); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_buffer_enough_data_callback(MMHandleType player, mm_player_buffer_enough_data_callback callback, void * user_param) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_buffer_enough_data_cb(player, callback, user_param); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_buffer_seek_data_callback(MMHandleType player, mm_player_buffer_seek_data_callback callback, void * user_param) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_buffer_seek_data_cb(player, callback, user_param); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_volume(MMHandleType player, MMPlayerVolumeType *volume) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(volume, MM_ERROR_INVALID_ARGUMENT); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_volume(player, *volume); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_get_volume(MMHandleType player, MMPlayerVolumeType *volume) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(volume, MM_ERROR_INVALID_ARGUMENT); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_get_volume(player, volume); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_mute(MMHandleType player, int mute) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_mute(player, mute); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_get_mute(MMHandleType player, int *mute) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(mute, MM_ERROR_INVALID_ARGUMENT); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_get_mute(player, mute); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_get_state(MMHandleType player, MMPlayerStateType *state) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(state, MM_ERROR_COMMON_INVALID_ARGUMENT); + + *state = MM_PLAYER_STATE_NULL; + + result = _mmplayer_get_state(player, (int*)state); + + return result; +} + +/* NOTE : Not supported */ +int mm_player_change_videosink(MMHandleType player, MMDisplaySurfaceType display_surface_type, void *display_overlay) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + return MM_ERROR_NOT_SUPPORT_API; +} + +int mm_player_push_buffer(MMHandleType player, unsigned char *buf, int size) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + //MMPLAYER_CMD_LOCK( player ); + + //MMTA_ACUM_ITEM_BEGIN("[KPI] start media player service", false); + result = _mmplayer_push_buffer(player, buf, size); + + //MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_start(MMHandleType player) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + MMTA_ACUM_ITEM_BEGIN("[KPI] start media player service", false); + result = _mmplayer_start(player); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_stop(MMHandleType player) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + __ta__("[KPI] stop media player service", + result = _mmplayer_stop(player); + ) + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_pause(MMHandleType player) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + __ta__("[KPI] pause media player service", + result = _mmplayer_pause(player); + ) + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_resume(MMHandleType player) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + __ta__("[KPI] resume media player service", + result = _mmplayer_resume(player); + ) + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_activate_section_repeat(MMHandleType player, int start_pos, int end_pos) +{ + int result = MM_ERROR_NONE; + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_activate_section_repeat(player, start_pos, end_pos); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_deactivate_section_repeat(MMHandleType player) +{ + int result = MM_ERROR_NONE; + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_deactivate_section_repeat(player); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_play_speed(MMHandleType player, float rate) +{ + int result = MM_ERROR_NONE; + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_playspeed(player, rate); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_position(MMHandleType player, MMPlayerPosFormatType format, int pos) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + if (format >= MM_PLAYER_POS_FORMAT_NUM) + { + debug_error("wrong format\n"); + return MM_ERROR_COMMON_INVALID_ARGUMENT; + } + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_position(player, format, pos); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_get_position(MMHandleType player, MMPlayerPosFormatType format, int *pos) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(pos, MM_ERROR_COMMON_INVALID_ARGUMENT); + + if (format >= MM_PLAYER_POS_FORMAT_NUM) + { + debug_error("wrong format\n"); + return MM_ERROR_COMMON_INVALID_ARGUMENT; + } + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_get_position(player, (int)format, (unsigned long*)pos); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + +int mm_player_get_buffer_position(MMHandleType player, MMPlayerPosFormatType format, int *start_pos, int *stop_pos) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(start_pos && stop_pos, MM_ERROR_COMMON_INVALID_ARGUMENT); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_get_buffer_position(player, (int)format, (unsigned long*)start_pos, (unsigned long*)stop_pos ); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + +int mm_player_adjust_subtitle_position(MMHandleType player, MMPlayerPosFormatType format, int pos) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + if (format >= MM_PLAYER_POS_FORMAT_NUM) + { + debug_error("wrong format\n"); + return MM_ERROR_COMMON_INVALID_ARGUMENT; + } + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_adjust_subtitle_postion(player, format, pos); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_subtitle_silent(MMHandleType player, int silent) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_set_subtitle_silent(player, silent); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_get_subtitle_silent(MMHandleType player, int* silent) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_CMD_LOCK( player ); + + result = _mmplayer_get_subtitle_silent(player, silent); + + MMPLAYER_CMD_UNLOCK( player ); + + return result; +} + + +int mm_player_set_attribute(MMHandleType player, char **err_attr_name, const char *first_attribute_name, ...) +{ + int result = MM_ERROR_NONE; + va_list var_args; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(first_attribute_name, MM_ERROR_COMMON_INVALID_ARGUMENT); + + va_start (var_args, first_attribute_name); + result = _mmplayer_set_attribute(player, err_attr_name, first_attribute_name, var_args); + va_end (var_args); + + return result; +} + + +int mm_player_get_attribute(MMHandleType player, char **err_attr_name, const char *first_attribute_name, ...) +{ + int result = MM_ERROR_NONE; + va_list var_args; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(first_attribute_name, MM_ERROR_COMMON_INVALID_ARGUMENT); + + va_start (var_args, first_attribute_name); + result = _mmplayer_get_attribute(player, err_attr_name, first_attribute_name, var_args); + va_end (var_args); + + return result; +} + + +int mm_player_get_attribute_info(MMHandleType player, const char *attribute_name, MMPlayerAttrsInfo *info) +{ + int result = MM_ERROR_NONE; + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(attribute_name, MM_ERROR_COMMON_INVALID_ARGUMENT); + return_val_if_fail(info, MM_ERROR_COMMON_INVALID_ARGUMENT); + + result = _mmplayer_get_attributes_info((MMHandleType)player, attribute_name, info); + + return result; +} + +int mm_player_get_pd_status(MMHandleType player, guint64 *current_pos, guint64 *total_size) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(current_pos, MM_ERROR_COMMON_INVALID_ARGUMENT); + return_val_if_fail(total_size, MM_ERROR_COMMON_INVALID_ARGUMENT); + + result = _mmplayer_get_pd_downloader_status(player, current_pos, total_size); + + return result; +} + +int mm_player_get_track_count(MMHandleType player, MMPlayerTrackType track_type, int *count) +{ + int result = MM_ERROR_NONE; + + debug_log("\n"); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(count, MM_ERROR_COMMON_INVALID_ARGUMENT); + + result = _mmplayer_get_track_count(player, track_type, count); + + return result; + +} diff --git a/src/mm_player_asm.c b/src/mm_player_asm.c new file mode 100644 index 0000000..e607806 --- /dev/null +++ b/src/mm_player_asm.c @@ -0,0 +1,247 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 "mm_player_priv.h" +#include "mm_player_asm.h" + +static ASM_sound_events_t __mmplayer_asm_get_event_type(gint type); + +gint +_mmplayer_asm_register(MMPlayerASM* sm, ASM_sound_cb_t callback, void* param) +{ + /* read mm-session type */ + gint sessionType = MM_SESSION_TYPE_SHARE; + gint errorcode = MM_ERROR_NONE; + gint asm_handle = -1; + gint event_type = ASM_EVENT_NONE; + gint pid = -1; + + debug_log("\n"); + + if ( ! sm ) + { + debug_error("invalid session handle\n"); + return MM_ERROR_PLAYER_NOT_INITIALIZED; + } + + /* check if it's running on the media_server */ + if ( sm->pid > 0 ) + { + pid = sm->pid; + debug_log("mm-player is running on different process. Just faking pid to [%d]. :-p\n", pid); + } + else + { + debug_log("no pid has assigned. using default(current) context\n"); + } + + /* read session type */ + errorcode = _mm_session_util_read_type(pid, &sessionType); + if ( errorcode ) + { + debug_warning("Read MMSession Type failed. use default \"share\" type\n"); + sessionType = MM_SESSION_TYPE_SHARE; + + /* init session */ + errorcode = mm_session_init(sessionType); + if ( errorcode ) + { + debug_critical("mm_session_init() failed\n"); + return errorcode; + } + } + + /* check if it's CALL */ + if ( sessionType == MM_SESSION_TYPE_CALL ) + { + debug_log("session type is CALL\n"); + sm->event = ASM_EVENT_CALL; + return MM_ERROR_NONE; + } + else if ( sessionType == MM_SESSION_TYPE_VIDEOCALL ) + { + debug_log("session type is VIDEOCALL\n"); + sm->event = ASM_EVENT_VIDEOCALL; + return MM_ERROR_NONE; + } + + /* interpret session type */ + event_type = __mmplayer_asm_get_event_type(sessionType); + + + + /* register audio-session-manager callback */ + if( ! ASM_register_sound(pid, &asm_handle, event_type, ASM_STATE_NONE, callback, (void*)param, ASM_RESOURCE_NONE, &errorcode)) + { + debug_critical("ASM_register_sound() failed\n"); + return errorcode; + } + + /* now succeed to register our callback. take result */ + sm->handle = asm_handle; + sm->state = ASM_STATE_NONE; + sm->event = event_type; + + return MM_ERROR_NONE; +} + +gint +_mmplayer_asm_deregister(MMPlayerASM* sm) +{ + gint event_type = ASM_EVENT_NONE; + gint errorcode = 0; + gint pid = -1; + + if ( ! sm ) + { + debug_error("invalid session handle\n"); + return MM_ERROR_PLAYER_NOT_INITIALIZED; + } + + /* check if it's running on the media_server */ + if ( sm->pid > 0 ) + { + pid = sm->pid; + debug_log("mm-player is running on different process. Just faking pid to [%d]. :-p\n", pid); + } + else + { + debug_log("no pid has assigned. using default(current) context\n"); + } + + /* check if it's CALL */ + if(sm->event == ASM_EVENT_CALL || sm->event == ASM_EVENT_VIDEOCALL) + { + debug_log("session type is VOICE or VIDEO CALL (%d)\n", sm->event); + return MM_ERROR_NONE; + } + event_type = sm->event; + + if( ! ASM_unregister_sound( sm->handle, event_type, &errorcode) ) + { + debug_error("Unregister sound failed 0x%X\n", errorcode); + return MM_ERROR_POLICY_INTERNAL; + } + + return MM_ERROR_NONE; +} + +gint _mmplayer_asm_set_state(MMHandleType hplayer, ASM_sound_states_t state) +{ + gint event_type = ASM_EVENT_NONE; + gint pid = -1; + ASM_resource_t resource = ASM_RESOURCE_NONE; + mm_player_t *player = (mm_player_t *)hplayer; + MMPlayerASM* sm = &player->sm; + + if ( ! sm ) + { + debug_error("invalid session handle\n"); + return MM_ERROR_PLAYER_NOT_INITIALIZED; + } + + /* check if it's running on the media_server */ + if ( sm->pid > 0 ) + { + pid = sm->pid; + debug_log("mm-player is running on different process. Just faking pid to [%d]. :-p\n", pid); + } + else + { + debug_log("no pid has assigned. using default(current) context\n"); + } + + /* check if it's CALL */ + if(sm->event == ASM_EVENT_CALL || sm->event == ASM_EVENT_VIDEOCALL) + { + debug_log("session type is VOICE or VIDEO CALL (%d)\n", sm->event); + return MM_ERROR_NONE; + } + + + if ( ! sm->by_asm_cb )//|| sm->state == ASM_STATE_PLAYING ) + { + int ret = 0; + event_type = sm->event; + + /* check if there is video */ + /* NOTE: resource can be set as NONE when it's not occupied or unknown resource is used. */ + if(ASM_STATE_PLAYING == state || ASM_STATE_PAUSE == state) + { + if(player->pipeline && player->pipeline->videobin) + resource = ASM_RESOURCE_VIDEO_OVERLAY | ASM_RESOURCE_HW_DECODER; + } + + if( ! ASM_set_sound_state( sm->handle, event_type, state, resource, &ret) ) + { + debug_error("Set state to [%d] failed 0x%X\n", state, ret); + return MM_ERROR_POLICY_BLOCKED; + } + + sm->state = state; + } + else + { + sm->by_asm_cb = 0; + sm->state = state; + } + + return MM_ERROR_NONE; +} + +static ASM_sound_events_t +__mmplayer_asm_get_event_type(gint type) +{ + gint event_type = ASM_EVENT_NONE; + + /* interpret session type */ + switch(type) + { + case MM_SESSION_TYPE_SHARE: + event_type = ASM_EVENT_SHARE_MMPLAYER; + break; + + case MM_SESSION_TYPE_EXCLUSIVE: + event_type = ASM_EVENT_EXCLUSIVE_MMPLAYER; + break; + + case MM_SESSION_TYPE_NOTIFY: + event_type = ASM_EVENT_NOTIFY; + break; + + case MM_SESSION_TYPE_ALARM: + event_type = ASM_EVENT_ALARM; + break; + + case MM_SESSION_TYPE_EMERGENCY: + event_type = ASM_EVENT_EMERGENCY; + break; + + default: + debug_critical("unexpected case!\n"); + event_type = ASM_EVENT_SHARE_MMPLAYER; + break; + } + + return event_type; +} diff --git a/src/mm_player_attrs.c b/src/mm_player_attrs.c new file mode 100644 index 0000000..1d511d5 --- /dev/null +++ b/src/mm_player_attrs.c @@ -0,0 +1,1038 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 "mm_player_priv.h" +#include "mm_player_attrs.h" + +/*=========================================================================================== +| | +| LOCAL DEFINITIONS AND DECLARATIONS FOR MODULE | +| | +========================================================================================== */ + +typedef struct{ + char *name; + int value_type; + int flags; // r, w + void *default_value; + int valid_type; // validity type + int value_min; //<- set validity value range + int value_max; //-> +}MMPlayerAttrsSpec; + +/*--------------------------------------------------------------------------- +| LOCAL FUNCTION PROTOTYPES: | +---------------------------------------------------------------------------*/ +int +__mmplayer_apply_attribute(MMHandleType handle, const char *attribute_name); + +/*=========================================================================================== +| | +| FUNCTION DEFINITIONS | +| | +========================================================================================== */ + +int +_mmplayer_get_attribute(MMHandleType handle, char **err_attr_name, const char *attribute_name, va_list args_list) +{ + int result = MM_ERROR_NONE; + MMHandleType attrs = 0; + + debug_fenter(); + + /* NOTE : Don't need to check err_attr_name because it can be set NULL */ + /* if it's not want to know it. */ + return_val_if_fail(attribute_name, MM_ERROR_COMMON_INVALID_ARGUMENT); + return_val_if_fail(handle, MM_ERROR_COMMON_INVALID_ARGUMENT); + + attrs = MM_PLAYER_GET_ATTRS(handle); + + result = mm_attrs_get_valist(attrs, err_attr_name, attribute_name, args_list); + + if ( result != MM_ERROR_NONE) + debug_error("failed to get %s attribute\n", attribute_name); + + debug_fleave(); + + return result; +} + +int +_mmplayer_set_attribute(MMHandleType handle, char **err_attr_name, const char *attribute_name, va_list args_list) +{ + int result = MM_ERROR_NONE; + MMHandleType attrs = 0; + + debug_fenter(); + + /* NOTE : Don't need to check err_attr_name because it can be set NULL */ + /* if it's not want to know it. */ + return_val_if_fail(attribute_name, MM_ERROR_COMMON_INVALID_ARGUMENT); + return_val_if_fail(handle, MM_ERROR_COMMON_INVALID_ARGUMENT); + + attrs = MM_PLAYER_GET_ATTRS(handle); + + /* set attributes and commit them */ + result = mm_attrs_set_valist(attrs, err_attr_name, attribute_name, args_list); + + if (result != MM_ERROR_NONE) + { + debug_error("failed to set %s attribute\n", attribute_name); + return result; + } + + result = __mmplayer_apply_attribute(handle, attribute_name); + if (result != MM_ERROR_NONE) + { + debug_error("failed to apply attributes\n"); + return result; + } + + debug_fleave(); + + return result; +} + +int +_mmplayer_get_attributes_info(MMHandleType handle, const char *attribute_name, MMPlayerAttrsInfo *dst_info) +{ + int result = MM_ERROR_NONE; + MMHandleType attrs = 0; + MMAttrsInfo src_info = {0, }; + + debug_fenter(); + + return_val_if_fail(attribute_name, MM_ERROR_COMMON_INVALID_ARGUMENT); + return_val_if_fail(dst_info, MM_ERROR_COMMON_INVALID_ARGUMENT); + return_val_if_fail(handle, MM_ERROR_COMMON_INVALID_ARGUMENT); + + attrs = MM_PLAYER_GET_ATTRS(handle); + + result = mm_attrs_get_info_by_name(attrs, attribute_name, &src_info); + + if ( result != MM_ERROR_NONE) + { + debug_error("failed to get attribute info\n"); + return result; + } + + memset(dst_info, 0x00, sizeof(MMPlayerAttrsInfo)); + + dst_info->type = src_info.type; + dst_info->flag = src_info.flag; + dst_info->validity_type= src_info.validity_type; + + switch(src_info.validity_type) + { + case MM_ATTRS_VALID_TYPE_INT_ARRAY: + dst_info->int_array.array = src_info.int_array.array; + dst_info->int_array.count = src_info.int_array.count; + dst_info->int_array.d_val = src_info.int_array.dval; + break; + + case MM_ATTRS_VALID_TYPE_INT_RANGE: + dst_info->int_range.min = src_info.int_range.min; + dst_info->int_range.max = src_info.int_range.max; + dst_info->int_range.d_val = src_info.int_range.dval; + break; + + case MM_ATTRS_VALID_TYPE_DOUBLE_ARRAY: + dst_info->double_array.array = src_info.double_array.array; + dst_info->double_array.count = src_info.double_array.count; + dst_info->double_array.d_val = src_info.double_array.dval; + break; + + case MM_ATTRS_VALID_TYPE_DOUBLE_RANGE: + dst_info->double_range.min = src_info.double_range.min; + dst_info->double_range.max = src_info.double_range.max; + dst_info->double_range.d_val = src_info.double_range.dval; + break; + + default: + break; + } + + debug_fleave(); + + return result; +} + +int +__mmplayer_apply_attribute(MMHandleType handle, const char *attribute_name) +{ + MMHandleType attrs = 0; + mm_player_t* player = 0; + + debug_fenter(); + + return_val_if_fail(handle, MM_ERROR_COMMON_INVALID_ARGUMENT); + return_val_if_fail(attribute_name, MM_ERROR_COMMON_INVALID_ARGUMENT); + + attrs = MM_PLAYER_GET_ATTRS(handle); + player = MM_PLAYER_CAST(handle); + + if ( g_strrstr(attribute_name, "display") ) + { + /* check videosink element is created */ + if ( !player->pipeline || + !player->pipeline->videobin || + !player->pipeline->videobin[MMPLAYER_V_SINK].gst ) + { + debug_warning("videosink element is not yet ready"); + /* + * The attribute should be committed even though videobin is not created yet. + * So, true should be returned here. + * Otherwise, video can be diaplayed abnormal. + */ + return MM_ERROR_NONE; + } + + if ( MM_ERROR_NONE != _mmplayer_update_video_param( player ) ) + { + debug_error("failed to update video param"); + return MM_ERROR_PLAYER_INTERNAL; + } + } + + debug_fleave(); + + return MM_ERROR_NONE; +} + +MMHandleType +_mmplayer_construct_attribute(MMHandleType handle) +{ + int idx = 0; + MMHandleType attrs = 0; + int num_of_attrs = 0; + mmf_attrs_construct_info_t *base = NULL; + gchar *system_ua = NULL; + gchar *system_proxy = NULL; + + debug_fenter(); + + return_val_if_fail (handle, 0); + + MMPlayerAttrsSpec player_attrs[] = + { + { + "profile_uri", // name + MM_ATTRS_TYPE_STRING, // type + MM_ATTRS_FLAG_RW, // flag + (void *) NULL, // default value + MM_ATTRS_VALID_TYPE_NONE, // validity type + 0, // validity min value + 0 // validity max value + }, + { + "profile_user_param", + MM_ATTRS_TYPE_DATA, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "profile_play_count", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 1, // -1 : repeat continually + MM_ATTRS_VALID_TYPE_INT_RANGE, + -1, + MMPLAYER_MAX_INT + }, + { + "profile_prepare_async", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + 1 + }, + { /* update registry for downloadable codec */ + "profile_update_registry", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + 1 + }, + { + "streaming_type", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) STREAMING_SERVICE_NONE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + STREAMING_SERVICE_VOD, + STREAMING_SERVICE_NUM + }, + { + "streaming_udp_timeout", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 10000, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "streaming_user_agent", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "streaming_wap_profile", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "streaming_network_bandwidth", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 128000, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "streaming_cookie", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "streaming_proxy", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "subtitle_uri", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "content_duration", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_bitrate", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_max_bitrate", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_video_found", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + 1 + }, + { + "content_video_codec", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "content_video_bitrate", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_video_fps", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_video_width", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_video_height", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_video_track_num", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_audio_found", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + 1 + }, + { + "content_audio_codec", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "content_audio_bitrate", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_audio_channels", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_audio_samplerate", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_audio_track_num", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "content_audio_format", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "tag_artist", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "tag_title", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "tag_album", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL + }, + { + "tag_genre", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "tag_author", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "tag_copyright", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "tag_date", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "tag_description", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "tag_track_num", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "tag_album_cover", + MM_ATTRS_TYPE_DATA, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "display_roi_x", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "display_roi_y", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "display_roi_width", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 480, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "display_roi_height", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 800, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "display_rotation", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) MM_DISPLAY_ROTATION_NONE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + MM_DISPLAY_ROTATION_NONE, + MM_DISPLAY_ROTATION_270 + }, + { + "display_visible", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) TRUE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + 1 + }, + { + "display_method", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) MM_DISPLAY_METHOD_LETTER_BOX, + MM_ATTRS_VALID_TYPE_INT_RANGE, + MM_DISPLAY_METHOD_LETTER_BOX, + MM_DISPLAY_METHOD_CUSTOM_ROI + }, + { + "display_overlay", + MM_ATTRS_TYPE_DATA, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "display_overlay_user_data", + MM_ATTRS_TYPE_DATA, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "display_zoom", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 1, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 1, + MMPLAYER_MAX_INT + }, + { + "display_surface_type", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) MM_DISPLAY_SURFACE_NULL, + MM_ATTRS_VALID_TYPE_INT_RANGE, + MM_DISPLAY_SURFACE_X, + MM_DISPLAY_SURFACE_X_EXT + }, + { + "display_evas_surface_sink", + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_READABLE, + (void *) PLAYER_INI()->videosink_element_evas, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + }, + { + "display_force_aspect_ration", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 1, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "display_width", // dest width of fimcconvert ouput + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "display_height", // dest height of fimcconvert ouput + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "display_evas_do_scaling", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) TRUE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + FALSE, + TRUE + }, + { + "sound_fadeup", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) FALSE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + FALSE, + TRUE + }, + { + "sound_fadedown", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) FALSE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + FALSE, + TRUE + }, + { + "sound_volume_type", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) MM_SOUND_VOLUME_TYPE_MEDIA, + MM_ATTRS_VALID_TYPE_INT_RANGE, + MM_SOUND_VOLUME_TYPE_SYSTEM, + MM_SOUND_VOLUME_TYPE_CALL + }, + { + "sound_route", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) MM_AUDIOROUTE_USE_EXTERNAL_SETTING, + MM_ATTRS_VALID_TYPE_INT_RANGE, + MM_AUDIOROUTE_USE_EXTERNAL_SETTING, + MM_AUDIOROUTE_CAPTURE_STEREOMIC_ONLY + }, + { + "sound_stop_when_unplugged", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) TRUE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + FALSE, + TRUE + }, + { + "sound_application_pid", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "sound_spk_out_only", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) FALSE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + FALSE, + TRUE + }, + { + "sound_priority", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, // 0: normal, 1: high 2: high with sound transition + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + 2 + }, + { + "audio_latency_mode", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 1, // 0: low latency, 1: middle latency 2: high latency + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + 2 + }, + { + "pcm_extraction", // enable pcm extraction + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) FALSE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + FALSE, + TRUE + }, + { + "pcm_extraction_samplerate", // set samplerate for pcm extraction + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 8000, // hz + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "pcm_extraction_depth", // set depth for pcm extraction + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 16, // bits + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "pcm_extraction_channels", // set channels for pcm extraction + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 1, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "pcm_extraction_start_msec", // set start position to extract pcm + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "pcm_extraction_end_msec", // set end position to extract pcm + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "profile_smooth_repeat", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) FALSE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "profile_progress_interval", // will be deprecated + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 500, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "display_x", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "display_y", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) 0, + MM_ATTRS_VALID_TYPE_INT_RANGE, + 0, + MMPLAYER_MAX_INT + }, + { + "pd_mode", + MM_ATTRS_TYPE_INT, + MM_ATTRS_FLAG_RW, + (void *) MM_PLAYER_PD_MODE_NONE, + MM_ATTRS_VALID_TYPE_INT_RANGE, + MM_PLAYER_PD_MODE_NONE, + MM_PLAYER_PD_MODE_URI // not tested yet, because of no fixed scenario + }, + { + "pd_location", // location of the file to write + MM_ATTRS_TYPE_STRING, + MM_ATTRS_FLAG_RW, + (void *) NULL, + MM_ATTRS_VALID_TYPE_NONE, + 0, + 0 + } + }; + + num_of_attrs = ARRAY_SIZE(player_attrs); + + base = (mmf_attrs_construct_info_t* )malloc(num_of_attrs * sizeof(mmf_attrs_construct_info_t)); + + if ( !base ) + { + debug_error("failed to alloc attrs constructor"); + return 0; + } + + /* initialize values of attributes */ + for ( idx = 0; idx < num_of_attrs; idx++ ) + { + base[idx].name = player_attrs[idx].name; + base[idx].value_type = player_attrs[idx].value_type; + base[idx].flags = player_attrs[idx].flags; + base[idx].default_value = player_attrs[idx].default_value; + } + + attrs = mmf_attrs_new_from_data( + "mmplayer_attrs", + base, + num_of_attrs, + NULL, + NULL); + + /* clean */ + MMPLAYER_FREEIF(base); + + if ( !attrs ) + { + debug_error("failed to create player attrs"); + return 0; + } + + /* set validity type and range */ + for ( idx = 0; idx < num_of_attrs; idx++ ) + { + switch ( player_attrs[idx].valid_type) + { + case MM_ATTRS_VALID_TYPE_INT_RANGE: + { + mmf_attrs_set_valid_type (attrs, idx, MM_ATTRS_VALID_TYPE_INT_RANGE); + mmf_attrs_set_valid_range (attrs, idx, + player_attrs[idx].value_min, + player_attrs[idx].value_max, + player_attrs[idx].default_value); + } + break; + + case MM_ATTRS_VALID_TYPE_INT_ARRAY: + case MM_ATTRS_VALID_TYPE_DOUBLE_ARRAY: + case MM_ATTRS_VALID_TYPE_DOUBLE_RANGE: + default: + break; + } + } + + /* set proxy and user agent */ + system_ua = vconf_get_str(VCONFKEY_ADMIN_UAGENT); + system_proxy = vconf_get_str(VCONFKEY_NETWORK_PROXY); + + if (system_ua) + { + mm_attrs_set_string_by_name(attrs, "streaming_user_agent", system_ua); + g_free(system_ua); + } + + if (system_proxy) + { + mm_attrs_set_string_by_name(attrs, "streaming_proxy", system_proxy); + g_free(system_proxy); + } + + /* commit */ + mmf_attrs_commit(attrs); + + debug_fleave(); + + return attrs; +} + +bool +_mmplayer_deconstruct_attribute(MMHandleType handle) // @ +{ + debug_fenter(); + + mm_player_t *player = MM_PLAYER_CAST(handle); + + return_val_if_fail ( player, FALSE ); + + if (player->attrs) + { + mmf_attrs_free (player->attrs); + player->attrs = 0; + } + + debug_fleave(); + + return TRUE; +} diff --git a/src/mm_player_audioeffect.c b/src/mm_player_audioeffect.c new file mode 100644 index 0000000..6141118 --- /dev/null +++ b/src/mm_player_audioeffect.c @@ -0,0 +1,1044 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi ,YeJin Cho , YoungHwan An + * + * 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 "mm_player_audioeffect.h" +#include "mm_player_ini.h" +#include "mm_player_priv.h" +#include + + +int +mm_player_get_foreach_present_supported_effect_type(MMHandleType player, MMAudioEffectType effect_type, mmplayer_supported_audio_effect_cb foreach_cb, void *user_data) +{ + int result = MM_ERROR_NONE; + mm_sound_device_in device_in; + mm_sound_device_out device_out; + int i = 0; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* get status if speaker is activated */ + result = mm_sound_get_active_device(&device_in, &device_out); + if ( result ) { + debug_fleave(); + debug_error("mm_sound_get_active_device() failed [%x]!!", result); + return result; + } + + /* preset */ + if (effect_type == MM_AUDIO_EFFECT_TYPE_PRESET) + { + for ( i = 0; i < MM_AUDIO_EFFECT_PRESET_NUM; i++ ) + { + if (PLAYER_INI()->audio_effect_preset_list[i] ) + { + if (device_out == MM_SOUND_DEVICE_OUT_SPEAKER && + PLAYER_INI()->audio_effect_preset_earphone_only_list[i]) + { + continue; + } + if (!foreach_cb(effect_type,i, user_data)) + { + goto CALLBACK_ERROR; + } + } + } + } + /* custom */ + else if (effect_type == MM_AUDIO_EFFECT_TYPE_CUSTOM) + { + for ( i = 0; i < MM_AUDIO_EFFECT_CUSTOM_NUM; i++ ) + { + if (PLAYER_INI()->audio_effect_custom_list[i] ) + { + if (device_out == MM_SOUND_DEVICE_OUT_SPEAKER && + PLAYER_INI()->audio_effect_custom_earphone_only_list[i]) + { + continue; + } + if (!foreach_cb(effect_type,i, user_data)) + { + goto CALLBACK_ERROR; + } + } + } + } + else + { + debug_error("invalid effect type(%d)", effect_type); + result = MM_ERROR_INVALID_ARGUMENT; + } + + debug_fleave(); + + return result; + +CALLBACK_ERROR: + debug_error("foreach callback returned error"); + debug_fleave(); + return MM_ERROR_PLAYER_INTERNAL; +} + + +int +__mmplayer_set_harmony_effect(mm_player_t *player, GstElement *audio_effect_element) +{ + gint *ext_effect_level_list = NULL; + int count = 1; /* start from 1, because of excepting eq index */ + int ext_level_index = 0; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( audio_effect_element, MM_ERROR_INVALID_ARGUMENT ); + + /* Custom EQ */ + if( PLAYER_INI()->audio_effect_custom_eq_band_num ) + { + debug_log("pass custom EQ level list to audio effect plugin"); + /* set custom-equalizer level list */ + g_object_set(audio_effect_element, "custom-eq", player->audio_effect_info.custom_eq_level, NULL); + } + else + { + debug_warning("no custom EQ"); + } + + /* Custom Extension effects */ + if( PLAYER_INI()->audio_effect_custom_ext_num ) + { + debug_log("pass custom extension level list to audio effect plugin"); + ext_effect_level_list = player->audio_effect_info.custom_ext_level_for_plugin; + if (!ext_effect_level_list) { + ext_effect_level_list = (gint*) malloc (sizeof(gint)*PLAYER_INI()->audio_effect_custom_ext_num); + if (!ext_effect_level_list) + { + debug_error("memory allocation for extension effect list failed"); + return MM_ERROR_OUT_OF_MEMORY; + } + else + { + memset (ext_effect_level_list, 0, PLAYER_INI()->audio_effect_custom_ext_num); + } + } + + while ( count < MM_AUDIO_EFFECT_CUSTOM_NUM ) + { + if ( PLAYER_INI()->audio_effect_custom_list[count] ) + { + ext_effect_level_list[ext_level_index] = player->audio_effect_info.custom_ext_level[count-1]; + ext_level_index++; + if (ext_level_index == PLAYER_INI()->audio_effect_custom_ext_num) + { + break; + } + } + count++; + } + + /* set custom-extension effects level list */ + g_object_set(audio_effect_element, "custom-ext", ext_effect_level_list, NULL); + } + else + { + debug_warning("no custom extension effect"); + } + + /* order action to audio effect plugin */ + g_object_set(audio_effect_element, "filter-action", MM_AUDIO_EFFECT_TYPE_CUSTOM, NULL); + debug_log("filter-action = %d", MM_AUDIO_EFFECT_TYPE_CUSTOM); + + debug_fleave(); + + return result; +} + + +gboolean +__mmplayer_is_earphone_only_effect_type(mm_player_t *player, MMAudioEffectType effect_type, int effect) +{ + gboolean result = FALSE; + int i = 0; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + /* preset */ + if (effect_type == MM_AUDIO_EFFECT_TYPE_PRESET) + { + if (PLAYER_INI()->audio_effect_preset_earphone_only_list[effect]) + { + debug_msg("this preset effect(%d) is only available with earphone", effect); + result = TRUE; + } + } + /* custom */ + else if (effect_type == MM_AUDIO_EFFECT_TYPE_CUSTOM) + { + for (i = 1; i < MM_AUDIO_EFFECT_CUSTOM_NUM; i++) /* it starts from 1(except testing for EQ) */ + { + if (PLAYER_INI()->audio_effect_custom_earphone_only_list[i]) + { + /* check if the earphone only custom effect was set */ + if (player->audio_effect_info.custom_ext_level[i-1]) + { + debug_msg("this custom effect(%d) is only available with earphone", i); + result = TRUE; + } + } + } + } + else + { + debug_error("invalid effect type(%d)", effect_type); + } + + debug_fleave(); + + return result; +} + + +gboolean +_mmplayer_is_supported_effect_type(MMAudioEffectType effect_type, int effect) +{ + gboolean result = TRUE; + mm_sound_device_in device_in; + mm_sound_device_out device_out; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + /* get status if speaker is activated */ + ret = mm_sound_get_active_device(&device_in, &device_out); + if ( ret ) { + debug_fleave(); + debug_error("mm_sound_get_active_device() failed [%x]!!", ret); + result = FALSE; + } + else + { + /* preset */ + if (effect_type == MM_AUDIO_EFFECT_TYPE_PRESET) + { + if ( effect < MM_AUDIO_EFFECT_PRESET_AUTO || effect >= MM_AUDIO_EFFECT_PRESET_NUM ) + { + debug_error("out of range, preset effect(%d)", effect); + result = FALSE; + } + if (!PLAYER_INI()->audio_effect_preset_list[effect]) + { + debug_error("this effect(%d) is not supported", effect); + result = FALSE; + } + else + { + if (device_out == MM_SOUND_DEVICE_OUT_SPEAKER && + PLAYER_INI()->audio_effect_preset_earphone_only_list[effect]) + { + result = FALSE; + } + } + } + /* custom */ + else if (effect_type == MM_AUDIO_EFFECT_TYPE_CUSTOM) + { + if ( effect < MM_AUDIO_EFFECT_CUSTOM_EQ || effect >= MM_AUDIO_EFFECT_CUSTOM_NUM ) + { + debug_error("out of range, custom effect(%d)", effect); + result = FALSE; + } + if (!PLAYER_INI()->audio_effect_custom_list[effect]) + { + debug_error("this custom effect(%d) is not supported", effect); + result = FALSE; + } + else + { + if (device_out == MM_SOUND_DEVICE_OUT_SPEAKER && + PLAYER_INI()->audio_effect_custom_earphone_only_list[effect]) + { + result = FALSE; + } + } + } + else + { + debug_error("invalid effect type(%d)", effect_type); + result = FALSE; + } + } + + debug_fleave(); + + return result; +} + + +int +_mmplayer_audio_effect_preset_apply(mm_player_t *player, MMAudioEffectPresetType effect_type) +{ + GstElement *audio_effect_element = NULL; + int result = MM_ERROR_NONE; + int output_type = 0; + mm_sound_device_in device_in; + mm_sound_device_out device_out; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* Music Player can set audio effect value before Audiobin is created. */ + if ( !player->pipeline || !player->pipeline->audiobin ) + { + debug_warning("effect element is not created yet."); + + player->bypass_audio_effect = FALSE; + + /* store audio effect setting in order to apply it when audio effect plugin is created */ + player->audio_effect_info.effect_type = MM_AUDIO_EFFECT_TYPE_PRESET; + player->audio_effect_info.preset = effect_type; + } + else + { + return_val_if_fail( player->pipeline->audiobin, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + audio_effect_element = player->pipeline->audiobin[MMPLAYER_A_FILTER].gst; + + /* get status if speaker is activated */ + result = mm_sound_get_active_device(&device_in, &device_out); + if ( result ) { + debug_error("mm_sound_get_active_device() failed [%x]!!", result); + debug_fleave(); + return result; + } + + /* SPEAKER case */ + if (device_out == MM_SOUND_DEVICE_OUT_SPEAKER) + { + output_type = MM_AUDIO_EFFECT_OUTPUT_SPK; + if (__mmplayer_is_earphone_only_effect_type(player, MM_AUDIO_EFFECT_TYPE_PRESET, effect_type)) + { + debug_error("earphone is not equipped, this filter will not be applied"); + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_INVALID_STATUS; + } + } + /* Other case, include WIRED_ACCESSORY, BLUETOOTH, DOCK */ + else + { + output_type = MM_AUDIO_EFFECT_OUTPUT_EAR; + } + + /* set filter output mode as SPEAKER or EARPHONE */ + g_object_set(audio_effect_element, "filter-output-mode", output_type, NULL); + debug_log("filter-output-mode = %d (0:spk,1:ear)", output_type); + + if (effect_type == MM_AUDIO_EFFECT_PRESET_AUTO) { + /* TODO: Add codes about auto selecting preset mode according to ID3 tag */ + /* set effect preset mode */ + g_object_set(audio_effect_element, "preset-mode", 0, NULL); /* forced set to 0(normal) temporarily */ + debug_log("preset-mode = %d", effect_type); + + } else { + /* set effect preset mode */ + g_object_set(audio_effect_element, "preset-mode", effect_type-1, NULL); /* effect_type-1, because of _PRESET_AUTO in MSL/CAPI which does not exist in soundAlive plugin */ + debug_log("preset-mode = %d", effect_type); + } + + /* order action to audio effect plugin */ + g_object_set(audio_effect_element, "filter-action", MM_AUDIO_EFFECT_TYPE_PRESET, NULL); + debug_log("filter-action = %d", MM_AUDIO_EFFECT_TYPE_PRESET); + + } + + debug_fleave(); + + return result; +} + + +int +_mmplayer_audio_effect_custom_apply(mm_player_t *player) +{ + GstElement *audio_effect_element = NULL; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* Music Player can set audio effect value before Audiobin is created. */ + if ( !player->pipeline || !player->pipeline->audiobin ) + { + debug_warning("effect element is not created yet."); + + player->bypass_audio_effect = FALSE; + + /* store audio effect setting in order to apply it when audio effect plugin is created */ + player->audio_effect_info.effect_type = MM_AUDIO_EFFECT_TYPE_CUSTOM; + } + else + { + int output_type = 0; + mm_sound_device_in device_in; + mm_sound_device_out device_out; + + return_val_if_fail( player->pipeline->audiobin, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + audio_effect_element = player->pipeline->audiobin[MMPLAYER_A_FILTER].gst; + + /* get status if speaker is activated */ + result = mm_sound_get_active_device(&device_in, &device_out); + if ( result ) { + debug_error("mm_sound_get_active_device() failed [%x]!!", result); + debug_fleave(); + return result; + } + + /* SPEAKER case */ + if (device_out == MM_SOUND_DEVICE_OUT_SPEAKER) + { + output_type = MM_AUDIO_EFFECT_OUTPUT_SPK; + if (__mmplayer_is_earphone_only_effect_type(player, MM_AUDIO_EFFECT_TYPE_CUSTOM, 0)) + { + debug_error("earphone is not equipped, some custom effect should operate with earphone"); + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_INVALID_STATUS; + } + } + /* Other case, include WIRED_ACCESSORY, BLUETOOTH, DOCK */ + else + { + output_type = MM_AUDIO_EFFECT_OUTPUT_EAR; + } + + /* set filter output mode as SPEAKER or EARPHONE */ + g_object_set(audio_effect_element, "filter-output-mode", output_type, NULL); + debug_log("filter-output-mode = %d (0:spk,1:ear)", output_type); + + result = __mmplayer_set_harmony_effect(player, audio_effect_element); + if ( result ) + { + debug_error("_set_harmony_effect() failed(%x)", result); + debug_fleave(); + return result; + } + } + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_custom_clear_eq_all(MMHandleType hplayer) +{ + int result = MM_ERROR_NONE; + mm_player_t* player = (mm_player_t*)hplayer; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + /* clear EQ custom effect */ + memset(player->audio_effect_info.custom_eq_level, MM_AUDIO_EFFECT_CUSTOM_LEVEL_INIT, sizeof(int)*MM_AUDIO_EFFECT_EQ_BAND_NUM_MAX); + + debug_msg("All the EQ bands clearing success"); + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_custom_clear_ext_all(MMHandleType hplayer) +{ + int i; + int result = MM_ERROR_NONE; + mm_player_t* player = (mm_player_t*)hplayer; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + /* clear ALL custom effects, except EQ */ + for ( i = 0 ; i < MM_AUDIO_EFFECT_CUSTOM_NUM - 1 ; i++ ) + { + player->audio_effect_info.custom_ext_level[i] = MM_AUDIO_EFFECT_CUSTOM_LEVEL_INIT; + } + + debug_msg("All the extension effects clearing success"); + + debug_fleave(); + + return result; +} + + +int +mm_player_is_supported_preset_effect_type(MMHandleType hplayer, MMAudioEffectPresetType effect) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_PRESET, effect ) ) + { + result = MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + debug_fleave(); + + return result; +} + + +int +mm_player_is_supported_custom_effect_type(MMHandleType hplayer, MMAudioEffectCustomType effect) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, effect ) ) + { + result = MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_preset_apply(MMHandleType hplayer, MMAudioEffectPresetType type) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + if (!PLAYER_INI()->use_audio_effect_preset) + { + debug_error("audio effect(preset) is not supported", type); + debug_fleave(); + return MM_ERROR_NOT_SUPPORT_API; + } + + if (type < MM_AUDIO_EFFECT_PRESET_AUTO || type >= MM_AUDIO_EFFECT_PRESET_NUM) + { + debug_error("out of range, type(%d)", type); + debug_fleave(); + return MM_ERROR_INVALID_ARGUMENT; + } + + /* check if this effect type is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_PRESET, type ) ) + { + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + result = _mmplayer_audio_effect_preset_apply(player, type); + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_custom_apply(MMHandleType hplayer) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + if (!PLAYER_INI()->use_audio_effect_custom) + { + debug_error("audio effect(custom) is not supported"); + debug_fleave(); + return MM_ERROR_NOT_SUPPORT_API; + } + + result = _mmplayer_audio_effect_custom_apply(player); + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_bypass (MMHandleType hplayer) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + GstElement *audio_effect_element = NULL; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + if ( !PLAYER_INI()->use_audio_effect_preset && !PLAYER_INI()->use_audio_effect_custom ) + { + debug_error("audio effect(preset/custom) is not supported"); + debug_fleave(); + return MM_ERROR_NOT_SUPPORT_API; + } + if ( !player->pipeline || !player->pipeline->audiobin ) + { + debug_warning("effect element is not created yet."); + } + else + { + return_val_if_fail( player->pipeline->audiobin, MM_ERROR_PLAYER_NOT_INITIALIZED ); + audio_effect_element = player->pipeline->audiobin[MMPLAYER_A_FILTER].gst; + + /* order action to audio effect plugin */ + g_object_set(audio_effect_element, "filter-action", MM_AUDIO_EFFECT_TYPE_NONE, NULL); + debug_log("filter-action = %d", MM_AUDIO_EFFECT_TYPE_NONE); + } + + debug_fleave(); + + return result; +} + + +int +_mmplayer_audio_effect_custom_set_level_ext(mm_player_t *player, MMAudioEffectCustomType custom_effect_type, int level) +{ + int effect_level_max = 0; + int effect_level_min = 0; + int count = 1; /* start from 1, because of excepting eq index */ + int ext_level_index = 1; /* start from 1, because of excepting eq index */ + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check if EQ is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, custom_effect_type ) ) + { + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + while ( count < MM_AUDIO_EFFECT_CUSTOM_NUM ) + { + if ( PLAYER_INI()->audio_effect_custom_list[count] ) + { + if ( count == custom_effect_type ) + { + effect_level_min = PLAYER_INI()->audio_effect_custom_min_level_list[ext_level_index]; + effect_level_max = PLAYER_INI()->audio_effect_custom_max_level_list[ext_level_index]; + debug_msg("level min value(%d), level max value(%d)", effect_level_min, effect_level_max); + break; + } + ext_level_index++; + if (ext_level_index == PLAYER_INI()->audio_effect_custom_ext_num + 1) + { + debug_error("could not find min, max value. maybe effect information in ini file is not proper for audio effect plugin"); + break; + } + } + count++; + } + + if ( level < effect_level_min || level > effect_level_max ) + { + debug_error("out of range, level(%d)", level); + result = MM_ERROR_INVALID_ARGUMENT; + } + else + { + player->audio_effect_info.custom_ext_level[custom_effect_type-1] = level; + debug_msg("set ext[%d] = %d", custom_effect_type-1, level); + } + + debug_fleave(); + + return result; +} + + +int +_mmplayer_audio_effect_custom_set_level_eq(mm_player_t *player, int index, int level) +{ + gint eq_level_max = 0; + gint eq_level_min = 0; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check if EQ is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, MM_AUDIO_EFFECT_CUSTOM_EQ ) ) + { + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + if ( index < 0 || index > PLAYER_INI()->audio_effect_custom_eq_band_num - 1 ) + { + debug_error("out of range, index(%d)", index); + result = MM_ERROR_INVALID_ARGUMENT; + } + else + { + eq_level_min = PLAYER_INI()->audio_effect_custom_min_level_list[MM_AUDIO_EFFECT_CUSTOM_EQ]; + eq_level_max = PLAYER_INI()->audio_effect_custom_max_level_list[MM_AUDIO_EFFECT_CUSTOM_EQ]; + debug_msg("EQ level min value(%d), EQ level max value(%d)", eq_level_min, eq_level_max); + + if ( level < eq_level_min || level > eq_level_max ) + { + debug_error("out of range, EQ level(%d)", level); + result = MM_ERROR_INVALID_ARGUMENT; + } + else + { + player->audio_effect_info.custom_eq_level[index] = level; + debug_msg("set EQ[%d] = %d", index, level); + } + } + + debug_fleave(); + + return result; +} + + +/* NOTE : parameter eq_index is only used for _set_eq_level() */ +int +mm_player_audio_effect_custom_set_level(MMHandleType hplayer, MMAudioEffectCustomType effect_custom_type, int eq_index, int level) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check if this effect type is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, effect_custom_type ) ) + { + result = MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + else + { + if (effect_custom_type == MM_AUDIO_EFFECT_CUSTOM_EQ) + { + result = _mmplayer_audio_effect_custom_set_level_eq(player, eq_index, level); + } + else if (effect_custom_type > MM_AUDIO_EFFECT_CUSTOM_EQ || effect_custom_type < MM_AUDIO_EFFECT_CUSTOM_NUM) + { + result = _mmplayer_audio_effect_custom_set_level_ext(player, effect_custom_type, level); + } + else + { + debug_error("out of range, effect type(%d)", effect_custom_type); + result = MM_ERROR_INVALID_ARGUMENT; + } + } + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_custom_get_eq_bands_number(MMHandleType hplayer, int *bands) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check if EQ is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, MM_AUDIO_EFFECT_CUSTOM_EQ ) ) + { + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + *bands = PLAYER_INI()->audio_effect_custom_eq_band_num; + debug_log("number of custom EQ band = %d", *bands); + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_custom_get_eq_bands_width(MMHandleType hplayer, int band_idx, int *width) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + unsigned int eq_num = 0; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check if EQ is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, MM_AUDIO_EFFECT_CUSTOM_EQ ) ) + { + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + return_val_if_fail( player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + eq_num = PLAYER_INI()->audio_effect_custom_eq_band_num; + if (band_idx < 0 || band_idx > eq_num-1) + { + debug_error("out of range, invalid band_idx(%d)", band_idx); + result = MM_ERROR_PLAYER_INTERNAL; + } + else + { + /* set the width of EQ band */ + *width = PLAYER_INI()->audio_effect_custom_eq_band_width[band_idx]; + debug_log("width of band_idx(%d) = %dHz", band_idx, *width); + } + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_custom_get_eq_bands_freq(MMHandleType hplayer, int band_idx, int *freq) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + unsigned int eq_num = 0; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check if EQ is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, MM_AUDIO_EFFECT_CUSTOM_EQ ) ) + { + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + return_val_if_fail( player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + eq_num = PLAYER_INI()->audio_effect_custom_eq_band_num; + if (band_idx < 0 || band_idx > eq_num-1) + { + debug_error("out of range, invalid band_idx(%d)", band_idx); + result = MM_ERROR_PLAYER_INTERNAL; + } + else + { + /* set the frequency of EQ band */ + *freq = PLAYER_INI()->audio_effect_custom_eq_band_freq[band_idx]; + debug_log("frequency of band_idx(%d) = %dHz", band_idx, *freq); + } + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_custom_get_level(MMHandleType hplayer, MMAudioEffectCustomType type, int eq_index, int *level) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail( level, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check if this effect type is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, type ) ) + { + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + if (type == MM_AUDIO_EFFECT_CUSTOM_EQ) + { + if ( eq_index < 0 || eq_index > PLAYER_INI()->audio_effect_custom_eq_band_num - 1 ) + { + debug_error("out of range, EQ index(%d)", eq_index); + result = MM_ERROR_INVALID_ARGUMENT; + } + else + { + *level = player->audio_effect_info.custom_eq_level[eq_index]; + debug_log("EQ index = %d, level = %d", eq_index, *level); + } + } + else if ( type > MM_AUDIO_EFFECT_CUSTOM_EQ && type < MM_AUDIO_EFFECT_CUSTOM_NUM ) + { + *level = player->audio_effect_info.custom_ext_level[type-1]; + debug_log("extension effect index = %d, level = %d", type, *level); + } + else + { + debug_error("out of range, type(%d)", type); + result = MM_ERROR_INVALID_ARGUMENT; + } + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_custom_get_level_range(MMHandleType hplayer, MMAudioEffectCustomType type, int *min, int *max) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int result = MM_ERROR_NONE; + int count = 1; /* start from 1, because of excepting eq index */ + int ext_level_index = 1; /* start from 1, because of excepting eq index */ + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail( min, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail( max, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check if this effect type is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, type ) ) + { + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + if ( type == MM_AUDIO_EFFECT_CUSTOM_EQ ) + { + *min = PLAYER_INI()->audio_effect_custom_min_level_list[MM_AUDIO_EFFECT_CUSTOM_EQ]; + *max = PLAYER_INI()->audio_effect_custom_max_level_list[MM_AUDIO_EFFECT_CUSTOM_EQ]; + debug_log("EQ min level = %d, max level = %d", *min, *max); + } + else + { + while ( count < MM_AUDIO_EFFECT_CUSTOM_NUM ) + { + if ( PLAYER_INI()->audio_effect_custom_list[count] ) + { + if ( count == type ) + { + *min = PLAYER_INI()->audio_effect_custom_min_level_list[ext_level_index]; + *max = PLAYER_INI()->audio_effect_custom_max_level_list[ext_level_index]; + debug_msg("Extension effect(%d) min level = %d, max level = %d", count, *min, *max); + break; + } + ext_level_index++; + if ( ext_level_index == PLAYER_INI()->audio_effect_custom_ext_num + 1 ) + { + debug_error("could not find min, max value. maybe effect information in ini file is not proper for audio effect plugin"); + break; + } + } + count++; + } + } + + debug_fleave(); + + return result; +} + + +int +mm_player_audio_effect_custom_set_level_eq_from_list(MMHandleType hplayer, int *level_list, int size) +{ + mm_player_t *player = (mm_player_t*)hplayer; + gint i = 0; + gint eq_level_min = 0; + gint eq_level_max = 0; + int result = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check if EQ is supported */ + if ( !_mmplayer_is_supported_effect_type( MM_AUDIO_EFFECT_TYPE_CUSTOM, MM_AUDIO_EFFECT_CUSTOM_EQ ) ) + { + debug_fleave(); + return MM_ERROR_PLAYER_SOUND_EFFECT_NOT_SUPPORTED_FILTER; + } + + if ( size != PLAYER_INI()->audio_effect_custom_eq_band_num ) + { + debug_error("input size variable(%d) does not match with number of eq band(%d)", size, PLAYER_INI()->audio_effect_custom_eq_band_num); + result = MM_ERROR_INVALID_ARGUMENT; + } + else + { + eq_level_min = PLAYER_INI()->audio_effect_custom_min_level_list[MM_AUDIO_EFFECT_CUSTOM_EQ]; + eq_level_max = PLAYER_INI()->audio_effect_custom_max_level_list[MM_AUDIO_EFFECT_CUSTOM_EQ]; + + for ( i = 0 ; i < size ; i++ ) + { + if ( level_list[i] < eq_level_min || level_list[i] > eq_level_max) + { + debug_error("out of range, level[%d]=%d", i, level_list[i]); + result = MM_ERROR_INVALID_ARGUMENT; + break; + } + player->audio_effect_info.custom_eq_level[i] = level_list[i]; + } + } + debug_fleave(); + + return result; +} diff --git a/src/mm_player_capture.c b/src/mm_player_capture.c new file mode 100755 index 0000000..b846e78 --- /dev/null +++ b/src/mm_player_capture.c @@ -0,0 +1,803 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 "mm_player_capture.h" +#include "mm_player_priv.h" + +#include + +/*--------------------------------------------------------------------------- +| LOCAL VARIABLE DEFINITIONS for internal | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL FUNCTION PROTOTYPES: | +---------------------------------------------------------------------------*/ +static gboolean __mmplayer_video_capture_probe (GstPad *pad, GstBuffer *buffer, gpointer u_data); +static int __mmplayer_get_video_frame_from_buffer(mm_player_t* player, GstBuffer *buffer); +static gpointer __mmplayer_capture_thread(gpointer data); +static void __csc_tiled_to_linear_crop(unsigned char *yuv420_dest, unsigned char *nv12t_src, int yuv420_width, int yuv420_height, int left, int top, int right, int buttom); +static int __tile_4x2_read(int x_size, int y_size, int x_pos, int y_pos); +static int __mm_player_convert_colorspace(mm_player_t* player, unsigned char* src_data, mm_util_img_format src_fmt, unsigned int src_w, unsigned int src_h, mm_util_img_format dst_fmt); + +/*=========================================================================================== +| | +| FUNCTION DEFINITIONS | +| | +========================================================================================== */ +int +_mmplayer_initialize_video_capture(mm_player_t* player) +{ + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + /* create capture mutex */ + player->capture_thread_mutex = g_mutex_new(); + if ( ! player->capture_thread_mutex ) + { + debug_critical("Cannot create capture mutex"); + goto ERROR; + } + + /* create capture cond */ + player->capture_thread_cond = g_cond_new(); + if ( ! player->capture_thread_cond ) + { + debug_critical("Cannot create capture cond"); + goto ERROR; + } + + player->capture_thread_exit = FALSE; + + /* create video capture thread */ + player->capture_thread = + g_thread_create (__mmplayer_capture_thread, (gpointer)player, TRUE, NULL); + if ( ! player->capture_thread ) + { + goto ERROR; + } + + return MM_ERROR_NONE; + +ERROR: + /* capture thread */ + if ( player->capture_thread_mutex ) + g_mutex_free ( player->capture_thread_mutex ); + + if ( player->capture_thread_cond ) + g_cond_free ( player->capture_thread_cond ); + + return MM_ERROR_PLAYER_INTERNAL; +} + +int +_mmplayer_release_video_capture(mm_player_t* player) +{ + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + /* release capture thread */ + if ( player->capture_thread_cond && + player->capture_thread_mutex && + player->capture_thread ) + { + g_mutex_lock(player->capture_thread_mutex); + player->capture_thread_exit = TRUE; + g_cond_signal( player->capture_thread_cond ); + g_mutex_unlock(player->capture_thread_mutex); + + debug_log("waitting for capture thread exit"); + g_thread_join ( player->capture_thread ); + g_mutex_free ( player->capture_thread_mutex ); + g_cond_free ( player->capture_thread_cond ); + debug_log("capture thread released"); + } + + return MM_ERROR_NONE; +} + +int +_mmplayer_do_video_capture(MMHandleType hplayer) +{ + mm_player_t* player = (mm_player_t*) hplayer; + int ret = MM_ERROR_NONE; + GstPad *pad = NULL; + + debug_fenter(); + + return_val_if_fail(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED); + + /* capturing or not */ + if (player->video_capture_cb_probe_id || player->capture.data || player->captured.a[0] || player->captured.a[1]) + { + debug_warning("capturing... we can't do any more"); + return MM_ERROR_PLAYER_INVALID_STATE; + } + + /* check if video pipeline is linked or not */ + if (!player->pipeline->videobin || !player->sent_bos) + { + debug_warning("not ready to capture"); + return MM_ERROR_PLAYER_INVALID_STATE; + } + + if (player->state != MM_PLAYER_STATE_PLAYING) + { + if (player->state == MM_PLAYER_STATE_PAUSED) // get last buffer from video sink + { + GstBuffer *buf = NULL; + g_object_get(player->pipeline->videobin[MMPLAYER_V_SINK].gst, "last-buffer", &buf, NULL); + + if (buf) + { + ret = __mmplayer_get_video_frame_from_buffer(player, buf); + gst_buffer_unref(buf); + } + return ret; + } + else + { + debug_warning("invalid state(%d) to capture", player->state); + return MM_ERROR_PLAYER_INVALID_STATE; + } + } + + pad = gst_element_get_static_pad(player->pipeline->videobin[MMPLAYER_V_SINK].gst, "sink" ); + + /* register probe */ + player->video_capture_cb_probe_id = gst_pad_add_buffer_probe (pad, + G_CALLBACK (__mmplayer_video_capture_probe), player); + + gst_object_unref(GST_OBJECT(pad)); + pad = NULL; + + debug_fleave(); + + return ret; +} + +static gpointer +__mmplayer_capture_thread(gpointer data) +{ + mm_player_t* player = (mm_player_t*) data; + MMMessageParamType msg = {0, }; + unsigned char * linear_y_plane = NULL; + unsigned char * linear_uv_plane = NULL; + + return_if_fail (player); + + while (!player->capture_thread_exit) + { + debug_log("capture thread started. waiting for signal"); + + g_mutex_lock(player->capture_thread_mutex); + g_cond_wait( player->capture_thread_cond, player->capture_thread_mutex ); + + if ( player->capture_thread_exit ) + { + debug_log("exiting capture thread"); + goto EXIT; + } + debug_log("capture thread is recieved signal"); + + /* NOTE: Don't use MMPLAYER_CMD_LOCK() here. + * Because deadlock can be happened if other player api is used in message callback. + */ + if (player->video_cs == MM_PLAYER_COLORSPACE_NV12_TILED) + { + /* Colorspace conversion : NV12T-> NV12-> RGB888 */ + int ret = 0; + int linear_y_plane_size; + int linear_uv_plane_size; + unsigned char * src_buffer = NULL; + + debug_log("w[0]=%d, w[1]=%d", player->captured.w[0], player->captured.w[1]); + debug_log("h[0]=%d, h[1]=%d", player->captured.h[0], player->captured.h[1]); + debug_log("s[0]=%d, s[1]=%d", player->captured.s[0], player->captured.s[1]); + debug_log("e[0]=%d, e[1]=%d", player->captured.e[0], player->captured.e[1]); + debug_log("a[0]=%p, a[1]=%p", player->captured.a[0], player->captured.a[1]); + + #if 0 + if (mm_attrs_get_int_by_name(player->attrs, "content_video_width", &(player->captured.w[0])) != MM_ERROR_NONE) + { + debug_error("failed to get content width attribute"); + goto ERROR; + } + + if (mm_attrs_get_int_by_name(player->attrs, "content_video_height", &(player->captured.h[0])) != MM_ERROR_NONE) + { + debug_error("failed to get content height attribute"); + goto ERROR; + } + #endif + + linear_y_plane_size = (player->captured.w[0] * player->captured.h[0]); + linear_uv_plane_size = (player->captured.w[0] * player->captured.h[0]/2); + + linear_y_plane = (unsigned char*) g_try_malloc(linear_y_plane_size); + if (linear_y_plane == NULL) + { + msg.code = MM_ERROR_PLAYER_NO_FREE_SPACE; + goto ERROR; + } + + linear_uv_plane = (unsigned char*) g_try_malloc(linear_uv_plane_size); + if (linear_uv_plane == NULL) + { + msg.code = MM_ERROR_PLAYER_NO_FREE_SPACE; + goto ERROR; + } + /* NV12 tiled to linear */ + __csc_tiled_to_linear_crop(linear_y_plane, player->captured.a[0], player->captured.w[0], player->captured.h[0], 0,0,0,0); + __csc_tiled_to_linear_crop(linear_uv_plane, player->captured.a[1], player->captured.w[0], player->captured.h[0]/2, 0,0,0,0); + + MMPLAYER_FREEIF(player->captured.a[0]); + MMPLAYER_FREEIF(player->captured.a[1]); + + src_buffer = (unsigned char*) g_try_malloc(linear_y_plane_size+linear_uv_plane_size); + + if (src_buffer == NULL) + { + msg.code = MM_ERROR_PLAYER_NO_FREE_SPACE; + goto ERROR; + } + memset(src_buffer, 0x00, linear_y_plane_size+linear_uv_plane_size); + memcpy(src_buffer, linear_y_plane, linear_y_plane_size); + memcpy(src_buffer+linear_y_plane_size, linear_uv_plane, linear_uv_plane_size); + + /* NV12 linear to RGB888 */ + ret = __mm_player_convert_colorspace(player, src_buffer, MM_UTIL_IMG_FMT_NV12, + player->captured.w[0], player->captured.h[0], MM_UTIL_IMG_FMT_RGB888); + + if (ret != MM_ERROR_NONE) + { + debug_error("failed to convert nv12 linear"); + goto ERROR; + } + /* clean */ + MMPLAYER_FREEIF(src_buffer); + MMPLAYER_FREEIF(linear_y_plane); + MMPLAYER_FREEIF(linear_uv_plane); + } + + player->capture.fmt = MM_PLAYER_COLORSPACE_RGB888; + msg.data = &player->capture; + msg.size = player->capture.size; + + if (player->cmd >= MMPLAYER_COMMAND_START) + { + MMPLAYER_POST_MSG( player, MM_MESSAGE_VIDEO_CAPTURED, &msg ); + debug_log("returned from capture message callback"); + } + + g_mutex_unlock(player->capture_thread_mutex); + + //MMPLAYER_FREEIF(player->capture.data); + continue; +ERROR: + if (player->video_cs == MM_PLAYER_COLORSPACE_NV12_TILED) + { + /* clean */ + MMPLAYER_FREEIF(linear_y_plane); + MMPLAYER_FREEIF(linear_uv_plane); + MMPLAYER_FREEIF(player->captured.a[0]); + MMPLAYER_FREEIF(player->captured.a[1]); + } + + msg.union_type = MM_MSG_UNION_CODE; + + g_mutex_unlock(player->capture_thread_mutex); + MMPLAYER_POST_MSG( player, MM_MESSAGE_VIDEO_NOT_CAPTURED, &msg ); + } + return NULL; +EXIT: + g_mutex_unlock(player->capture_thread_mutex); + return NULL; +} + +/** + * The output is fixed as RGB888 + */ +static int +__mmplayer_get_video_frame_from_buffer(mm_player_t* player, GstBuffer *buffer) +{ + gint yplane_size = 0; + gint uvplane_size = 0; + gint src_width = 0; + gint src_height = 0; + guint32 fourcc = 0; + GstCaps *caps = NULL; + GstStructure *structure = NULL; + mm_util_img_format src_fmt = MM_UTIL_IMG_FMT_YUV420; + mm_util_img_format dst_fmt = MM_UTIL_IMG_FMT_RGB888; // fixed + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( buffer, MM_ERROR_INVALID_ARGUMENT ); + + /* get fourcc */ + caps = GST_BUFFER_CAPS(buffer); + + return_val_if_fail ( caps, MM_ERROR_INVALID_ARGUMENT ); + debug_log("caps to capture: %s\n", gst_caps_to_string(caps)); + + structure = gst_caps_get_structure (caps, 0); + + return_val_if_fail (structure != NULL, MM_ERROR_PLAYER_INTERNAL); + + /* init capture image buffer */ + memset(&player->capture, 0x00, sizeof(MMPlayerVideoCapture)); + + gst_structure_get_int (structure, "width", &src_width); + gst_structure_get_int (structure, "height", &src_height); + + /* check rgb or yuv */ + if (gst_structure_has_name(structure, "video/x-raw-yuv")) + { + gst_structure_get_fourcc (structure, "format", &fourcc); + + switch(fourcc) + { + /* NV12T */ + case GST_MAKE_FOURCC ('S', 'T', '1', '2'): + { + debug_msg ("captured format is ST12\n"); + + MMPlayerMPlaneImage *proved = NULL; + player->video_cs = MM_PLAYER_COLORSPACE_NV12_TILED; + + /* get video frame info from proved buffer */ + proved = (MMPlayerMPlaneImage *)GST_BUFFER_MALLOCDATA(buffer); + + if ( !proved || !proved->a[0] || !proved->a[1] ) + return MM_ERROR_PLAYER_INTERNAL; + + yplane_size = proved->y_size; + uvplane_size = proved->uv_size; + + debug_msg ("yplane_size=%d, uvplane_size=%d\n",yplane_size,uvplane_size); + memset(&player->captured, 0x00, sizeof(MMPlayerMPlaneImage)); + memcpy(&player->captured, proved, sizeof(MMPlayerMPlaneImage)); + + player->captured.a[0] = g_try_malloc(yplane_size); + if ( !player->captured.a[0] ) + return MM_ERROR_SOUND_NO_FREE_SPACE; + + player->captured.a[1] = g_try_malloc(uvplane_size); + if ( !player->captured.a[1] ) + return MM_ERROR_SOUND_NO_FREE_SPACE; + + memcpy(player->captured.a[0], proved->a[0], yplane_size); + memcpy(player->captured.a[1], proved->a[1], uvplane_size); + goto DONE; + } + break; + + case GST_MAKE_FOURCC ('I', '4', '2', '0'): + { + src_fmt = MM_UTIL_IMG_FMT_I420; + } + break; + + default: + { + goto UNKNOWN; + } + break; + } + } + else if (gst_structure_has_name(structure, "video/x-raw-rgb")) + { + gint bpp; + gint depth; + gint endianess; + gint blue_mask; + gboolean bigendian = FALSE; + gboolean isbluefirst = FALSE; + + /** + * The followings will be considered. + * RGBx, xRGB, BGRx, xBGR + * RGB888, BGR888 + * RGB565 + * + */ + gst_structure_get_int (structure, "bpp", &bpp); + gst_structure_get_int (structure, "depth", &depth); + gst_structure_get_int (structure, "endianness", &endianess); + gst_structure_get_int (structure, "blue_mask", &blue_mask); + + if (endianess == 4321) + bigendian = TRUE; + + if (blue_mask == -16777216) + isbluefirst = TRUE; + + switch(bpp) + { + case 32: + { + switch(depth) + { + case 32: + if (bigendian && isbluefirst) + src_fmt = MM_UTIL_IMG_FMT_BGRA8888; + case 24: + if (bigendian && isbluefirst) + src_fmt = MM_UTIL_IMG_FMT_BGRX8888; + break; + default: + goto UNKNOWN; + break; + } + } + break; + + case 24: + default: + { + goto UNKNOWN; + } + break; + } + } + else + { + goto UNKNOWN; + } + __mm_player_convert_colorspace(player, GST_BUFFER_DATA(buffer), src_fmt, src_width, src_height, dst_fmt); + +DONE: + /* do convert colorspace */ + g_cond_signal( player->capture_thread_cond ); + + debug_fleave(); + + return MM_ERROR_NONE; + +UNKNOWN: + debug_error("unknown format to capture\n"); + return MM_ERROR_PLAYER_INTERNAL; +} + +static gboolean +__mmplayer_video_capture_probe (GstPad *pad, GstBuffer *buffer, gpointer u_data) +{ + mm_player_t* player = (mm_player_t*) u_data; + int ret = MM_ERROR_NONE; + + return_val_if_fail ( buffer, FALSE); + debug_fenter(); + + ret = __mmplayer_get_video_frame_from_buffer(player, buffer); + + if ( ret != MM_ERROR_NONE) + { + debug_error("faild to get video frame. %x\n", ret); + return FALSE; + } + + /* remove probe to be called at one time */ + if (player->video_capture_cb_probe_id) + { + gst_pad_remove_buffer_probe (pad, player->video_capture_cb_probe_id); + player->video_capture_cb_probe_id = 0; + } + + debug_fleave(); + + return TRUE; +} + +static int +__mm_player_convert_colorspace(mm_player_t* player, unsigned char* src_data, mm_util_img_format src_fmt, unsigned int src_w, unsigned int src_h, mm_util_img_format dst_fmt) +{ + unsigned char *dst_data = NULL; + unsigned int dst_size; + int ret = MM_ERROR_NONE; + + return_val_if_fail(player, MM_ERROR_PLAYER_INTERNAL); + ret = mm_util_get_image_size(dst_fmt, src_w, src_h, &dst_size); + + if (ret != MM_ERROR_NONE) + { + debug_error("failed to get image size for capture, %d\n", ret); + return MM_ERROR_PLAYER_INTERNAL; + } + + debug_log("width: %d, height: %d to capture, dest size: %d\n", src_w, src_h, dst_size); + + dst_data = (unsigned char*)g_malloc0(dst_size); + + if (!dst_data) + { + debug_error("no free space to capture\n"); + return MM_ERROR_PLAYER_NO_FREE_SPACE; + } + + ret = mm_util_convert_colorspace(src_data, src_w, src_h, src_fmt, dst_data, dst_fmt); + + if (ret != MM_ERROR_NONE) + { + debug_error("failed to convert for capture, %d\n", ret); + return MM_ERROR_PLAYER_INTERNAL; + } + + player->capture.size = dst_size; + player->capture.data = dst_data; + + return MM_ERROR_NONE; +} + +/* + * Get tiled address of position(x,y) + * + * @param x_size + * width of tiled[in] + * + * @param y_size + * height of tiled[in] + * + * @param x_pos + * x position of tield[in] + * + * @param src_size + * y position of tield[in] + * + * @return + * address of tiled data + */ +static int +__tile_4x2_read(int x_size, int y_size, int x_pos, int y_pos) +{ + int pixel_x_m1, pixel_y_m1; + int roundup_x, roundup_y; + int linear_addr0, linear_addr1, bank_addr ; + int x_addr; + int trans_addr; + + pixel_x_m1 = x_size -1; + pixel_y_m1 = y_size -1; + + roundup_x = ((pixel_x_m1 >> 7) + 1); + roundup_y = ((pixel_x_m1 >> 6) + 1); + + x_addr = x_pos >> 2; + + if ((y_size <= y_pos+32) && ( y_pos < y_size) && + (((pixel_y_m1 >> 5) & 0x1) == 0) && (((y_pos >> 5) & 0x1) == 0)) { + linear_addr0 = (((y_pos & 0x1f) <<4) | (x_addr & 0xf)); + linear_addr1 = (((y_pos >> 6) & 0xff) * roundup_x + ((x_addr >> 6) & 0x3f)); + + if (((x_addr >> 5) & 0x1) == ((y_pos >> 5) & 0x1)) + bank_addr = ((x_addr >> 4) & 0x1); + else + bank_addr = 0x2 | ((x_addr >> 4) & 0x1); + } else { + linear_addr0 = (((y_pos & 0x1f) << 4) | (x_addr & 0xf)); + linear_addr1 = (((y_pos >> 6) & 0xff) * roundup_x + ((x_addr >> 5) & 0x7f)); + + if (((x_addr >> 5) & 0x1) == ((y_pos >> 5) & 0x1)) + bank_addr = ((x_addr >> 4) & 0x1); + else + bank_addr = 0x2 | ((x_addr >> 4) & 0x1); + } + + linear_addr0 = linear_addr0 << 2; + trans_addr = (linear_addr1 <<13) | (bank_addr << 11) | linear_addr0; + + return trans_addr; +} + +/* + * Converts tiled data to linear + * Crops left, top, right, buttom + * 1. Y of NV12T to Y of YUV420P + * 2. Y of NV12T to Y of YUV420S + * 3. UV of NV12T to UV of YUV420S + * + * @param yuv420_dest + * Y or UV plane address of YUV420[out] + * + * @param nv12t_src + * Y or UV plane address of NV12T[in] + * + * @param yuv420_width + * Width of YUV420[in] + * + * @param yuv420_height + * Y: Height of YUV420, UV: Height/2 of YUV420[in] + * + * @param left + * Crop size of left + * + * @param top + * Crop size of top + * + * @param right + * Crop size of right + * + * @param buttom + * Crop size of buttom + */ +static void +__csc_tiled_to_linear_crop(unsigned char *yuv420_dest, unsigned char *nv12t_src, int yuv420_width, int yuv420_height, + int left, int top, int right, int buttom) +{ + int i, j; + int tiled_offset = 0, tiled_offset1 = 0; + int linear_offset = 0; + int temp1 = 0, temp2 = 0, temp3 = 0, temp4 = 0; + + temp3 = yuv420_width-right; + temp1 = temp3-left; + /* real width is greater than or equal 256 */ + if (temp1 >= 256) { + for (i=top; i>8)<<8; + temp3 = temp3>>6; + temp4 = i>>5; + if (temp4 & 0x1) { + /* odd fomula: 2+x+(x>>2)<<2+x_block_num*(y-1) */ + tiled_offset = temp4-1; + temp1 = ((yuv420_width+127)>>7)<<7; + tiled_offset = tiled_offset*(temp1>>6); + tiled_offset = tiled_offset+temp3; + tiled_offset = tiled_offset+2; + temp1 = (temp3>>2)<<2; + tiled_offset = tiled_offset+temp1; + tiled_offset = tiled_offset<<11; + tiled_offset1 = tiled_offset+2048*2; + temp4 = 8; + } else { + temp2 = ((yuv420_height+31)>>5)<<5; + if ((i+32)>2)<<2+x_block_num*y */ + temp1 = temp3+2; + temp1 = (temp1>>2)<<2; + tiled_offset = temp3+temp1; + temp1 = ((yuv420_width+127)>>7)<<7; + tiled_offset = tiled_offset+temp4*(temp1>>6); + tiled_offset = tiled_offset<<11; + tiled_offset1 = tiled_offset+2048*6; + temp4 = 8; + } else { + /* even2 fomula: x+x_block_num*y */ + temp1 = ((yuv420_width+127)>>7)<<7; + tiled_offset = temp4*(temp1>>6); + tiled_offset = tiled_offset+temp3; + tiled_offset = tiled_offset<<11; + tiled_offset1 = tiled_offset+2048*2; + temp4 = 4; + } + } + + temp1 = i&0x1F; + tiled_offset = tiled_offset+64*(temp1); + tiled_offset1 = tiled_offset1+64*(temp1); + temp2 = yuv420_width-left-right; + linear_offset = temp2*(i-top); + temp3 = ((j+256)>>8)<<8; + temp3 = temp3-j; + temp1 = left&0x3F; + if (temp3 > 192) { + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset+temp1, 64-temp1); + temp2 = ((left+63)>>6)<<6; + temp3 = ((yuv420_width-right)>>6)<<6; + if (temp2 == temp3) { + temp2 = yuv420_width-right-(64-temp1); + } + memcpy(yuv420_dest+linear_offset+64-temp1, nv12t_src+tiled_offset+2048, 64); + memcpy(yuv420_dest+linear_offset+128-temp1, nv12t_src+tiled_offset1, 64); + memcpy(yuv420_dest+linear_offset+192-temp1, nv12t_src+tiled_offset1+2048, 64); + linear_offset = linear_offset+256-temp1; + } else if (temp3 > 128) { + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset+2048+temp1, 64-temp1); + memcpy(yuv420_dest+linear_offset+64-temp1, nv12t_src+tiled_offset1, 64); + memcpy(yuv420_dest+linear_offset+128-temp1, nv12t_src+tiled_offset1+2048, 64); + linear_offset = linear_offset+192-temp1; + } else if (temp3 > 64) { + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset1+temp1, 64-temp1); + memcpy(yuv420_dest+linear_offset+64-temp1, nv12t_src+tiled_offset1+2048, 64); + linear_offset = linear_offset+128-temp1; + } else if (temp3 > 0) { + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset1+2048+temp1, 64-temp1); + linear_offset = linear_offset+64-temp1; + } + + tiled_offset = tiled_offset+temp4*2048; + j = (left>>8)<<8; + j = j + 256; + temp2 = yuv420_width-right-256; + for (; j<=temp2; j=j+256) { + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, 64); + tiled_offset1 = tiled_offset1+temp4*2048; + memcpy(yuv420_dest+linear_offset+64, nv12t_src+tiled_offset+2048, 64); + memcpy(yuv420_dest+linear_offset+128, nv12t_src+tiled_offset1, 64); + tiled_offset = tiled_offset+temp4*2048; + memcpy(yuv420_dest+linear_offset+192, nv12t_src+tiled_offset1+2048, 64); + linear_offset = linear_offset+256; + } + + tiled_offset1 = tiled_offset1+temp4*2048; + temp2 = yuv420_width-right-j; + if (temp2 > 192) { + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, 64); + memcpy(yuv420_dest+linear_offset+64, nv12t_src+tiled_offset+2048, 64); + memcpy(yuv420_dest+linear_offset+128, nv12t_src+tiled_offset1, 64); + memcpy(yuv420_dest+linear_offset+192, nv12t_src+tiled_offset1+2048, temp2-192); + } else if (temp2 > 128) { + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, 64); + memcpy(yuv420_dest+linear_offset+64, nv12t_src+tiled_offset+2048, 64); + memcpy(yuv420_dest+linear_offset+128, nv12t_src+tiled_offset1, temp2-128); + } else if (temp2 > 64) { + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, 64); + memcpy(yuv420_dest+linear_offset+64, nv12t_src+tiled_offset+2048, temp2-64); + } else { + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, temp2); + } + } + } else if (temp1 >= 64) { + for (i=top; i<(yuv420_height-buttom); i=i+1) { + j = left; + tiled_offset = __tile_4x2_read(yuv420_width, yuv420_height, j, i); + temp2 = ((j+64)>>6)<<6; + temp2 = temp2-j; + linear_offset = temp1*(i-top); + temp4 = j&0x3; + tiled_offset = tiled_offset+temp4; + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, temp2); + linear_offset = linear_offset+temp2; + j = j+temp2; + if ((j+64) <= temp3) { + tiled_offset = __tile_4x2_read(yuv420_width, yuv420_height, j, i); + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, 64); + linear_offset = linear_offset+64; + j = j+64; + } + if ((j+64) <= temp3) { + tiled_offset = __tile_4x2_read(yuv420_width, yuv420_height, j, i); + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, 64); + linear_offset = linear_offset+64; + j = j+64; + } + if (j < temp3) { + tiled_offset = __tile_4x2_read(yuv420_width, yuv420_height, j, i); + temp2 = temp3-j; + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, temp2); + } + } + } else { + for (i=top; i<(yuv420_height-buttom); i=i+1) { + linear_offset = temp1*(i-top); + for (j=left; j<(yuv420_width-right); j=j+2) { + tiled_offset = __tile_4x2_read(yuv420_width, yuv420_height, j, i); + temp4 = j&0x3; + tiled_offset = tiled_offset+temp4; + memcpy(yuv420_dest+linear_offset, nv12t_src+tiled_offset, 2); + linear_offset = linear_offset+2; + } + } + } +} diff --git a/src/mm_player_ini.c b/src/mm_player_ini.c new file mode 100644 index 0000000..2ce1487 --- /dev/null +++ b/src/mm_player_ini.c @@ -0,0 +1,552 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 __MM_PLAYER_INI_C__ +#define __MM_PLAYER_INI_C__ + +/* includes here */ +#include +#include +#include "iniparser.h" +#include +#include "mm_debug.h" +#include +#include + +/* global variables here */ +static mm_player_ini_t g_player_ini; + +/* internal functions, macros here */ +static gboolean __generate_default_ini(void); +static void __get_string_list(gchar** out_list, gchar* str); +static void __mm_player_ini_check_ini_status(void); + +/* macro */ +#define MMPLAYER_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 ) < PLAYER_INI_MAX_STRLEN ) ) \ + { \ + strcpy ( x_item, str ); \ + } \ + else \ + { \ + strcpy ( x_item, x_default ); \ + } \ +}while(0) + +/* x_ini is the list of index to set TRUE at x_list[index] */ +#define MMPLAYER_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[PLAYER_INI_MAX_STRLEN] = {0}; \ + MMPLAYER_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) \ + { \ + debug_warning("%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 MMPLAYER_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[PLAYER_INI_MAX_STRLEN] = {0}; \ + MMPLAYER_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) \ + { \ + debug_error("%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) + +int +mm_player_ini_load(void) +{ + static gboolean loaded = FALSE; + dictionary * dict = NULL; + gint idx = 0; + + if ( loaded ) + return MM_ERROR_NONE; + + /* disabling ini parsing for launching */ +#if 1 //debianize + /* get player ini status because system will be crashed + * if ini file is corrupted. + */ + /* FIXIT : the api actually deleting illregular ini. but the function name said it's just checking. */ + __mm_player_ini_check_ini_status(); + + /* first, try to load existing ini file */ + dict = iniparser_load(MM_PLAYER_INI_DEFAULT_PATH); + + /* if no file exists. create one with set of default values */ + if ( !dict ) + { + #if 0 + debug_log("No inifile found. player will create default inifile.\n"); + if ( FALSE == __generate_default_ini() ) + { + debug_warning("Creating default inifile failed. Player will use default values.\n"); + } + else + { + /* load default ini */ + dict = iniparser_load(MM_PLAYER_INI_DEFAULT_PATH); + } + #else + debug_log("No inifile found. \n"); + + return MM_ERROR_FILE_NOT_FOUND; + #endif + } +#endif + + /* get ini values */ + memset( &g_player_ini, 0, sizeof(mm_player_ini_t) ); + + if ( dict ) /* if dict is available */ + { + /* general */ + g_player_ini.use_decodebin = iniparser_getboolean(dict, "general:use decodebin", DEFAULT_USE_DECODEBIN); + g_player_ini.disable_segtrap = iniparser_getboolean(dict, "general:disable segtrap", DEFAULT_DISABLE_SEGTRAP); + g_player_ini.skip_rescan = iniparser_getboolean(dict, "general:skip rescan", DEFAULT_SKIP_RESCAN); + g_player_ini.generate_dot = iniparser_getboolean(dict, "general:generate dot", DEFAULT_GENERATE_DOT); + g_player_ini.provide_clock= iniparser_getboolean(dict, "general:provide clock", DEFAULT_PROVIDE_CLOCK); + g_player_ini.live_state_change_timeout = iniparser_getint(dict, "general:live state change timeout", DEFAULT_LIVE_STATE_CHANGE_TIMEOUT); + g_player_ini.localplayback_state_change_timeout = iniparser_getint(dict, "general:localplayback state change timeout", DEFAULT_LOCALPLAYBACK_STATE_CHANGE_TIMEOUT); + g_player_ini.eos_delay = iniparser_getint(dict, "general:eos delay", DEFAULT_EOS_DELAY); + g_player_ini.async_start = iniparser_getboolean(dict, "general:async start", DEFAULT_ASYNC_START); + g_player_ini.multiple_codec_supported = iniparser_getboolean(dict, "general:multiple codec supported", DEFAULT_MULTIPLE_CODEC_SUPPORTED); + + g_player_ini.delay_before_repeat = iniparser_getint(dict, "general:delay before repeat", DEFAULT_DELAY_BEFORE_REPEAT); + + MMPLAYER_INI_GET_STRING(dict, g_player_ini.videosink_element_x, "general:videosink element x", DEFAULT_VIDEOSINK_X); + MMPLAYER_INI_GET_STRING(dict, g_player_ini.videosink_element_evas, "general:videosink element evas", DEFAULT_VIDEOSINK_EVAS); + MMPLAYER_INI_GET_STRING(dict, g_player_ini.videosink_element_fake, "general:videosink element fake", DEFAULT_VIDEOSINK_FAKE); + MMPLAYER_INI_GET_STRING(dict, g_player_ini.name_of_drmsrc, "general:drmsrc element", DEFAULT_DRMSRC ); + MMPLAYER_INI_GET_STRING(dict, g_player_ini.name_of_audiosink, "general:audiosink element", DEFAULT_AUDIOSINK ); + MMPLAYER_INI_GET_STRING(dict, g_player_ini.name_of_video_converter, "general:video converter element", DEFAULT_VIDEO_CONVERTER ); + + __get_string_list( (gchar**) g_player_ini.exclude_element_keyword, + iniparser_getstring(dict, "general:element exclude keyword", DEFAULT_EXCLUDE_KEYWORD)); + + MMPLAYER_INI_GET_STRING(dict, g_player_ini.gst_param[0], "general:gstparam1", DEFAULT_GST_PARAM ); + MMPLAYER_INI_GET_STRING(dict, g_player_ini.gst_param[1], "general:gstparam2", DEFAULT_GST_PARAM ); + MMPLAYER_INI_GET_STRING(dict, g_player_ini.gst_param[2], "general:gstparam3", DEFAULT_GST_PARAM ); + MMPLAYER_INI_GET_STRING(dict, g_player_ini.gst_param[3], "general:gstparam4", DEFAULT_GST_PARAM ); + MMPLAYER_INI_GET_STRING(dict, g_player_ini.gst_param[4], "general:gstparam5", DEFAULT_GST_PARAM ); + + /* http streaming */ + MMPLAYER_INI_GET_STRING( dict, g_player_ini.name_of_httpsrc, "http streaming:httpsrc element", DEFAULT_HTTPSRC ); + MMPLAYER_INI_GET_STRING( dict, g_player_ini.http_file_buffer_path, "http streaming:http file buffer path", DEFAULT_HTTP_FILE_BUFFER_PATH ); + g_player_ini.http_buffering_limit = iniparser_getdouble(dict, "http streaming:http buffering high limit", DEFAULT_HTTP_BUFFERING_LIMIT); + g_player_ini.http_max_size_bytes = iniparser_getint(dict, "http streaming:http max size bytes", DEFAULT_HTTP_MAX_SIZE_BYTES); + g_player_ini.http_buffering_time = iniparser_getdouble(dict, "http streaming:http buffering time", DEFAULT_HTTP_BUFFERING_TIME); + g_player_ini.http_timeout = iniparser_getint(dict, "http streaming:http timeout", DEFAULT_HTTP_TIMEOUT); + + /* rtsp streaming */ + MMPLAYER_INI_GET_STRING( dict, g_player_ini.name_of_rtspsrc, "rtsp streaming:rtspsrc element", DEFAULT_RTSPSRC ); + g_player_ini.rtsp_buffering_time = iniparser_getint(dict, "rtsp streaming:rtsp buffering time", DEFAULT_RTSP_BUFFERING); + g_player_ini.rtsp_rebuffering_time = iniparser_getint(dict, "rtsp streaming:rtsp rebuffering time", DEFAULT_RTSP_REBUFFERING); + g_player_ini.rtsp_do_typefinding = iniparser_getboolean(dict, "rtsp streaming:rtsp do typefinding", DEFAULT_RTSP_DO_TYPEFINDING); + g_player_ini.rtsp_error_concealment = iniparser_getboolean(dict, "rtsp streaming:rtsp error concealment", DEFAULT_RTSP_ERROR_CONCEALMENT); + } + else /* if dict is not available just fill the structure with default value */ + { + debug_warning("failed to load ini. using hardcoded default\n"); + + /* general */ + g_player_ini.use_decodebin = DEFAULT_USE_DECODEBIN; + g_player_ini.disable_segtrap = DEFAULT_DISABLE_SEGTRAP; + g_player_ini.skip_rescan = DEFAULT_SKIP_RESCAN; + strncpy( g_player_ini.videosink_element_x, DEFAULT_VIDEOSINK_X, PLAYER_INI_MAX_STRLEN - 1 ); + strncpy( g_player_ini.videosink_element_evas, DEFAULT_VIDEOSINK_EVAS, PLAYER_INI_MAX_STRLEN - 1 ); + strncpy( g_player_ini.videosink_element_fake, DEFAULT_VIDEOSINK_FAKE, PLAYER_INI_MAX_STRLEN - 1 ); + g_player_ini.generate_dot = DEFAULT_GENERATE_DOT; + g_player_ini.provide_clock= DEFAULT_PROVIDE_CLOCK; + g_player_ini.live_state_change_timeout = DEFAULT_LIVE_STATE_CHANGE_TIMEOUT; + g_player_ini.localplayback_state_change_timeout = DEFAULT_LOCALPLAYBACK_STATE_CHANGE_TIMEOUT; + g_player_ini.eos_delay = DEFAULT_EOS_DELAY; + g_player_ini.multiple_codec_supported = DEFAULT_MULTIPLE_CODEC_SUPPORTED; + g_player_ini.async_start = DEFAULT_ASYNC_START; + g_player_ini.delay_before_repeat = DEFAULT_DELAY_BEFORE_REPEAT; + + + strncpy( g_player_ini.name_of_drmsrc, DEFAULT_DRMSRC, PLAYER_INI_MAX_STRLEN - 1 ); + strncpy( g_player_ini.name_of_audiosink, DEFAULT_AUDIOSINK, PLAYER_INI_MAX_STRLEN -1 ); + strncpy( g_player_ini.name_of_video_converter, DEFAULT_VIDEO_CONVERTER, PLAYER_INI_MAX_STRLEN -1 ); + + { + __get_string_list( (gchar**) g_player_ini.exclude_element_keyword, DEFAULT_EXCLUDE_KEYWORD); + } + + + strncpy( g_player_ini.gst_param[0], DEFAULT_GST_PARAM, PLAYER_INI_MAX_PARAM_STRLEN - 1 ); + strncpy( g_player_ini.gst_param[1], DEFAULT_GST_PARAM, PLAYER_INI_MAX_PARAM_STRLEN - 1 ); + strncpy( g_player_ini.gst_param[2], DEFAULT_GST_PARAM, PLAYER_INI_MAX_PARAM_STRLEN - 1 ); + strncpy( g_player_ini.gst_param[3], DEFAULT_GST_PARAM, PLAYER_INI_MAX_PARAM_STRLEN - 1 ); + strncpy( g_player_ini.gst_param[4], DEFAULT_GST_PARAM, PLAYER_INI_MAX_PARAM_STRLEN - 1 ); + + /* http streaming */ + strncpy( g_player_ini.name_of_httpsrc, DEFAULT_HTTPSRC, PLAYER_INI_MAX_STRLEN - 1 ); + strncpy( g_player_ini.http_file_buffer_path, DEFAULT_HTTP_FILE_BUFFER_PATH, PLAYER_INI_MAX_STRLEN - 1 ); + g_player_ini.http_buffering_limit = DEFAULT_HTTP_BUFFERING_LIMIT; + g_player_ini.http_max_size_bytes = DEFAULT_HTTP_MAX_SIZE_BYTES; + g_player_ini.http_buffering_time = DEFAULT_HTTP_BUFFERING_TIME; + g_player_ini.http_timeout = DEFAULT_HTTP_TIMEOUT; + + /* rtsp streaming */ + strncpy( g_player_ini.name_of_rtspsrc, DEFAULT_RTSPSRC, PLAYER_INI_MAX_STRLEN - 1 ); + g_player_ini.rtsp_buffering_time = DEFAULT_RTSP_BUFFERING; + g_player_ini.rtsp_rebuffering_time = DEFAULT_RTSP_REBUFFERING; + g_player_ini.rtsp_do_typefinding = DEFAULT_RTSP_DO_TYPEFINDING; + g_player_ini.rtsp_error_concealment = DEFAULT_RTSP_ERROR_CONCEALMENT; + } + + /* free dict as we got our own structure */ + iniparser_freedict (dict); + + loaded = TRUE; + + /* dump structure */ + debug_log("player settings -----------------------------------\n"); + + /* general */ + debug_log("use_decodebin : %d\n", g_player_ini.use_decodebin); + debug_log("disable_segtrap : %d\n", g_player_ini.disable_segtrap); + debug_log("skip rescan : %d\n", g_player_ini.skip_rescan); + debug_log("videosink element x: %s\n", g_player_ini.videosink_element_x); + debug_log("videosink element evas: %s\n", g_player_ini.videosink_element_evas); + debug_log("videosink element fake: %s\n", g_player_ini.videosink_element_fake); + debug_log("generate_dot : %d\n", g_player_ini.generate_dot); + debug_log("provide_clock : %d\n", g_player_ini.provide_clock); + debug_log("live_state_change_timeout(sec) : %d\n", g_player_ini.live_state_change_timeout); + debug_log("localplayback_state_change_timeout(sec) : %d\n", g_player_ini.localplayback_state_change_timeout); + debug_log("eos_delay(msec) : %d\n", g_player_ini.eos_delay); + debug_log("delay_before_repeat(msec) : %d\n", g_player_ini.delay_before_repeat); + debug_log("name_of_drmsrc : %s\n", g_player_ini.name_of_drmsrc); + debug_log("name_of_audiosink : %s\n", g_player_ini.name_of_audiosink); + debug_log("name_of_video_converter : %s\n", g_player_ini.name_of_video_converter); + debug_log("async_start : %d\n", g_player_ini.async_start); + debug_log("multiple_codec_supported : %d\n", g_player_ini.multiple_codec_supported); + + debug_log("gst_param1 : %s\n", g_player_ini.gst_param[0]); + debug_log("gst_param2 : %s\n", g_player_ini.gst_param[1]); + debug_log("gst_param3 : %s\n", g_player_ini.gst_param[2]); + debug_log("gst_param4 : %s\n", g_player_ini.gst_param[3]); + debug_log("gst_param5 : %s\n", g_player_ini.gst_param[4]); + + for ( idx = 0; g_player_ini.exclude_element_keyword[idx][0] != '\0'; idx++ ) + { + debug_log("exclude_element_keyword [%d] : %s\n", idx, g_player_ini.exclude_element_keyword[idx]); + } + + /* http streaming */ + debug_log("name_of_httpsrc : %s\n", g_player_ini.name_of_httpsrc); + debug_log("http_file_buffer_path : %s \n", g_player_ini.http_file_buffer_path); + debug_log("http_buffering_limit : %f \n", g_player_ini.http_buffering_limit); + debug_log("http_max_size_bytes : %d \n", g_player_ini.http_max_size_bytes); + debug_log("http_buffering_time : %f \n", g_player_ini.http_buffering_time); + debug_log("http_timeout : %d \n", g_player_ini.http_timeout); + + /* rtsp streaming */ + debug_log("name_of_rtspsrc : %s\n", g_player_ini.name_of_rtspsrc); + debug_log("rtsp_buffering_time(msec) : %d\n", g_player_ini.rtsp_buffering_time); + debug_log("rtsp_rebuffering_time(msec) : %d\n", g_player_ini.rtsp_rebuffering_time); + debug_log("rtsp_do_typefinding : %d \n", g_player_ini.rtsp_do_typefinding); + debug_log("rtsp_error_concealment : %d \n", g_player_ini.rtsp_error_concealment); + + debug_log("---------------------------------------------------\n"); + + return MM_ERROR_NONE; +} + + +int +mm_player_audio_effect_ini_load(void) +{ + static gboolean loaded_audioeffect = FALSE; + dictionary * dict_audioeffect = NULL; + + if ( loaded_audioeffect ) + return MM_ERROR_NONE; + + dict_audioeffect = iniparser_load(MM_PLAYER_INI_DEFAULT_AUDIOEFFECT_PATH); + if ( !dict_audioeffect ) + { + debug_warning("No audio effect ini file found. \n"); + //return MM_ERROR_FILE_NOT_FOUND; + } + + /* audio effect element name */ + MMPLAYER_INI_GET_STRING( dict_audioeffect, g_player_ini.name_of_audio_effect, "audio effect:audio effect element", DEFAULT_AUDIO_EFFECT_ELEMENT ); + if (!g_player_ini.name_of_audio_effect) + { + debug_error("could not parse name of audio effect. \n"); + iniparser_freedict (dict_audioeffect); + return MM_ERROR_PLAYER_INTERNAL; + } + + /* audio effect (Preset)*/ + g_player_ini.use_audio_effect_preset = iniparser_getboolean(dict_audioeffect, "audio effect:audio effect preset", DEFAULT_USE_AUDIO_EFFECT_PRESET); + if (g_player_ini.use_audio_effect_preset) + { + MMPLAYER_INI_GET_BOOLEAN_FROM_LIST( dict_audioeffect, g_player_ini.audio_effect_preset_list, MM_AUDIO_EFFECT_PRESET_NUM, + "audio effect:audio effect preset list", DEFAULT_AUDIO_EFFECT_PRESET_LIST ); + MMPLAYER_INI_GET_BOOLEAN_FROM_LIST( dict_audioeffect, g_player_ini.audio_effect_preset_earphone_only_list, MM_AUDIO_EFFECT_PRESET_NUM, + "audio effect:audio effect preset earphone only", DEFAULT_AUDIO_EFFECT_PRESET_LIST_EARPHONE_ONLY ); + } + + /* audio effect custom (EQ / Extension effects) */ + g_player_ini.use_audio_effect_custom = iniparser_getboolean(dict_audioeffect, "audio effect:audio effect custom", DEFAULT_USE_AUDIO_EFFECT_CUSTOM); + if (g_player_ini.use_audio_effect_custom) + { + MMPLAYER_INI_GET_BOOLEAN_FROM_LIST( dict_audioeffect, g_player_ini.audio_effect_custom_list, MM_AUDIO_EFFECT_CUSTOM_NUM, + "audio effect:audio effect custom list", DEFAULT_AUDIO_EFFECT_CUSTOM_LIST ); + MMPLAYER_INI_GET_BOOLEAN_FROM_LIST( dict_audioeffect, g_player_ini.audio_effect_custom_earphone_only_list, MM_AUDIO_EFFECT_CUSTOM_NUM, + "audio effect:audio effect custom earphone only", DEFAULT_AUDIO_EFFECT_CUSTOM_LIST_EARPHONE_ONLY ); + + /* audio effect custom : EQ */ + if (g_player_ini.audio_effect_custom_list[MM_AUDIO_EFFECT_CUSTOM_EQ]) + { + g_player_ini.audio_effect_custom_eq_band_num = iniparser_getint(dict_audioeffect, "audio effect:audio effect custom eq band num", + DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_NUM); + if (g_player_ini.audio_effect_custom_eq_band_num < DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_NUM || + g_player_ini.audio_effect_custom_eq_band_num > MM_AUDIO_EFFECT_EQ_BAND_NUM_MAX) + { + debug_error("audio_effect_custom_eq_band_num(%d) is not valid range(%d - %d), set the value %d", + g_player_ini.audio_effect_custom_eq_band_num, DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_NUM, MM_AUDIO_EFFECT_EQ_BAND_NUM_MAX, DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_NUM); + g_player_ini.audio_effect_custom_eq_band_num = DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_NUM; + + iniparser_freedict (dict_audioeffect); + return MM_ERROR_PLAYER_INTERNAL; + } + else + { + if (g_player_ini.audio_effect_custom_eq_band_num) + { + MMPLAYER_INI_GET_INT_FROM_LIST( dict_audioeffect, g_player_ini.audio_effect_custom_eq_band_width, MM_AUDIO_EFFECT_EQ_BAND_NUM_MAX, + "audio effect:audio effect custom eq band width", DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_WIDTH ); + MMPLAYER_INI_GET_INT_FROM_LIST( dict_audioeffect, g_player_ini.audio_effect_custom_eq_band_freq, MM_AUDIO_EFFECT_EQ_BAND_NUM_MAX, + "audio effect:audio effect custom eq band freq", DEFAULT_AUDIO_EFFECT_CUSTOM_EQ_BAND_FREQ ); + } + } + } + + /* audio effect custom : Extension effects */ + g_player_ini.audio_effect_custom_ext_num = iniparser_getint(dict_audioeffect, "audio effect:audio effect custom ext num", + DEFAULT_AUDIO_EFFECT_CUSTOM_EXT_NUM); + + /* Min/Max value list of EQ / Extension effects */ + if (g_player_ini.audio_effect_custom_eq_band_num || g_player_ini.audio_effect_custom_ext_num) + { + + MMPLAYER_INI_GET_INT_FROM_LIST( dict_audioeffect, g_player_ini.audio_effect_custom_min_level_list, MM_AUDIO_EFFECT_CUSTOM_NUM, + "audio effect:audio effect custom min list", DEFAULT_AUDIO_EFFECT_CUSTOM_LIST ); + MMPLAYER_INI_GET_INT_FROM_LIST( dict_audioeffect, g_player_ini.audio_effect_custom_max_level_list, MM_AUDIO_EFFECT_CUSTOM_NUM, + "audio effect:audio effect custom max list", DEFAULT_AUDIO_EFFECT_CUSTOM_LIST ); + } + } + + /* dump structure */ + debug_log("player audio-effect settings ----------------------\n"); + debug_log("name_of_audio_effect : %s\n", g_player_ini.name_of_audio_effect); + debug_log("use_audio_effect_preset : %d\n", g_player_ini.use_audio_effect_preset); + debug_log("use_audio_effect_custom : %d\n", g_player_ini.use_audio_effect_custom); +#if 0 + int i; + for (i=0; i, naveen cherukuri , + * YeJin Cho , YoungHwan An + * + * 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 "mm_player_pd.h" +#include "mm_player_utils.h" +#include "mm_player_priv.h" + +/*--------------------------------------------------------------------------------------- +| LOCAL FUNCTION PROTOTYPES: | +---------------------------------------------------------------------------------------*/ + +/* It's callback to process whenever there is some changes in PD downloader. */ +static gboolean __pd_downloader_callback(GstBus *bus, GstMessage *msg, gpointer data); + +/* This function posts messages to application. */ +/* Currently, MM_MESSAGE_PD_DOWNLOADER_START and MM_MESSAGE_PD_DOWNLOADER_END are used. */ +static gboolean __pd_downloader_post_message(mm_player_t * player, enum MMMessageType msgtype, MMMessageParamType* param); + +/*======================================================================================= +| FUNCTION DEFINITIONS | +=======================================================================================*/ +static gboolean +__pd_downloader_callback(GstBus *bus, GstMessage *msg, gpointer data) +{ + mm_player_t * player = NULL; + mm_player_pd_t *pd = NULL; + gboolean bret = TRUE; + + debug_fenter(); + + /* chech player handle */ + return_val_if_fail ( data, MM_ERROR_INVALID_ARGUMENT ); + + player = MM_PLAYER_CAST((MMHandleType)data); + + /* get PD downloader handle */ + pd = MM_PLAYER_GET_PD((MMHandleType)data); + + return_val_if_fail ( pd, MM_ERROR_INVALID_ARGUMENT ); + +// g_print("%s\n", GST_MESSAGE_TYPE_NAME(msg)); + + switch ( GST_MESSAGE_TYPE( msg ) ) + { + case GST_MESSAGE_EOS: + { + debug_log("PD Downloader EOS received....\n"); + + g_object_set (G_OBJECT (pd->playback_pipeline_src), "eos", TRUE, NULL); + + /* notify application that download is completed */ + __pd_downloader_post_message(player, MM_MESSAGE_PD_DOWNLOADER_END, NULL); + + #ifdef PD_SELF_DOWNLOAD + _mmplayer_unrealize_pd_downloader ((MMHandleType)data); + #endif + } + break; + + case GST_MESSAGE_ERROR: + { + gboolean ret = FALSE; + GError *error = NULL; + gchar* debug = NULL; + GstMessage *new_msg = NULL; + + /* get error code */ + gst_message_parse_error( msg, &error, &debug ); + debug_error ("GST_MESSAGE_ERROR = %s\n", debug); + + new_msg = gst_message_new_error (GST_OBJECT_CAST (pd->playback_pipeline_src), error, debug); + + /* notify application that pd has any error */ + ret = gst_element_post_message (pd->playback_pipeline_src, new_msg); + + _mmplayer_unrealize_pd_downloader ((MMHandleType)data); + } + break; + + case GST_MESSAGE_WARNING: + { + char* debug = NULL; + GError* error = NULL; + + gst_message_parse_warning(msg, &error, &debug); + debug_warning("warning : %s\n", error->message); + debug_warning("debug : %s\n", debug); + + MMPLAYER_FREEIF(debug); + g_error_free( error); + } + break; + + case GST_MESSAGE_STATE_CHANGED: + { + GstState old_state, new_state; + gchar *src_name; + + /* get old and new state */ + gst_message_parse_state_changed (msg, &old_state, &new_state, NULL); + + if (old_state == new_state) + break; + + /* we only care about pipeline state changes */ + if (GST_MESSAGE_SRC (msg) != GST_OBJECT (pd->downloader_pipeline)) + break; + + src_name = gst_object_get_name (msg->src); + debug_log ("%s changed state from %s to %s", src_name, + gst_element_state_get_name (old_state), + gst_element_state_get_name (new_state)); + g_free (src_name); + + switch(new_state) + { + case GST_STATE_VOID_PENDING: + case GST_STATE_NULL: + case GST_STATE_READY: + case GST_STATE_PAUSED: + break; + + case GST_STATE_PLAYING: + /* notify application that download is stated */ + __pd_downloader_post_message(player, MM_MESSAGE_PD_DOWNLOADER_START, NULL); + break; + + default: + break; + } + } + break; + + case GST_MESSAGE_DURATION: + { + GstFormat fmt= GST_FORMAT_BYTES; + + gint64 size = 0LL; + + /* get total size of download file, (bytes) */ + if ( ! gst_element_query_duration( pd->downloader_pipeline, &fmt, &size ) ) + { + GError *err = NULL; + GstMessage *new_msg = NULL; + + err = g_error_new (GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED, "can't get total size"); + new_msg = gst_message_new_error (GST_OBJECT_CAST (pd->playback_pipeline_src), err, NULL); + gst_element_post_message (pd->playback_pipeline_src, new_msg); + + g_error_free (err); + + // TODO: check if playback pipeline is closed well or not + g_object_set (G_OBJECT (pd->playback_pipeline_src), "eos", TRUE, NULL); + + _mmplayer_unrealize_pd_downloader ((MMHandleType)data); + + debug_error("failed to query total size for download\n"); + break; + } + + pd->total_size = size; + + debug_log("PD total size : %lld bytes\n", size); + } + break; + + default: + debug_warning("unhandled message\n"); + break; + } + + debug_fleave(); + + return bret; +} + + +gboolean __pd_downloader_post_message(mm_player_t * player, enum MMMessageType msgtype, MMMessageParamType* param) +{ + debug_fenter(); + + return_val_if_fail( player, FALSE ); + + if ( !player->pd_msg_cb ) + { + debug_warning("no msg callback. can't post\n"); + return FALSE; + } + + player->pd_msg_cb(msgtype, param, player->pd_msg_cb_param); + + debug_fleave(); + + return TRUE; +} + + +gboolean _mmplayer_get_pd_downloader_status(MMHandleType handle, guint64 *current_pos, guint64 *total_size) +{ + debug_fenter(); + + mm_player_pd_t * pd = NULL; + guint64 bytes = 0; + + return_val_if_fail(handle, MM_ERROR_INVALID_ARGUMENT); + + pd = MM_PLAYER_GET_PD(handle); + + return_val_if_fail(pd, MM_ERROR_INVALID_ARGUMENT); + return_val_if_fail(pd->downloader_pipeline, MM_ERROR_INVALID_ARGUMENT); + + if ( !pd->total_size ) + { + debug_warning("not ready to get total size\n"); + return FALSE; + } + + g_object_get(pd->downloader_sink, "current-bytes", &bytes, NULL); + + debug_log("PD status : %lld / %lld\n", bytes, pd->total_size); + + *current_pos = bytes; + *total_size = pd->total_size; + + debug_fleave(); + + return TRUE; +} + + +mm_player_pd_t * _mmplayer_create_pd_downloader() +{ + debug_fenter(); + + mm_player_pd_t * pd = NULL; + + /* create PD handle */ + pd = (mm_player_pd_t *) malloc (sizeof (mm_player_pd_t)); + if ( !pd ) + { + debug_error ("Failed to create pd downloader handle...\n"); + return FALSE; + } + + debug_fleave(); + + return pd; +} + + +gboolean _mmplayer_destroy_pd_downloader (MMHandleType handle) +{ + debug_fenter(); + + mm_player_pd_t * pd = NULL; + + return_val_if_fail ( handle, MM_ERROR_INVALID_ARGUMENT ); + + pd = MM_PLAYER_GET_PD(handle); + + if ( pd && pd->downloader_pipeline) + _mmplayer_unrealize_pd_downloader (handle); + + /* release PD handle */ + MMPLAYER_FREEIF(pd); + + debug_fleave(); + + return TRUE; +} + + +gboolean _mmplayer_realize_pd_downloader (MMHandleType handle, gchar *src_uri, gchar *dst_uri, GstElement *pushsrc) +{ + debug_fenter(); + + mm_player_pd_t * pd = NULL; + + return_val_if_fail ( handle, MM_ERROR_INVALID_ARGUMENT ); + return_val_if_fail ( src_uri, MM_ERROR_INVALID_ARGUMENT ); + return_val_if_fail ( dst_uri, MM_ERROR_INVALID_ARGUMENT ); + return_val_if_fail ( pushsrc, MM_ERROR_INVALID_ARGUMENT ); + + pd = MM_PLAYER_GET_PD(handle); + + /* initialize */ + pd->path_read_from = g_strdup (src_uri); + pd->location_to_save = g_strdup (dst_uri); + pd->playback_pipeline_src = pushsrc; + pd->total_size = 0LL; + + debug_fleave(); + + return TRUE; +} + + +gboolean _mmplayer_start_pd_downloader (MMHandleType handle) +{ + GstBus* bus = NULL; + gboolean bret = FALSE; + GstStateChangeReturn sret = GST_STATE_CHANGE_SUCCESS; + GstState cur_state; + GstState pending_state; + + debug_fenter(); + + mm_player_pd_t * pd = NULL; + + return_val_if_fail ( handle, MM_ERROR_INVALID_ARGUMENT ); + + pd = MM_PLAYER_GET_PD(handle); + + /* pipeline */ + pd->downloader_pipeline = gst_pipeline_new ("PD Downloader"); + if (NULL == pd->downloader_pipeline) + { + debug_error ("Can't create PD download pipeline..."); + return FALSE; + } + + /* source */ + pd->downloader_src = gst_element_factory_make ("souphttpsrc", "PD HTTP download source"); + if (NULL == pd->downloader_src) + { + debug_error ("Can't create PD download src..."); + return FALSE; + } + + /* queue */ + pd->downloader_queue = gst_element_factory_make ("queue", "PD download queue"); + if (NULL == pd->downloader_queue) + { + debug_error ("Can't create PD download queue..."); + return FALSE; + } + + /* filesink */ + pd->downloader_sink = gst_element_factory_make ("filesink", "PD download sink"); + if (NULL == pd->downloader_sink) + { + debug_error ("Can't create PD download sink..."); + return FALSE; + } + + g_object_set(pd->downloader_sink, "sync", FALSE, NULL); + + /* Add to bin and link */ + gst_bin_add_many (GST_BIN (pd->downloader_pipeline), + pd->downloader_src, pd->downloader_queue, pd->downloader_sink, + NULL); + + bret = gst_element_link_many (pd->downloader_src, pd->downloader_queue, pd->downloader_sink, NULL); + if (FALSE == bret) + { + debug_error ("Can't link elements src and sink..."); + return FALSE; + } + + /* Get Bus and set callback to watch */ + bus = gst_pipeline_get_bus (GST_PIPELINE (pd->downloader_pipeline)); + gst_bus_add_watch (bus, __pd_downloader_callback, (gpointer)handle); + gst_object_unref (bus); + + /* Set URI on HTTP source */ + g_object_set (G_OBJECT (pd->downloader_src), "location", pd->path_read_from, NULL); + + /* set file download location on filesink*/ + g_object_set (G_OBJECT (pd->downloader_sink), "location", pd->location_to_save, NULL); + + debug_log ("src location = %s, save location = %s\n", pd->path_read_from, pd->location_to_save); + + /* Start to download */ + sret = gst_element_set_state (pd->downloader_pipeline, GST_STATE_PLAYING); + if (GST_STATE_CHANGE_FAILURE == sret) + { + debug_error ("PD download pipeline failed to go to PLAYING state..."); + return FALSE; + } + + debug_log ("set_state :: sret = %d\n", sret); + + sret = gst_element_get_state (pd->downloader_pipeline, &cur_state, &pending_state, GST_CLOCK_TIME_NONE); + if (GST_STATE_CHANGE_FAILURE == sret) + { + debug_error ("PD download pipeline failed to do get_state..."); + return FALSE; + } + + debug_log ("get-state :: sret = %d\n", sret); + + debug_fleave(); + + return TRUE; +} + + +gboolean _mmplayer_unrealize_pd_downloader (MMHandleType handle) +{ + debug_fenter(); + + mm_player_pd_t * pd = NULL; + + return_val_if_fail ( handle, FALSE ); + + pd = MM_PLAYER_GET_PD(handle); + + return_val_if_fail ( pd && pd->downloader_pipeline, FALSE ); + + gst_element_set_state (pd->downloader_pipeline, GST_STATE_NULL); + gst_element_get_state (pd->downloader_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + + gst_object_unref (G_OBJECT (pd->downloader_pipeline)); + pd->downloader_pipeline = NULL; + + /* free */ + MMPLAYER_FREEIF(pd->path_read_from); + MMPLAYER_FREEIF(pd->location_to_save); + + debug_fleave(); + + return TRUE; +} + + +gint _mm_player_set_pd_downloader_message_cb(MMHandleType handle, MMMessageCallback callback, gpointer user_param) +{ + debug_fenter(); + + mm_player_t * player = NULL; + + return_val_if_fail ( handle, MM_ERROR_INVALID_ARGUMENT ); + + player = MM_PLAYER_CAST((MMHandleType)handle); + + /* PD callback can be set as soon as player handle is created. + * So, player handle must have it. + */ + player->pd_msg_cb = callback; + player->pd_msg_cb_param = user_param; + + debug_log("msg_cb : 0x%x msg_cb_param : 0x%x\n", (guint)callback, (guint)user_param); + + debug_fleave(); + + return MM_ERROR_NONE; +} diff --git a/src/mm_player_priv.c b/src/mm_player_priv.c new file mode 100644 index 0000000..d918e87 --- /dev/null +++ b/src/mm_player_priv.c @@ -0,0 +1,10252 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 +#include +#include +#include + +#include "mm_player_priv.h" +#include "mm_player_ini.h" +#include "mm_player_attrs.h" +#include "mm_player_capture.h" + +/*=========================================================================================== +| | +| LOCAL DEFINITIONS AND DECLARATIONS FOR MODULE | +| | +========================================================================================== */ + +/*--------------------------------------------------------------------------- +| GLOBAL CONSTANT DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| IMPORTED VARIABLE DECLARATIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| IMPORTED FUNCTION DECLARATIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL #defines: | +---------------------------------------------------------------------------*/ +#define TRICK_PLAY_MUTE_THRESHOLD_MAX 2.0 +#define TRICK_PLAY_MUTE_THRESHOLD_MIN 0.0 + +#define MM_VOLUME_FACTOR_DEFAULT 1.0 +#define MM_VOLUME_FACTOR_MIN 0 +#define MM_VOLUME_FACTOR_MAX 1.0 + +#define MM_PLAYER_FADEOUT_TIME_DEFAULT 700000 // 700 msec + +#define MM_PLAYER_MPEG_VNAME "mpegversion" +#define MM_PLAYER_DIVX_VNAME "divxversion" +#define MM_PLAYER_WMV_VNAME "wmvversion" +#define MM_PLAYER_WMA_VNAME "wmaversion" + +#define DEFAULT_PLAYBACK_RATE 1.0 + +#define GST_QUEUE_DEFAULT_TIME 8 +#define GST_QUEUE_HLS_TIME 8 + +/* video capture callback*/ +gulong ahs_appsrc_cb_probe_id = 0; + +#define MMPLAYER_USE_FILE_FOR_BUFFERING(player) (((player)->profile.uri_type != MM_PLAYER_URI_TYPE_HLS) && (PLAYER_INI()->http_file_buffer_path) && (strlen(PLAYER_INI()->http_file_buffer_path) > 0) ) + +#define LAZY_PAUSE_TIMEOUT_MSEC 700 + +/*--------------------------------------------------------------------------- +| LOCAL CONSTANT DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL DATA TYPE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| GLOBAL VARIABLE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL VARIABLE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL FUNCTION PROTOTYPES: | +---------------------------------------------------------------------------*/ +static gboolean __mmplayer_set_state(mm_player_t* player, int state); +static int __mmplayer_get_state(mm_player_t* player); +static int __mmplayer_gst_create_video_pipeline(mm_player_t* player, GstCaps *caps, MMDisplaySurfaceType surface_type); +static int __mmplayer_gst_create_audio_pipeline(mm_player_t* player); +static int __mmplayer_gst_create_text_pipeline(mm_player_t* player); +static int __mmplayer_gst_create_subtitle_src(mm_player_t* player); +static int __mmplayer_gst_create_pipeline(mm_player_t* player); +static int __mmplayer_gst_destroy_pipeline(mm_player_t* player); +static int __mmplayer_gst_element_link_bucket(GList* element_bucket); + +static gboolean __mmplayer_gst_callback(GstBus *bus, GstMessage *msg, gpointer data); +static void __mmplayer_gst_decode_callback(GstElement *decodebin, GstPad *pad, gboolean last, gpointer data); + +static void __mmplayer_typefind_have_type( GstElement *tf, guint probability, GstCaps *caps, gpointer data); +static gboolean __mmplayer_try_to_plug(mm_player_t* player, GstPad *pad, const GstCaps *caps); +static void __mmplayer_pipeline_complete(GstElement *decodebin, gpointer data); +static gboolean __mmplayer_is_midi_type(gchar* str_caps); +static gboolean __mmplayer_is_amr_type (gchar *str_caps); +static gboolean __mmplayer_is_only_mp3_type (gchar *str_caps); + +static gboolean __mmplayer_close_link(mm_player_t* player, GstPad *srcpad, GstElement *sinkelement, const char *padname, const GList *templlist); +static gboolean __mmplayer_feature_filter(GstPluginFeature *feature, gpointer data); +static void __mmplayer_add_new_pad(GstElement *element, GstPad *pad, gpointer data); + +static void __mmplayer_gst_rtp_no_more_pads (GstElement *element, gpointer data); +static void __mmplayer_gst_rtp_dynamic_pad (GstElement *element, GstPad *pad, gpointer data); +static gboolean __mmplayer_update_stream_service_type( mm_player_t* player ); +static gboolean __mmplayer_update_subtitle( GstElement* object, GstBuffer *buffer, GstPad *pad, gpointer data); + + +static void __mmplayer_init_factories(mm_player_t* player); +static void __mmplayer_release_factories(mm_player_t* player); +static void __mmplayer_release_misc(mm_player_t* player); +static gboolean __mmplayer_gstreamer_init(void); + +static int __mmplayer_gst_set_state (mm_player_t* player, GstElement * pipeline, GstState state, gboolean async, gint timeout ); +gboolean __mmplayer_post_message(mm_player_t* player, enum MMMessageType msgtype, MMMessageParamType* param); +static gboolean __mmplayer_gst_extract_tag_from_msg(mm_player_t* player, GstMessage *msg); +int __mmplayer_switch_audio_sink (mm_player_t* player); +static gboolean __mmplayer_gst_remove_fakesink(mm_player_t* player, MMPlayerGstElement* fakesink); +static int __mmplayer_check_state(mm_player_t* player, enum PlayerCommandState command); +static gboolean __mmplayer_audio_stream_probe (GstPad *pad, GstBuffer *buffer, gpointer u_data); + +static gboolean __mmplayer_dump_pipeline_state( mm_player_t* player ); +static gboolean __mmplayer_check_subtitle( mm_player_t* player ); +static gboolean __mmplayer_handle_gst_error ( mm_player_t* player, GstMessage * message, GError* error ); +static gboolean __mmplayer_handle_streaming_error ( mm_player_t* player, GstMessage * message ); +static void __mmplayer_post_delayed_eos( mm_player_t* player, int delay_in_ms ); +static void __mmplayer_cancel_delayed_eos( mm_player_t* player ); +static gboolean __mmplayer_eos_timer_cb(gpointer u_data); +static gboolean __mmplayer_link_decoder( mm_player_t* player,GstPad *srcpad); +static gboolean __mmplayer_link_sink( mm_player_t* player,GstPad *srcpad); +static int __mmplayer_post_missed_plugin(mm_player_t* player); +static int __mmplayer_check_not_supported_codec(mm_player_t* player, gchar* mime); +static gboolean __mmplayer_configure_audio_callback(mm_player_t* player); +static void __mmplayer_add_sink( mm_player_t* player, GstElement* sink); +static void __mmplayer_del_sink( mm_player_t* player, GstElement* sink); +static void __mmplayer_release_signal_connection(mm_player_t* player); +static void __mmplayer_set_antishock( mm_player_t* player, gboolean disable_by_force); +static gpointer __mmplayer_repeat_thread(gpointer data); +int _mmplayer_get_track_count(MMHandleType hplayer, MMPlayerTrackType track_type, int *count); + +static int __gst_realize(mm_player_t* player); +static int __gst_unrealize(mm_player_t* player); +static int __gst_start(mm_player_t* player); +static int __gst_stop(mm_player_t* player); +static int __gst_pause(mm_player_t* player, gboolean async); +static int __gst_resume(mm_player_t* player, gboolean async); +static gboolean __gst_seek(mm_player_t* player, GstElement * element, gdouble rate, + GstFormat format, GstSeekFlags flags, GstSeekType cur_type, + gint64 cur, GstSeekType stop_type, gint64 stop ); +static int __gst_pending_seek ( mm_player_t* player ); + +static int __gst_set_position(mm_player_t* player, int format, unsigned long position, gboolean internal_called); +static int __gst_get_position(mm_player_t* player, int format, unsigned long *position); +static int __gst_get_buffer_position(mm_player_t* player, int format, unsigned long* start_pos, unsigned long* stop_pos); +static int __gst_adjust_subtitle_position(mm_player_t* player, int format, int position); +static int __gst_set_message_callback(mm_player_t* player, MMMessageCallback callback, gpointer user_param); +static void __gst_set_async_state_change(mm_player_t* player, gboolean async); + +static gint __gst_handle_core_error( mm_player_t* player, int code ); +static gint __gst_handle_library_error( mm_player_t* player, int code ); +static gint __gst_handle_resource_error( mm_player_t* player, int code ); +static gint __gst_handle_stream_error( mm_player_t* player, GError* error, GstMessage * message ); +static gint __gst_transform_gsterror( mm_player_t* player, GstMessage * message, GError* error); +static gboolean __gst_send_event_to_sink( mm_player_t* player, GstEvent* event ); + +static int __mmplayer_set_pcm_extraction(mm_player_t* player); +static gboolean __mmplayer_can_extract_pcm( mm_player_t* player ); + +/*fadeout */ +static void __mmplayer_do_sound_fadedown(mm_player_t* player, unsigned int time); +static void __mmplayer_undo_sound_fadedown(mm_player_t* player); + +static void __mmplayer_add_new_caps(GstPad* pad, GParamSpec* unused, gpointer data); +static void __mmplayer_set_unlinked_mime_type(mm_player_t* player, GstCaps *caps); + +/* util */ +const gchar * __get_state_name ( int state ); +static gboolean __is_streaming( mm_player_t* player ); +static gboolean __is_rtsp_streaming( mm_player_t* player ); +static gboolean __is_live_streaming ( mm_player_t* player ); +static gboolean __is_http_streaming( mm_player_t* player ); +static gboolean __is_http_live_streaming( mm_player_t* player ); +static gboolean __is_http_progressive_down(mm_player_t* player); + +static gboolean __mmplayer_warm_up_video_codec( mm_player_t* player, GstElementFactory *factory); +static GstBusSyncReply __mmplayer_bus_sync_callback (GstBus * bus, GstMessage * message, gpointer data); + +static int __mmplayer_realize_streaming_ext(mm_player_t* player); +static int __mmplayer_unrealize_streaming_ext(mm_player_t *player); +static int __mmplayer_start_streaming_ext(mm_player_t *player); +static int __mmplayer_destroy_streaming_ext(mm_player_t* player); + + +/*=========================================================================================== +| | +| FUNCTION DEFINITIONS | +| | +========================================================================================== */ + +/* implementing player FSM */ +/* FIXIT : We need to handle state transition also at here since start api is no more sync */ +static int +__mmplayer_check_state(mm_player_t* player, enum PlayerCommandState command) +{ + MMPlayerStateType current_state = MM_PLAYER_STATE_NUM; + MMPlayerStateType pending_state = MM_PLAYER_STATE_NUM; + MMPlayerStateType target_state = MM_PLAYER_STATE_NUM; + MMPlayerStateType prev_state = MM_PLAYER_STATE_NUM; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + //debug_log("incomming command : %d \n", command ); + + current_state = MMPLAYER_CURRENT_STATE(player); + pending_state = MMPLAYER_PENDING_STATE(player); + target_state = MMPLAYER_TARGET_STATE(player); + prev_state = MMPLAYER_PREV_STATE(player); + + MMPLAYER_PRINT_STATE(player); + + switch( command ) + { + case MMPLAYER_COMMAND_CREATE: + { + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_NULL; + + if ( current_state == MM_PLAYER_STATE_NULL || + current_state == MM_PLAYER_STATE_READY || + current_state == MM_PLAYER_STATE_PAUSED || + current_state == MM_PLAYER_STATE_PLAYING ) + goto NO_OP; + } + break; + + case MMPLAYER_COMMAND_DESTROY: + { + /* destroy can called anytime */ + + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_NONE; + } + break; + + case MMPLAYER_COMMAND_REALIZE: + { + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_READY; + + if ( pending_state != MM_PLAYER_STATE_NONE ) + { + goto INVALID_STATE; + } + else + { + /* need ready state to realize */ + if ( current_state == MM_PLAYER_STATE_READY ) + goto NO_OP; + + if ( current_state != MM_PLAYER_STATE_NULL ) + goto INVALID_STATE; + } + } + break; + + case MMPLAYER_COMMAND_UNREALIZE: + { + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_NULL; + + if ( current_state == MM_PLAYER_STATE_NULL ) + goto NO_OP; + } + break; + + case MMPLAYER_COMMAND_START: + { + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_PLAYING; + + if ( pending_state == MM_PLAYER_STATE_NONE ) + { + if ( current_state == MM_PLAYER_STATE_PLAYING ) + goto NO_OP; + else if ( current_state != MM_PLAYER_STATE_READY && + current_state != MM_PLAYER_STATE_PAUSED ) + goto INVALID_STATE; + } + else if ( pending_state == MM_PLAYER_STATE_PLAYING ) + { + goto ALREADY_GOING; + } + else if ( pending_state == MM_PLAYER_STATE_PAUSED ) + { + debug_log("player is going to paused state, just change the pending state as playing"); + } + else + { + goto INVALID_STATE; + } + } + break; + + case MMPLAYER_COMMAND_STOP: + { + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_READY; + + if ( current_state == MM_PLAYER_STATE_READY ) + goto NO_OP; + + /* need playing/paused state to stop */ + if ( current_state != MM_PLAYER_STATE_PLAYING && + current_state != MM_PLAYER_STATE_PAUSED ) + goto INVALID_STATE; + } + break; + + case MMPLAYER_COMMAND_PAUSE: + { + if ( MMPLAYER_IS_LIVE_STREAMING( player ) ) + goto NO_OP; + + if (player->doing_seek) + goto NOT_COMPLETED_SEEK; + + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_PAUSED; + + if ( pending_state == MM_PLAYER_STATE_NONE ) + { + if ( current_state == MM_PLAYER_STATE_PAUSED ) + goto NO_OP; + else if ( current_state != MM_PLAYER_STATE_PLAYING && current_state != MM_PLAYER_STATE_READY ) // support loading state of broswer + goto INVALID_STATE; + } + else if ( pending_state == MM_PLAYER_STATE_PAUSED ) + { + goto ALREADY_GOING; + } + else if ( pending_state == MM_PLAYER_STATE_PLAYING ) + { + if ( current_state == MM_PLAYER_STATE_PAUSED ) { + debug_log("player is PAUSED going to PLAYING, just change the pending state as PAUSED"); + } else { + goto INVALID_STATE; + } + } + } + break; + + case MMPLAYER_COMMAND_RESUME: + { + if ( MMPLAYER_IS_LIVE_STREAMING(player) ) + goto NO_OP; + + if (player->doing_seek) + goto NOT_COMPLETED_SEEK; + + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_PLAYING; + + if ( pending_state == MM_PLAYER_STATE_NONE ) + { + if ( current_state == MM_PLAYER_STATE_PLAYING ) + goto NO_OP; + else if ( current_state != MM_PLAYER_STATE_PAUSED ) + goto INVALID_STATE; + } + else if ( pending_state == MM_PLAYER_STATE_PLAYING ) + { + goto ALREADY_GOING; + } + else if ( pending_state == MM_PLAYER_STATE_PAUSED ) + { + debug_log("player is going to paused state, just change the pending state as playing"); + } + else + { + goto INVALID_STATE; + } + } + break; + + default: + break; + } + player->cmd = command; + + debug_fleave(); + return MM_ERROR_NONE; + +INVALID_STATE: + debug_warning("since player is in wrong state(%s). it's not able to apply the command(%d)", + MMPLAYER_STATE_GET_NAME(current_state), command); + return MM_ERROR_PLAYER_INVALID_STATE; + +NOT_COMPLETED_SEEK: + debug_warning("not completed seek"); + return MM_ERROR_PLAYER_DOING_SEEK; + +NO_OP: + debug_warning("player is in the desired state(%s). doing noting", MMPLAYER_STATE_GET_NAME(current_state)); + return MM_ERROR_PLAYER_NO_OP; + +ALREADY_GOING: + debug_warning("player is already going to %s, doing nothing", MMPLAYER_STATE_GET_NAME(pending_state)); + return MM_ERROR_PLAYER_NO_OP; +} + +int +__mmplayer_gst_set_state (mm_player_t* player, GstElement * element, GstState state, gboolean async, gint timeout) // @ +{ + GstState element_state = GST_STATE_VOID_PENDING; + GstState element_pending_state = GST_STATE_VOID_PENDING; + GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( element, MM_ERROR_INVALID_ARGUMENT ); + + debug_log("setting [%s] element state to : %d\n", GST_ELEMENT_NAME(element), state); + + /* set state */ + ret = gst_element_set_state(element, state); + + if ( ret == GST_STATE_CHANGE_FAILURE ) + { + debug_error("failed to set [%s] state to [%d]\n", GST_ELEMENT_NAME(element), state); + return MM_ERROR_PLAYER_INTERNAL; + } + + /* return here so state transition to be done in async mode */ + if ( async ) + { + debug_log("async state transition. not waiting for state complete.\n"); + return MM_ERROR_NONE; + } + + /* wait for state transition */ + ret = gst_element_get_state( element, &element_state, &element_pending_state, timeout * GST_SECOND ); + + if ( ret == GST_STATE_CHANGE_FAILURE || ( state != element_state ) ) + { + debug_error("failed to change [%s] element state to [%s] within %d sec\n", + GST_ELEMENT_NAME(element), + gst_element_state_get_name(state), timeout ); + + debug_error(" [%s] state : %s pending : %s \n", + GST_ELEMENT_NAME(element), + gst_element_state_get_name(element_state), + gst_element_state_get_name(element_pending_state) ); + + return MM_ERROR_PLAYER_INTERNAL; + } + + debug_log("[%s] element state has changed to %s \n", + GST_ELEMENT_NAME(element), + gst_element_state_get_name(element_state)); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +static void +__mmplayer_videostream_cb(GstElement *element, void *stream, +int width, int height, gpointer data) // @ +{ + mm_player_t* player = (mm_player_t*)data; + int length = 0; + + return_if_fail ( player ); + + debug_fenter(); + + if (player->video_stream_cb ) + { + length = width * height * 4; // for rgb 32bit + player->video_stream_cb(stream, length, player->video_stream_cb_user_param, width, height); + } + + debug_fleave(); +} + +static void +__mmplayer_videoframe_render_error_cb(GstElement *element, void *error_id, gpointer data) +{ + mm_player_t* player = (mm_player_t*)data; + + return_if_fail ( player ); + + debug_fenter(); + + if (player->video_frame_render_error_cb ) + { + if (player->attrs) + { + int surface_type = 0; + mm_attrs_get_int_by_name (player->attrs, "display_surface_type", &surface_type); + switch (surface_type) + { + case MM_DISPLAY_SURFACE_X_EXT: + player->video_frame_render_error_cb((unsigned int*)error_id, player->video_frame_render_error_cb_user_param); + debug_log("display surface type(X_EXT) : render error callback(%p) is finished", player->video_frame_render_error_cb); + break; + default: + debug_error("video_frame_render_error_cb was set, but this surface type(%d) is not supported", surface_type); + break; + } + } + else + { + debug_error("could not get surface type"); + } + } + else + { + debug_warning("video_frame_render_error_cb was not set"); + } + + debug_fleave(); +} + +gboolean +_mmplayer_update_content_attrs(mm_player_t* player) // @ +{ + GstFormat fmt = GST_FORMAT_TIME; + gint64 dur_nsec = 0; + GstStructure* p = NULL; + MMHandleType attrs = 0; + gint retry_count = 0; + gint retry_count_max = 10; + gchar *path = NULL; + struct stat sb; + + return_val_if_fail ( player, FALSE ); + + if ( ! player->need_update_content_attrs ) + { + debug_log("content attributes are already updated"); + return TRUE; + } + + /* get content attribute first */ + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute"); + return FALSE; + } + + /* update duration + * NOTE : we need to wait for a while until is possible to get duration from pipeline + * as getting duration timing is depends on behavier of demuxers ( or etc ). + * we set timeout 100ms * 10 as initial value. fix it if needed. + */ + if ( player->need_update_content_dur ) + { + while ( retry_count < retry_count_max) + { + if ( FALSE == gst_element_query_duration( player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, + &fmt, &dur_nsec ) ) + { + /* retry if failed */ + debug_warning("failed to get duraton. waiting 100ms and then retrying..."); + usleep(100000); + retry_count++; + continue; + } + + if ( dur_nsec == 0 && ( !MMPLAYER_IS_LIVE_STREAMING( player ) ) ) + { + /* retry if duration is zero in case of not live stream */ + debug_warning("returned duration is zero. but it's not an live stream. retrying..."); + usleep(100000); + retry_count++; + continue; + } + + break; + } + + player->duration = dur_nsec; + debug_log("duration : %lld msec", GST_TIME_AS_MSECONDS(dur_nsec)); + + /* try to get streaming service type */ + __mmplayer_update_stream_service_type( player ); + + /* check duration is OK */ + if ( dur_nsec == 0 && !MMPLAYER_IS_LIVE_STREAMING( player ) ) + { + /* FIXIT : find another way to get duration here. */ + debug_error("finally it's failed to get duration from pipeline. progressbar will not work correctely!"); + } + else + { + player->need_update_content_dur = FALSE; + } + + /*update duration */ + mm_attrs_set_int_by_name(attrs, "content_duration", GST_TIME_AS_MSECONDS(dur_nsec)); + } + else + { + debug_log("not ready to get duration or already updated"); + } + + /* update rate, channels */ + if ( player->pipeline->audiobin && + player->pipeline->audiobin[MMPLAYER_A_SINK].gst ) + { + GstCaps *caps_a = NULL; + GstPad* pad = NULL; + gint samplerate = 0, channels = 0; + + pad = gst_element_get_static_pad( + player->pipeline->audiobin[MMPLAYER_A_CONV].gst, "sink" ); + + if ( pad ) + { + caps_a = gst_pad_get_negotiated_caps( pad ); + + if ( caps_a ) + { + p = gst_caps_get_structure (caps_a, 0); + + mm_attrs_get_int_by_name(attrs, "content_audio_samplerate", &samplerate); + if ( ! samplerate ) // check if update already or not + { + gst_structure_get_int (p, "rate", &samplerate); + mm_attrs_set_int_by_name(attrs, "content_audio_samplerate", samplerate); + + gst_structure_get_int (p, "channels", &channels); + mm_attrs_set_int_by_name(attrs, "content_audio_channels", channels); + + debug_log("samplerate : %d channels : %d", samplerate, channels); + } + gst_caps_unref( caps_a ); + caps_a = NULL; + } + else + { + debug_warning("not ready to get audio caps"); + } + + gst_object_unref( pad ); + } + else + { + debug_warning("failed to get pad from audiosink"); + } + } + + /* update width, height, framerate */ + if ( player->pipeline->videobin && + player->pipeline->videobin[MMPLAYER_V_SINK].gst ) + { + GstCaps *caps_v = NULL; + GstPad* pad = NULL; + gint tmpNu, tmpDe; + gint width, height; + + pad = gst_element_get_static_pad( player->pipeline->videobin[MMPLAYER_V_SINK].gst, "sink" ); + if ( pad ) + { + caps_v = gst_pad_get_negotiated_caps( pad ); + if (caps_v) + { + p = gst_caps_get_structure (caps_v, 0); + gst_structure_get_int (p, "width", &width); + mm_attrs_set_int_by_name(attrs, "content_video_width", width); + + gst_structure_get_int (p, "height", &height); + mm_attrs_set_int_by_name(attrs, "content_video_height", height); + + gst_structure_get_fraction (p, "framerate", &tmpNu, &tmpDe); + + debug_log("width : %d height : %d", width, height ); + + gst_caps_unref( caps_v ); + caps_v = NULL; + + if (tmpDe > 0) + { + mm_attrs_set_int_by_name(attrs, "content_video_fps", tmpNu / tmpDe); + debug_log("fps : %d", tmpNu / tmpDe); + } + } + else + { + debug_warning("failed to get negitiated caps from videosink"); + } + gst_object_unref( pad ); + pad = NULL; + } + else + { + debug_warning("failed to get pad from videosink"); + } + } + + if (player->duration) + { + guint64 data_size = 0; + + if (!MMPLAYER_IS_STREAMING(player) && (player->can_support_codec & FOUND_PLUGIN_VIDEO)) + { + mm_attrs_get_string_by_name(attrs, "profile_uri", &path); + + if (stat(path, &sb) == 0) + { + data_size = (guint64)sb.st_size; + } + } + else if (MMPLAYER_IS_HTTP_STREAMING(player)) + { + data_size = player->http_content_size; + } + + if (data_size) + { + guint64 bitrate = 0; + guint64 msec_dur = 0; + + msec_dur = GST_TIME_AS_MSECONDS(player->duration); + bitrate = data_size * 8 * 1000 / msec_dur; + debug_log("file size : %u, video bitrate = %llu", data_size, bitrate); + mm_attrs_set_int_by_name(attrs, "content_video_bitrate", bitrate); + } + } + + + /* validate all */ + if ( mmf_attrs_commit ( attrs ) ) + { + debug_error("failed to update attributes\n"); + return FALSE; + } + + player->need_update_content_attrs = FALSE; + + return TRUE; +} + +gboolean __mmplayer_update_stream_service_type( mm_player_t* player ) +{ + MMHandleType attrs = 0; + gint streaming_type = STREAMING_SERVICE_NONE; + + debug_fenter(); + + return_val_if_fail ( player && + player->pipeline && + player->pipeline->mainbin && + player->pipeline->mainbin[MMPLAYER_M_SRC].gst, + FALSE ); + + /* streaming service type if streaming */ + if ( ! MMPLAYER_IS_STREAMING(player) ); + return FALSE; + + if (MMPLAYER_IS_RTSP_STREAMING(player)) + { + /* get property from rtspsrc element */ + g_object_get(G_OBJECT(player->pipeline->mainbin[MMPLAYER_M_SRC].gst), "service_type", &streaming_type, NULL); + } + else if (MMPLAYER_IS_HTTP_STREAMING(player)) + { + if ( player->duration == 0) + streaming_type = STREAMING_SERVICE_LIVE; + else + streaming_type = STREAMING_SERVICE_VOD; + } + + player->streaming_type = streaming_type; + + if ( player->streaming_type == STREAMING_SERVICE_LIVE) + { + debug_log("It's live streaming. pause/resume/seek are not working.\n"); + } + else if (player->streaming_type == STREAMING_SERVICE_LIVE) + { + debug_log("It's vod streaming. pause/resume/seek are working.\n"); + } + else + { + debug_warning("fail to determine streaming type. pause/resume/seek may not working properly if stream is live stream\n"); + } + + /* get profile attribute */ + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute\n"); + return FALSE; + } + + mm_attrs_set_int_by_name ( attrs, "streaming_type", streaming_type ); + /* validate all */ + if ( mmf_attrs_commit ( attrs ) ) + { + debug_warning("updating streaming service type failed. pause/resume/seek may not working properly if stream is live stream\n"); + return FALSE; + } + + debug_fleave(); + + return TRUE; +} + + +/* this function sets the player state and also report + * it to applicaton by calling callback function + */ +static gboolean +__mmplayer_set_state(mm_player_t* player, int state) // @ +{ + MMMessageParamType msg = {0, }; + int asm_result = MM_ERROR_NONE; + + debug_fenter(); + return_val_if_fail ( player, FALSE ); + + if ( MMPLAYER_CURRENT_STATE(player) == state ) + { + debug_warning("already same state(%s)\n", MMPLAYER_STATE_GET_NAME(state)); + MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_NONE; + return TRUE; + } + + /* post message to application */ + if (MMPLAYER_TARGET_STATE(player) == state) + { + /* fill the message with state of player */ + msg.state.previous = MMPLAYER_CURRENT_STATE(player); + msg.state.current = state; + + /* state changed by asm callback */ + if ( player->sm.by_asm_cb ) + { + msg.union_type = MM_MSG_UNION_CODE; + msg.code = player->sm.event_src; + MMPLAYER_POST_MSG( player, MM_MESSAGE_STATE_INTERRUPTED, &msg ); + } + /* state changed by usecase */ + else + { + MMPLAYER_POST_MSG( player, MM_MESSAGE_STATE_CHANGED, &msg ); + } + + debug_log ("player reach the target state, then do something in each state(%s).\n", + MMPLAYER_STATE_GET_NAME(MMPLAYER_TARGET_STATE(player))); + } + else + { + debug_log ("intermediate state, do nothing.\n"); + MMPLAYER_PRINT_STATE(player); + + return TRUE; + } + + /* update player states */ + MMPLAYER_PREV_STATE(player) = MMPLAYER_CURRENT_STATE(player); + MMPLAYER_CURRENT_STATE(player) = state; + if ( MMPLAYER_CURRENT_STATE(player) == MMPLAYER_PENDING_STATE(player) ) + MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_NONE; + + /* print state */ + MMPLAYER_PRINT_STATE(player); + + switch ( MMPLAYER_TARGET_STATE(player) ) + { + case MM_PLAYER_STATE_NULL: + case MM_PLAYER_STATE_READY: + { + if (player->cmd == MMPLAYER_COMMAND_STOP) + { + asm_result = _mmplayer_asm_set_state((MMHandleType)player, ASM_STATE_STOP); + if ( asm_result != MM_ERROR_NONE ) + { + debug_error("failed to set asm state to stop\n"); + return FALSE; + } + } + } + break; + + case MM_PLAYER_STATE_PAUSED: + { + /* special care for local playback. normaly we can get some content attribute + * when the demuxer is changed to PAUSED. so we are trying it. it will be tried again + * when PLAYING state has signalled if failed. + * note that this is only happening pause command has come before the state of pipeline + * reach to the PLAYING. + */ + if ( ! player->sent_bos ) + { + player->need_update_content_attrs = TRUE; + player->need_update_content_dur = TRUE; + _mmplayer_update_content_attrs( player ); + } + + /* add audio callback probe if condition is satisfied */ + if ( ! player->audio_cb_probe_id && player->is_sound_extraction ) + __mmplayer_configure_audio_callback(player); + + asm_result = _mmplayer_asm_set_state((MMHandleType)player, ASM_STATE_PAUSE); + if ( asm_result ) + { + debug_error("failed to set asm state to PAUSE\n"); + return FALSE; + } + } + break; + + case MM_PLAYER_STATE_PLAYING: + { + /* non-managed prepare case, should be updated */ + if ( ! player->need_update_content_dur) + { + player->need_update_content_dur = TRUE; + _mmplayer_update_content_attrs ( player ); + } + if (MMPLAYER_IS_STREAMING(player)) + { + /* force setting value to TRUE for streaming */ + player->need_update_content_attrs = TRUE; + _mmplayer_update_content_attrs ( player ); + } + + if ( player->cmd == MMPLAYER_COMMAND_START && !player->sent_bos ) + { + __mmplayer_post_missed_plugin ( player ); + + /* update video resource status */ + if ( ( player->can_support_codec & 0x02) == FOUND_PLUGIN_VIDEO ) + { + asm_result = _mmplayer_asm_set_state((MMHandleType)player, ASM_STATE_PLAYING); + if ( asm_result ) + { + MMMessageParamType msg = {0, }; + + debug_error("failed to go ahead because of video conflict\n"); + + msg.union_type = MM_MSG_UNION_CODE; + msg.code = MM_ERROR_POLICY_INTERRUPTED; + MMPLAYER_POST_MSG( player, MM_MESSAGE_STATE_INTERRUPTED, &msg); + + _mmplayer_unrealize((MMHandleType)player); + + return FALSE; + } + } + } + + if ( player->resumed_by_rewind && player->playback_rate < 0.0 ) + { + /* initialize because auto resume is done well. */ + player->resumed_by_rewind = FALSE; + player->playback_rate = 1.0; + } + + if ( !player->sent_bos ) + { + /* check audio codec field is set or not + * we can get it from typefinder or codec's caps. + */ + gchar *audio_codec = NULL; + mm_attrs_get_string_by_name(player->attrs, "content_audio_codec", &audio_codec); + + /* The codec format can't be sent for audio only case like amr, mid etc. + * Because, parser don't make related TAG. + * So, if it's not set yet, fill it with found data. + */ + if ( ! audio_codec ) + { + if ( g_strrstr(player->type, "audio/midi")) + { + audio_codec = g_strdup("MIDI"); + + } + else if ( g_strrstr(player->type, "audio/x-amr")) + { + audio_codec = g_strdup("AMR"); + } + else if ( g_strrstr(player->type, "audio/mpeg") && !g_strrstr(player->type, "mpegversion=(int)1")) + { + audio_codec = g_strdup("AAC"); + } + else + { + audio_codec = g_strdup("unknown"); + } + mm_attrs_set_string_by_name(player->attrs, "content_audio_codec", audio_codec); + + MMPLAYER_FREEIF(audio_codec); + mmf_attrs_commit(player->attrs); + debug_log("set audio codec type with caps\n"); + } + + MMTA_ACUM_ITEM_END("[KPI] start media player service", FALSE); + MMTA_ACUM_ITEM_END("[KPI] media player service create->playing", FALSE); + + MMPLAYER_POST_MSG ( player, MM_MESSAGE_BEGIN_OF_STREAM, NULL ); + player->sent_bos = TRUE; + } + } + break; + + case MM_PLAYER_STATE_NONE: + default: + debug_warning("invalid target state, there is nothing to do.\n"); + break; + } + + debug_fleave(); + + return TRUE; +} + + +gboolean +__mmplayer_post_message(mm_player_t* player, enum MMMessageType msgtype, MMMessageParamType* param) // @ +{ + return_val_if_fail( player, FALSE ); + + debug_fenter(); + + if ( !player->msg_cb ) + { + debug_warning("no msg callback. can't post\n"); + return FALSE; + } + + //debug_log("Message (type : %d) will be posted using msg-cb(%p). \n", msgtype, player->msg_cb); + + player->msg_cb(msgtype, param, player->msg_cb_param); + + debug_fleave(); + + return TRUE; +} + + +static int +__mmplayer_get_state(mm_player_t* player) // @ +{ + int state = MM_PLAYER_STATE_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_PLAYER_STATE_NONE ); + + state = MMPLAYER_CURRENT_STATE(player); + + debug_log("player state is %s.\n", MMPLAYER_STATE_GET_NAME(state)); + + debug_fleave(); + + return state; +} + +static void +__gst_set_async_state_change(mm_player_t* player, gboolean async) +{ + //debug_fenter(); + return_if_fail( player && player->pipeline && player->pipeline->mainbin ); + + /* need only when we are using decodebin */ + if ( ! PLAYER_INI()->use_decodebin ) + return; + + /* audio sink */ + if ( player->pipeline->audiobin && + player->pipeline->audiobin[MMPLAYER_A_SINK].gst ) + { + debug_log("audiosink async : %d\n", async); + g_object_set (G_OBJECT (player->pipeline->audiobin[MMPLAYER_A_SINK].gst), "async", async, NULL); + } + + /* video sink */ + if ( player->pipeline->videobin && + player->pipeline->videobin[MMPLAYER_V_SINK].gst ) + { + debug_log("videosink async : %d\n", async); + g_object_set (G_OBJECT (player->pipeline->videobin[MMPLAYER_V_SINK].gst), "async", async, NULL); + } + + /* decodebin if enabled */ + if ( PLAYER_INI()->use_decodebin ) + { + debug_log("decodebin async : %d\n", async); + g_object_set (G_OBJECT (player->pipeline->mainbin[MMPLAYER_M_AUTOPLUG].gst), "async-handling", async, NULL); + } + + //debug_fleave(); +} + +static gpointer __mmplayer_repeat_thread(gpointer data) +{ + mm_player_t* player = (mm_player_t*) data; + gboolean ret_value = FALSE; + MMHandleType attrs = 0; + gint count = 0; + + return_val_if_fail ( player, NULL ); + + while ( ! player->repeat_thread_exit ) + { + debug_log("repeat thread started. waiting for signal.\n"); + g_cond_wait( player->repeat_thread_cond, player->repeat_thread_mutex ); + + if ( player->repeat_thread_exit ) + { + debug_log("exiting repeat thread\n"); + break; + } + + if ( !player->cmd_lock ) + { + debug_log("can't get cmd lock\n"); + return NULL; + } + + /* lock */ + g_mutex_lock(player->cmd_lock); + + attrs = MMPLAYER_GET_ATTRS(player); + + if (mm_attrs_get_int_by_name(attrs, "profile_play_count", &count) != MM_ERROR_NONE) + { + debug_error("can not get play count\n"); + break; + } + + if ( player->section_repeat ) + { + ret_value = _mmplayer_activate_section_repeat((MMHandleType)player, player->section_repeat_start, player->section_repeat_end); + } + else + { + if ( player->playback_rate < 0.0 ) + { + player->resumed_by_rewind = TRUE; + _mmplayer_set_mute((MMHandleType)player, 0); + MMPLAYER_POST_MSG( player, MM_MESSAGE_RESUMED_BY_REW, NULL ); + } + + ret_value = __gst_seek( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, 1.0, + GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, + 0, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); + + /* initialize */ + player->sent_bos = FALSE; + } + + if ( ! ret_value ) + { + debug_error("failed to set position to zero for rewind\n"); + continue; + } + + /* decrease play count */ + if ( count > 1 ) + { + /* we successeded to rewind. update play count and then wait for next EOS */ + count--; + + mm_attrs_set_int_by_name(attrs, "profile_play_count", count); + + /* commit attribute */ + if ( mmf_attrs_commit ( attrs ) ) + { + debug_error("failed to commit attribute\n"); + } + } + + /* unlock */ + g_mutex_unlock(player->cmd_lock); + } + + return NULL; +} + +static void +__mmplayer_handle_buffering_message ( mm_player_t* player ) +{ + MMPlayerStateType prev_state = MM_PLAYER_STATE_NONE; + MMPlayerStateType current_state = MM_PLAYER_STATE_NONE; + MMPlayerStateType target_state = MM_PLAYER_STATE_NONE; + MMPlayerStateType pending_state = MM_PLAYER_STATE_NONE; + + return_if_fail ( player ); + + prev_state = MMPLAYER_PREV_STATE(player), + current_state = MMPLAYER_CURRENT_STATE(player); + target_state = MMPLAYER_TARGET_STATE(player); + pending_state = MMPLAYER_PENDING_STATE(player); + + if ( MMPLAYER_IS_RTSP_STREAMING(player) ) + return; + + if ( !player->streamer->is_buffering ) + { + debug_log( "player state : prev %s, current %s, pending %s, target %s \n", + MMPLAYER_STATE_GET_NAME(prev_state), + MMPLAYER_STATE_GET_NAME(current_state), + MMPLAYER_STATE_GET_NAME(pending_state), + MMPLAYER_STATE_GET_NAME(target_state)); + + /* NOTE : if buffering has done, player has to go to target state. */ + switch ( target_state ) + { + case MM_PLAYER_STATE_PAUSED : + { + switch ( pending_state ) + { + case MM_PLAYER_STATE_PLAYING: + { + __gst_pause ( player, TRUE ); + } + break; + + case MM_PLAYER_STATE_PAUSED: + { + debug_log("player is already going to paused state, there is nothing to do.\n"); + } + break; + + case MM_PLAYER_STATE_NONE: + case MM_PLAYER_STATE_NULL: + case MM_PLAYER_STATE_READY: + default : + { + debug_warning("invalid pending state [%s].\n", MMPLAYER_STATE_GET_NAME(pending_state) ); + } + break; + } + } + break; + + case MM_PLAYER_STATE_PLAYING : + { + switch ( pending_state ) + { + case MM_PLAYER_STATE_NONE: + { + if (current_state != MM_PLAYER_STATE_PLAYING) + __gst_resume ( player, TRUE ); + } + break; + + case MM_PLAYER_STATE_PAUSED: + { + /* NOTE: It should be worked as asynchronously. + * Because, buffering can be completed during autoplugging when pipeline would try to go playing state directly. + */ + __gst_resume ( player, TRUE ); + } + break; + + case MM_PLAYER_STATE_PLAYING: + { + debug_log("player is already going to playing state, there is nothing to do.\n"); + } + break; + + case MM_PLAYER_STATE_NULL: + case MM_PLAYER_STATE_READY: + default : + { + debug_warning("invalid pending state [%s].\n", MMPLAYER_STATE_GET_NAME(pending_state) ); + } + break; + } + } + break; + + case MM_PLAYER_STATE_NULL : + case MM_PLAYER_STATE_READY : + case MM_PLAYER_STATE_NONE : + default: + { + debug_warning("invalid target state [%s].\n", MMPLAYER_STATE_GET_NAME(target_state) ); + } + break; + } + } + else + { + /* NOTE : during the buffering, pause the player for stopping pipeline clock. + * it's for stopping the pipeline clock to prevent dropping the data in sink element. + */ + switch ( pending_state ) + { + case MM_PLAYER_STATE_NONE: + { + if (current_state != MM_PLAYER_STATE_PAUSED) + __gst_pause ( player, TRUE ); + } + break; + + case MM_PLAYER_STATE_PLAYING: + { + __gst_pause ( player, TRUE ); + } + break; + + case MM_PLAYER_STATE_PAUSED: + { + debug_log("player is already going to paused state, there is nothing to do.\n"); + } + break; + + case MM_PLAYER_STATE_NULL: + case MM_PLAYER_STATE_READY: + default : + { + debug_warning("invalid pending state [%s].\n", MMPLAYER_STATE_GET_NAME(pending_state) ); + } + break; + } + } +} + +static gboolean +__mmplayer_gst_callback(GstBus *bus, GstMessage *msg, gpointer data) // @ +{ + mm_player_t* player = (mm_player_t*) data; + gboolean ret = TRUE; + static gboolean async_done = FALSE; + + return_val_if_fail ( player, FALSE ); + return_val_if_fail ( msg && GST_IS_MESSAGE(msg), FALSE ); + + switch ( GST_MESSAGE_TYPE( msg ) ) + { + case GST_MESSAGE_UNKNOWN: + debug_warning("unknown message received\n"); + break; + + case GST_MESSAGE_EOS: + { + MMHandleType attrs = 0; + gint count = 0; + + debug_log("GST_MESSAGE_EOS received\n"); + + /* NOTE : EOS event is comming multiple time. watch out it */ + /* check state. we only process EOS when pipeline state goes to PLAYING */ + if ( ! (player->cmd == MMPLAYER_COMMAND_START || player->cmd == MMPLAYER_COMMAND_RESUME) ) + { + debug_warning("EOS received on non-playing state. ignoring it\n"); + break; + } + + if ( (player->audio_stream_cb) && (player->is_sound_extraction) ) + { + GstPad *pad = NULL; + + pad = gst_element_get_static_pad (player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "sink"); + + debug_error("release audio callback\n"); + + /* release audio callback */ + gst_pad_remove_buffer_probe (pad, player->audio_cb_probe_id); + player->audio_cb_probe_id = 0; + /* audio callback should be free because it can be called even though probe remove.*/ + player->audio_stream_cb = NULL; + player->audio_stream_cb_user_param = NULL; + + } + + /* rewind if repeat count is greater then zero */ + /* get play count */ + attrs = MMPLAYER_GET_ATTRS(player); + + if ( attrs ) + { + gboolean smooth_repeat = FALSE; + + mm_attrs_get_int_by_name(attrs, "profile_play_count", &count); + mm_attrs_get_int_by_name(attrs, "profile_smooth_repeat", &smooth_repeat); + + debug_log("remaining play count: %d, playback rate: %f\n", count, player->playback_rate); + + if ( count > 1 || count == -1 || player->playback_rate < 0.0 ) /* default value is 1 */ + { + if ( smooth_repeat ) + { + debug_log("smooth repeat enabled. seeking operation will be excuted in new thread\n"); + + g_cond_signal( player->repeat_thread_cond ); + + break; + } + else + { + gint ret_value = 0; + + if ( player->section_repeat ) + { + ret_value = _mmplayer_activate_section_repeat((MMHandleType)player, player->section_repeat_start, player->section_repeat_end); + } + else + { + + if ( player->playback_rate < 0.0 ) + { + player->resumed_by_rewind = TRUE; + _mmplayer_set_mute((MMHandleType)player, 0); + MMPLAYER_POST_MSG( player, MM_MESSAGE_RESUMED_BY_REW, NULL ); + } + + ret_value = __gst_set_position( player, MM_PLAYER_POS_FORMAT_TIME, 0, TRUE); + + /* initialize */ + player->sent_bos = FALSE; + } + + if ( MM_ERROR_NONE != ret_value ) + { + debug_error("failed to set position to zero for rewind\n"); + } + else + { + if ( count > 1 ) + { + /* we successeded to rewind. update play count and then wait for next EOS */ + count--; + + mm_attrs_set_int_by_name(attrs, "profile_play_count", count); + + if ( mmf_attrs_commit ( attrs ) ) + debug_error("failed to commit attrs\n"); + } + } + + break; + } + } + } + + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-eos" ); + + /* post eos message to application */ + __mmplayer_post_delayed_eos( player, PLAYER_INI()->eos_delay ); + + /* reset last position */ + player->last_position = 0; + } + break; + + case GST_MESSAGE_ERROR: + { + GError *error = NULL; + gchar* debug = NULL; + gchar *msg_src_element = NULL; + + /* generating debug info before returning error */ + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-error" ); + + /* get error code */ + gst_message_parse_error( msg, &error, &debug ); + + msg_src_element = GST_ELEMENT_NAME( GST_ELEMENT_CAST( msg->src ) ); + if ( gst_structure_has_name ( msg->structure, "streaming_error" ) ) + { + /* Note : the streaming error from the streaming source is handled + * using __mmplayer_handle_streaming_error. + */ + __mmplayer_handle_streaming_error ( player, msg ); + + /* dump state of all element */ + __mmplayer_dump_pipeline_state( player ); + } + else + { + /* traslate gst error code to msl error code. then post it + * to application if needed + */ + __mmplayer_handle_gst_error( player, msg, error ); + + /* dump state of all element */ + __mmplayer_dump_pipeline_state( player ); + + } + + if (MMPLAYER_IS_HTTP_PD(player)) + { + _mmplayer_unrealize_pd_downloader ((MMHandleType)player); + } + + MMPLAYER_FREEIF( debug ); + g_error_free( error ); + } + break; + + case GST_MESSAGE_WARNING: + { + char* debug = NULL; + GError* error = NULL; + + gst_message_parse_warning(msg, &error, &debug); + + debug_warning("warning : %s\n", error->message); + debug_warning("debug : %s\n", debug); + + MMPLAYER_POST_MSG( player, MM_MESSAGE_WARNING, NULL ); + + MMPLAYER_FREEIF( debug ); + g_error_free( error ); + } + break; + + case GST_MESSAGE_INFO: debug_log("GST_MESSAGE_STATE_DIRTY\n"); break; + + case GST_MESSAGE_TAG: + { + debug_log("GST_MESSAGE_TAG\n"); + if ( ! __mmplayer_gst_extract_tag_from_msg( player, msg ) ) + { + debug_warning("failed to extract tags from gstmessage\n"); + } + } + break; + + case GST_MESSAGE_BUFFERING: + { + MMMessageParamType msg_param = {0, }; + gboolean update_buffering_percent = TRUE; + + if ( !MMPLAYER_IS_STREAMING(player) || (player->profile.uri_type == MM_PLAYER_URI_TYPE_HLS) ) // pure hlsdemux case, don't consider buffering of msl currently + break; + + __mm_player_streaming_buffering (player->streamer, msg); + + __mmplayer_handle_buffering_message ( player ); + + update_buffering_percent = (player->pipeline_is_constructed || MMPLAYER_IS_RTSP_STREAMING(player) ); + if (update_buffering_percent) + { + msg_param.connection.buffering = player->streamer->buffering_percent; + MMPLAYER_POST_MSG ( player, MM_MESSAGE_BUFFERING, &msg_param ); + } + } + break; + + case GST_MESSAGE_STATE_CHANGED: + { + MMPlayerGstElement *mainbin; + const GValue *voldstate, *vnewstate, *vpending; + GstState oldstate, newstate, pending; + + if ( ! ( player->pipeline && player->pipeline->mainbin ) ) + { + debug_error("player pipeline handle is null"); + + break; + } + + mainbin = player->pipeline->mainbin; + + /* get state info from msg */ + voldstate = gst_structure_get_value (msg->structure, "old-state"); + vnewstate = gst_structure_get_value (msg->structure, "new-state"); + vpending = gst_structure_get_value (msg->structure, "pending-state"); + + oldstate = (GstState)voldstate->data[0].v_int; + newstate = (GstState)vnewstate->data[0].v_int; + pending = (GstState)vpending->data[0].v_int; + + if (oldstate == newstate) + break; + + debug_log("state changed [%s] : %s ---> %s final : %s\n", + GST_OBJECT_NAME(GST_MESSAGE_SRC(msg)), + gst_element_state_get_name( (GstState)oldstate ), + gst_element_state_get_name( (GstState)newstate ), + gst_element_state_get_name( (GstState)pending ) ); + + /* we only handle messages from pipeline */ + if( msg->src != (GstObject *)mainbin[MMPLAYER_M_PIPE].gst ) + break; + + switch(newstate) + { + case GST_STATE_VOID_PENDING: + break; + + case GST_STATE_NULL: + break; + + case GST_STATE_READY: + break; + + case GST_STATE_PAUSED: + { + gboolean prepare_async = FALSE; + + player->need_update_content_dur = TRUE; + + if ( ! player->audio_cb_probe_id && player->is_sound_extraction ) + __mmplayer_configure_audio_callback(player); + + if ( ! player->sent_bos && oldstate == GST_STATE_READY) // managed prepare async case + { + mm_attrs_get_int_by_name(player->attrs, "profile_prepare_async", &prepare_async); + debug_log("checking prepare mode for async transition - %d", prepare_async); + } + + if ( MMPLAYER_IS_STREAMING(player) || prepare_async ) + { + MMPLAYER_SET_STATE ( player, MM_PLAYER_STATE_PAUSED ); + + if (player->streamer) + __mm_player_streaming_set_content_bitrate(player->streamer, player->total_maximum_bitrate, player->total_bitrate); + } + } + break; + + case GST_STATE_PLAYING: + { + if (player->doing_seek && async_done) + { + player->doing_seek = FALSE; + async_done = FALSE; + MMPLAYER_POST_MSG ( player, MM_MESSAGE_SEEK_COMPLETED, NULL ); + } + + if ( MMPLAYER_IS_STREAMING(player) ) // managed prepare async case when buffering is completed + { + // pending state should be reset oyherwise, it's still playing even though it's resumed after bufferging. + MMPLAYER_SET_STATE ( player, MM_PLAYER_STATE_PLAYING); + } + } + break; + + default: + break; + } + } + break; + + case GST_MESSAGE_STATE_DIRTY: debug_log("GST_MESSAGE_STATE_DIRTY\n"); break; + case GST_MESSAGE_STEP_DONE: debug_log("GST_MESSAGE_STEP_DONE\n"); break; + case GST_MESSAGE_CLOCK_PROVIDE: debug_log("GST_MESSAGE_CLOCK_PROVIDE\n"); break; + + case GST_MESSAGE_CLOCK_LOST: + { + GstClock *clock = NULL; + gst_message_parse_clock_lost (msg, &clock); + debug_log("GST_MESSAGE_CLOCK_LOST : %s\n", (clock ? GST_OBJECT_NAME (clock) : "NULL")); + g_print ("GST_MESSAGE_CLOCK_LOST : %s\n", (clock ? GST_OBJECT_NAME (clock) : "NULL")); + + if (PLAYER_INI()->provide_clock) + { + debug_log ("Provide clock is TRUE, do pause->resume\n"); + __gst_pause(player, FALSE); + __gst_resume(player, FALSE); + } + } + break; + + case GST_MESSAGE_NEW_CLOCK: + { + GstClock *clock = NULL; + gst_message_parse_new_clock (msg, &clock); + debug_log("GST_MESSAGE_NEW_CLOCK : %s\n", (clock ? GST_OBJECT_NAME (clock) : "NULL")); + } + break; + + case GST_MESSAGE_STRUCTURE_CHANGE: debug_log("GST_MESSAGE_STRUCTURE_CHANGE\n"); break; + case GST_MESSAGE_STREAM_STATUS: debug_log("GST_MESSAGE_STREAM_STATUS\n"); break; + case GST_MESSAGE_APPLICATION: debug_log("GST_MESSAGE_APPLICATION\n"); break; + + case GST_MESSAGE_ELEMENT: + { + debug_log("GST_MESSAGE_ELEMENT\n"); + } + break; + + case GST_MESSAGE_SEGMENT_START: debug_log("GST_MESSAGE_SEGMENT_START\n"); break; + case GST_MESSAGE_SEGMENT_DONE: debug_log("GST_MESSAGE_SEGMENT_DONE\n"); break; + + case GST_MESSAGE_DURATION: + { + debug_log("GST_MESSAGE_DURATION\n"); + + if (MMPLAYER_IS_STREAMING(player)) + { + GstFormat format; + gint64 bytes = 0; + + gst_message_parse_duration (msg, &format, &bytes); + if (format == GST_FORMAT_BYTES) + { + debug_log("data total size of http content: %lld", bytes); + player->http_content_size = bytes; + } + } + + player->need_update_content_attrs = TRUE; + player->need_update_content_dur = TRUE; + _mmplayer_update_content_attrs(player); + } + break; + + case GST_MESSAGE_LATENCY: debug_log("GST_MESSAGE_LATENCY\n"); break; + case GST_MESSAGE_ASYNC_START: debug_log("GST_MESSAGE_ASYNC_DONE : %s\n", gst_element_get_name(GST_MESSAGE_SRC(msg))); break; + + case GST_MESSAGE_ASYNC_DONE: + { + debug_log("GST_MESSAGE_ASYNC_DONE : %s\n", gst_element_get_name(GST_MESSAGE_SRC(msg))); + + if (player->doing_seek) + { + if (MMPLAYER_TARGET_STATE(player) == MM_PLAYER_STATE_PAUSED) + { + player->doing_seek = FALSE; + MMPLAYER_POST_MSG ( player, MM_MESSAGE_SEEK_COMPLETED, NULL ); + } + else if (MMPLAYER_TARGET_STATE(player) == MM_PLAYER_STATE_PLAYING) + { + async_done = TRUE; + } + } + } + break; + + case GST_MESSAGE_REQUEST_STATE: debug_log("GST_MESSAGE_REQUEST_STATE\n"); break; + case GST_MESSAGE_STEP_START: debug_log("GST_MESSAGE_STEP_START\n"); break; + case GST_MESSAGE_QOS: debug_log("GST_MESSAGE_QOS\n"); break; + case GST_MESSAGE_PROGRESS: debug_log("GST_MESSAGE_PROGRESS\n"); break; + case GST_MESSAGE_ANY: debug_log("GST_MESSAGE_ANY\n"); break; + + default: + debug_warning("unhandled message\n"); + break; + } + + /* FIXIT : this cause so many warnings/errors from glib/gstreamer. we should not call it since + * gst_element_post_message api takes ownership of the message. + */ + //gst_message_unref( msg ); + + return ret; +} + +static gboolean +__mmplayer_gst_extract_tag_from_msg(mm_player_t* player, GstMessage* msg) // @ +{ + +/* macro for better code readability */ +#define MMPLAYER_UPDATE_TAG_STRING(gsttag, attribute, playertag) \ +if (gst_tag_list_get_string(tag_list, gsttag, &string)) \ +{\ + if (string != NULL)\ + {\ + debug_log ( "update tag string : %s\n", string); \ + mm_attrs_set_string_by_name(attribute, playertag, string); \ + g_free(string);\ + string = NULL;\ + }\ +} + +#define MMPLAYER_UPDATE_TAG_IMAGE(gsttag, attribute, playertag) \ +value = gst_tag_list_get_value_index(tag_list, gsttag, index); \ +if (value) \ +{\ + buffer = gst_value_get_buffer (value); \ + debug_log ( "update album cover data : %p, size : %d\n", GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer)); \ + player->album_art = (gchar *)g_malloc(GST_BUFFER_SIZE(buffer)); \ + if (player->album_art); \ + { \ + memcpy(player->album_art, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer)); \ + mm_attrs_set_data_by_name(attribute, playertag, (void *)player->album_art, GST_BUFFER_SIZE(buffer)); \ + } \ +} + +#define MMPLAYER_UPDATE_TAG_UINT(gsttag, attribute, playertag) \ +if (gst_tag_list_get_uint(tag_list, gsttag, &v_uint))\ +{\ + if(v_uint)\ + {\ + if(gsttag==GST_TAG_BITRATE)\ + {\ + if (player->updated_bitrate_count == 0) \ + mm_attrs_set_int_by_name(attribute, "content_audio_bitrate", v_uint); \ + if (player->updated_bitrate_countbitrate[player->updated_bitrate_count] = v_uint;\ + player->total_bitrate += player->bitrate[player->updated_maximum_bitrate_count]; \ + player->updated_bitrate_count++; \ + mm_attrs_set_int_by_name(attribute, playertag, player->total_bitrate);\ + debug_log ( "update bitrate %d[bps] of stream #%d.\n", v_uint, player->updated_bitrate_count);\ + }\ + }\ + else if (gsttag==GST_TAG_MAXIMUM_BITRATE)\ + {\ + if (player->updated_maximum_bitrate_countmaximum_bitrate[player->updated_maximum_bitrate_count] = v_uint;\ + player->total_maximum_bitrate += player->maximum_bitrate[player->updated_maximum_bitrate_count]; \ + player->updated_maximum_bitrate_count++; \ + mm_attrs_set_int_by_name(attribute, playertag, player->total_maximum_bitrate); \ + debug_log ( "update maximum bitrate %d[bps] of stream #%d\n", v_uint, player->updated_maximum_bitrate_count);\ + }\ + }\ + else\ + {\ + mm_attrs_set_int_by_name(attribute, playertag, v_uint); \ + }\ + v_uint = 0;\ + }\ +} + +#define MMPLAYER_UPDATE_TAG_DATE(gsttag, attribute, playertag) \ +if (gst_tag_list_get_date(tag_list, gsttag, &date))\ +{\ + if (date != NULL)\ + {\ + string = g_strdup_printf("%d", g_date_get_year(date));\ + mm_attrs_set_string_by_name(attribute, playertag, string);\ + debug_log ( "metainfo year : %s\n", string);\ + MMPLAYER_FREEIF(string);\ + g_date_free(date);\ + }\ +} + +#define MMPLAYER_UPDATE_TAG_UINT64(gsttag, attribute, playertag) \ +if(gst_tag_list_get_uint64(tag_list, gsttag, &v_uint64))\ +{\ + if(v_uint64)\ + {\ + /* FIXIT : don't know how to store date */\ + g_assert(1);\ + v_uint64 = 0;\ + }\ +} + +#define MMPLAYER_UPDATE_TAG_DOUBLE(gsttag, attribute, playertag) \ +if(gst_tag_list_get_double(tag_list, gsttag, &v_double))\ +{\ + if(v_double)\ + {\ + /* FIXIT : don't know how to store date */\ + g_assert(1);\ + v_double = 0;\ + }\ +} + + /* function start */ + GstTagList* tag_list = NULL; + + MMHandleType attrs = 0; + + char *string = NULL; + guint v_uint = 0; + GDate *date = NULL; + /* album cover */ + GstBuffer *buffer = NULL; + gint index = 0; + const GValue *value; + + /* currently not used. but those are needed for above macro */ + //guint64 v_uint64 = 0; + //gdouble v_double = 0; + + return_val_if_fail( player && msg, FALSE ); + + attrs = MMPLAYER_GET_ATTRS(player); + + return_val_if_fail( attrs, FALSE ); + + /* get tag list from gst message */ + gst_message_parse_tag(msg, &tag_list); + + /* store tags to player attributes */ + MMPLAYER_UPDATE_TAG_STRING(GST_TAG_TITLE, attrs, "tag_title"); + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_TITLE_SORTNAME, ?, ?); */ + MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ARTIST, attrs, "tag_artist"); + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ARTIST_SORTNAME, ?, ?); */ + MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ALBUM, attrs, "tag_album"); + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ALBUM_SORTNAME, ?, ?); */ + MMPLAYER_UPDATE_TAG_STRING(GST_TAG_COMPOSER, attrs, "tag_author"); + MMPLAYER_UPDATE_TAG_DATE(GST_TAG_DATE, attrs, "tag_date"); + MMPLAYER_UPDATE_TAG_STRING(GST_TAG_GENRE, attrs, "tag_genre"); + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_COMMENT, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_EXTENDED_COMMENT, ?, ?); */ + MMPLAYER_UPDATE_TAG_UINT(GST_TAG_TRACK_NUMBER, attrs, "tag_track_num"); + /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_TRACK_COUNT, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_ALBUM_VOLUME_NUMBER, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_ALBUM_VOLUME_COUNT, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_LOCATION, ?, ?); */ + MMPLAYER_UPDATE_TAG_STRING(GST_TAG_DESCRIPTION, attrs, "tag_description"); + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_VERSION, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ISRC, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ORGANIZATION, ?, ?); */ + MMPLAYER_UPDATE_TAG_STRING(GST_TAG_COPYRIGHT, attrs, "tag_copyright"); + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_COPYRIGHT_URI, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_CONTACT, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_LICENSE, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_LICENSE_URI, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_PERFORMER, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_UINT64(GST_TAG_DURATION, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_CODEC, ?, ?); */ + MMPLAYER_UPDATE_TAG_STRING(GST_TAG_VIDEO_CODEC, attrs, "content_video_codec"); + MMPLAYER_UPDATE_TAG_STRING(GST_TAG_AUDIO_CODEC, attrs, "content_audio_codec"); + MMPLAYER_UPDATE_TAG_UINT(GST_TAG_BITRATE, attrs, "content_bitrate"); + MMPLAYER_UPDATE_TAG_UINT(GST_TAG_MAXIMUM_BITRATE, attrs, "content_max_bitrate"); + MMPLAYER_UPDATE_TAG_IMAGE(GST_TAG_IMAGE, attrs, "tag_album_cover"); + /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_NOMINAL_BITRATE, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_MINIMUM_BITRATE, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_SERIAL, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_ENCODER, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_UINT(GST_TAG_ENCODER_VERSION, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_TRACK_GAIN, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_TRACK_PEAK, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_ALBUM_GAIN, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_ALBUM_PEAK, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_REFERENCE_LEVEL, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_STRING(GST_TAG_LANGUAGE_CODE, ?, ?); */ + /* MMPLAYER_UPDATE_TAG_DOUBLE(GST_TAG_BEATS_PER_MINUTE, ?, ?); */ + + if ( mmf_attrs_commit ( attrs ) ) + debug_error("failed to commit.\n"); + + gst_tag_list_free(tag_list); + + return TRUE; +} + +static void +__mmplayer_gst_rtp_no_more_pads (GstElement *element, gpointer data) // @ +{ + mm_player_t* player = (mm_player_t*) data; + + debug_fenter(); + + /* NOTE : we can remove fakesink here if there's no rtp_dynamic_pad. because whenever + * we connect autoplugging element to the pad which is just added to rtspsrc, we increase + * num_dynamic_pad. and this is no-more-pad situation which means mo more pad will be added. + * So we can say this. if num_dynamic_pad is zero, it must be one of followings + + * [1] audio and video will be dumped with filesink. + * [2] autoplugging is done by just using pad caps. + * [3] typefinding has happend in audio but audiosink is created already before no-more-pad signal + * and the video will be dumped via filesink. + */ + if ( player->num_dynamic_pad == 0 ) + { + debug_log("it seems pad caps is directely used for autoplugging. removing fakesink now\n"); + + if ( ! __mmplayer_gst_remove_fakesink( player, + &player->pipeline->mainbin[MMPLAYER_M_SRC_FAKESINK]) ) + { + /* NOTE : __mmplayer_pipeline_complete() can be called several time. because + * signaling mechanism ( pad-added, no-more-pad, new-decoded-pad ) from various + * source element are not same. To overcome this situation, this function will called + * several places and several times. Therefore, this is not an error case. + */ + return; + } + } + + /* create dot before error-return. for debugging */ + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-no-more-pad" ); + + /* NOTE : if rtspsrc goes to PLAYING before adding it's src pads, a/v sink elements will + * not goes to PLAYING. they will just remain in PAUSED state. simply we are giving + * PLAYING state again. + */ + __mmplayer_gst_set_state(player, + player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_PLAYING, TRUE, 5000 ); + + player->no_more_pad = TRUE; + + debug_fleave(); +} + +static gboolean +__mmplayer_gst_remove_fakesink(mm_player_t* player, MMPlayerGstElement* fakesink) // @ +{ + GstElement* parent = NULL; + + return_val_if_fail(player && player->pipeline && fakesink, FALSE); + + /* lock */ + g_mutex_lock( player->fsink_lock ); + + if ( ! fakesink->gst ) + { + goto ERROR; + } + + /* get parent of fakesink */ + parent = (GstElement*)gst_object_get_parent( (GstObject*)fakesink->gst ); + if ( ! parent ) + { + debug_log("fakesink already removed\n"); + goto ERROR; + } + + gst_element_set_locked_state( fakesink->gst, TRUE ); + + /* setting the state to NULL never returns async + * so no need to wait for completion of state transiton + */ + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state (fakesink->gst, GST_STATE_NULL) ) + { + debug_error("fakesink state change failure!\n"); + + /* FIXIT : should I return here? or try to proceed to next? */ + /* return FALSE; */ + } + + /* remove fakesink from it's parent */ + if ( ! gst_bin_remove( GST_BIN( parent ), fakesink->gst ) ) + { + debug_error("failed to remove fakesink\n"); + + gst_object_unref( parent ); + + goto ERROR; + } + + gst_object_unref( parent ); + + /* FIXIT : releasing fakesink takes too much time (around 700ms) + * we need to figure out the reason why. just for now, fakesink will be released + * in __mmplayer_gst_destroy_pipeline() + */ + // gst_object_unref ( fakesink->gst ); + // fakesink->gst = NULL; + + debug_log("state-holder removed\n"); + + gst_element_set_locked_state( fakesink->gst, FALSE ); + + g_mutex_unlock( player->fsink_lock ); + return TRUE; + +ERROR: + if ( fakesink->gst ) + { + gst_element_set_locked_state( fakesink->gst, FALSE ); + } + + g_mutex_unlock( player->fsink_lock ); + return FALSE; +} + + +static void +__mmplayer_gst_rtp_dynamic_pad (GstElement *element, GstPad *pad, gpointer data) // @ +{ + GstPad *sinkpad = NULL; + GstCaps* caps = NULL; + GstElement* new_element = NULL; + + mm_player_t* player = (mm_player_t*) data; + + debug_fenter(); + + return_if_fail( element && pad ); + return_if_fail( player && + player->pipeline && + player->pipeline->mainbin ); + + + /* payload type is recognizable. increase num_dynamic and wait for sinkbin creation. + * num_dynamic_pad will decreased after creating a sinkbin. + */ + player->num_dynamic_pad++; + debug_log("stream count inc : %d\n", player->num_dynamic_pad); + + /* perform autoplugging if dump is disabled */ + if ( PLAYER_INI()->rtsp_do_typefinding ) + { + /* create typefind */ + new_element = gst_element_factory_make( "typefind", NULL ); + if ( ! new_element ) + { + debug_error("failed to create typefind\n"); + goto ERROR; + } + + MMPLAYER_SIGNAL_CONNECT( player, + G_OBJECT(new_element), + "have-type", + G_CALLBACK(__mmplayer_typefind_have_type), + (gpointer)player); + + /* FIXIT : try to remove it */ + player->have_dynamic_pad = FALSE; + } + else /* NOTE : use pad's caps directely. if enabled. what I am assuming is there's no elemnt has dynamic pad */ + { + debug_log("using pad caps to autopluging instead of doing typefind\n"); + + caps = gst_pad_get_caps( pad ); + + MMPLAYER_CHECK_NULL( caps ); + + /* clear previous result*/ + player->have_dynamic_pad = FALSE; + + if ( ! __mmplayer_try_to_plug( player, pad, caps ) ) + { + debug_error("failed to autoplug for caps : %s\n", gst_caps_to_string( caps ) ); + goto ERROR; + } + + /* check if there's dynamic pad*/ + if( player->have_dynamic_pad ) + { + debug_error("using pad caps assums there's no dynamic pad !\n"); + debug_error("try with enalbing rtsp_do_typefinding\n"); + goto ERROR; + } + + gst_caps_unref( caps ); + caps = NULL; + } + + /* excute new_element if created*/ + if ( new_element ) + { + debug_log("adding new element to pipeline\n"); + + /* set state to READY before add to bin */ + MMPLAYER_ELEMENT_SET_STATE( new_element, GST_STATE_READY ); + + /* add new element to the pipeline */ + if ( FALSE == gst_bin_add( GST_BIN(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst), new_element) ) + { + debug_error("failed to add autoplug element to bin\n"); + goto ERROR; + } + + /* get pad from element */ + sinkpad = gst_element_get_static_pad ( GST_ELEMENT(new_element), "sink" ); + if ( !sinkpad ) + { + debug_error("failed to get sinkpad from autoplug element\n"); + goto ERROR; + } + + /* link it */ + if ( GST_PAD_LINK_OK != GST_PAD_LINK(pad, sinkpad) ) + { + debug_error("failed to link autoplug element\n"); + goto ERROR; + } + + gst_object_unref (sinkpad); + sinkpad = NULL; + + /* run. setting PLAYING here since streamming source is live source */ + MMPLAYER_ELEMENT_SET_STATE( new_element, GST_STATE_PLAYING ); + } + + debug_fleave(); + + return; + +STATE_CHANGE_FAILED: +ERROR: + /* FIXIT : take care if new_element has already added to pipeline */ + if ( new_element ) + gst_object_unref(GST_OBJECT(new_element)); + + if ( sinkpad ) + gst_object_unref(GST_OBJECT(sinkpad)); + + if ( caps ) + gst_object_unref(GST_OBJECT(caps)); + + /* FIXIT : how to inform this error to MSL ????? */ + /* FIXIT : I think we'd better to use g_idle_add() to destroy pipeline and + * then post an error to application + */ +} + + +static void +__mmplayer_gst_decode_callback(GstElement *decodebin, GstPad *pad, gboolean last, gpointer data) // @ +{ + mm_player_t* player = NULL; + MMHandleType attrs = 0; + GstElement* pipeline = NULL; + GstCaps* caps = NULL; + GstStructure* str = NULL; + const gchar* name = NULL; + GstPad* sinkpad = NULL; + GstElement* sinkbin = NULL; + + /* check handles */ + player = (mm_player_t*) data; + + return_if_fail( decodebin && pad ); + return_if_fail(player && player->pipeline && player->pipeline->mainbin); + + pipeline = player->pipeline->mainbin[MMPLAYER_M_PIPE].gst; + + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute\n"); + goto ERROR; + } + + /* get mimetype from caps */ + caps = gst_pad_get_caps( pad ); + if ( !caps ) + { + debug_error("cannot get caps from pad.\n"); + goto ERROR; + } + + str = gst_caps_get_structure( caps, 0 ); + if ( ! str ) + { + debug_error("cannot get structure from capse.\n"); + goto ERROR; + } + + name = gst_structure_get_name(str); + if ( ! name ) + { + debug_error("cannot get mimetype from structure.\n"); + goto ERROR; + } + + debug_log("detected mimetype : %s\n", name); + + if (strstr(name, "audio")) + { + if (player->pipeline->audiobin == NULL) + { + __ta__("__mmplayer_gst_create_audio_pipeline", + if (MM_ERROR_NONE != __mmplayer_gst_create_audio_pipeline(player)) + { + debug_error("failed to create audiobin. continuing without audio\n"); + goto ERROR; + } + ) + + sinkbin = player->pipeline->audiobin[MMPLAYER_A_BIN].gst; + debug_log("creating audiosink bin success\n"); + } + else + { + sinkbin = player->pipeline->audiobin[MMPLAYER_A_BIN].gst; + debug_log("re-using audiobin\n"); + } + + /* FIXIT : track number shouldn't be hardcoded */ + mm_attrs_set_int_by_name(attrs, "content_audio_track_num", 1); + + player->audiosink_linked = 1; + debug_msg("player->audsink_linked set to 1\n"); + + sinkpad = gst_element_get_static_pad( GST_ELEMENT(sinkbin), "sink" ); + if ( !sinkpad ) + { + debug_error("failed to get pad from sinkbin\n"); + goto ERROR; + } + } + else if (strstr(name, "video")) + { + if (player->pipeline->videobin == NULL) + { + /* NOTE : not make videobin because application dose not want to play it even though file has video stream. + */ + + /* get video surface type */ + int surface_type = 0; + mm_attrs_get_int_by_name (player->attrs, "display_surface_type", &surface_type); + + if (surface_type == MM_DISPLAY_SURFACE_NULL) + { + debug_log("not make videobin because it dose not want\n"); + goto ERROR; + } + + __ta__("__mmplayer_gst_create_video_pipeline", + if (MM_ERROR_NONE != __mmplayer_gst_create_video_pipeline(player, caps, surface_type) ) + { + debug_error("failed to create videobin. continuing without video\n"); + goto ERROR; + } + ) + + sinkbin = player->pipeline->videobin[MMPLAYER_V_BIN].gst; + debug_log("creating videosink bin success\n"); + } + else + { + sinkbin = player->pipeline->videobin[MMPLAYER_V_BIN].gst; + debug_log("re-using videobin\n"); + } + + /* FIXIT : track number shouldn't be hardcoded */ + mm_attrs_set_int_by_name(attrs, "content_video_track_num", 1); + + player->videosink_linked = 1; + debug_msg("player->videosink_linked set to 1\n"); + + sinkpad = gst_element_get_static_pad( GST_ELEMENT(sinkbin), "sink" ); + if ( !sinkpad ) + { + debug_error("failed to get pad from sinkbin\n"); + goto ERROR; + } + } + else if (strstr(name, "text")) + { + if (player->pipeline->textbin == NULL) + { + __ta__("__mmplayer_gst_create_text_pipeline", + if (MM_ERROR_NONE != __mmplayer_gst_create_text_pipeline(player)) + { + debug_error("failed to create textbin. continuing without text\n"); + goto ERROR; + } + ) + + sinkbin = player->pipeline->textbin[MMPLAYER_T_BIN].gst; + debug_log("creating textink bin success\n"); + } + else + { + sinkbin = player->pipeline->textbin[MMPLAYER_T_BIN].gst; + debug_log("re-using textbin\n"); + } + + /* FIXIT : track number shouldn't be hardcoded */ + mm_attrs_set_int_by_name(attrs, "content_text_track_num", 1); + + player->textsink_linked = 1; + debug_msg("player->textsink_linked set to 1\n"); + + sinkpad = gst_element_get_static_pad( GST_ELEMENT(sinkbin), "text_sink" ); + if ( !sinkpad ) + { + debug_error("failed to get pad from sinkbin\n"); + goto ERROR; + } + } + else + { + debug_warning("unknown type of elementary stream! ignoring it...\n"); + goto ERROR; + } + + if ( sinkbin ) + { + /* warm up */ + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state( sinkbin, GST_STATE_READY ) ) + { + debug_error("failed to set state(READY) to sinkbin\n"); + goto ERROR; + } + + /* add */ + if ( FALSE == gst_bin_add( GST_BIN(pipeline), sinkbin ) ) + { + debug_error("failed to add sinkbin to pipeline\n"); + goto ERROR; + } + + /* link */ + if ( GST_PAD_LINK_OK != GST_PAD_LINK(pad, sinkpad) ) + { + debug_error("failed to get pad from sinkbin\n"); + goto ERROR; + } + + /* run */ + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state( sinkbin, GST_STATE_PAUSED ) ) + { + debug_error("failed to set state(PLAYING) to sinkbin\n"); + goto ERROR; + } + + gst_object_unref( sinkpad ); + sinkpad = NULL; + } + + /* update track number attributes */ + if ( mmf_attrs_commit ( attrs ) ) + debug_error("failed to commit attrs\n"); + + debug_log("linking sink bin success\n"); + + /* FIXIT : we cannot hold callback for 'no-more-pad' signal because signal was emitted in + * streaming task. if the task blocked, then buffer will not flow to the next element + * ( autoplugging element ). so this is special hack for streaming. please try to remove it + */ + /* dec stream count. we can remove fakesink if it's zero */ + player->num_dynamic_pad--; + + debug_log("stream count dec : %d (num of dynamic pad)\n", player->num_dynamic_pad); + + if ( ( player->no_more_pad ) && ( player->num_dynamic_pad == 0 ) ) + { + __mmplayer_pipeline_complete( NULL, player ); + } + +ERROR: + if ( caps ) + gst_caps_unref( caps ); + + if ( sinkpad ) + gst_object_unref(GST_OBJECT(sinkpad)); + + return; +} + +static gboolean +__mmplayer_get_property_value_for_rotation(mm_player_t* player, int rotation_angle, int *value) +{ + int pro_value = 0; // in the case of expection, default will be returned. + int dest_angle = rotation_angle; + char *element_name = NULL; + int rotation_using_type = -1; + #define ROTATION_USING_X 0 + #define ROTATION_USING_FLIP 1 + + return_val_if_fail(player, FALSE); + return_val_if_fail(value, FALSE); + return_val_if_fail(rotation_angle >= 0, FALSE); + + if (rotation_angle >= 360) + { + dest_angle = rotation_angle - 360; + } + + /* chech if supported or not */ + if ( dest_angle % 90 ) + { + debug_log("not supported rotation angle = %d", rotation_angle); + return FALSE; + } + + if (player->use_video_stream) + { + rotation_using_type = ROTATION_USING_FLIP; + } + else + { + int surface_type = 0; + mm_attrs_get_int_by_name(player->attrs, "display_surface_type", &surface_type); + debug_log("check display surface type for rotation: %d", surface_type); + + switch (surface_type) + { + case MM_DISPLAY_SURFACE_X: + rotation_using_type = ROTATION_USING_X; + break; + case MM_DISPLAY_SURFACE_EVAS: + default: + rotation_using_type = ROTATION_USING_FLIP; + break; + } + } + + debug_log("using %d type for rotation", rotation_using_type); + + /* get property value for setting */ + switch(rotation_using_type) + { + case ROTATION_USING_X: // xvimagesink + { + switch (dest_angle) + { + case 0: + break; + case 90: + pro_value = 3; // clockwise 90 + break; + case 180: + pro_value = 2; + break; + case 270: + pro_value = 1; // counter-clockwise 90 + break; + } + } + break; + case ROTATION_USING_FLIP: // videoflip + { + switch (dest_angle) + { + + case 0: + break; + case 90: + pro_value = 1; // clockwise 90 + break; + case 180: + pro_value = 2; + break; + case 270: + pro_value = 3; // counter-clockwise 90 + break; + } + } + break; + } + + debug_log("setting rotation property value : %d", pro_value); + + *value = pro_value; + + return TRUE; +} + +int +_mmplayer_update_video_param(mm_player_t* player) // @ +{ + MMHandleType attrs = 0; + int surface_type = 0; + int org_angle = 0; // current supported angle values are 0, 90, 180, 270 + int user_angle = 0; + int user_angle_type= 0; + int rotation_value = 0; + + debug_fenter(); + + /* check video sinkbin is created */ + return_val_if_fail ( player && + player->pipeline && + player->pipeline->videobin && + player->pipeline->videobin[MMPLAYER_V_BIN].gst && + player->pipeline->videobin[MMPLAYER_V_SINK].gst, + MM_ERROR_PLAYER_NOT_INITIALIZED ); + + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute"); + return MM_ERROR_PLAYER_INTERNAL; + } + + /* update user roation */ + mm_attrs_get_int_by_name(attrs, "display_rotation", &user_angle_type); + + /* get angle with user type */ + switch(user_angle_type) + { + case MM_DISPLAY_ROTATION_NONE: + user_angle = 0; + break; + case MM_DISPLAY_ROTATION_90: // counter-clockwise 90 + user_angle = 270; + break; + case MM_DISPLAY_ROTATION_180: + user_angle = 180; + break; + case MM_DISPLAY_ROTATION_270: // clockwise 90 + user_angle = 90; + break; + } + + /* get original orientation */ + if (player->v_stream_caps) + { + GstStructure *str = NULL; + + str = gst_caps_get_structure (player->v_stream_caps, 0); + if ( !gst_structure_get_int (str, "orientation", &org_angle)) + { + debug_log ("missing 'orientation' field in video caps"); + } + else + { + debug_log("origianl video orientation = %d", org_angle); + } + } + + debug_log("check user angle: %d, org angle: %d", user_angle, org_angle); + + /* get rotation value to set */ + __mmplayer_get_property_value_for_rotation(player, org_angle+user_angle, &rotation_value); + + /* check video stream callback is used */ + if( player->use_video_stream ) + { + debug_log("using video stream callback with memsink. player handle : [%p]", player); + + /* apply roate */ + g_object_set(player->pipeline->videobin[MMPLAYER_V_FLIP].gst, "method", rotation_value, NULL); + + return MM_ERROR_NONE; + } + + /* update display surface */ + mm_attrs_get_int_by_name(attrs, "display_surface_type", &surface_type); + debug_log("check display surface type attribute: %d", surface_type); + + /* configuring display */ + switch ( surface_type ) + { + case MM_DISPLAY_SURFACE_X: + { + /* ximagesink or xvimagesink */ + void *xid = NULL; + int zoom = 0; + int display_method = 0; + int roi_x = 0; + int roi_y = 0; + int roi_w = 0; + int roi_h = 0; + int force_aspect_ratio = 0; + gboolean visible = TRUE; + + /* common case if using x surface */ + mm_attrs_get_data_by_name(attrs, "display_overlay", &xid); + if ( xid ) + { + debug_log("set video param : xid %d", *(int*)xid); + gst_x_overlay_set_xwindow_id( GST_X_OVERLAY( player->pipeline->videobin[MMPLAYER_V_SINK].gst ), *(int*)xid ); + } + else + { + /* FIXIT : is it error case? */ + debug_warning("still we don't have xid on player attribute. create it's own surface."); + } + + /* if xvimagesink */ + if (!strcmp(PLAYER_INI()->videosink_element_x,"xvimagesink")) + { + mm_attrs_get_int_by_name(attrs, "display_force_aspect_ration", &force_aspect_ratio); + mm_attrs_get_int_by_name(attrs, "display_zoom", &zoom); + mm_attrs_get_int_by_name(attrs, "display_method", &display_method); + mm_attrs_get_int_by_name(attrs, "display_roi_x", &roi_x); + mm_attrs_get_int_by_name(attrs, "display_roi_y", &roi_y); + mm_attrs_get_int_by_name(attrs, "display_roi_width", &roi_w); + mm_attrs_get_int_by_name(attrs, "display_roi_height", &roi_h); + mm_attrs_get_int_by_name(attrs, "display_visible", &visible); + + g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, + "force-aspect-ratio", force_aspect_ratio, + "zoom", zoom, + "rotate", rotation_value, + "handle-events", TRUE, + "display-geometry-method", display_method, + "draw-borders", FALSE, + "dst-roi-x", roi_x, + "dst-roi-y", roi_y, + "dst-roi-w", roi_w, + "dst-roi-h", roi_h, + "visible", visible, + NULL ); + + debug_log("set video param : zoom %d", zoom); + debug_log("set video param : rotate %d", rotation_value); + debug_log("set video param : method %d", display_method); + debug_log("set video param : dst-roi-x: %d, dst-roi-y: %d, dst-roi-w: %d, dst-roi-h: %d", + roi_x, roi_y, roi_w, roi_h ); + debug_log("set video param : visible %d", visible); + debug_log("set video param : force aspect ratio %d", force_aspect_ratio); + } + } + break; + case MM_DISPLAY_SURFACE_EVAS: + { + void *object = NULL; + int scaling = 0; + gboolean visible = TRUE; + + /* common case if using evas surface */ + mm_attrs_get_data_by_name(attrs, "display_overlay", &object); + mm_attrs_get_int_by_name(attrs, "display_visible", &visible); + mm_attrs_get_int_by_name(attrs, "display_evas_do_scaling", &scaling); + if (object) + { + g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, + "evas-object", object, + "visible", visible, + NULL); + debug_log("set video param : evas-object %x", object); + debug_log("set video param : visible %d", visible); + } + else + { + debug_error("no evas object"); + return MM_ERROR_PLAYER_INTERNAL; + } + + /* if evaspixmapsink */ + if (!strcmp(PLAYER_INI()->videosink_element_evas,"evaspixmapsink")) + { + int display_method = 0; + int roi_x = 0; + int roi_y = 0; + int roi_w = 0; + int roi_h = 0; + int force_aspect_ratio = 0; + int origin_size = !scaling; + + mm_attrs_get_int_by_name(attrs, "display_force_aspect_ration", &force_aspect_ratio); + mm_attrs_get_int_by_name(attrs, "display_method", &display_method); + mm_attrs_get_int_by_name(attrs, "display_roi_x", &roi_x); + mm_attrs_get_int_by_name(attrs, "display_roi_y", &roi_y); + mm_attrs_get_int_by_name(attrs, "display_roi_width", &roi_w); + mm_attrs_get_int_by_name(attrs, "display_roi_height", &roi_h); + + g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, + "force-aspect-ratio", force_aspect_ratio, + "origin-size", origin_size, + "dst-roi-x", roi_x, + "dst-roi-y", roi_y, + "dst-roi-w", roi_w, + "dst-roi-h", roi_h, + "display-geometry-method", display_method, + NULL ); + + debug_log("set video param : method %d", display_method); + debug_log("set video param : dst-roi-x: %d, dst-roi-y: %d, dst-roi-w: %d, dst-roi-h: %d", + roi_x, roi_y, roi_w, roi_h ); + debug_log("set video param : force aspect ratio %d", force_aspect_ratio); + debug_log("set video param : display_evas_do_scaling %d (origin-size %d)", scaling, origin_size); + } + g_object_set(player->pipeline->videobin[MMPLAYER_V_FLIP].gst, "method", rotation_value, NULL); + } + break; + case MM_DISPLAY_SURFACE_X_EXT: /* NOTE : this surface type is used for the videoTexture(canvasTexture) overlay */ + { + void *pixmap_id_cb = NULL; + void *pixmap_id_cb_user_data = NULL; + int display_method = 0; + gboolean visible = TRUE; + + /* if xvimagesink */ + if (strcmp(PLAYER_INI()->videosink_element_x,"xvimagesink")) + { + debug_error("videosink is not xvimagesink"); + return MM_ERROR_PLAYER_INTERNAL; + } + + /* get information from attributes */ + mm_attrs_get_data_by_name(attrs, "display_overlay", &pixmap_id_cb); + mm_attrs_get_data_by_name(attrs, "display_overlay_user_data", &pixmap_id_cb_user_data); + mm_attrs_get_int_by_name(attrs, "display_method", &display_method); + + if ( pixmap_id_cb ) + { + debug_log("set video param : display_overlay(0x%x)", pixmap_id_cb); + if (pixmap_id_cb_user_data) + { + debug_log("set video param : display_overlay_user_data(0x%x)", pixmap_id_cb_user_data); + } + } + else + { + debug_error("failed to set pixmap-id-callback"); + return MM_ERROR_PLAYER_INTERNAL; + } + debug_log("set video param : method %d", display_method); + debug_log("set video param : visible %d", visible); + + /* set properties of videosink plugin */ + g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, + "display-geometry-method", display_method, + "draw-borders", FALSE, + "visible", visible, + "pixmap-id-callback", pixmap_id_cb, + "pixmap-id-callback-userdata", pixmap_id_cb_user_data, + NULL ); + } + break; + case MM_DISPLAY_SURFACE_NULL: + { + /* do nothing */ + } + break; + } + + debug_fleave(); + + return MM_ERROR_NONE; +} + +static int +__mmplayer_gst_element_link_bucket(GList* element_bucket) // @ +{ + GList* bucket = element_bucket; + MMPlayerGstElement* element = NULL; + MMPlayerGstElement* prv_element = NULL; + gint successful_link_count = 0; + + debug_fenter(); + + return_val_if_fail(element_bucket, -1); + + prv_element = (MMPlayerGstElement*)bucket->data; + bucket = bucket->next; + + for ( ; bucket; bucket = bucket->next ) + { + element = (MMPlayerGstElement*)bucket->data; + + if ( element && element->gst ) + { + if ( GST_ELEMENT_LINK(GST_ELEMENT(prv_element->gst), GST_ELEMENT(element->gst)) ) + { + debug_log("linking [%s] to [%s] success\n", + GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst)), + GST_ELEMENT_NAME(GST_ELEMENT(element->gst)) ); + successful_link_count ++; + } + else + { + debug_log("linking [%s] to [%s] failed\n", + GST_ELEMENT_NAME(GST_ELEMENT(prv_element->gst)), + GST_ELEMENT_NAME(GST_ELEMENT(element->gst)) ); + return -1; + } + } + + prv_element = element; + } + + debug_fleave(); + + return successful_link_count; +} + +static int +__mmplayer_gst_element_add_bucket_to_bin(GstBin* bin, GList* element_bucket) // @ +{ + GList* bucket = element_bucket; + MMPlayerGstElement* element = NULL; + int successful_add_count = 0; + + debug_fenter(); + + return_val_if_fail(element_bucket, 0); + return_val_if_fail(bin, 0); + + for ( ; bucket; bucket = bucket->next ) + { + element = (MMPlayerGstElement*)bucket->data; + + if ( element && element->gst ) + { + if( !gst_bin_add(bin, GST_ELEMENT(element->gst)) ) + { + debug_log("__mmplayer_gst_element_link_bucket : Adding element [%s] to bin [%s] failed\n", + GST_ELEMENT_NAME(GST_ELEMENT(element->gst)), + GST_ELEMENT_NAME(GST_ELEMENT(bin) ) ); + return 0; + } + successful_add_count ++; + } + } + + debug_fleave(); + + return successful_add_count; +} + + + +/** + * This function is to create audio pipeline for playing. + * + * @param player [in] handle of player + * + * @return This function returns zero on success. + * @remark + * @see __mmplayer_gst_create_midi_pipeline, __mmplayer_gst_create_video_pipeline + */ +#define MMPLAYER_CREATEONLY_ELEMENT(x_bin, x_id, x_factory, x_name) \ +x_bin[x_id].id = x_id;\ +x_bin[x_id].gst = gst_element_factory_make(x_factory, x_name);\ +if ( ! x_bin[x_id].gst )\ +{\ + debug_critical("failed to create %s \n", x_factory);\ + goto ERROR;\ +}\ + +#define MMPLAYER_CREATE_ELEMENT_ADD_BIN(x_bin, x_id, x_factory, x_name, y_bin) \ +x_bin[x_id].id = x_id;\ +x_bin[x_id].gst = gst_element_factory_make(x_factory, x_name);\ +if ( ! x_bin[x_id].gst )\ +{\ + debug_critical("failed to create %s \n", x_factory);\ + goto ERROR;\ +}\ +if( !gst_bin_add(GST_BIN(y_bin), GST_ELEMENT(x_bin[x_id].gst)))\ +{\ + debug_log("__mmplayer_gst_element_link_bucket : Adding element [%s] to bin [%s] failed\n",\ + GST_ELEMENT_NAME(GST_ELEMENT(x_bin[x_id].gst)),\ + GST_ELEMENT_NAME(GST_ELEMENT(y_bin) ) );\ + goto ERROR;\ +}\ + +/* macro for code readability. just for sinkbin-creation functions */ +#define MMPLAYER_CREATE_ELEMENT(x_bin, x_id, x_factory, x_name, x_add_bucket) \ +do \ +{ \ + x_bin[x_id].id = x_id;\ + x_bin[x_id].gst = gst_element_factory_make(x_factory, x_name);\ + if ( ! x_bin[x_id].gst )\ + {\ + debug_critical("failed to create %s \n", x_factory);\ + goto ERROR;\ + }\ + if ( x_add_bucket )\ + element_bucket = g_list_append(element_bucket, &x_bin[x_id]);\ +} while(0); + + +/** + * AUDIO PIPELINE + * - Local playback : audioconvert !volume ! capsfilter ! audioeq ! audiosink + * - Streaming : audioconvert !volume ! audiosink + * - PCM extraction : audioconvert ! audioresample ! capsfilter ! fakesink + */ +static int +__mmplayer_gst_create_audio_pipeline(mm_player_t* player) +{ + MMPlayerGstElement* first_element = NULL; + MMPlayerGstElement* audiobin = NULL; + MMHandleType attrs = 0; + GstPad *pad = NULL; + GstPad *ghostpad = NULL; + GList* element_bucket = NULL; + char *device_name = NULL; + gboolean link_audio_sink_now = TRUE; + int i =0; + + debug_fenter(); + + return_val_if_fail( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* alloc handles */ + audiobin = (MMPlayerGstElement*)g_malloc0(sizeof(MMPlayerGstElement) * MMPLAYER_A_NUM); + + if ( ! audiobin ) + { + debug_error("failed to allocate memory for audiobin\n"); + return MM_ERROR_PLAYER_NO_FREE_SPACE; + } + + attrs = MMPLAYER_GET_ATTRS(player); + + /* create bin */ + audiobin[MMPLAYER_A_BIN].id = MMPLAYER_A_BIN; + audiobin[MMPLAYER_A_BIN].gst = gst_bin_new("audiobin"); + if ( !audiobin[MMPLAYER_A_BIN].gst ) + { + debug_critical("failed to create audiobin\n"); + goto ERROR; + } + + /* take it */ + player->pipeline->audiobin = audiobin; + + player->is_sound_extraction = __mmplayer_can_extract_pcm(player); + + /* Adding audiotp plugin for reverse trickplay feature */ + MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_TP, "audiotp", "audiotrickplay", TRUE); + + /* converter */ + MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CONV, "audioconvert", "audioconverter", TRUE); + + if ( ! player->is_sound_extraction ) + { + GstCaps* caps = NULL; + guint channels = 0; + + /* for logical volume control */ + MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_VOL, "volume", "volume", TRUE); + g_object_set(G_OBJECT (audiobin[MMPLAYER_A_VOL].gst), "volume", player->sound.volume, NULL); + + if (player->sound.mute) + { + debug_log("mute enabled\n"); + g_object_set(G_OBJECT (audiobin[MMPLAYER_A_VOL].gst), "mute", player->sound.mute, NULL); + } + + /*capsfilter */ + MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CAPS_DEFAULT, "capsfilter", "audiocapsfilter", TRUE); + + caps = gst_caps_from_string( "audio/x-raw-int, " + "endianness = (int) LITTLE_ENDIAN, " + "signed = (boolean) true, " + "width = (int) 16, " + "depth = (int) 16" ); + g_object_set (GST_ELEMENT(audiobin[MMPLAYER_A_CAPS_DEFAULT].gst), "caps", caps, NULL ); + + gst_caps_unref( caps ); + + /* chech if multi-chennels */ + if (player->pipeline->mainbin && player->pipeline->mainbin[MMPLAYER_M_DEMUX].gst) + { + GstPad *srcpad = NULL; + GstCaps *caps = NULL; + + if (srcpad = gst_element_get_static_pad(player->pipeline->mainbin[MMPLAYER_M_DEMUX].gst, "src")) + { + if (caps = gst_pad_get_caps(srcpad)) + { + MMPLAYER_LOG_GST_CAPS_TYPE(caps); + GstStructure *str = gst_caps_get_structure(caps, 0); + if (str) + gst_structure_get_int (str, "channels", &channels); + gst_caps_unref(caps); + } + gst_object_unref(srcpad); + } + } + + /* audio effect element. if audio effect is enabled */ + if ( channels <= 2 && (PLAYER_INI()->use_audio_effect_preset || PLAYER_INI()->use_audio_effect_custom) ) + { + MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_FILTER, PLAYER_INI()->name_of_audio_effect, "audiofilter", TRUE); + } + + /* create audio sink */ + MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_SINK, PLAYER_INI()->name_of_audiosink, + "audiosink", link_audio_sink_now); + + /* sync on */ + if (MMPLAYER_IS_RTSP_STREAMING(player)) + g_object_set (G_OBJECT (audiobin[MMPLAYER_A_SINK].gst), "sync", FALSE, NULL); /* sync off */ + else + g_object_set (G_OBJECT (audiobin[MMPLAYER_A_SINK].gst), "sync", TRUE, NULL); /* sync on */ + + /* qos on */ + g_object_set (G_OBJECT (audiobin[MMPLAYER_A_SINK].gst), "qos", TRUE, NULL); /* qos on */ + + /* FIXIT : using system clock. isn't there another way? */ + g_object_set (G_OBJECT (audiobin[MMPLAYER_A_SINK].gst), "provide-clock", PLAYER_INI()->provide_clock, NULL); + + __mmplayer_add_sink( player, audiobin[MMPLAYER_A_SINK].gst ); + + if(player->audio_buffer_cb) + { + g_object_set(audiobin[MMPLAYER_A_SINK].gst, "audio-handle", player->audio_buffer_cb_user_param, NULL); + g_object_set(audiobin[MMPLAYER_A_SINK].gst, "audio-callback", player->audio_buffer_cb, NULL); + } + + if ( g_strrstr(PLAYER_INI()->name_of_audiosink, "avsysaudiosink") ) + { + gint volume_type = 0; + gint audio_route = 0; + gint sound_priority = FALSE; + gint is_spk_out_only = 0; + gint latency_mode = 0; + + /* set volume table + * It should be set after player creation through attribute. + * But, it can not be changed during playing. + */ + mm_attrs_get_int_by_name(attrs, "sound_volume_type", &volume_type); + mm_attrs_get_int_by_name(attrs, "sound_route", &audio_route); + mm_attrs_get_int_by_name(attrs, "sound_priority", &sound_priority); + mm_attrs_get_int_by_name(attrs, "sound_spk_out_only", &is_spk_out_only); + mm_attrs_get_int_by_name(attrs, "audio_latency_mode", &latency_mode); + + /* hook sound_type if emergency case */ + if ( player->sm.event == ASM_EVENT_EMERGENCY) + { + debug_log ("This is emergency session, hook sound_type from [%d] to [%d]\n", volume_type, MM_SOUND_VOLUME_TYPE_EMERGENCY); + volume_type = MM_SOUND_VOLUME_TYPE_EMERGENCY; + } + + g_object_set(audiobin[MMPLAYER_A_SINK].gst, + "volumetype", volume_type, + "audio-route", audio_route, + "priority", sound_priority, + "user-route", is_spk_out_only, + "latency", latency_mode, + NULL); + + debug_log("audiosink property status...volume type:%d, route:%d, priority=%d, user-route=%d, latency=%d\n", + volume_type, audio_route, sound_priority, is_spk_out_only, latency_mode); + } + + /* Antishock can be enabled when player is resumed by soundCM. + * But, it's not used in MMS, setting and etc. + * Because, player start seems like late. + */ + __mmplayer_set_antishock( player , FALSE ); + } + else // pcm extraction only and no sound output + { + int dst_samplerate = 0; + int dst_channels = 0; + int dst_depth = 0; + char *caps_type = NULL; + GstCaps* caps = NULL; + + /* resampler */ + MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_RESAMPLER, "audioresample", "resampler", TRUE); + + /* get conf. values */ + mm_attrs_multiple_get(player->attrs, + NULL, + "pcm_extraction_samplerate", &dst_samplerate, + "pcm_extraction_channels", &dst_channels, + "pcm_extraction_depth", &dst_depth, + NULL); + /* capsfilter */ + MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_CAPS_DEFAULT, "capsfilter", "audiocapsfilter", TRUE); + + caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, dst_samplerate, + "channels", G_TYPE_INT, dst_channels, + "depth", G_TYPE_INT, dst_depth, + NULL); + + caps_type = gst_caps_to_string(caps); + debug_log("resampler new caps : %s\n", caps_type); + + g_object_set (GST_ELEMENT(audiobin[MMPLAYER_A_CAPS_DEFAULT].gst), "caps", caps, NULL ); + + /* clean */ + gst_caps_unref( caps ); + MMPLAYER_FREEIF( caps_type ); + + /* fake sink */ + MMPLAYER_CREATE_ELEMENT(audiobin, MMPLAYER_A_SINK, "fakesink", "fakesink", TRUE); + + /* set sync */ + g_object_set (G_OBJECT (audiobin[MMPLAYER_A_SINK].gst), "sync", FALSE, NULL); + + __mmplayer_add_sink( player, audiobin[MMPLAYER_A_SINK].gst ); + } + + /* adding created elements to bin */ + debug_log("adding created elements to bin\n"); + if( !__mmplayer_gst_element_add_bucket_to_bin( GST_BIN(audiobin[MMPLAYER_A_BIN].gst), element_bucket )) + { + debug_error("failed to add elements\n"); + goto ERROR; + } + + /* linking elements in the bucket by added order. */ + debug_log("Linking elements in the bucket by added order.\n"); + if ( __mmplayer_gst_element_link_bucket(element_bucket) == -1 ) + { + debug_error("failed to link elements\n"); + goto ERROR; + } + + /* get first element's sinkpad for creating ghostpad */ + first_element = (MMPlayerGstElement *)element_bucket->data; + + pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink"); + if ( ! pad ) + { + debug_error("failed to get pad from first element of audiobin\n"); + goto ERROR; + } + + ghostpad = gst_ghost_pad_new("sink", pad); + if ( ! ghostpad ) + { + debug_error("failed to create ghostpad\n"); + goto ERROR; + } + + if ( FALSE == gst_element_add_pad(audiobin[MMPLAYER_A_BIN].gst, ghostpad) ) + { + debug_error("failed to add ghostpad to audiobin\n"); + goto ERROR; + } + + gst_object_unref(pad); + + if ( !player->bypass_audio_effect && (PLAYER_INI()->use_audio_effect_preset || PLAYER_INI()->use_audio_effect_custom) ) + { + if ( player->audio_effect_info.effect_type == MM_AUDIO_EFFECT_TYPE_PRESET ) + { + if (!_mmplayer_audio_effect_preset_apply(player, player->audio_effect_info.preset)) + { + debug_msg("apply audio effect(preset:%d) setting success\n",player->audio_effect_info.preset); + } + } + else if ( player->audio_effect_info.effect_type == MM_AUDIO_EFFECT_TYPE_CUSTOM ) + { + if (!_mmplayer_audio_effect_custom_apply(player)) + { + debug_msg("apply audio effect(custom) setting success\n"); + } + } + } + + /* done. free allocated variables */ + MMPLAYER_FREEIF( device_name ); + g_list_free(element_bucket); + + mm_attrs_set_int_by_name(attrs, "content_audio_found", TRUE); + if ( mmf_attrs_commit ( attrs ) ) /* return -1 if error */ + debug_error("failed to commit attribute ""content_audio_found"".\n"); + + debug_fleave(); + + return MM_ERROR_NONE; + +ERROR: + + debug_log("ERROR : releasing audiobin\n"); + + MMPLAYER_FREEIF( device_name ); + + if ( pad ) + gst_object_unref(GST_OBJECT(pad)); + + if ( ghostpad ) + gst_object_unref(GST_OBJECT(ghostpad)); + + g_list_free( element_bucket ); + + + /* release element which are not added to bin */ + for ( i = 1; i < MMPLAYER_A_NUM; i++ ) /* NOTE : skip bin */ + { + if ( audiobin[i].gst ) + { + GstObject* parent = NULL; + parent = gst_element_get_parent( audiobin[i].gst ); + + if ( !parent ) + { + gst_object_unref(GST_OBJECT(audiobin[i].gst)); + audiobin[i].gst = NULL; + } + else + { + gst_object_unref(GST_OBJECT(parent)); + } + } + } + + /* release audiobin with it's childs */ + if ( audiobin[MMPLAYER_A_BIN].gst ) + { + gst_object_unref(GST_OBJECT(audiobin[MMPLAYER_A_BIN].gst)); + } + + MMPLAYER_FREEIF( audiobin ); + + player->pipeline->audiobin = NULL; + + return MM_ERROR_PLAYER_INTERNAL; +} + +static gboolean +__mmplayer_audio_stream_probe (GstPad *pad, GstBuffer *buffer, gpointer u_data) +{ + mm_player_t* player = (mm_player_t*) u_data; + gint size; + guint8 *data; + + data = GST_BUFFER_DATA(buffer); + size = GST_BUFFER_SIZE(buffer); + + if (player->audio_stream_cb && size && data) + player->audio_stream_cb((void *)data, size, player->audio_stream_cb_user_param); + + return TRUE; +} + +/** + * This function is to create video pipeline. + * + * @param player [in] handle of player + * caps [in] src caps of decoder + * surface_type [in] surface type for video rendering + * + * @return This function returns zero on success. + * @remark + * @see __mmplayer_gst_create_audio_pipeline, __mmplayer_gst_create_midi_pipeline + */ +/** + * VIDEO PIPELINE + * - x surface (arm/x86) : videoflip ! xvimagesink + * - evas surface (arm) : ffmpegcolorspace ! videoflip ! evasimagesink + * - evas surface (x86) : videoconvertor ! videoflip ! evasimagesink + */ +static int +__mmplayer_gst_create_video_pipeline(mm_player_t* player, GstCaps* caps, MMDisplaySurfaceType surface_type) +{ + GstPad *pad = NULL; + MMHandleType attrs; + GList*element_bucket = NULL; + MMPlayerGstElement* first_element = NULL; + MMPlayerGstElement* videobin = NULL; + gchar* vconv_factory = NULL; + gchar *videosink_element = NULL; + + debug_fenter(); + + return_val_if_fail(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED); + + /* alloc handles */ + videobin = (MMPlayerGstElement*)g_malloc0(sizeof(MMPlayerGstElement) * MMPLAYER_V_NUM); + if ( !videobin ) + { + return MM_ERROR_PLAYER_NO_FREE_SPACE; + } + + player->pipeline->videobin = videobin; + + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute"); + return MM_ERROR_PLAYER_INTERNAL; + } + + /* create bin */ + videobin[MMPLAYER_V_BIN].id = MMPLAYER_V_BIN; + videobin[MMPLAYER_V_BIN].gst = gst_bin_new("videobin"); + if ( !videobin[MMPLAYER_V_BIN].gst ) + { + debug_critical("failed to create videobin"); + goto ERROR; + } + + if( player->use_video_stream ) // video stream callback, so send raw video data to application + { + GstStructure *str = NULL; + guint32 fourcc = 0; + gint ret = 0; + gint width = 0; //width of video + gint height = 0; //height of video + GstCaps* video_caps = NULL; + + debug_log("using memsink\n"); + + /* first, create colorspace convert */ + if (strlen(PLAYER_INI()->name_of_video_converter) > 0) + { + vconv_factory = PLAYER_INI()->name_of_video_converter; + } + + if (vconv_factory) + { + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_CONV, vconv_factory, "video converter", TRUE); + } + + /* rotator, scaler and capsfilter */ + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_FLIP, "videoflip", "video rotator", TRUE); + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_SCALE, "videoscale", "video scaler", TRUE); + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_CAPS, "capsfilter", "videocapsfilter", TRUE); + + /* get video stream caps parsed by demuxer */ + str = gst_caps_get_structure (player->v_stream_caps, 0); + if ( !str ) + { + debug_error("cannot get structure"); + goto ERROR; + } + + mm_attrs_get_int_by_name(attrs, "display_width", &width); + mm_attrs_get_int_by_name(attrs, "display_height", &height); + if (!width || !height) + { + /* we set width/height of original media's size to capsfilter for scaling video */ + ret = gst_structure_get_int (str, "width", &width); + if ( !ret ) + { + debug_error("cannot get width"); + goto ERROR; + } + + ret = gst_structure_get_int(str, "height", &height); + if ( !ret ) + { + debug_error("cannot get height"); + goto ERROR; + } + } + + video_caps = gst_caps_new_simple( "video/x-raw-rgb", + "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, + NULL); + + g_object_set (GST_ELEMENT(videobin[MMPLAYER_V_CAPS].gst), "caps", video_caps, NULL ); + + gst_caps_unref( video_caps ); + + /* finally, create video sink. output will be BGRA8888. */ + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_SINK, "avsysmemsink", "videosink", TRUE); + + MMPLAYER_SIGNAL_CONNECT( player, + videobin[MMPLAYER_V_SINK].gst, + "video-stream", + G_CALLBACK(__mmplayer_videostream_cb), + player ); + } + else // render video data using sink plugin like xvimagesink + { + debug_log("using videosink"); + + /* set video converter */ + if (strlen(PLAYER_INI()->name_of_video_converter) > 0) + { + vconv_factory = PLAYER_INI()->name_of_video_converter; + if (vconv_factory) + { + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_CONV, vconv_factory, "video converter", TRUE); + debug_log("using video converter: %s", vconv_factory); + } + } + + /* set video rotator */ + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_FLIP, "videoflip", "video rotator", TRUE); + + /* videoscaler */ + #if !defined(__arm__) + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_SCALE, "videoscale", "videoscaler", TRUE); + #endif + + /* set video sink */ + switch (surface_type) + { + case MM_DISPLAY_SURFACE_X: + if (strlen(PLAYER_INI()->videosink_element_x) > 0) + videosink_element = PLAYER_INI()->videosink_element_x; + else + goto ERROR; + break; + case MM_DISPLAY_SURFACE_EVAS: + if (strlen(PLAYER_INI()->videosink_element_evas) > 0) + videosink_element = PLAYER_INI()->videosink_element_evas; + else + goto ERROR; + break; + case MM_DISPLAY_SURFACE_X_EXT: + { + void *pixmap_id_cb = NULL; + mm_attrs_get_data_by_name(attrs, "display_overlay", &pixmap_id_cb); + if (pixmap_id_cb) /* this is used for the videoTextue(canvasTexture) overlay */ + { + videosink_element = PLAYER_INI()->videosink_element_x; + debug_warning("video texture usage"); + } + else + { + debug_error("something wrong.. callback function for getting pixmap id is null"); + goto ERROR; + } + break; + } + case MM_DISPLAY_SURFACE_NULL: + if (strlen(PLAYER_INI()->videosink_element_fake) > 0) + videosink_element = PLAYER_INI()->videosink_element_fake; + else + goto ERROR; + break; + default: + debug_error("unidentified surface type"); + goto ERROR; + } + + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_SINK, videosink_element, videosink_element, TRUE); + debug_log("selected videosink name: %s", videosink_element); + + /* connect signal handlers for sink plug-in */ + switch (surface_type) { + case MM_DISPLAY_SURFACE_X_EXT: + MMPLAYER_SIGNAL_CONNECT( player, + player->pipeline->videobin[MMPLAYER_V_SINK].gst, + "frame-render-error", + G_CALLBACK(__mmplayer_videoframe_render_error_cb), + player ); + debug_log("videoTexture usage, connect a signal handler for pixmap rendering error"); + break; + default: + break; + } + } + + if ( _mmplayer_update_video_param(player) != MM_ERROR_NONE) + goto ERROR; + + /* qos on */ + g_object_set (G_OBJECT (videobin[MMPLAYER_V_SINK].gst), "qos", TRUE, NULL); + + /* store it as it's sink element */ + __mmplayer_add_sink( player, videobin[MMPLAYER_V_SINK].gst ); + + /* adding created elements to bin */ + if( ! __mmplayer_gst_element_add_bucket_to_bin(GST_BIN(videobin[MMPLAYER_V_BIN].gst), element_bucket) ) + { + debug_error("failed to add elements\n"); + goto ERROR; + } + + /* Linking elements in the bucket by added order */ + if ( __mmplayer_gst_element_link_bucket(element_bucket) == -1 ) + { + debug_error("failed to link elements\n"); + goto ERROR; + } + + /* get first element's sinkpad for creating ghostpad */ + first_element = (MMPlayerGstElement *)element_bucket->data; + if ( !first_element ) + { + debug_error("failed to get first element from bucket\n"); + goto ERROR; + } + + pad = gst_element_get_static_pad(GST_ELEMENT(first_element->gst), "sink"); + if ( !pad ) + { + debug_error("failed to get pad from first element\n"); + goto ERROR; + } + + /* create ghostpad */ + if (FALSE == gst_element_add_pad(videobin[MMPLAYER_V_BIN].gst, gst_ghost_pad_new("sink", pad))) + { + debug_error("failed to add ghostpad to videobin\n"); + goto ERROR; + } + gst_object_unref(pad); + + /* done. free allocated variables */ + g_list_free(element_bucket); + + mm_attrs_set_int_by_name(attrs, "content_video_found", TRUE); + if ( mmf_attrs_commit ( attrs ) ) /* return -1 if error */ + debug_error("failed to commit attribute ""content_video_found"".\n"); + + debug_fleave(); + + return MM_ERROR_NONE; + +ERROR: + debug_error("ERROR : releasing videobin\n"); + + g_list_free( element_bucket ); + + if (pad) + gst_object_unref(GST_OBJECT(pad)); + + /* release videobin with it's childs */ + if ( videobin[MMPLAYER_V_BIN].gst ) + { + gst_object_unref(GST_OBJECT(videobin[MMPLAYER_V_BIN].gst)); + } + + + MMPLAYER_FREEIF( videobin ); + + player->pipeline->videobin = NULL; + + return MM_ERROR_PLAYER_INTERNAL; +} + +static int __mmplayer_gst_create_text_pipeline(mm_player_t* player) +{ + MMPlayerGstElement* first_element = NULL; + MMPlayerGstElement* textbin = NULL; + GList* element_bucket = NULL; + GstPad *pad = NULL; + GstPad *ghostpad = NULL; + gint i = 0; + + debug_fenter(); + + return_val_if_fail( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* alloc handles */ + textbin = (MMPlayerGstElement*)g_malloc0(sizeof(MMPlayerGstElement) * MMPLAYER_T_NUM); + if ( ! textbin ) + { + debug_error("failed to allocate memory for textbin\n"); + return MM_ERROR_PLAYER_NO_FREE_SPACE; + } + + /* create bin */ + textbin[MMPLAYER_T_BIN].id = MMPLAYER_T_BIN; + textbin[MMPLAYER_T_BIN].gst = gst_bin_new("textbin"); + if ( !textbin[MMPLAYER_T_BIN].gst ) + { + debug_critical("failed to create textbin\n"); + goto ERROR; + } + + /* take it */ + player->pipeline->textbin = textbin; + + /* fakesink */ + if (player->use_textoverlay) + { + debug_log ("use textoverlay for displaying \n"); + + MMPLAYER_CREATE_ELEMENT_ADD_BIN(textbin, MMPLAYER_T_TEXT_QUEUE, "queue", "text_t_queue", textbin[MMPLAYER_T_BIN].gst); + + MMPLAYER_CREATE_ELEMENT_ADD_BIN(textbin, MMPLAYER_T_VIDEO_QUEUE, "queue", "text_v_queue", textbin[MMPLAYER_T_BIN].gst); + + MMPLAYER_CREATE_ELEMENT_ADD_BIN(textbin, MMPLAYER_T_VIDEO_CONVERTER, "fimcconvert", "text_v_converter", textbin[MMPLAYER_T_BIN].gst); + + MMPLAYER_CREATE_ELEMENT_ADD_BIN(textbin, MMPLAYER_T_OVERLAY, "textoverlay", "text_overlay", textbin[MMPLAYER_T_BIN].gst); + + if (!gst_element_link_pads (textbin[MMPLAYER_T_VIDEO_QUEUE].gst, "src", textbin[MMPLAYER_T_VIDEO_CONVERTER].gst, "sink")) + { + debug_error("failed to link queue and converter\n"); + goto ERROR; + } + + if (!gst_element_link_pads (textbin[MMPLAYER_T_VIDEO_CONVERTER].gst, "src", textbin[MMPLAYER_T_OVERLAY].gst, "video_sink")) + { + debug_error("failed to link queue and textoverlay\n"); + goto ERROR; + } + + if (!gst_element_link_pads (textbin[MMPLAYER_T_TEXT_QUEUE].gst, "src", textbin[MMPLAYER_T_OVERLAY].gst, "text_sink")) + { + debug_error("failed to link queue and textoverlay\n"); + goto ERROR; + } + + } + else + { + debug_log ("use subtitle message for displaying \n"); + + MMPLAYER_CREATE_ELEMENT(textbin, MMPLAYER_T_TEXT_QUEUE, "queue", "text_queue", TRUE); + + MMPLAYER_CREATE_ELEMENT(textbin, MMPLAYER_T_SINK, "fakesink", "text_sink", TRUE); + + g_object_set (G_OBJECT (textbin[MMPLAYER_T_SINK].gst), "sync", TRUE, NULL); + g_object_set (G_OBJECT (textbin[MMPLAYER_T_SINK].gst), "async", FALSE, NULL); + g_object_set (G_OBJECT (textbin[MMPLAYER_T_SINK].gst), "signal-handoffs", TRUE, NULL); + + MMPLAYER_SIGNAL_CONNECT( player, + G_OBJECT(textbin[MMPLAYER_T_SINK].gst), + "handoff", + G_CALLBACK(__mmplayer_update_subtitle), + (gpointer)player ); + + if (!player->play_subtitle) + { + debug_log ("add textbin sink as sink element of whole pipeline.\n"); + __mmplayer_add_sink (player, GST_ELEMENT(textbin[MMPLAYER_T_SINK].gst)); + } + + /* adding created elements to bin */ + debug_log("adding created elements to bin\n"); + if( !__mmplayer_gst_element_add_bucket_to_bin( GST_BIN(textbin[MMPLAYER_T_BIN].gst), element_bucket )) + { + debug_error("failed to add elements\n"); + goto ERROR; + } + + /* linking elements in the bucket by added order. */ + debug_log("Linking elements in the bucket by added order.\n"); + if ( __mmplayer_gst_element_link_bucket(element_bucket) == -1 ) + { + debug_error("failed to link elements\n"); + goto ERROR; + } + + /* done. free allocated variables */ + g_list_free(element_bucket); + } + + if (textbin[MMPLAYER_T_TEXT_QUEUE].gst) + { + pad = gst_element_get_static_pad(GST_ELEMENT(textbin[MMPLAYER_T_TEXT_QUEUE].gst), "sink"); + if (!pad) + { + debug_error("failed to get text pad of textbin\n"); + goto ERROR; + } + + ghostpad = gst_ghost_pad_new("text_sink", pad); + if (!ghostpad) + { + debug_error("failed to create ghostpad of textbin\n"); + goto ERROR; + } + + if ( FALSE == gst_element_add_pad(textbin[MMPLAYER_T_BIN].gst, ghostpad) ) + { + debug_error("failed to add ghostpad to textbin\n"); + goto ERROR; + } + } + + if (textbin[MMPLAYER_T_VIDEO_QUEUE].gst) + { + pad = gst_element_get_static_pad(GST_ELEMENT(textbin[MMPLAYER_T_VIDEO_QUEUE].gst), "sink"); + if (!pad) + { + debug_error("failed to get video pad of textbin\n"); + goto ERROR; + } + + ghostpad = gst_ghost_pad_new("video_sink", pad); + if (!ghostpad) + { + debug_error("failed to create ghostpad of textbin\n"); + goto ERROR; + } + + if (!gst_element_add_pad(textbin[MMPLAYER_T_BIN].gst, ghostpad)) + { + debug_error("failed to add ghostpad to textbin\n"); + goto ERROR; + } + } + + if (textbin[MMPLAYER_T_OVERLAY].gst) + { + pad = gst_element_get_static_pad(GST_ELEMENT(textbin[MMPLAYER_T_OVERLAY].gst), "src"); + if (!pad) + { + debug_error("failed to get src pad of textbin\n"); + goto ERROR; + } + + ghostpad = gst_ghost_pad_new("src", pad); + if (!ghostpad) + { + debug_error("failed to create ghostpad of textbin\n"); + goto ERROR; + } + + if (!gst_element_add_pad(textbin[MMPLAYER_T_BIN].gst, ghostpad)) + { + debug_error("failed to add ghostpad to textbin\n"); + goto ERROR; + } + } + + gst_object_unref(pad); + + debug_fleave(); + + return MM_ERROR_NONE; + +ERROR: + + debug_log("ERROR : releasing textbin\n"); + + if ( pad ) + gst_object_unref(GST_OBJECT(pad)); + + if ( ghostpad ) + gst_object_unref(GST_OBJECT(ghostpad)); + + g_list_free( element_bucket ); + + + /* release element which are not added to bin */ + for ( i = 1; i < MMPLAYER_T_NUM; i++ ) /* NOTE : skip bin */ + { + if ( textbin[i].gst ) + { + GstObject* parent = NULL; + parent = gst_element_get_parent( textbin[i].gst ); + + if ( !parent ) + { + gst_object_unref(GST_OBJECT(textbin[i].gst)); + textbin[i].gst = NULL; + } + else + { + gst_object_unref(GST_OBJECT(parent)); + } + } + } + + /* release textbin with it's childs */ + if ( textbin[MMPLAYER_T_BIN].gst ) + { + gst_object_unref(GST_OBJECT(textbin[MMPLAYER_T_BIN].gst)); + } + + MMPLAYER_FREEIF( textbin ); + + player->pipeline->textbin = NULL; + + return MM_ERROR_PLAYER_INTERNAL; +} + + +static int +__mmplayer_gst_create_subtitle_src(mm_player_t* player) +{ + MMPlayerGstElement* mainbin = NULL; + MMHandleType attrs = 0; + GstElement * pipeline = NULL; + GstElement *subsrc = NULL; + GstElement *subparse = NULL; + GstPad *sinkpad = NULL; + gchar *subtitle_uri =NULL; + gchar *charset = NULL; + + debug_fenter(); + + /* get mainbin */ + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED); + + pipeline = player->pipeline->mainbin[MMPLAYER_M_PIPE].gst; + mainbin = player->pipeline->mainbin; + + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute\n"); + return MM_ERROR_PLAYER_INTERNAL; + } + + mm_attrs_get_string_by_name ( attrs, "subtitle_uri", &subtitle_uri ); + if ( !subtitle_uri || strlen(subtitle_uri) < 1) + { + debug_error("subtitle uri is not proper filepath.\n"); + return MM_ERROR_PLAYER_INVALID_URI; + } + debug_log("subtitle file path is [%s].\n", subtitle_uri); + + + /* create the subtitle source */ + subsrc = gst_element_factory_make("filesrc", "subtitle_source"); + if ( !subsrc ) + { + debug_error ( "failed to create filesrc element\n" ); + goto ERROR; + } + g_object_set(G_OBJECT (subsrc), "location", subtitle_uri, NULL); + + mainbin[MMPLAYER_M_SUBSRC].id = MMPLAYER_M_SUBSRC; + mainbin[MMPLAYER_M_SUBSRC].gst = subsrc; + + if (!gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), subsrc)) + { + debug_warning("failed to add queue\n"); + goto ERROR; + } + + /* subparse */ + subparse = gst_element_factory_make("subparse", "subtitle_parser"); + if ( !subparse ) + { + debug_error ( "failed to create subparse element\n" ); + goto ERROR; + } + + charset = util_get_charset(subtitle_uri); + if (charset) + { + debug_log ("detected charset is %s\n", charset ); + g_object_set (G_OBJECT (subparse), "subtitle-encoding", charset, NULL); + } + + mainbin[MMPLAYER_M_SUBPARSE].id = MMPLAYER_M_SUBPARSE; + mainbin[MMPLAYER_M_SUBPARSE].gst = subparse; + + if (!gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), subparse)) + { + debug_warning("failed to add subparse\n"); + goto ERROR; + } + + if (!gst_element_link_pads (subsrc, "src", subparse, "sink")) + { + debug_warning("failed to link subsrc and subparse\n"); + goto ERROR; + } + + player->play_subtitle = TRUE; + debug_log ("play subtitle using subtitle file\n"); + + if (MM_ERROR_NONE != __mmplayer_gst_create_text_pipeline(player)) + { + debug_error("failed to create textbin. continuing without text\n"); + goto ERROR; + } + + if (!gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), GST_ELEMENT(player->pipeline->textbin[MMPLAYER_T_BIN].gst))) + { + debug_warning("failed to add textbin\n"); + goto ERROR; + } + + if (!gst_element_link_pads (subparse, "src", player->pipeline->textbin[MMPLAYER_T_BIN].gst, "text_sink")) + { + debug_warning("failed to link subparse and textbin\n"); + goto ERROR; + } + + debug_fleave(); + + return MM_ERROR_NONE; + + +ERROR: + return MM_ERROR_PLAYER_INTERNAL; +} + +gboolean +__mmplayer_update_subtitle( GstElement* object, GstBuffer *buffer, GstPad *pad, gpointer data) +{ + mm_player_t* player = (mm_player_t*) data; + MMMessageParamType msg = {0, }; + GstClockTime duration = 0; + guint8 *text = NULL; + gboolean ret = TRUE; + + debug_fenter(); + + return_val_if_fail ( player, FALSE ); + return_val_if_fail ( buffer, FALSE ); + + text = GST_BUFFER_DATA(buffer); + duration = GST_BUFFER_DURATION(buffer); + + if ( player->is_subtitle_off ) + { + debug_log("subtitle is OFF.\n" ); + return TRUE; + } + + if ( !text ) + { + debug_log("There is no subtitle to be displayed.\n" ); + return TRUE; + } + + msg.data = (void *) text; + msg.subtitle.duration = GST_TIME_AS_MSECONDS(duration); + + debug_warning("update subtitle : [%ld msec] %s\n'", msg.subtitle.duration, (char*)msg.data ); + + MMPLAYER_POST_MSG( player, MM_MESSAGE_UPDATE_SUBTITLE, &msg ); + + debug_fleave(); + + return ret; +} + +static int __gst_adjust_subtitle_position(mm_player_t* player, int format, int position) +{ + GstEvent* event = NULL; + gint64 current_pos = 0; + gint64 adusted_pos = 0; + gboolean ret = TRUE; + + debug_fenter(); + + /* check player and subtitlebin are created */ + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( player->play_subtitle, MM_ERROR_NOT_SUPPORT_API ); + + if (position == 0) + { + debug_log ("nothing to do\n"); + return MM_ERROR_NONE; + } + + switch (format) + { + case MM_PLAYER_POS_FORMAT_TIME: + { + /* check current postion */ + if (__gst_get_position(player, MM_PLAYER_POS_FORMAT_TIME, ¤t_pos )) + { + debug_error("failed to get position"); + return MM_ERROR_PLAYER_INTERNAL; + } + + adusted_pos = (gint64)current_pos + ((gint64)position * G_GINT64_CONSTANT(1000000)); + if (adusted_pos < 0) + adusted_pos = G_GUINT64_CONSTANT(0); + debug_log("adjust subtitle postion : %lu -> %lu [msec]\n", GST_TIME_AS_MSECONDS(current_pos), GST_TIME_AS_MSECONDS(adusted_pos)); + + event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ), + GST_SEEK_TYPE_SET, adusted_pos, + GST_SEEK_TYPE_SET, -1); + } + break; + + default: + { + debug_warning("invalid format.\n"); + return MM_ERROR_INVALID_ARGUMENT; + } + } + + /* keep ref to the event */ + gst_event_ref (event); + + debug_log("sending event[%s] to subparse element [%s]\n", + GST_EVENT_TYPE_NAME(event), GST_ELEMENT_NAME(player->pipeline->mainbin[MMPLAYER_M_SUBPARSE].gst) ); + + if (gst_element_send_event (player->pipeline->mainbin[MMPLAYER_M_SUBPARSE].gst, event)) + { + debug_log("sending event[%s] to subparse element [%s] success!\n", + GST_EVENT_TYPE_NAME(event), GST_ELEMENT_NAME(player->pipeline->mainbin[MMPLAYER_M_SUBPARSE].gst) ); + } + + /* unref to the event */ + gst_event_unref (event); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +static void +__gst_appsrc_feed_data_mem(GstElement *element, guint size, gpointer user_data) // @ +{ + GstElement *appsrc = element; + tBuffer *buf = (tBuffer *)user_data; + GstBuffer *buffer = NULL; + GstFlowReturn ret = GST_FLOW_OK; + gint len = size; + + return_if_fail ( element ); + return_if_fail ( buf ); + + buffer = gst_buffer_new (); + + if (buf->offset >= buf->len) + { + debug_log("call eos appsrc\n"); + g_signal_emit_by_name (appsrc, "end-of-stream", &ret); + return; + } + + if ( buf->len - buf->offset < size) + { + len = buf->len - buf->offset + buf->offset; + } + + GST_BUFFER_DATA(buffer) = (guint8*)(buf->buf + buf->offset); + GST_BUFFER_SIZE(buffer) = len; + GST_BUFFER_OFFSET(buffer) = buf->offset; + GST_BUFFER_OFFSET_END(buffer) = buf->offset + len; + + debug_log("feed buffer %p, offset %u-%u length %u\n", buffer, buf->offset, buf->len,len); + g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret); + + buf->offset += len; +} + +static gboolean +__gst_appsrc_seek_data_mem(GstElement *element, guint64 size, gpointer user_data) // @ +{ + tBuffer *buf = (tBuffer *)user_data; + + return_val_if_fail ( buf, FALSE ); + + buf->offset = (int)size; + + return TRUE; +} + +static void +__gst_appsrc_feed_data(GstElement *element, guint size, gpointer user_data) // @ +{ + mm_player_t *player = (mm_player_t*)user_data; + + return_if_fail ( player ); + + debug_msg("app-src: feed data\n"); + + if(player->need_data_cb) + player->need_data_cb(size, player->buffer_cb_user_param); +} + +static gboolean +__gst_appsrc_seek_data(GstElement *element, guint64 offset, gpointer user_data) // @ +{ + mm_player_t *player = (mm_player_t*)user_data; + + return_val_if_fail ( player, FALSE ); + + debug_msg("app-src: seek data\n"); + + if(player->seek_data_cb) + player->seek_data_cb(offset, player->buffer_cb_user_param); + + return TRUE; +} + + +static gboolean +__gst_appsrc_enough_data(GstElement *element, gpointer user_data) // @ +{ + mm_player_t *player = (mm_player_t*)user_data; + + return_val_if_fail ( player, FALSE ); + + debug_msg("app-src: enough data:%p\n", player->enough_data_cb); + + if(player->enough_data_cb) + player->enough_data_cb(player->buffer_cb_user_param); + + return TRUE; +} + +int +_mmplayer_push_buffer(MMHandleType hplayer, unsigned char *buf, int size) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + GstBuffer *buffer = NULL; + GstFlowReturn gst_ret = GST_FLOW_OK; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check current state */ +// MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_START ); + + + /* NOTE : we should check and create pipeline again if not created as we destroy + * whole pipeline when stopping in streamming playback + */ + if ( ! player->pipeline ) + { + if ( MM_ERROR_NONE != __gst_realize( player ) ) + { + debug_error("failed to realize before starting. only in streamming\n"); + return MM_ERROR_PLAYER_INTERNAL; + } + } + + debug_msg("app-src: pushing data\n"); + + if ( buf == NULL ) + { + debug_error("buf is null\n"); + return MM_ERROR_NONE; + } + + buffer = gst_buffer_new (); + + if (size <= 0) + { + debug_log("call eos appsrc\n"); + g_signal_emit_by_name (player->pipeline->mainbin[MMPLAYER_M_SRC].gst, "end-of-stream", &gst_ret); + return MM_ERROR_NONE; + } + + GST_BUFFER_DATA(buffer) = (guint8*)(buf); + GST_BUFFER_SIZE(buffer) = size; + + debug_log("feed buffer %p, length %u\n", buf, size); + g_signal_emit_by_name (player->pipeline->mainbin[MMPLAYER_M_SRC].gst, "push-buffer", buffer, &gst_ret); + + debug_fleave(); + + return ret; +} + +static GstBusSyncReply +__mmplayer_bus_sync_callback (GstBus * bus, GstMessage * message, gpointer data) +{ + mm_player_t *player = (mm_player_t *)data; + + switch (GST_MESSAGE_TYPE (message)) + { + case GST_MESSAGE_TAG: + __mmplayer_gst_extract_tag_from_msg(player, message); + break; + + default: + return GST_BUS_PASS; + } + gst_message_unref (message); + + return GST_BUS_DROP; +} + +/** + * This function is to create audio or video pipeline for playing. + * + * @param player [in] handle of player + * + * @return This function returns zero on success. + * @remark + * @see + */ +static int +__mmplayer_gst_create_pipeline(mm_player_t* player) // @ +{ + GstBus *bus = NULL; + MMPlayerGstElement *mainbin = NULL; + MMHandleType attrs = 0; + GstElement* element = NULL; + GList* element_bucket = NULL; + gboolean need_state_holder = TRUE; + gint i = 0; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + /* get profile attribute */ + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute\n"); + goto INIT_ERROR; + } + + /* create pipeline handles */ + if ( player->pipeline ) + { + debug_warning("pipeline should be released before create new one\n"); + goto INIT_ERROR; + } + + player->pipeline = (MMPlayerGstPipelineInfo*) g_malloc0( sizeof(MMPlayerGstPipelineInfo) ); + if (player->pipeline == NULL) + goto INIT_ERROR; + + memset( player->pipeline, 0, sizeof(MMPlayerGstPipelineInfo) ); + + + /* create mainbin */ + mainbin = (MMPlayerGstElement*) g_malloc0( sizeof(MMPlayerGstElement) * MMPLAYER_M_NUM ); + if (mainbin == NULL) + goto INIT_ERROR; + + memset( mainbin, 0, sizeof(MMPlayerGstElement) * MMPLAYER_M_NUM); + + + /* create pipeline */ + mainbin[MMPLAYER_M_PIPE].id = MMPLAYER_M_PIPE; + mainbin[MMPLAYER_M_PIPE].gst = gst_pipeline_new("player"); + if ( ! mainbin[MMPLAYER_M_PIPE].gst ) + { + debug_error("failed to create pipeline\n"); + goto INIT_ERROR; + } + + + /* create source element */ + switch ( player->profile.uri_type ) + { + /* rtsp streamming */ + case MM_PLAYER_URI_TYPE_URL_RTSP: + { + gint network_bandwidth; + gchar *user_agent, *wap_profile; + + element = gst_element_factory_make(PLAYER_INI()->name_of_rtspsrc, "streaming_source"); + + if ( !element ) + { + debug_critical("failed to create streaming source element\n"); + break; + } + + debug_log("using streamming source [%s].\n", PLAYER_INI()->name_of_rtspsrc); + + /* make it zero */ + network_bandwidth = 0; + user_agent = wap_profile = NULL; + + /* get attribute */ + mm_attrs_get_string_by_name ( attrs, "streaming_user_agent", &user_agent ); + mm_attrs_get_string_by_name ( attrs,"streaming_wap_profile", &wap_profile ); + mm_attrs_get_int_by_name ( attrs, "streaming_network_bandwidth", &network_bandwidth ); + + debug_log("setting streaming source ----------------\n"); + debug_log("user_agent : %s\n", user_agent); + debug_log("wap_profile : %s\n", wap_profile); + debug_log("network_bandwidth : %d\n", network_bandwidth); + debug_log("buffering time : %d\n", PLAYER_INI()->rtsp_buffering_time); + debug_log("rebuffering time : %d\n", PLAYER_INI()->rtsp_rebuffering_time); + debug_log("-----------------------------------------\n"); + + /* setting property to streaming source */ + g_object_set(G_OBJECT(element), "location", player->profile.uri, NULL); + g_object_set(G_OBJECT(element), "bandwidth", network_bandwidth, NULL); + g_object_set(G_OBJECT(element), "buffering_time", PLAYER_INI()->rtsp_buffering_time, NULL); + g_object_set(G_OBJECT(element), "rebuffering_time", PLAYER_INI()->rtsp_rebuffering_time, NULL); + if ( user_agent ) + g_object_set(G_OBJECT(element), "user_agent", user_agent, NULL); + if ( wap_profile ) + g_object_set(G_OBJECT(element), "wap_profile", wap_profile, NULL); + + MMPLAYER_SIGNAL_CONNECT ( player, G_OBJECT(element), "pad-added", + G_CALLBACK (__mmplayer_gst_rtp_dynamic_pad), player ); + MMPLAYER_SIGNAL_CONNECT ( player, G_OBJECT(element), "no-more-pads", + G_CALLBACK (__mmplayer_gst_rtp_no_more_pads), player ); + + player->no_more_pad = FALSE; + player->num_dynamic_pad = 0; + + /* NOTE : we cannot determine it yet. this filed will be filled by + * _mmplayer_update_content_attrs() after START. + */ + player->streaming_type = STREAMING_SERVICE_NONE; + } + break; + + /* http streaming*/ + case MM_PLAYER_URI_TYPE_URL_HTTP: + { + gchar *user_agent, *proxy, *cookies, **cookie_list; + user_agent = proxy = cookies = NULL; + cookie_list = NULL; + gint mode = MM_PLAYER_PD_MODE_NONE; + + mm_attrs_get_int_by_name ( attrs, "pd_mode", &mode ); + + player->pd_mode = mode; + + debug_log("http playback, PD mode : %d\n", player->pd_mode); + + if ( ! MMPLAYER_IS_HTTP_PD(player) ) + { + element = gst_element_factory_make(PLAYER_INI()->name_of_httpsrc, "http_streaming_source"); + if ( !element ) + { + debug_critical("failed to create http streaming source element[%s].\n", PLAYER_INI()->name_of_httpsrc); + break; + } + debug_log("using http streamming source [%s].\n", PLAYER_INI()->name_of_httpsrc); + + /* get attribute */ + mm_attrs_get_string_by_name ( attrs, "streaming_cookie", &cookies ); + mm_attrs_get_string_by_name ( attrs, "streaming_user_agent", &user_agent ); + mm_attrs_get_string_by_name ( attrs, "streaming_proxy", &proxy ); + + /* get attribute */ + debug_log("setting http streaming source ----------------\n"); + debug_log("location : %s\n", player->profile.uri); + debug_log("cookies : %s\n", cookies); + debug_log("proxy : %s\n", proxy); + debug_log("user_agent : %s\n", user_agent); + debug_log("timeout : %d\n", PLAYER_INI()->http_timeout); + debug_log("-----------------------------------------\n"); + + /* setting property to streaming source */ + g_object_set(G_OBJECT(element), "location", player->profile.uri, NULL); + g_object_set(G_OBJECT(element), "timeout", PLAYER_INI()->http_timeout, NULL); + /* check if prosy is vailid or not */ + if ( util_check_valid_url ( proxy ) ) + g_object_set(G_OBJECT(element), "proxy", proxy, NULL); + /* parsing cookies */ + if ( ( cookie_list = util_get_cookie_list ((const char*)cookies) ) ) + g_object_set(G_OBJECT(element), "cookies", cookie_list, NULL); + if ( user_agent ) + g_object_set(G_OBJECT(element), "user_agent", user_agent, NULL); + } + else // progressive download + { + if (player->pd_mode == MM_PLAYER_PD_MODE_URI) + { + gchar *path = NULL; + + mm_attrs_get_string_by_name ( attrs, "pd_location", &path ); + + MMPLAYER_FREEIF(player->pd_file_save_path); + + debug_log("PD Location : %s\n", path); + + if ( path ) + { + player->pd_file_save_path = g_strdup(path); + } + else + { + debug_error("can't find pd location so, it should be set \n"); + return MM_ERROR_PLAYER_FILE_NOT_FOUND; + } + } + + element = gst_element_factory_make("pdpushsrc", "PD pushsrc"); + if ( !element ) + { + debug_critical("failed to create PD push source element[%s].\n", "pdpushsrc"); + break; + } + + g_object_set(G_OBJECT(element), "location", player->pd_file_save_path, NULL); + } + + player->streaming_type = STREAMING_SERVICE_NONE; + } + break; + + /* file source */ + case MM_PLAYER_URI_TYPE_FILE: + { + char* drmsrc = PLAYER_INI()->name_of_drmsrc; + + debug_log("using [%s] for 'file://' handler.\n", drmsrc); + + element = gst_element_factory_make(drmsrc, "source"); + if ( !element ) + { + debug_critical("failed to create %s\n", drmsrc); + break; + } + + g_object_set(G_OBJECT(element), "location", (player->profile.uri)+7, NULL); /* uri+7 -> remove "file:// */ + //g_object_set(G_OBJECT(element), "use-mmap", TRUE, NULL); + } + break; + + /* appsrc */ + case MM_PLAYER_URI_TYPE_BUFF: + { + guint64 stream_type = GST_APP_STREAM_TYPE_STREAM; + + debug_log("mem src is selected\n"); + + element = gst_element_factory_make("appsrc", "buff-source"); + if ( !element ) + { + debug_critical("failed to create appsrc element\n"); + break; + } + + g_object_set( element, "stream-type", stream_type, NULL ); + //g_object_set( element, "size", player->mem_buf.len, NULL ); + //g_object_set( element, "blocksize", (guint64)20480, NULL ); + + MMPLAYER_SIGNAL_CONNECT( player, element, "seek-data", + G_CALLBACK(__gst_appsrc_seek_data), player); + MMPLAYER_SIGNAL_CONNECT( player, element, "need-data", + G_CALLBACK(__gst_appsrc_feed_data), player); + MMPLAYER_SIGNAL_CONNECT( player, element, "enough-data", + G_CALLBACK(__gst_appsrc_enough_data), player); + } + break; + + /* appsrc */ + case MM_PLAYER_URI_TYPE_MEM: + { + guint64 stream_type = GST_APP_STREAM_TYPE_RANDOM_ACCESS; + + debug_log("mem src is selected\n"); + + element = gst_element_factory_make("appsrc", "mem-source"); + if ( !element ) + { + debug_critical("failed to create appsrc element\n"); + break; + } + + g_object_set( element, "stream-type", stream_type, NULL ); + g_object_set( element, "size", player->mem_buf.len, NULL ); + g_object_set( element, "blocksize", (guint64)20480, NULL ); + + MMPLAYER_SIGNAL_CONNECT( player, element, "seek-data", + G_CALLBACK(__gst_appsrc_seek_data_mem), &player->mem_buf ); + MMPLAYER_SIGNAL_CONNECT( player, element, "need-data", + G_CALLBACK(__gst_appsrc_feed_data_mem), &player->mem_buf ); + } + break; + case MM_PLAYER_URI_TYPE_URL: + break; + + case MM_PLAYER_URI_TYPE_TEMP: + break; + + case MM_PLAYER_URI_TYPE_NONE: + default: + break; + } + + /* check source element is OK */ + if ( ! element ) + { + debug_critical("no source element was created.\n"); + goto INIT_ERROR; + } + + /* take source element */ + mainbin[MMPLAYER_M_SRC].id = MMPLAYER_M_SRC; + mainbin[MMPLAYER_M_SRC].gst = element; + element_bucket = g_list_append(element_bucket, &mainbin[MMPLAYER_M_SRC]); + + if (MMPLAYER_IS_STREAMING(player)) + { + player->streamer = __mm_player_streaming_create(); + __mm_player_streaming_initialize(player->streamer); + } + + if ( MMPLAYER_IS_HTTP_PD(player) ) + { + debug_log ("Picked queue2 element....\n"); + element = gst_element_factory_make("queue2", "hls_stream_buffer"); + if ( !element ) + { + debug_critical ( "failed to create http streaming buffer element\n" ); + goto INIT_ERROR; + } + + /* take it */ + mainbin[MMPLAYER_M_S_BUFFER].id = MMPLAYER_M_S_BUFFER; + mainbin[MMPLAYER_M_S_BUFFER].gst = element; + element_bucket = g_list_append(element_bucket, &mainbin[MMPLAYER_M_S_BUFFER]); + + __mm_player_streaming_set_buffer(player->streamer, + element, + TRUE, + PLAYER_INI()->http_max_size_bytes, + 1.0, + PLAYER_INI()->http_buffering_limit, + PLAYER_INI()->http_buffering_time, + FALSE, + NULL, + 0); + } + + /* create autoplugging element if src element is not a streamming src */ + if ( player->profile.uri_type != MM_PLAYER_URI_TYPE_URL_RTSP ) + { + element = NULL; + + if( PLAYER_INI()->use_decodebin ) + { + /* create decodebin */ + element = gst_element_factory_make("decodebin", "decodebin"); + + g_object_set(G_OBJECT(element), "async-handling", TRUE, NULL); + + /* set signal handler */ + MMPLAYER_SIGNAL_CONNECT( player, G_OBJECT(element), "new-decoded-pad", + G_CALLBACK(__mmplayer_gst_decode_callback), player); + + /* we don't need state holder, bcz decodebin is doing well by itself */ + need_state_holder = FALSE; + } + else + { + element = gst_element_factory_make("typefind", "typefinder"); + MMPLAYER_SIGNAL_CONNECT( player, element, "have-type", + G_CALLBACK(__mmplayer_typefind_have_type), (gpointer)player ); + } + + /* check autoplug element is OK */ + if ( ! element ) + { + debug_critical("can not create autoplug element\n"); + goto INIT_ERROR; + } + + mainbin[MMPLAYER_M_AUTOPLUG].id = MMPLAYER_M_AUTOPLUG; + mainbin[MMPLAYER_M_AUTOPLUG].gst = element; + + element_bucket = g_list_append(element_bucket, &mainbin[MMPLAYER_M_AUTOPLUG]); + } + + + /* add elements to pipeline */ + if( !__mmplayer_gst_element_add_bucket_to_bin(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), element_bucket)) + { + debug_error("Failed to add elements to pipeline\n"); + goto INIT_ERROR; + } + + + /* linking elements in the bucket by added order. */ + if ( __mmplayer_gst_element_link_bucket(element_bucket) == -1 ) + { + debug_error("Failed to link some elements\n"); + goto INIT_ERROR; + } + + + /* create fakesink element for keeping the pipeline state PAUSED. if needed */ + if ( need_state_holder ) + { + /* create */ + mainbin[MMPLAYER_M_SRC_FAKESINK].id = MMPLAYER_M_SRC_FAKESINK; + mainbin[MMPLAYER_M_SRC_FAKESINK].gst = gst_element_factory_make ("fakesink", "state-holder"); + + if (!mainbin[MMPLAYER_M_SRC_FAKESINK].gst) + { + debug_error ("fakesink element could not be created\n"); + goto INIT_ERROR; + } + GST_OBJECT_FLAG_UNSET (mainbin[MMPLAYER_M_SRC_FAKESINK].gst, GST_ELEMENT_IS_SINK); + + /* take ownership of fakesink. we are reusing it */ + gst_object_ref( mainbin[MMPLAYER_M_SRC_FAKESINK].gst ); + + /* add */ + if ( FALSE == gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), + mainbin[MMPLAYER_M_SRC_FAKESINK].gst) ) + { + debug_error("failed to add fakesink to bin\n"); + goto INIT_ERROR; + } + } + + /* now we have completed mainbin. take it */ + player->pipeline->mainbin = mainbin; + + /* connect bus callback */ + bus = gst_pipeline_get_bus(GST_PIPELINE(mainbin[MMPLAYER_M_PIPE].gst)); + if ( !bus ) + { + debug_error ("cannot get bus from pipeline.\n"); + goto INIT_ERROR; + } + player->bus_watcher = gst_bus_add_watch(bus, (GstBusFunc)__mmplayer_gst_callback, player); + + /* Note : check whether subtitle atrribute uri is set. If uri is set, then tyr to play subtitle file */ + if ( __mmplayer_check_subtitle ( player ) ) + { + if ( MM_ERROR_NONE != __mmplayer_gst_create_subtitle_src(player) ) + debug_error("fail to create subtitle src\n") + } + + /* set sync handler to get tag synchronously */ + gst_bus_set_sync_handler(bus, __mmplayer_bus_sync_callback, player); + + /* finished */ + gst_object_unref(GST_OBJECT(bus)); + g_list_free(element_bucket); + + debug_fleave(); + + return MM_ERROR_NONE; + +INIT_ERROR: + + __mmplayer_gst_destroy_pipeline(player); + g_list_free(element_bucket); + + /* release element which are not added to bin */ + for ( i = 1; i < MMPLAYER_M_NUM; i++ ) /* NOTE : skip pipeline */ + { + if ( mainbin[i].gst ) + { + GstObject* parent = NULL; + parent = gst_element_get_parent( mainbin[i].gst ); + + if ( !parent ) + { + gst_object_unref(GST_OBJECT(mainbin[i].gst)); + mainbin[i].gst = NULL; + } + else + { + gst_object_unref(GST_OBJECT(parent)); + } + } + } + + /* release pipeline with it's childs */ + if ( mainbin[MMPLAYER_M_PIPE].gst ) + { + gst_object_unref(GST_OBJECT(mainbin[MMPLAYER_M_PIPE].gst)); + } + + MMPLAYER_FREEIF( player->pipeline ); + MMPLAYER_FREEIF( mainbin ); + + return MM_ERROR_PLAYER_INTERNAL; +} + + +static int +__mmplayer_gst_destroy_pipeline(mm_player_t* player) // @ +{ + gint timeout = 0; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_INVALID_HANDLE ); + + /* cleanup stuffs */ + MMPLAYER_FREEIF(player->type); + player->have_dynamic_pad = FALSE; + player->no_more_pad = FALSE; + player->num_dynamic_pad = 0; + + if (player->v_stream_caps) + { + gst_caps_unref(player->v_stream_caps); + player->v_stream_caps = NULL; + } + + if (ahs_appsrc_cb_probe_id ) + { + GstPad *pad = NULL; + pad = gst_element_get_static_pad(player->pipeline->mainbin[MMPLAYER_M_SRC].gst, "src" ); + + gst_pad_remove_buffer_probe (pad, ahs_appsrc_cb_probe_id); + gst_object_unref(pad); + pad = NULL; + ahs_appsrc_cb_probe_id = 0; + } + + if ( player->sink_elements ) + g_list_free ( player->sink_elements ); + player->sink_elements = NULL; + + /* cleanup unlinked mime type */ + MMPLAYER_FREEIF(player->unlinked_audio_mime); + MMPLAYER_FREEIF(player->unlinked_video_mime); + MMPLAYER_FREEIF(player->unlinked_demuxer_mime); + + /* cleanup running stuffs */ + __mmplayer_cancel_delayed_eos( player ); + + /* cleanup gst stuffs */ + if ( player->pipeline ) + { + MMPlayerGstElement* mainbin = player->pipeline->mainbin; + GstTagList* tag_list = player->pipeline->tag_list; + + /* first we need to disconnect all signal hander */ + __mmplayer_release_signal_connection( player ); + + /* disconnecting bus watch */ + if ( player->bus_watcher ) + g_source_remove( player->bus_watcher ); + player->bus_watcher = 0; + + if ( mainbin ) + { + MMPlayerGstElement* audiobin = player->pipeline->audiobin; + MMPlayerGstElement* videobin = player->pipeline->videobin; + MMPlayerGstElement* textbin = player->pipeline->textbin; + GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (mainbin[MMPLAYER_M_PIPE].gst)); + gst_bus_set_sync_handler (bus, NULL, NULL); + + debug_log("pipeline status before set state to NULL\n"); + __mmplayer_dump_pipeline_state( player ); + + timeout = MMPLAYER_STATE_CHANGE_TIMEOUT(player); + ret = __mmplayer_gst_set_state ( player, mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_NULL, FALSE, timeout ); + if ( ret != MM_ERROR_NONE ) + { + debug_error("fail to change state to NULL\n"); + return MM_ERROR_PLAYER_INTERNAL; + } + + debug_log("pipeline status before unrefering pipeline\n"); + __mmplayer_dump_pipeline_state( player ); + + gst_object_unref(GST_OBJECT(mainbin[MMPLAYER_M_PIPE].gst)); + + /* free fakesink */ + if ( mainbin[MMPLAYER_M_SRC_FAKESINK].gst ) + gst_object_unref(GST_OBJECT(mainbin[MMPLAYER_M_SRC_FAKESINK].gst)); + + /* free avsysaudiosink + avsysaudiosink should be unref when destory pipeline just after start play with BT. + Because audiosink is created but never added to bin, and therefore it will not be unref when pipeline is destroyed. + */ + MMPLAYER_FREEIF( audiobin ); + MMPLAYER_FREEIF( videobin ); + MMPLAYER_FREEIF( textbin ); + MMPLAYER_FREEIF( mainbin ); + } + + if ( tag_list ) + gst_tag_list_free(tag_list); + + MMPLAYER_FREEIF( player->pipeline ); + } + + player->pipeline_is_constructed = FALSE; + + debug_fleave(); + + return ret; +} + +static int __gst_realize(mm_player_t* player) // @ +{ + gint timeout = 0; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_READY; + + __ta__("__mmplayer_gst_create_pipeline", + ret = __mmplayer_gst_create_pipeline(player); + if ( ret ) + { + debug_critical("failed to create pipeline\n"); + return ret; + } + ) + + /* set pipeline state to READY */ + /* NOTE : state change to READY must be performed sync. */ + timeout = MMPLAYER_STATE_CHANGE_TIMEOUT(player); + ret = __mmplayer_gst_set_state(player, + player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_READY, FALSE, timeout); + + if ( ret != MM_ERROR_NONE ) + { + /* return error if failed to set state */ + debug_error("failed to set state PAUSED (live : READY).\n"); + + /* dump state of all element */ + __mmplayer_dump_pipeline_state( player ); + + return ret; + } + else + { + MMPLAYER_SET_STATE ( player, MM_PLAYER_STATE_READY ); + } + + /* create dot before error-return. for debugging */ + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-realize" ); + + debug_fleave(); + + return ret; +} + +static int __gst_unrealize(mm_player_t* player) // @ +{ + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_NULL; + MMPLAYER_PRINT_STATE(player); + + /* release miscellaneous information */ + __mmplayer_release_misc( player ); + + /* destroy pipeline */ + ret = __mmplayer_gst_destroy_pipeline( player ); + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to destory pipeline\n"); + return ret; + } + + MMPLAYER_SET_STATE ( player, MM_PLAYER_STATE_NULL ); + + debug_fleave(); + + return ret; +} + +static int __gst_pending_seek ( mm_player_t* player ) +{ + MMPlayerStateType current_state = MM_PLAYER_STATE_NONE; + MMPlayerStateType pending_state = MM_PLAYER_STATE_NONE; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + if ( !player->pending_seek.is_pending ) + { + debug_log("pending seek is not reserved. nothing to do.\n" ); + return ret; + } + + /* check player state if player could pending seek or not. */ + current_state = MMPLAYER_CURRENT_STATE(player); + pending_state = MMPLAYER_PENDING_STATE(player); + + if ( current_state != MM_PLAYER_STATE_PAUSED && current_state != MM_PLAYER_STATE_PLAYING ) + { + debug_warning("try to pending seek in %s state, try next time. \n", + MMPLAYER_STATE_GET_NAME(current_state)); + return ret; + } + + debug_log("trying to play from (%lu) pending position\n", player->pending_seek.pos); + + ret = __gst_set_position ( player, player->pending_seek.format, player->pending_seek.pos, FALSE ); + + if ( MM_ERROR_NONE != ret ) + debug_error("failed to seek pending postion. just keep staying current position.\n"); + + player->pending_seek.is_pending = FALSE; + + debug_fleave(); + + return ret; +} + +static int __gst_start(mm_player_t* player) // @ +{ + gboolean sound_extraction = 0; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* get sound_extraction property */ + mm_attrs_get_int_by_name(player->attrs, "pcm_extraction", &sound_extraction); + + /* NOTE : if SetPosition was called before Start. do it now */ + /* streaming doesn't support it. so it should be always sync */ + /* !! create one more api to check if there is pending seek rather than checking variables */ + if ( (player->pending_seek.is_pending || sound_extraction) && !MMPLAYER_IS_STREAMING(player)) + { + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_PAUSED; + ret = __gst_pause(player, FALSE); + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to set state to PAUSED for pending seek\n"); + return ret; + } + + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_PLAYING; + + if ( sound_extraction ) + { + debug_log("setting pcm extraction\n"); + + ret = __mmplayer_set_pcm_extraction(player); + if ( MM_ERROR_NONE != ret ) + { + debug_warning("failed to set pcm extraction\n"); + return ret; + } + } + else + { + if ( MM_ERROR_NONE != __gst_pending_seek(player) ) + { + debug_warning("failed to seek pending postion. starting from the begin of content.\n"); + } + } + } + + debug_log("current state before doing transition"); + MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_PLAYING; + MMPLAYER_PRINT_STATE(player); + + /* set pipeline state to PLAYING */ + ret = __mmplayer_gst_set_state(player, + player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_PLAYING, FALSE, MMPLAYER_STATE_CHANGE_TIMEOUT(player) ); + if (ret == MM_ERROR_NONE) + { + MMPLAYER_SET_STATE(player, MM_PLAYER_STATE_PLAYING); + } + else + { + debug_error("failed to set state to PLAYING"); + + /* dump state of all element */ + __mmplayer_dump_pipeline_state( player ); + + return ret; + } + + /* FIXIT : analyze so called "async problem" */ + /* set async off */ + __gst_set_async_state_change( player, FALSE ); + + /* generating debug info before returning error */ + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-start" ); + + debug_fleave(); + + return ret; +} + +static void __mmplayer_do_sound_fadedown(mm_player_t* player, unsigned int time) +{ + debug_fenter(); + + return_if_fail(player + && player->pipeline + && player->pipeline->audiobin + && player->pipeline->audiobin[MMPLAYER_A_SINK].gst); + + g_object_set(G_OBJECT(player->pipeline->audiobin[MMPLAYER_A_SINK].gst), "mute", 2, NULL); + + usleep(time); + + debug_fleave(); +} + +static void __mmplayer_undo_sound_fadedown(mm_player_t* player) +{ + debug_fenter(); + + return_if_fail(player + && player->pipeline + && player->pipeline->audiobin + && player->pipeline->audiobin[MMPLAYER_A_SINK].gst); + + g_object_set(G_OBJECT(player->pipeline->audiobin[MMPLAYER_A_SINK].gst), "mute", 0, NULL); + + debug_fleave(); +} + +static int __gst_stop(mm_player_t* player) // @ +{ + GstStateChangeReturn change_ret = GST_STATE_CHANGE_SUCCESS; + MMHandleType attrs = 0; + gboolean fadewown = FALSE; + gboolean rewind = FALSE; + gint timeout = 0; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED); + + debug_log("current state before doing transition"); + MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_READY; + MMPLAYER_PRINT_STATE(player); + + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute\n"); + return MM_ERROR_PLAYER_INTERNAL; + } + + mm_attrs_get_int_by_name(attrs,"sound_fadedown", &fadewown); + + /* enable fadedown */ + if (fadewown) + __mmplayer_do_sound_fadedown(player, MM_PLAYER_FADEOUT_TIME_DEFAULT); + + /* Just set state to PAUESED and the rewind. it's usual player behavior. */ + timeout = MMPLAYER_STATE_CHANGE_TIMEOUT ( player ); + if ( player->profile.uri_type == MM_PLAYER_URI_TYPE_BUFF || player->profile.uri_type == MM_PLAYER_URI_TYPE_HLS) + { + ret = __mmplayer_gst_set_state(player, + player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_READY, FALSE, timeout ); + } + else + { + ret = __mmplayer_gst_set_state( player, + player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_PAUSED, FALSE, timeout ); + + if ( !MMPLAYER_IS_STREAMING(player)) + rewind = TRUE; + } + + /* disable fadeout */ + if (fadewown) + __mmplayer_undo_sound_fadedown(player); + + + /* return if set_state has failed */ + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to set state.\n"); + + /* dump state of all element. don't care it success or not */ + __mmplayer_dump_pipeline_state( player ); + + return ret; + } + + /* rewind */ + if ( rewind ) + { + if ( ! __gst_seek( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, 1.0, + GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 0, + GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE) ) + { + debug_warning("failed to rewind\n"); + ret = MM_ERROR_PLAYER_SEEK; + } + } + + /* initialize */ + player->sent_bos = FALSE; + + /* wait for seek to complete */ + change_ret = gst_element_get_state (player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, NULL, NULL, timeout * GST_SECOND); + if ( change_ret == GST_STATE_CHANGE_SUCCESS || change_ret == GST_STATE_CHANGE_NO_PREROLL ) + { + MMPLAYER_SET_STATE ( player, MM_PLAYER_STATE_READY ); + } + else + { + debug_error("fail to stop player.\n"); + ret = MM_ERROR_PLAYER_INTERNAL; + } + + /* generate dot file if enabled */ + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-stop" ); + + debug_fleave(); + + return ret; +} + +int __gst_pause(mm_player_t* player, gboolean async) // @ +{ + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED); + + debug_log("current state before doing transition"); + MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_PAUSED; + MMPLAYER_PRINT_STATE(player); + + /* set pipeline status to PAUSED */ + ret = __mmplayer_gst_set_state(player, + player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_PAUSED, async, MMPLAYER_STATE_CHANGE_TIMEOUT(player)); + + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to set state to PAUSED\n"); + + /* dump state of all element */ + __mmplayer_dump_pipeline_state( player ); + + return ret; + } + else + { + if ( async == FALSE ) + { + MMPLAYER_SET_STATE ( player, MM_PLAYER_STATE_PAUSED ); + } + } + + /* FIXIT : analyze so called "async problem" */ + /* set async off */ + __gst_set_async_state_change( player, TRUE); + + /* generate dot file before returning error */ + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-pause" ); + + debug_fleave(); + + return ret; +} + +int __gst_resume(mm_player_t* player, gboolean async) // @ +{ + int ret = MM_ERROR_NONE; + gint timeout = 0; + + debug_fenter(); + + return_val_if_fail(player && player->pipeline, + MM_ERROR_PLAYER_NOT_INITIALIZED); + + debug_log("current state before doing transition"); + MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_PLAYING; + MMPLAYER_PRINT_STATE(player); + + /* generate dot file before returning error */ + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-resume" ); + + __mmplayer_set_antishock( player , FALSE ); + + if ( async ) + debug_log("do async state transition to PLAYING.\n"); + + /* set pipeline state to PLAYING */ + timeout = MMPLAYER_STATE_CHANGE_TIMEOUT(player); + ret = __mmplayer_gst_set_state(player, + player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_STATE_PLAYING, async, timeout ); + if (ret != MM_ERROR_NONE) + { + debug_error("failed to set state to PLAYING\n"); + + /* dump state of all element */ + __mmplayer_dump_pipeline_state( player ); + + return ret; + } + else + { + if (async == FALSE) + { + MMPLAYER_SET_STATE ( player, MM_PLAYER_STATE_PLAYING ); + } + } + + /* FIXIT : analyze so called "async problem" */ + /* set async off */ + __gst_set_async_state_change( player, FALSE ); + + /* generate dot file before returning error */ + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-resume" ); + + debug_fleave(); + + return ret; +} + +static int +__gst_set_position(mm_player_t* player, int format, unsigned long position, gboolean internal_called) // @ +{ + GstFormat fmt = GST_FORMAT_TIME; + unsigned long dur_msec = 0; + gint64 dur_nsec = 0; + gint64 pos_nsec = 0; + gboolean ret = TRUE; + + debug_fenter(); + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( !MMPLAYER_IS_LIVE_STREAMING(player), MM_ERROR_PLAYER_NO_OP ); + + if ( MMPLAYER_CURRENT_STATE(player) != MM_PLAYER_STATE_PLAYING + && MMPLAYER_CURRENT_STATE(player) != MM_PLAYER_STATE_PAUSED ) + goto PENDING; + + /* check duration */ + /* NOTE : duration cannot be zero except live streaming. + * Since some element could have some timing problemn with quering duration, try again. + */ + if ( !player->duration ) + { + if ( !gst_element_query_duration( player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &dur_nsec )) + { + goto SEEK_ERROR; + } + player->duration = dur_nsec; + } + + if ( player->duration ) + { + dur_msec = GST_TIME_AS_MSECONDS(player->duration); + } + else + { + debug_error("could not get the duration. fail to seek.\n"); + goto SEEK_ERROR; + } + + debug_log("playback rate: %f\n", player->playback_rate); + + /* do seek */ + switch ( format ) + { + case MM_PLAYER_POS_FORMAT_TIME: + { + /* check position is valid or not */ + if ( position > dur_msec ) + goto INVALID_ARGS; + + debug_log("seeking to (%lu) msec, duration is %d msec\n", position, dur_msec); + + if (player->doing_seek) + { + debug_log("not completed seek"); + return MM_ERROR_PLAYER_DOING_SEEK; + } + + if ( !internal_called) + player->doing_seek = TRUE; + + pos_nsec = position * G_GINT64_CONSTANT(1000000); + ret = __gst_seek ( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, 1.0, + GST_FORMAT_TIME, ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ), + GST_SEEK_TYPE_SET, pos_nsec, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE ); + if ( !ret ) + { + debug_error("failed to set position. dur[%lu] pos[%lu] pos_msec[%llu]\n", dur_msec, position, pos_nsec); + goto SEEK_ERROR; + } + } + break; + + case MM_PLAYER_POS_FORMAT_PERCENT: + { + debug_log("seeking to (%lu)%% \n", position); + + if (player->doing_seek) + { + debug_log("not completed seek"); + return MM_ERROR_PLAYER_DOING_SEEK; + } + + if ( !internal_called) + player->doing_seek = TRUE; + + /* FIXIT : why don't we use 'GST_FORMAT_PERCENT' */ + pos_nsec = (gint64) ( ( position * player->duration ) / 100 ); + ret = __gst_seek ( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, 1.0, + GST_FORMAT_TIME, ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ), + GST_SEEK_TYPE_SET, pos_nsec, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE ); + if ( !ret ) + { + debug_error("failed to set position. dur[%lud] pos[%lud] pos_msec[%llud]\n", dur_msec, position, pos_nsec); + goto SEEK_ERROR; + } + } + break; + + default: + goto INVALID_ARGS; + + } + + /* NOTE : store last seeking point to overcome some bad operation + * ( returning zero when getting current position ) of some elements + */ + player->last_position = pos_nsec; + + /* MSL should guarante playback rate when seek is selected during trick play of fast forward. */ + if ( player->playback_rate > 1.0 ) + _mmplayer_set_playspeed ( (MMHandleType)player, player->playback_rate ); + + debug_fleave(); + return MM_ERROR_NONE; + +PENDING: + player->pending_seek.is_pending = TRUE; + player->pending_seek.format = format; + player->pending_seek.pos = position; + + debug_warning("player current-state : %s, pending-state : %s, just preserve pending position(%lu).\n", + MMPLAYER_STATE_GET_NAME(MMPLAYER_CURRENT_STATE(player)), MMPLAYER_STATE_GET_NAME(MMPLAYER_PENDING_STATE(player)), player->pending_seek.pos); + + return MM_ERROR_NONE; + +INVALID_ARGS: + debug_error("invalid arguments, position : %ld dur : %ld format : %d \n", position, dur_msec, format); + return MM_ERROR_INVALID_ARGUMENT; + +SEEK_ERROR: + player->doing_seek = FALSE; + return MM_ERROR_PLAYER_SEEK; +} + +#define TRICKPLAY_OFFSET GST_MSECOND + +static int +__gst_get_position(mm_player_t* player, int format, unsigned long* position) // @ +{ + MMPlayerStateType current_state = MM_PLAYER_STATE_NONE; + GstFormat fmt = GST_FORMAT_TIME; + signed long long pos_msec = 0; + gboolean ret = TRUE; + + return_val_if_fail( player && position && player->pipeline && player->pipeline->mainbin, + MM_ERROR_PLAYER_NOT_INITIALIZED ); + + current_state = MMPLAYER_CURRENT_STATE(player); + + /* NOTE : query position except paused state to overcome some bad operation + * please refer to below comments in details + */ + if ( current_state != MM_PLAYER_STATE_PAUSED ) + { + ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &pos_msec); + } + + /* NOTE : get last point to overcome some bad operation of some elements + * ( returning zero when getting current position in paused state + * and when failed to get postion during seeking + */ + if ( ( current_state == MM_PLAYER_STATE_PAUSED ) + || ( ! ret )) + //|| ( player->last_position != 0 && pos_msec == 0 ) ) + { + debug_warning ("pos_msec = %"GST_TIME_FORMAT" and ret = %d and state = %d", GST_TIME_ARGS (pos_msec), ret, current_state); + + if(player->playback_rate < 0.0) + pos_msec = player->last_position - TRICKPLAY_OFFSET; + else + pos_msec = player->last_position; + + if (!ret) + pos_msec = player->last_position; + else + player->last_position = pos_msec; + + debug_warning("returning last point : %"GST_TIME_FORMAT, GST_TIME_ARGS(pos_msec)); + + } + else + { + player->last_position = pos_msec; + } + + switch (format) { + case MM_PLAYER_POS_FORMAT_TIME: + *position = GST_TIME_AS_MSECONDS(pos_msec); + break; + + case MM_PLAYER_POS_FORMAT_PERCENT: + { + int dur = 0; + int pos = 0; + + dur = player->duration / GST_SECOND; + if (dur <= 0) + { + debug_log ("duration is [%d], so returning position 0\n",dur); + *position = 0; + } + else + { + pos = pos_msec / GST_SECOND; + *position = pos * 100 / dur; + } + break; + } + default: + return MM_ERROR_PLAYER_INTERNAL; + } + + debug_log("current position : %lu\n", *position); + + + return MM_ERROR_NONE; +} + + +static int __gst_get_buffer_position(mm_player_t* player, int format, unsigned long* start_pos, unsigned long* stop_pos) +{ + GstElement *element = NULL; + GstQuery *query = NULL; + + return_val_if_fail( player && + player->pipeline && + player->pipeline->mainbin, + MM_ERROR_PLAYER_NOT_INITIALIZED ); + + return_val_if_fail( start_pos && stop_pos, MM_ERROR_INVALID_ARGUMENT ); + + if ( MMPLAYER_IS_HTTP_STREAMING ( player )) + { + /* Note : In case of http streaming or HLS, the buffering queue [ queue2 ] could handle buffering query. */ + element = GST_ELEMENT ( player->pipeline->mainbin[MMPLAYER_M_S_BUFFER].gst ); + } + else if ( MMPLAYER_IS_RTSP_STREAMING ( player ) ) + { + debug_warning ( "it's not supported yet.\n" ); + return MM_ERROR_NONE; + } + else + { + debug_warning ( "it's only used for streaming case.\n" ); + return MM_ERROR_NONE; + } + + *start_pos = 0; + *stop_pos = 0; + + switch ( format ) + { + case MM_PLAYER_POS_FORMAT_PERCENT : + { + query = gst_query_new_buffering ( GST_FORMAT_PERCENT ); + if ( gst_element_query ( element, query ) ) + { + gint64 start, stop; + GstFormat format; + gboolean busy; + gint percent; + + gst_query_parse_buffering_percent ( query, &busy, &percent); + gst_query_parse_buffering_range ( query, &format, &start, &stop, NULL ); + + debug_log ( "buffering start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT "\n", start, stop); + + if ( start != -1) + *start_pos = 100 * start / GST_FORMAT_PERCENT_MAX; + else + *start_pos = 0; + + if ( stop != -1) + *stop_pos = 100 * stop / GST_FORMAT_PERCENT_MAX; + else + *stop_pos = 0; + } + gst_query_unref (query); + } + break; + + case MM_PLAYER_POS_FORMAT_TIME : + debug_warning ( "Time format is not supported yet.\n" ); + break; + + default : + break; + } + + debug_log("current buffer position : %lu~%lu \n", *start_pos, *stop_pos ); + + return MM_ERROR_NONE; +} + +static int +__gst_set_message_callback(mm_player_t* player, MMMessageCallback callback, gpointer user_param) // @ +{ + debug_fenter(); + + if ( !player ) + { + debug_warning("set_message_callback is called with invalid player handle\n"); + return MM_ERROR_PLAYER_NOT_INITIALIZED; + } + + player->msg_cb = callback; + player->msg_cb_param = user_param; + + debug_log("msg_cb : 0x%x msg_cb_param : 0x%x\n", (guint)callback, (guint)user_param); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +static gboolean __mmfplayer_parse_profile(const char *uri, void *param, MMPlayerParseProfile* data) // @ +{ + gboolean ret = FALSE; + char *path = NULL; + + debug_fenter(); + + return_val_if_fail ( uri , FALSE); + return_val_if_fail ( data , FALSE); + return_val_if_fail ( ( strlen(uri) <= MM_MAX_URL_LEN ), FALSE ); + + memset(data, 0, sizeof(MMPlayerParseProfile)); + + if ((path = strstr(uri, "file://"))) + { + if (util_exist_file_path(path + 7)) { + strncpy(data->uri, path, MM_MAX_URL_LEN-1); + + if ( util_is_sdp_file ( path ) ) + { + debug_log("uri is actually a file but it's sdp file. giving it to rtspsrc\n"); + data->uri_type = MM_PLAYER_URI_TYPE_URL_RTSP; + } + else + { + data->uri_type = MM_PLAYER_URI_TYPE_FILE; + } + ret = TRUE; + } + else + { + debug_warning("could access %s.\n", path); + } + } + else if ((path = strstr(uri, "buff://"))) + { + data->uri_type = MM_PLAYER_URI_TYPE_BUFF; + ret = TRUE; + } + else if ((path = strstr(uri, "rtsp://"))) + { + if (strlen(path)) { + strcpy(data->uri, uri); + data->uri_type = MM_PLAYER_URI_TYPE_URL_RTSP; + ret = TRUE; + } + } + else if ((path = strstr(uri, "http://"))) + { + if (strlen(path)) { + strcpy(data->uri, uri); + data->uri_type = MM_PLAYER_URI_TYPE_URL_HTTP; + + ret = TRUE; + } + } + else if ((path = strstr(uri, "https://"))) + { + if (strlen(path)) { + strcpy(data->uri, uri); + data->uri_type = MM_PLAYER_URI_TYPE_URL_HTTP; + + ret = TRUE; + } + } + else if ((path = strstr(uri, "rtspu://"))) + { + if (strlen(path)) { + strcpy(data->uri, uri); + data->uri_type = MM_PLAYER_URI_TYPE_URL_RTSP; + ret = TRUE; + } + } + else if ((path = strstr(uri, "rtspr://"))) + { + strcpy(data->uri, path); + char *separater =strstr(path, "*"); + + if (separater) { + int urgent_len = 0; + char *urgent = separater + strlen("*"); + + if ((urgent_len = strlen(urgent))) { + data->uri[strlen(path) - urgent_len - strlen("*")] = '\0'; + strcpy(data->urgent, urgent); + data->uri_type = MM_PLAYER_URI_TYPE_URL_RTSP; + ret = TRUE; + } + } + } + else if ((path = strstr(uri, "mms://"))) + { + if (strlen(path)) { + strcpy(data->uri, uri); + data->uri_type = MM_PLAYER_URI_TYPE_URL_MMS; + ret = TRUE; + } + } + else if ((path = strstr(uri, "mem://"))) + { + if (strlen(path)) { + int mem_size = 0; + char *buffer = NULL; + char *seperator = strchr(path, ','); + char ext[100] = {0,}, size[100] = {0,}; + + if (seperator) { + if ((buffer = strstr(path, "ext="))) { + buffer += strlen("ext="); + + if (strlen(buffer)) { + strcpy(ext, buffer); + + if ((seperator = strchr(ext, ',')) + || (seperator = strchr(ext, ' ')) + || (seperator = strchr(ext, '\0'))) { + seperator[0] = '\0'; + } + } + } + + if ((buffer = strstr(path, "size="))) { + buffer += strlen("size="); + + if (strlen(buffer) > 0) { + strcpy(size, buffer); + + if ((seperator = strchr(size, ',')) + || (seperator = strchr(size, ' ')) + || (seperator = strchr(size, '\0'))) { + seperator[0] = '\0'; + } + + mem_size = atoi(size); + } + } + } + + debug_log("ext: %s, mem_size: %d, mmap(param): %p\n", ext, mem_size, param); + if ( mem_size && param) { + data->mem = param; + data->mem_size = mem_size; + data->uri_type = MM_PLAYER_URI_TYPE_MEM; + ret = TRUE; + } + } + } + else + { + /* if no protocol prefix exist. check file existence and then give file:// as it's prefix */ + if (util_exist_file_path(uri)) + { + debug_warning("uri has no protocol-prefix. giving 'file://' by default.\n"); + g_snprintf(data->uri, MM_MAX_URL_LEN, "file://%s", uri); + + if ( util_is_sdp_file( (char*)uri ) ) + { + debug_log("uri is actually a file but it's sdp file. giving it to rtspsrc\n"); + data->uri_type = MM_PLAYER_URI_TYPE_URL_RTSP; + } + else + { + data->uri_type = MM_PLAYER_URI_TYPE_FILE; + } + ret = TRUE; + } + else + { + debug_error ("invalid uri, could not play..\n"); + data->uri_type = MM_PLAYER_URI_TYPE_NONE; + } + } + + if (data->uri_type == MM_PLAYER_URI_TYPE_NONE) { + ret = FALSE; + } + + /* dump parse result */ + debug_log("profile parsing result ---\n"); + debug_warning("incomming uri : %s\n", uri); + debug_log("uri : %s\n", data->uri); + debug_log("uri_type : %d\n", data->uri_type); + debug_log("play_mode : %d\n", data->play_mode); + debug_log("mem : 0x%x\n", (guint)data->mem); + debug_log("mem_size : %d\n", data->mem_size); + debug_log("urgent : %s\n", data->urgent); + debug_log("--------------------------\n"); + + debug_fleave(); + + return ret; +} + +gboolean _asm_postmsg(gpointer *data) +{ + mm_player_t* player = (mm_player_t*)data; + MMMessageParamType msg = {0, }; + + debug_fenter(); + + return_val_if_fail ( player, FALSE ); + + msg.union_type = MM_MSG_UNION_CODE; + msg.code = player->sm.event_src; + + MMPLAYER_POST_MSG( player, MM_MESSAGE_READY_TO_RESUME, &msg); + + return FALSE; +} +gboolean _asm_lazy_pause(gpointer *data) +{ + mm_player_t* player = (mm_player_t*)data; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, FALSE ); + + if (MMPLAYER_CURRENT_STATE(player) == MM_PLAYER_STATE_PLAYING) + { + debug_log ("Ready to proceed lazy pause\n"); + ret = _mmplayer_pause((MMHandleType)player); + if(MM_ERROR_NONE != ret) + { + debug_error("MMPlayer pause failed in ASM callback lazy pause\n"); + } + } + else + { + debug_log ("Invalid state to proceed lazy pause\n"); + } + + /* unset mute */ + if (player->pipeline && player->pipeline->audiobin) + g_object_set(G_OBJECT(player->pipeline->audiobin[MMPLAYER_A_SINK].gst), "mute", 0, NULL); + + player->sm.by_asm_cb = 0; //should be reset here + + debug_fleave(); + + return FALSE; +} +ASM_cb_result_t +__mmplayer_asm_callback(int handle, ASM_event_sources_t event_src, ASM_sound_commands_t command, unsigned int sound_status, void* cb_data) +{ + mm_player_t* player = (mm_player_t*) cb_data; + ASM_cb_result_t cb_res = ASM_CB_RES_IGNORE; + int result = MM_ERROR_NONE; + gboolean lazy_pause = FALSE; + + debug_fenter(); + + return_val_if_fail ( player && player->pipeline, ASM_CB_RES_IGNORE ); + return_val_if_fail ( player->attrs, MM_ERROR_PLAYER_INTERNAL ); + + if (player->is_sound_extraction) + { + debug_log("sound extraction is working...so, asm command is ignored.\n"); + return result; + } + + player->sm.by_asm_cb = 1; // it should be enabled for player state transition with called application command + player->sm.event_src = event_src; + + if(event_src == ASM_EVENT_SOURCE_EARJACK_UNPLUG ) + { + int stop_by_asm = 0; + + mm_attrs_get_int_by_name(player->attrs, "sound_stop_when_unplugged", &stop_by_asm); + if (!stop_by_asm) + return cb_res; + } + else if (event_src == ASM_EVENT_SOURCE_RESOURCE_CONFLICT) + { + /* can use video overlay simultaneously */ + /* video resource conflict */ + if(player->pipeline->videobin) + { + if (PLAYER_INI()->multiple_codec_supported) + { + debug_log("video conflict but, can support to use video overlay simultaneously"); + result = _mmplayer_pause((MMHandleType)player); + cb_res = ASM_CB_RES_PAUSE; + } + else + { + debug_log("video conflict, can't support for multiple codec instance"); + result = _mmplayer_unrealize((MMHandleType)player); + cb_res = ASM_CB_RES_STOP; + } + } + return cb_res; + } + + switch(command) + { + case ASM_COMMAND_PLAY: + debug_warning ("Got unexpected asm command (%d)", command); + break; + + case ASM_COMMAND_STOP: // notification case + { + debug_log("Got msg from asm to stop"); + + result = _mmplayer_stop((MMHandleType)player); + if (result != MM_ERROR_NONE) + { + debug_warning("fail to set stop state by asm"); + cb_res = ASM_CB_RES_IGNORE; + } + else + { + cb_res = ASM_CB_RES_STOP; + } + player->sm.by_asm_cb = 0; // reset because no message any more from asm + } + break; + + case ASM_COMMAND_PAUSE: + { + debug_log("Got msg from asm to Pause"); + + if(event_src == ASM_EVENT_SOURCE_CALL_START + || event_src == ASM_EVENT_SOURCE_ALARM_START + || event_src == ASM_EVENT_SOURCE_MEDIA) + { + //hold 0.7 second to excute "fadedown mute" effect + debug_log ("do fade down->pause->undo fade down"); + + __mmplayer_do_sound_fadedown(player, MM_PLAYER_FADEOUT_TIME_DEFAULT); + + result = _mmplayer_pause((MMHandleType)player); + if (result != MM_ERROR_NONE) + { + debug_warning("fail to set Pause state by asm"); + cb_res = ASM_CB_RES_IGNORE; + break; + } + __mmplayer_undo_sound_fadedown(player); + } + else if(event_src == ASM_EVENT_SOURCE_OTHER_PLAYER_APP) + { + lazy_pause = TRUE; // return as soon as possible, for fast start of other app + + if ( player->pipeline->audiobin && player->pipeline->audiobin[MMPLAYER_A_SINK].gst ) + g_object_set( player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "mute", 2, NULL); + + player->lazy_pause_event_id = g_timeout_add(LAZY_PAUSE_TIMEOUT_MSEC, (GSourceFunc)_asm_lazy_pause, (gpointer)player); + debug_log ("set lazy pause timer (id=[%d], timeout=[%d ms])", player->lazy_pause_event_id, LAZY_PAUSE_TIMEOUT_MSEC); + } + else + { + //immediate pause + debug_log ("immediate pause"); + result = _mmplayer_pause((MMHandleType)player); + } + cb_res = ASM_CB_RES_PAUSE; + } + break; + + case ASM_COMMAND_RESUME: + { + debug_log("Got msg from asm to Resume. So, application can resume. code (%d) \n", event_src); + player->sm.by_asm_cb = 0; + //ASM server is single thread daemon. So use g_idle_add() to post resume msg + g_idle_add((GSourceFunc)_asm_postmsg, (gpointer)player); + cb_res = ASM_CB_RES_IGNORE; + } + break; + + default: + break; + } + + if (!lazy_pause) + player->sm.by_asm_cb = 0; + + debug_fleave(); + + return cb_res; +} + +int +_mmplayer_create_player(MMHandleType handle) // @ +{ + mm_player_t* player = MM_PLAYER_CAST(handle); + gint i; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + MMTA_ACUM_ITEM_BEGIN("[KPI] media player service create->playing", FALSE); + + /* initialize player state */ + MMPLAYER_CURRENT_STATE(player) = MM_PLAYER_STATE_NONE; + MMPLAYER_PREV_STATE(player) = MM_PLAYER_STATE_NONE; + MMPLAYER_PENDING_STATE(player) = MM_PLAYER_STATE_NONE; + MMPLAYER_TARGET_STATE(player) = MM_PLAYER_STATE_NONE; + + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL ( player, MMPLAYER_COMMAND_CREATE ); + + /* construct attributes */ + player->attrs = _mmplayer_construct_attribute(handle); + + if ( !player->attrs ) + { + debug_critical("Failed to construct attributes\n"); + goto ERROR; + } + + /* initialize gstreamer with configured parameter */ + if ( ! __mmplayer_gstreamer_init() ) + { + debug_critical("Initializing gstreamer failed\n"); + goto ERROR; + } + + /* initialize factories if not using decodebin */ + if ( FALSE == PLAYER_INI()->use_decodebin ) + { + if( player->factories == NULL ) + __mmplayer_init_factories(player); + } + + /* create lock. note that g_tread_init() has already called in gst_init() */ + player->fsink_lock = g_mutex_new(); + if ( ! player->fsink_lock ) + { + debug_critical("Cannot create mutex for command lock\n"); + goto ERROR; + } + + /* create repeat mutex */ + player->repeat_thread_mutex = g_mutex_new(); + if ( ! player->repeat_thread_mutex ) + { + debug_critical("Cannot create repeat mutex\n"); + goto ERROR; + } + + /* create repeat cond */ + player->repeat_thread_cond = g_cond_new(); + if ( ! player->repeat_thread_cond ) + { + debug_critical("Cannot create repeat cond\n"); + goto ERROR; + } + + /* create repeat thread */ + player->repeat_thread = + g_thread_create (__mmplayer_repeat_thread, (gpointer)player, TRUE, NULL); + if ( ! player->repeat_thread ) + { + goto ERROR; + } + + if ( MM_ERROR_NONE != _mmplayer_initialize_video_capture(player)) + { + debug_error("failed to initialize video capture\n"); + goto ERROR; + } + + /* register to asm */ + if ( MM_ERROR_NONE != _mmplayer_asm_register(&player->sm, (ASM_sound_cb_t)__mmplayer_asm_callback, (void*)player) ) + { + /* NOTE : we are dealing it as an error since we cannot expect it's behavior */ + debug_error("failed to register asm server\n"); + return MM_ERROR_POLICY_INTERNAL; + } + + if (MMPLAYER_IS_HTTP_PD(player)) + { + player->pd_downloader = NULL; + player->pd_file_save_path = NULL; + } + + /* give default value of audio effect setting */ + player->bypass_audio_effect = TRUE; + player->sound.volume = MM_VOLUME_FACTOR_DEFAULT; + player->playback_rate = DEFAULT_PLAYBACK_RATE; + player->no_more_pad = TRUE; + + player->play_subtitle = FALSE; + player->use_textoverlay = FALSE; + + /* set player state to null */ + MMPLAYER_STATE_CHANGE_TIMEOUT(player) = PLAYER_INI()->localplayback_state_change_timeout; + MMPLAYER_SET_STATE ( player, MM_PLAYER_STATE_NULL ); + + debug_fleave(); + + return MM_ERROR_NONE; + +ERROR: + /* free lock */ + if ( player->fsink_lock ) + g_mutex_free( player->fsink_lock ); + player->fsink_lock = NULL; + + /* free thread */ + if ( player->repeat_thread_cond && + player->repeat_thread_mutex && + player->repeat_thread ) + { + player->repeat_thread_exit = TRUE; + g_cond_signal( player->repeat_thread_cond ); + + g_thread_join( player->repeat_thread ); + player->repeat_thread = NULL; + + g_mutex_free ( player->repeat_thread_mutex ); + player->repeat_thread_mutex = NULL; + + g_cond_free ( player->repeat_thread_cond ); + player->repeat_thread_cond = NULL; + } + /* clear repeat thread mutex/cond if still alive + * this can happen if only thread creating has failed + */ + if ( player->repeat_thread_mutex ) + g_mutex_free ( player->repeat_thread_mutex ); + + if ( player->repeat_thread_cond ) + g_cond_free ( player->repeat_thread_cond ); + + /* release attributes */ + _mmplayer_deconstruct_attribute(handle); + + return MM_ERROR_PLAYER_INTERNAL; +} + +static gboolean +__mmplayer_gstreamer_init(void) // @ +{ + static gboolean initialized = FALSE; + static const int max_argc = 50; + gint* argc = NULL; + gchar** argv = NULL; + GError *err = NULL; + int i = 0; + + debug_fenter(); + + if ( initialized ) + { + debug_log("gstreamer already initialized.\n"); + return TRUE; + } + + /* alloc */ + argc = malloc( sizeof(int) ); + argv = malloc( sizeof(gchar*) * max_argc ); + + if ( !argc || !argv ) + goto ERROR; + + memset( argv, 0, sizeof(gchar*) * max_argc ); + + /* add initial */ + *argc = 1; + argv[0] = g_strdup( "mmplayer" ); + + /* add gst_param */ + for ( i = 0; i < 5; i++ ) /* FIXIT : num of param is now fixed to 5. make it dynamic */ + { + if ( strlen( PLAYER_INI()->gst_param[i] ) > 0 ) + { + argv[*argc] = g_strdup( PLAYER_INI()->gst_param[i] ); + (*argc)++; + } + } + + /* we would not do fork for scanning plugins */ + argv[*argc] = g_strdup("--gst-disable-registry-fork"); + (*argc)++; + + /* check disable registry scan */ + if ( PLAYER_INI()->skip_rescan ) + { + argv[*argc] = g_strdup("--gst-disable-registry-update"); + (*argc)++; + } + + /* check disable segtrap */ + if ( PLAYER_INI()->disable_segtrap ) + { + argv[*argc] = g_strdup("--gst-disable-segtrap"); + (*argc)++; + } + + debug_log("initializing gstreamer with following parameter\n"); + debug_log("argc : %d\n", *argc); + + for ( i = 0; i < *argc; i++ ) + { + debug_log("argv[%d] : %s\n", i, argv[i]); + } + + + /* initializing gstreamer */ + __ta__("gst_init time", + + if ( ! gst_init_check (argc, &argv, &err)) + { + debug_error("Could not initialize GStreamer: %s\n", err ? err->message : "unknown error occurred"); + if (err) + { + g_error_free (err); + } + + goto ERROR; + } + ); + + /* release */ + for ( i = 0; i < *argc; i++ ) + { + MMPLAYER_FREEIF( argv[i] ); + } + + MMPLAYER_FREEIF( argv ); + MMPLAYER_FREEIF( argc ); + + /* done */ + initialized = TRUE; + + debug_fleave(); + + return TRUE; + +ERROR: + + MMPLAYER_FREEIF( argv ); + MMPLAYER_FREEIF( argc ); + + return FALSE; +} + +int +__mmplayer_destroy_streaming_ext(mm_player_t* player) +{ + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + if (player->pd_downloader) + _mmplayer_unrealize_pd_downloader((MMHandleType)player); + + if (MMPLAYER_IS_HTTP_PD(player)) + _mmplayer_destroy_pd_downloader((MMHandleType)player); + + if (MMPLAYER_IS_STREAMING(player)) + { + if (player->streamer) + { + __mm_player_streaming_deinitialize (player->streamer); + __mm_player_streaming_destroy(player->streamer); + player->streamer = NULL; + } + } + return MM_ERROR_NONE; +} + +int +_mmplayer_destroy(MMHandleType handle) // @ +{ + mm_player_t* player = MM_PLAYER_CAST(handle); + + debug_fenter(); + + /* check player handle */ + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* destroy can called at anytime */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL ( player, MMPLAYER_COMMAND_DESTROY ); + + __mmplayer_destroy_streaming_ext(player); + + /* release repeat thread */ + if ( player->repeat_thread_cond && + player->repeat_thread_mutex && + player->repeat_thread ) + { + player->repeat_thread_exit = TRUE; + g_cond_signal( player->repeat_thread_cond ); + + debug_log("waitting for repeat thread exit\n"); + g_thread_join ( player->repeat_thread ); + g_mutex_free ( player->repeat_thread_mutex ); + g_cond_free ( player->repeat_thread_cond ); + debug_log("repeat thread released\n"); + } + + if (MM_ERROR_NONE != _mmplayer_release_video_capture(player)) + { + debug_error("failed to release video capture\n"); + return MM_ERROR_PLAYER_INTERNAL; + } + + /* withdraw asm */ + if ( MM_ERROR_NONE != _mmplayer_asm_deregister(&player->sm) ) + { + debug_error("failed to deregister asm server\n"); + return MM_ERROR_PLAYER_INTERNAL; + } + + /* release pipeline */ + if ( MM_ERROR_NONE != __mmplayer_gst_destroy_pipeline( player ) ) + { + debug_error("failed to destory pipeline\n"); + return MM_ERROR_PLAYER_INTERNAL; + } + + /* release attributes */ + _mmplayer_deconstruct_attribute( handle ); + + /* release factories */ + __mmplayer_release_factories( player ); + + /* release lock */ + if ( player->fsink_lock ) + g_mutex_free( player->fsink_lock ); + + if ( player->msg_cb_lock ) + g_mutex_free( player->msg_cb_lock ); + + if (player->lazy_pause_event_id) + { + g_source_remove (player->lazy_pause_event_id); + player->lazy_pause_event_id = 0; + } + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +__mmplayer_realize_streaming_ext(mm_player_t* player) +{ + int ret = MM_ERROR_NONE; + + debug_fenter(); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + if (MMPLAYER_IS_HTTP_PD(player)) + { + gboolean bret = FALSE; + + player->pd_downloader = _mmplayer_create_pd_downloader(); + if ( !player->pd_downloader ) + { + debug_error ("Unable to create PD Downloader..."); + ret = MM_ERROR_PLAYER_NO_FREE_SPACE; + } + + bret = _mmplayer_realize_pd_downloader((MMHandleType)player, player->profile.uri, player->pd_file_save_path, player->pipeline->mainbin[MMPLAYER_M_SRC].gst); + + if (FALSE == bret) + { + debug_error ("Unable to create PD Downloader..."); + ret = MM_ERROR_PLAYER_NOT_INITIALIZED; + } + } + + debug_fleave(); + return ret; +} + +int +_mmplayer_realize(MMHandleType hplayer) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + char *uri =NULL; + void *param = NULL; + int application_pid = -1; + gboolean update_registry = FALSE; + MMHandleType attrs = 0; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + /* check player handle */ + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ) + + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_REALIZE ); + + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("fail to get attributes.\n"); + return MM_ERROR_PLAYER_INTERNAL; + } + + mm_attrs_get_int_by_name(attrs, "sound_application_pid", &application_pid ); + player->sm.pid = application_pid; + + mm_attrs_get_string_by_name(attrs, "profile_uri", &uri); + mm_attrs_get_data_by_name(attrs, "profile_user_param", ¶m); + + if (! __mmfplayer_parse_profile((const char*)uri, param, &player->profile) ) + { + debug_error("failed to parse profile\n"); + return MM_ERROR_PLAYER_INVALID_URI; + } + + /* FIXIT : we can use thouse in player->profile directly */ + if (player->profile.uri_type == MM_PLAYER_URI_TYPE_MEM) + { + player->mem_buf.buf = (char *)player->profile.mem; + player->mem_buf.len = player->profile.mem_size; + player->mem_buf.offset = 0; + } + + if (player->profile.uri_type == MM_PLAYER_URI_TYPE_URL_MMS) + { + debug_warning("mms protocol is not supported format.\n"); + return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT; + } + + if (MMPLAYER_IS_STREAMING(player)) + MMPLAYER_STATE_CHANGE_TIMEOUT(player) = PLAYER_INI()->live_state_change_timeout; + else + MMPLAYER_STATE_CHANGE_TIMEOUT(player) = PLAYER_INI()->localplayback_state_change_timeout; + + player->videodec_linked = 0; + player->videosink_linked = 0; + player->audiodec_linked = 0; + player->audiosink_linked = 0; + player->textsink_linked = 0; + + /* set the subtitle ON default */ + player->is_subtitle_off = FALSE; + + /* we need to update content attrs only the content has changed */ + player->need_update_content_attrs = TRUE; + player->need_update_content_dur = FALSE; + + /* registry should be updated for downloadable codec */ + mm_attrs_get_int_by_name(attrs, "profile_update_registry", &update_registry); + + if ( update_registry ) + { + debug_log("updating registry...\n"); + gst_update_registry(); + + /* then we have to rebuild factories */ + __mmplayer_release_factories( player ); + __mmplayer_init_factories(player); + } + + /* realize pipeline */ + ret = __gst_realize( player ); + if ( ret != MM_ERROR_NONE ) + { + debug_error("fail to realize the player.\n"); + } + else + { + __mmplayer_realize_streaming_ext(player); + } + + debug_fleave(); + + return ret; +} + +int +__mmplayer_unrealize_streaming_ext(mm_player_t *player) +{ + debug_fenter(); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* destroy can called at anytime */ + if (player->pd_downloader && MMPLAYER_IS_HTTP_PD(player)) + { + _mmplayer_unrealize_pd_downloader ((MMHandleType)player); + player->pd_downloader = NULL; + } + + debug_fleave(); + return MM_ERROR_NONE; +} + +int +_mmplayer_unrealize(MMHandleType hplayer) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ) + + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_UNREALIZE ); + + __mmplayer_unrealize_streaming_ext(player); + + /* unrealize pipeline */ + ret = __gst_unrealize( player ); + + /* set player state if success */ + if ( MM_ERROR_NONE == ret ) + { + ret = _mmplayer_asm_set_state(hplayer, ASM_STATE_STOP); + if ( ret ) + { + debug_error("failed to set asm state to STOP\n"); + return ret; + } + } + + debug_fleave(); + + return ret; +} + +int +_mmplayer_set_message_callback(MMHandleType hplayer, MMMessageCallback callback, gpointer user_param) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + return __gst_set_message_callback(player, callback, user_param); +} + +int +_mmplayer_get_state(MMHandleType hplayer, int* state) // @ +{ + mm_player_t *player = (mm_player_t*)hplayer; + + return_val_if_fail(state, MM_ERROR_INVALID_ARGUMENT); + + *state = MMPLAYER_CURRENT_STATE(player); + + return MM_ERROR_NONE; +} + + +int +_mmplayer_set_volume(MMHandleType hplayer, MMPlayerVolumeType volume) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + GstElement* vol_element = NULL; + int i = 0; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + debug_log("volume [L]=%f:[R]=%f\n", + volume.level[MM_VOLUME_CHANNEL_LEFT], volume.level[MM_VOLUME_CHANNEL_RIGHT]); + + /* invalid factor range or not */ + for ( i = 0; i < MM_VOLUME_CHANNEL_NUM; i++ ) + { + if (volume.level[i] < MM_VOLUME_FACTOR_MIN || volume.level[i] > MM_VOLUME_FACTOR_MAX) { + debug_error("Invalid factor! (valid factor:0~1.0)\n"); + return MM_ERROR_INVALID_ARGUMENT; + } + } + + /* Save volume to handle. Currently the first array element will be saved. */ + player->sound.volume = volume.level[0]; + + /* check pipeline handle */ + if ( ! player->pipeline || ! player->pipeline->audiobin ) + { + debug_log("audiobin is not created yet\n"); + debug_log("but, current stored volume will be set when it's created.\n"); + + /* NOTE : stored volume will be used in create_audiobin + * returning MM_ERROR_NONE here makes application to able to + * set volume at anytime. + */ + return MM_ERROR_NONE; + } + + /* setting volume to volume element */ + vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst; + + if ( vol_element ) + { + debug_log("volume is set [%f]\n", player->sound.volume); + g_object_set(vol_element, "volume", player->sound.volume, NULL); + } + + debug_fleave(); + + return MM_ERROR_NONE; +} + + +int +_mmplayer_get_volume(MMHandleType hplayer, MMPlayerVolumeType* volume) +{ + mm_player_t* player = (mm_player_t*) hplayer; + int i = 0; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail( volume, MM_ERROR_INVALID_ARGUMENT ); + + /* returning stored volume */ + for (i = 0; i < MM_VOLUME_CHANNEL_NUM; i++) + volume->level[i] = player->sound.volume; + + debug_fleave(); + + return MM_ERROR_NONE; +} + + + +int +_mmplayer_set_mute(MMHandleType hplayer, int mute) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + GstElement* vol_element = NULL; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + debug_log("mute : %d\n", mute); + + /* mute value shoud 0 or 1 */ + if ( mute != 0 && mute != 1 ) + { + debug_error("bad mute value\n"); + + /* FIXIT : definitly, we need _BAD_PARAM error code */ + return MM_ERROR_INVALID_ARGUMENT; + } + + + /* just hold mute value if pipeline is not ready */ + if ( !player->pipeline || !player->pipeline->audiobin ) + { + debug_log("pipeline is not ready. holding mute value\n"); + player->sound.mute = mute; + return MM_ERROR_NONE; + } + + + vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst; + + /* NOTE : volume will only created when the bt is enabled */ + if ( vol_element ) + { + g_object_set(vol_element, "mute", mute, NULL); + } + else + { + debug_log("volume elemnet is not created. using volume in audiosink\n"); + } + + player->sound.mute = mute; + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_get_mute(MMHandleType hplayer, int* pmute) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + GstElement* vol_element = NULL; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( pmute, MM_ERROR_INVALID_ARGUMENT ); + + /* just hold mute value if pipeline is not ready */ + if ( !player->pipeline || !player->pipeline->audiobin ) + { + debug_log("pipeline is not ready. returning stored value\n"); + *pmute = player->sound.mute; + return MM_ERROR_NONE; + } + + + vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst; + + if ( vol_element ) + { + g_object_get(vol_element, "mute", pmute, NULL); + debug_log("mute=%d\n\n", *pmute); + } + else + { + *pmute = player->sound.mute; + } + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_set_videostream_cb(MMHandleType hplayer, mm_player_video_stream_callback callback, void *user_param) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( callback, MM_ERROR_INVALID_ARGUMENT ); + + player->video_stream_cb = callback; + player->video_stream_cb_user_param = user_param; + player->use_video_stream = TRUE; + debug_log("Stream cb Handle value is %p : %p\n", player, player->video_stream_cb); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_set_audiostream_cb(MMHandleType hplayer, mm_player_audio_stream_callback callback, void *user_param) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail(callback, MM_ERROR_INVALID_ARGUMENT); + + player->audio_stream_cb = callback; + player->audio_stream_cb_user_param = user_param; + debug_log("Audio Stream cb Handle value is %p : %p\n", player, player->audio_stream_cb); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_set_audiobuffer_cb(MMHandleType hplayer, mm_player_audio_stream_callback callback, void *user_param) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail(callback, MM_ERROR_INVALID_ARGUMENT); + + player->audio_buffer_cb = callback; + player->audio_buffer_cb_user_param = user_param; + debug_log("Audio Stream cb Handle value is %p : %p\n", player, player->audio_buffer_cb); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_set_buffer_need_data_cb(MMHandleType hplayer, mm_player_buffer_need_data_callback callback, void *user_param) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + + debug_fenter(); + + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail(callback, MM_ERROR_INVALID_ARGUMENT); + + player->need_data_cb = callback; + player->buffer_cb_user_param = user_param; + + debug_log("buffer need dataHandle value is %p : %p\n", player, player->need_data_cb); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_set_buffer_enough_data_cb(MMHandleType hplayer, mm_player_buffer_enough_data_callback callback, void *user_param) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + + debug_fenter(); + + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail(callback, MM_ERROR_INVALID_ARGUMENT); + + player->enough_data_cb = callback; + player->buffer_cb_user_param = user_param; + + debug_log("buffer enough data cb Handle value is %p : %p\n", player, player->enough_data_cb); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_set_buffer_seek_data_cb(MMHandleType hplayer, mm_player_buffer_seek_data_callback callback, void *user_param) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + + debug_fenter(); + + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail(callback, MM_ERROR_INVALID_ARGUMENT); + + player->seek_data_cb = callback; + player->buffer_cb_user_param = user_param; + + debug_log("buffer seek data cb Handle value is %p : %p\n", player, player->seek_data_cb); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_set_videoframe_render_error_cb(MMHandleType hplayer, mm_player_video_frame_render_error_callback callback, void *user_param) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( callback, MM_ERROR_INVALID_ARGUMENT ); + + player->video_frame_render_error_cb = callback; + player->video_frame_render_error_cb_user_param = user_param; + + debug_log("Video frame render error cb Handle value is %p : %p\n", player, player->video_frame_render_error_cb); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +__mmplayer_start_streaming_ext(mm_player_t *player) +{ + gint ret = MM_ERROR_NONE; + + debug_fenter(); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + if (MMPLAYER_IS_HTTP_PD(player)) + { + if ( !player->pd_downloader ) + { + ret = __mmplayer_realize_streaming_ext(player); + + if ( ret != MM_ERROR_NONE) + { + debug_error ("failed to realize streaming ext\n"); + return ret; + } + } + + if (player->pd_downloader && player->pd_mode == MM_PLAYER_PD_MODE_URI) + { + ret = _mmplayer_start_pd_downloader ((MMHandleType)player); + if ( !ret ) + { + debug_error ("ERROR while starting PD...\n"); + return MM_ERROR_PLAYER_NOT_INITIALIZED; + } + ret = MM_ERROR_NONE; + } + } + + debug_fleave(); + return ret; +} + +int +_mmplayer_start(MMHandleType hplayer) // @ +{ + mm_player_t* player = (mm_player_t*) hplayer; + gint ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_START ); + + ret = _mmplayer_asm_set_state(hplayer, ASM_STATE_PLAYING); + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to set asm state to PLAYING\n"); + return ret; + } + + /* NOTE : we should check and create pipeline again if not created as we destroy + * whole pipeline when stopping in streamming playback + */ + if ( ! player->pipeline ) + { + ret = __gst_realize( player ); + if ( MM_ERROR_NONE != ret ) + { + debug_error("failed to realize before starting. only in streamming\n"); + return ret; + } + } + + ret = __mmplayer_start_streaming_ext(player); + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to start streaming ext \n"); + } + + /* start pipeline */ + ret = __gst_start( player ); + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to start player.\n"); + } + + debug_fleave(); + + return ret; +} + +/* NOTE: post "not supported codec message" to application + * when one codec is not found during AUTOPLUGGING in MSL. + * So, it's separated with error of __mmplayer_gst_callback(). + * And, if any codec is not found, don't send message here. + * Because GST_ERROR_MESSAGE is posted by other plugin internally. + */ +int +__mmplayer_post_missed_plugin(mm_player_t* player) +{ + MMMessageParamType msg_param; + memset (&msg_param, 0, sizeof(MMMessageParamType)); + gboolean post_msg_direct = FALSE; + + debug_fenter(); + + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + + debug_log("not_supported_codec = 0x%02x, can_support_codec = 0x%02x\n", + player->not_supported_codec, player->can_support_codec); + + if( player->not_found_demuxer ) + { + msg_param.code = MM_ERROR_PLAYER_CODEC_NOT_FOUND; + msg_param.data = g_strdup_printf("%s", player->unlinked_demuxer_mime); + + MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); + MMPLAYER_FREEIF(msg_param.data); + + return MM_ERROR_NONE; + } + + if (player->not_supported_codec) + { + if ( player->can_support_codec ) // There is one codec to play + { + post_msg_direct = TRUE; + } + else + { + if ( player->pipeline->audiobin ) // Some content has only PCM data in container. + post_msg_direct = TRUE; + } + + if ( post_msg_direct ) + { + MMMessageParamType msg_param; + memset (&msg_param, 0, sizeof(MMMessageParamType)); + + if ( player->not_supported_codec == MISSING_PLUGIN_AUDIO ) + { + debug_warning("not found AUDIO codec, posting error code to application.\n"); + + msg_param.code = MM_ERROR_PLAYER_AUDIO_CODEC_NOT_FOUND; + msg_param.data = g_strdup_printf("%s", player->unlinked_audio_mime); + } + else if ( player->not_supported_codec == MISSING_PLUGIN_VIDEO ) + { + debug_warning("not found VIDEO codec, posting error code to application.\n"); + + msg_param.code = MM_ERROR_PLAYER_VIDEO_CODEC_NOT_FOUND; + msg_param.data = g_strdup_printf("%s", player->unlinked_video_mime); + } + + MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); + + MMPLAYER_FREEIF(msg_param.data); + + return MM_ERROR_NONE; + } + else // no any supported codec case + { + debug_warning("not found any codec, posting error code to application.\n"); + + if ( player->not_supported_codec == MISSING_PLUGIN_AUDIO ) + { + msg_param.code = MM_ERROR_PLAYER_AUDIO_CODEC_NOT_FOUND; + msg_param.data = g_strdup_printf("%s", player->unlinked_audio_mime); + } + else + { + msg_param.code = MM_ERROR_PLAYER_CODEC_NOT_FOUND; + msg_param.data = g_strdup_printf("%s, %s", player->unlinked_video_mime, player->unlinked_audio_mime); + } + + MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); + + MMPLAYER_FREEIF(msg_param.data); + } + } + + debug_fleave(); + + return MM_ERROR_NONE; +} + +/* NOTE : it should be able to call 'stop' anytime*/ +int +_mmplayer_stop(MMHandleType hplayer) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_STOP ); + + /* NOTE : application should not wait for EOS after calling STOP */ + __mmplayer_cancel_delayed_eos( player ); + + __mmplayer_unrealize_streaming_ext(player); + + /* stop pipeline */ + ret = __gst_stop( player ); + + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to stop player.\n"); + } + + debug_fleave(); + + return ret; +} + +int +_mmplayer_pause(MMHandleType hplayer) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + GstFormat fmt = GST_FORMAT_TIME; + gint64 pos_msec = 0; + gboolean async = FALSE; + gint ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_PAUSE ); + + switch (MMPLAYER_CURRENT_STATE(player)) + { + case MM_PLAYER_STATE_READY: + { + /* check prepare async or not. + * In the case of streaming playback, it's recommned to avoid blocking wait. + */ + mm_attrs_get_int_by_name(player->attrs, "profile_prepare_async", &async); + debug_log("prepare mode : %s", (async ? "async" : "sync")); + } + break; + + case MM_PLAYER_STATE_PLAYING: + { + /* NOTE : store current point to overcome some bad operation + * ( returning zero when getting current position in paused state) of some + * elements + */ + ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &pos_msec); + if ( ! ret ) + debug_warning("getting current position failed in paused\n"); + + player->last_position = pos_msec; + } + break; + } + + /* pause pipeline */ + ret = __gst_pause( player, async ); + + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to pause player.\n"); + } + + debug_fleave(); + + return ret; +} + +int +_mmplayer_resume(MMHandleType hplayer) +{ + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + ret = _mmplayer_asm_set_state(hplayer, ASM_STATE_PLAYING); + if ( ret ) + { + debug_error("failed to set asm state to PLAYING\n"); + return ret; + } + + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_RESUME ); + + /* resume pipeline */ + ret = __gst_resume( player, FALSE ); + + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to resume player.\n"); + } + + + debug_fleave(); + + return ret; +} + +int +__mmplayer_set_play_count(mm_player_t* player, gint count) +{ + MMHandleType attrs = 0; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("fail to get attributes.\n"); + return MM_ERROR_PLAYER_INTERNAL; + } + + mm_attrs_set_int_by_name(attrs, "profile_play_count", count); + if ( mmf_attrs_commit ( attrs ) ) /* return -1 if error */ + debug_error("failed to commit\n"); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_activate_section_repeat(MMHandleType hplayer, unsigned long start, unsigned long end) +{ + mm_player_t* player = (mm_player_t*)hplayer; + gint64 start_pos = 0; + gint64 end_pos = 0; + gint infinity = -1; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( end <= GST_TIME_AS_MSECONDS(player->duration), MM_ERROR_INVALID_ARGUMENT ); + + player->section_repeat = TRUE; + player->section_repeat_start = start; + player->section_repeat_end = end; + + start_pos = player->section_repeat_start * G_GINT64_CONSTANT(1000000); + end_pos = player->section_repeat_end * G_GINT64_CONSTANT(1000000); + + __mmplayer_set_play_count( player, infinity ); + + if ( (!__gst_seek( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, + 1.0, + GST_FORMAT_TIME, + ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ), + GST_SEEK_TYPE_SET, start_pos, + GST_SEEK_TYPE_SET, end_pos))) + { + debug_error("failed to activate section repeat\n"); + + return MM_ERROR_PLAYER_SEEK; + } + + debug_log("succeeded to set section repeat from %d to %d\n", + player->section_repeat_start, player->section_repeat_end); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +static int +__mmplayer_set_pcm_extraction(mm_player_t* player) +{ + guint64 start_nsec = 0; + guint64 end_nsec = 0; + guint64 dur_nsec = 0; + guint64 dur_msec = 0; + GstFormat fmt = GST_FORMAT_TIME; + int required_start = 0; + int required_end = 0; + int ret = 0; + + debug_fenter(); + + return_val_if_fail( player, FALSE ); + + mm_attrs_multiple_get(player->attrs, + NULL, + "pcm_extraction_start_msec", &required_start, + "pcm_extraction_end_msec", &required_end, + NULL); + + debug_log("pcm extraction required position is from [%d] to [%d] (msec)\n", required_start, required_end); + + if (required_start == 0 && required_end == 0) + { + debug_log("extracting entire stream"); + return MM_ERROR_NONE; + } + else if (required_start < 0 || required_start > required_end || required_end < 0 ) + { + debug_log("invalid range for pcm extraction"); + return MM_ERROR_INVALID_ARGUMENT; + } + + /* get duration */ + ret = gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &dur_nsec); + if ( !ret ) + { + debug_error("failed to get duration"); + return MM_ERROR_PLAYER_INTERNAL; + } + dur_msec = GST_TIME_AS_MSECONDS(dur_nsec); + + if (dur_msec < required_end) // FIXME + { + debug_log("invalid end pos for pcm extraction"); + return MM_ERROR_INVALID_ARGUMENT; + } + + start_nsec = required_start * G_GINT64_CONSTANT(1000000); + end_nsec = required_end * G_GINT64_CONSTANT(1000000); + + if ( (!__gst_seek( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, + 1.0, + GST_FORMAT_TIME, + ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ), + GST_SEEK_TYPE_SET, start_nsec, + GST_SEEK_TYPE_SET, end_nsec))) + { + debug_error("failed to seek for pcm extraction\n"); + + return MM_ERROR_PLAYER_SEEK; + } + + debug_log("succeeded to set up segment extraction from [%llu] to [%llu] (nsec)\n", start_nsec, end_nsec); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_deactivate_section_repeat(MMHandleType hplayer) +{ + mm_player_t* player = (mm_player_t*)hplayer; + gint64 cur_pos = 0; + GstFormat fmt = GST_FORMAT_TIME; + gint onetime = 1; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + player->section_repeat = FALSE; + + __mmplayer_set_play_count( player, onetime ); + + gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &cur_pos); + + if ( (!__gst_seek( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, + 1.0, + GST_FORMAT_TIME, + ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ), + GST_SEEK_TYPE_SET, cur_pos, + GST_SEEK_TYPE_SET, player->duration ))) + { + debug_error("failed to deactivate section repeat\n"); + + return MM_ERROR_PLAYER_SEEK; + } + + debug_fenter(); + + return MM_ERROR_NONE; +} + +int +_mmplayer_set_playspeed(MMHandleType hplayer, gdouble rate) +{ + mm_player_t* player = (mm_player_t*)hplayer; + signed long long pos_msec = 0; + int ret = MM_ERROR_NONE; + int mute = FALSE; + GstFormat format =GST_FORMAT_TIME; + MMPlayerStateType current_state = MM_PLAYER_STATE_NONE; + debug_fenter(); + + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( !MMPLAYER_IS_STREAMING(player), MM_ERROR_NOT_SUPPORT_API ); + + /* The sound of video is not supported under 0.0 and over 2.0. */ + if(rate >= TRICK_PLAY_MUTE_THRESHOLD_MAX || rate < TRICK_PLAY_MUTE_THRESHOLD_MIN) + { + if (player->can_support_codec & FOUND_PLUGIN_VIDEO) + mute = TRUE; + } + _mmplayer_set_mute(hplayer, mute); + + if (player->playback_rate == rate) + return MM_ERROR_NONE; + + /* If the position is reached at start potion during fast backward, EOS is posted. + * So, This EOS have to be classified with it which is posted at reaching the end of stream. + * */ + player->playback_rate = rate; + + current_state = MMPLAYER_CURRENT_STATE(player); + + if ( current_state != MM_PLAYER_STATE_PAUSED ) + ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &format, &pos_msec); + + debug_log ("pos_msec = %"GST_TIME_FORMAT" and ret = %d and state = %d", GST_TIME_ARGS (pos_msec), ret, current_state); + + if ( ( current_state == MM_PLAYER_STATE_PAUSED ) + || ( ! ret )) + //|| ( player->last_position != 0 && pos_msec == 0 ) ) + { + debug_warning("returning last point : %lld\n", player->last_position ); + pos_msec = player->last_position; + } + + if ((!gst_element_seek (player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, + rate, + GST_FORMAT_TIME, + ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ), + //( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_KEY_UNIT), + GST_SEEK_TYPE_SET, pos_msec, + //GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE, + GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))) + { + debug_error("failed to set speed playback\n"); + return MM_ERROR_PLAYER_SEEK; + } + + debug_log("succeeded to set speed playback as %fl\n", rate); + + debug_fleave(); + + return MM_ERROR_NONE;; +} + +int +_mmplayer_set_position(MMHandleType hplayer, int format, int position) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + ret = __gst_set_position ( player, format, (unsigned long)position, FALSE ); + + debug_fleave(); + + return ret; +} + +int +_mmplayer_get_position(MMHandleType hplayer, int format, unsigned long *position) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + ret = __gst_get_position ( player, format, position ); + + return ret; +} + +int +_mmplayer_get_buffer_position(MMHandleType hplayer, int format, unsigned long* start_pos, unsigned long* stop_pos) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + ret = __gst_get_buffer_position ( player, format, start_pos, stop_pos ); + + return ret; +} + +int +_mmplayer_adjust_subtitle_postion(MMHandleType hplayer, int format, int position) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + ret = __gst_adjust_subtitle_position(player, format, position); + + debug_fleave(); + + return ret; +} + +static gboolean +__mmplayer_is_midi_type( gchar* str_caps) +{ + if ( ( g_strrstr(str_caps, "audio/midi") ) || + ( g_strrstr(str_caps, "application/x-gst_ff-mmf") ) || + ( g_strrstr(str_caps, "application/x-smaf") ) || + ( g_strrstr(str_caps, "audio/x-imelody") ) || + ( g_strrstr(str_caps, "audio/mobile-xmf") ) || + ( g_strrstr(str_caps, "audio/xmf") ) || + ( g_strrstr(str_caps, "audio/mxmf") ) ) + { + debug_log("midi\n"); + + return TRUE; + } + + debug_log("not midi.\n"); + + return FALSE; +} + +static gboolean +__mmplayer_is_amr_type (gchar *str_caps) +{ + if ((g_strrstr(str_caps, "AMR")) || + (g_strrstr(str_caps, "amr"))) + { + return TRUE; + } + return FALSE; +} + +static gboolean +__mmplayer_is_only_mp3_type (gchar *str_caps) +{ + if (g_strrstr(str_caps, "application/x-id3") || + (g_strrstr(str_caps, "audio/mpeg") && g_strrstr(str_caps, "mpegversion=(int)1"))) + { + return TRUE; + } + return FALSE; +} + +static void +__mmplayer_typefind_have_type( GstElement *tf, guint probability, // @ +GstCaps *caps, gpointer data) +{ + mm_player_t* player = (mm_player_t*)data; + GstPad* pad = NULL; + + debug_fenter(); + + return_if_fail( player && tf && caps ); + + /* store type string */ + MMPLAYER_FREEIF(player->type); + player->type = gst_caps_to_string(caps); + if (player->type) + debug_log("meida type %s found, probability %d%% / %d\n", player->type, probability, gst_caps_get_size(caps)); + + /* midi type should be stored because it will be used to set audio gain in avsysaudiosink */ + if ( __mmplayer_is_midi_type(player->type)) + { + player->profile.play_mode = MM_PLAYER_MODE_MIDI; + } + else if (__mmplayer_is_amr_type(player->type)) + { + player->bypass_audio_effect = FALSE; + if ( (PLAYER_INI()->use_audio_effect_preset || PLAYER_INI()->use_audio_effect_custom) ) + { + if ( player->audio_effect_info.effect_type == MM_AUDIO_EFFECT_TYPE_PRESET ) + { + if (!_mmplayer_audio_effect_preset_apply(player, player->audio_effect_info.preset)) + { + debug_msg("apply audio effect(preset:%d) setting success\n",player->audio_effect_info.preset); + } + } + else if ( player->audio_effect_info.effect_type == MM_AUDIO_EFFECT_TYPE_CUSTOM ) + { + if (!_mmplayer_audio_effect_custom_apply(player)) + { + debug_msg("apply audio effect(custom) setting success\n"); + } + } + } + } + else if ( g_strrstr(player->type, "application/x-hls")) + { + /* If it can't know exact type when it parses uri because of redirection case, + * it will be fixed by typefinder here. + */ + player->profile.uri_type = MM_PLAYER_URI_TYPE_HLS; + } + + pad = gst_element_get_static_pad(tf, "src"); + if ( !pad ) + { + debug_error("fail to get typefind src pad.\n"); + return; + } + + + /* try to plug */ + if ( ! __mmplayer_try_to_plug( player, pad, caps ) ) + { + debug_error("failed to autoplug for type : %s\n", player->type); + + if ( ( PLAYER_INI()->async_start ) && + ( player->posted_msg == FALSE ) ) + { + __mmplayer_post_missed_plugin( player ); + } + + goto DONE; + } + + /* finish autopluging if no dynamic pad waiting */ + if( ( ! player->have_dynamic_pad) && ( ! player->has_many_types) ) + { + if ( ! MMPLAYER_IS_RTSP_STREAMING( player ) ) + { + __mmplayer_pipeline_complete( NULL, (gpointer)player ); + } + } + +DONE: + gst_object_unref( GST_OBJECT(pad) ); + + debug_fleave(); + + return; +} + +static gboolean +__mmplayer_warm_up_video_codec( mm_player_t* player, GstElementFactory *factory) +{ + GstElement *element; + GstStateChangeReturn ret; + gboolean usable = TRUE; + + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( factory, MM_ERROR_COMMON_INVALID_ARGUMENT ); + + element = gst_element_factory_create (factory, NULL); + + ret = gst_element_set_state (element, GST_STATE_READY); + + if (ret != GST_STATE_CHANGE_SUCCESS) + { + debug_error ("resource conflict so, %s unusable\n", GST_PLUGIN_FEATURE_NAME (factory)); + usable = FALSE; + } + + gst_element_set_state (element, GST_STATE_NULL); + gst_object_unref (element); + + return usable; +} + +/* it will return first created element */ +static gboolean +__mmplayer_try_to_plug(mm_player_t* player, GstPad *pad, const GstCaps *caps) // @ +{ + MMPlayerGstElement* mainbin = NULL; + const char* mime = NULL; + const GList* item = NULL; + const gchar* klass = NULL; + GstCaps* res = NULL; + gboolean skip = FALSE; + GstPad* queue_pad = NULL; + GstElement* queue = NULL; + GstElement *element = NULL; + + debug_fenter(); + + return_val_if_fail( player && + player->pipeline && + player->pipeline->mainbin, + FALSE ); + + + mainbin = player->pipeline->mainbin; + + mime = gst_structure_get_name(gst_caps_get_structure(caps, 0)); + + /* return if we got raw output */ + if(g_str_has_prefix(mime, "video/x-raw") || g_str_has_prefix(mime, "audio/x-raw") || g_str_has_prefix(mime, "text/plain") ||g_str_has_prefix(mime, "text/x-pango-markup")) + { + + element = (GstElement*)gst_pad_get_parent(pad); + + +/* NOTE : When no decoder has added during autoplugging. like a simple wave playback. + * No queue will be added. I think it can caused breaking sound when playing raw audio + * frames but there's no different. Decodebin also doesn't add with those wav fils. + * Anyway, currentely raw-queue seems not necessary. + */ +#if 1 + /* NOTE : check if previously linked element is demuxer/depayloader/parse means no decoder + * has linked. if so, we need to add queue for quality of output. note that + * decodebin also has same problem. + */ + + klass = gst_element_factory_get_klass( gst_element_get_factory(element) ); + + /* add queue if needed */ + if( (g_strrstr(klass, "Demux") || + g_strrstr(klass, "Depayloader") || + g_strrstr(klass, "Parse")) && !g_str_has_prefix(mime, "text")) + { + debug_log("adding raw queue\n"); + + queue = gst_element_factory_make("queue", NULL); + if ( ! queue ) + { + debug_warning("failed to create queue\n"); + goto ERROR; + } + + /* warmup */ + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(queue, GST_STATE_READY) ) + { + debug_warning("failed to set state READY to queue\n"); + goto ERROR; + } + + /* add to pipeline */ + if ( ! gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), queue) ) + { + debug_warning("failed to add queue\n"); + goto ERROR; + } + + /* link queue */ + queue_pad = gst_element_get_static_pad(queue, "sink"); + + if ( GST_PAD_LINK_OK != gst_pad_link(pad, queue_pad) ) + { + debug_warning("failed to link queue\n"); + goto ERROR; + } + gst_object_unref ( GST_OBJECT(queue_pad) ); + queue_pad = NULL; + + /* running */ + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(queue, GST_STATE_PAUSED) ) + { + debug_warning("failed to set state READY to queue\n"); + goto ERROR; + } + + /* replace given pad to queue:src */ + pad = gst_element_get_static_pad(queue, "src"); + if ( ! pad ) + { + debug_warning("failed to get pad from queue\n"); + goto ERROR; + } + } +#endif + /* check if player can do start continually */ + MMPLAYER_CHECK_CMD_IF_EXIT(player); + + if(__mmplayer_link_sink(player,pad)) + __mmplayer_gst_decode_callback(element, pad, FALSE, player); + + gst_object_unref( GST_OBJECT(element)); + element = NULL; + + return TRUE; + } + + item = player->factories; + for(; item != NULL ; item = item->next) + { + + GstElementFactory *factory = GST_ELEMENT_FACTORY(item->data); + const GList *pads; + gint idx = 0; + + skip = FALSE; + + /* filtering exclude keyword */ + for ( idx = 0; PLAYER_INI()->exclude_element_keyword[idx][0] != '\0'; idx++ ) + { + if ( g_strrstr(GST_PLUGIN_FEATURE_NAME (factory), + PLAYER_INI()->exclude_element_keyword[idx] ) ) + { + debug_warning("skipping [%s] by exculde keyword [%s]\n", + GST_PLUGIN_FEATURE_NAME (factory), + PLAYER_INI()->exclude_element_keyword[idx] ); + + skip = TRUE; + break; + } + } + + if ( skip ) continue; + + + /* check factory class for filtering */ + klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(factory)); + + /* NOTE : msl don't need to use image plugins. + * So, those plugins should be skipped for error handling. + */ + if ( g_strrstr(klass, "Codec/Decoder/Image") ) + { + debug_log("player doesn't need [%s] so, skipping it\n", + GST_PLUGIN_FEATURE_NAME (factory) ); + + continue; + } + + + /* check pad compatability */ + for(pads = gst_element_factory_get_static_pad_templates(factory); + pads != NULL; pads=pads->next) + { + GstStaticPadTemplate *temp1 = pads->data; + GstCaps* static_caps = NULL; + + if( temp1->direction != GST_PAD_SINK || + temp1->presence != GST_PAD_ALWAYS) + continue; + + + if ( GST_IS_CAPS( &temp1->static_caps.caps) ) + { + /* using existing caps */ + static_caps = gst_caps_ref( &temp1->static_caps.caps ); + } + else + { + /* create one */ + static_caps = gst_caps_from_string ( temp1->static_caps.string ); + } + + res = gst_caps_intersect(caps, static_caps); + + gst_caps_unref( static_caps ); + static_caps = NULL; + + if( res && !gst_caps_is_empty(res) ) + { + GstElement *new_element; + GList *elements = player->parsers; + char *name_template = g_strdup(temp1->name_template); + gchar *name_to_plug = GST_PLUGIN_FEATURE_NAME(factory); + + gst_caps_unref(res); + + debug_log("found %s to plug\n", name_to_plug); + + new_element = gst_element_factory_create(GST_ELEMENT_FACTORY(factory), NULL); + if ( ! new_element ) + { + debug_error("failed to create element [%s]. continue with next.\n", + GST_PLUGIN_FEATURE_NAME (factory)); + + MMPLAYER_FREEIF(name_template); + + continue; + } + + /* check and skip it if it was already used. Otherwise, it can be an infinite loop + * because parser can accept its own output as input. + */ + if (g_strrstr(klass, "Parser")) + { + gchar *selected = NULL; + + for ( ; elements; elements = g_list_next(elements)) + { + gchar *element_name = elements->data; + + if (g_strrstr(element_name, name_to_plug)) + { + debug_log("but, %s already linked, so skipping it\n", name_to_plug); + skip = TRUE; + } + } + + if (skip) continue; + + selected = g_strdup(name_to_plug); + + player->parsers = g_list_append(player->parsers, selected); + } + + /* store specific handles for futher control */ + if(g_strrstr(klass, "Demux") || g_strrstr(klass, "Parse")) + { + /* FIXIT : first value will be overwritten if there's more + * than 1 demuxer/parser + */ + debug_log("plugged element is demuxer. take it\n"); + mainbin[MMPLAYER_M_DEMUX].id = MMPLAYER_M_DEMUX; + mainbin[MMPLAYER_M_DEMUX].gst = new_element; + } + else if(g_strrstr(klass, "Decoder") && __mmplayer_link_decoder(player,pad)) + { + if(mainbin[MMPLAYER_M_DEC1].gst == NULL) + { + debug_log("plugged element is decoder. take it[MMPLAYER_M_DEC1]\n"); + mainbin[MMPLAYER_M_DEC1].id = MMPLAYER_M_DEC1; + mainbin[MMPLAYER_M_DEC1].gst = new_element; + } + else if(mainbin[MMPLAYER_M_DEC2].gst == NULL) + { + debug_log("plugged element is decoder. take it[MMPLAYER_M_DEC2]\n"); + mainbin[MMPLAYER_M_DEC2].id = MMPLAYER_M_DEC2; + mainbin[MMPLAYER_M_DEC2].gst = new_element; + } + + /* NOTE : IF one codec is found, add it to supported_codec and remove from + * missing plugin. Both of them are used to check what's supported codec + * before returning result of play start. And, missing plugin should be + * updated here for multi track files. + */ + if(g_str_has_prefix(mime, "video")) + { + GstPad *src_pad = NULL; + GstPadTemplate *pad_templ = NULL; + GstCaps *caps = NULL; + gchar *caps_type = NULL; + + debug_log("found VIDEO decoder\n"); + player->not_supported_codec &= MISSING_PLUGIN_AUDIO; + player->can_support_codec |= FOUND_PLUGIN_VIDEO; + + src_pad = gst_element_get_static_pad (new_element, "src"); + pad_templ = gst_pad_get_pad_template (src_pad); + caps = GST_PAD_TEMPLATE_CAPS(pad_templ); + + caps_type = gst_caps_to_string(caps); + + if ( g_strrstr( caps_type, "ST12") ) + player->is_nv12_tiled = TRUE; + + /* clean */ + MMPLAYER_FREEIF( caps_type ); + gst_object_unref (src_pad); + } + else if (g_str_has_prefix(mime, "audio")) + { + debug_log("found AUDIO decoder\n"); + player->not_supported_codec &= MISSING_PLUGIN_VIDEO; + player->can_support_codec |= FOUND_PLUGIN_AUDIO; + } + } + if ( ! __mmplayer_close_link(player, pad, new_element, + name_template,gst_element_factory_get_static_pad_templates(factory)) ) + { + if (player->keep_detecting_vcodec) + continue; + + /* Link is failed even though a supportable codec is found. */ + __mmplayer_check_not_supported_codec(player, (gchar *)mime); + + MMPLAYER_FREEIF(name_template); + debug_error("failed to call _close_link\n"); + return FALSE; + } + + MMPLAYER_FREEIF(name_template); + return TRUE; + } + + gst_caps_unref(res); + + break; + } + } + + /* There is no any found codec. */ + __mmplayer_check_not_supported_codec(player,(gchar *)mime); + + debug_error("failed to autoplug\n"); + + debug_fleave(); + + return FALSE; + + +ERROR: + + /* release */ + if ( queue ) + gst_object_unref( queue ); + + + if ( queue_pad ) + gst_object_unref( queue_pad ); + + if ( element ) + gst_object_unref ( element ); + + return FALSE; +} + + +static +int __mmplayer_check_not_supported_codec(mm_player_t* player, gchar* mime) +{ + debug_fenter(); + + return_val_if_fail(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail ( mime, MM_ERROR_INVALID_ARGUMENT ); + + debug_log("mimetype to check: %s\n", mime ); + + /* add missing plugin */ + /* NOTE : msl should check missing plugin for image mime type. + * Some motion jpeg clips can have playable audio track. + * So, msl have to play audio after displaying popup written video format not supported. + */ + if ( !( player->pipeline->mainbin[MMPLAYER_M_DEMUX].gst ) ) + { + if ( !( player->can_support_codec | player->videodec_linked | player->audiodec_linked ) ) + { + debug_log("not found demuxer\n"); + player->not_found_demuxer = TRUE; + player->unlinked_demuxer_mime = g_strdup_printf ( "%s", mime ); + + goto DONE; + } + } + + if( ( g_str_has_prefix(mime, "video") ) ||( g_str_has_prefix(mime, "image") ) ) + { + debug_log("can support codec=%d, vdec_linked=%d, adec_linked=%d\n", + player->can_support_codec, player->videodec_linked, player->audiodec_linked); + + /* check that clip have multi tracks or not */ + if ( ( player->can_support_codec & FOUND_PLUGIN_VIDEO ) && ( player->videodec_linked ) ) + { + debug_log("video plugin is already linked\n"); + } + else + { + debug_warning("add VIDEO to missing plugin\n"); + player->not_supported_codec |= MISSING_PLUGIN_VIDEO; + } + } + else if ( g_str_has_prefix(mime, "audio") ) + { + if ( ( player->can_support_codec & FOUND_PLUGIN_AUDIO ) && ( player->audiodec_linked ) ) + { + debug_log("audio plugin is already linked\n"); + } + else + { + debug_warning("add AUDIO to missing plugin\n"); + player->not_supported_codec |= MISSING_PLUGIN_AUDIO; + } + } + +DONE: + debug_fleave(); + + return MM_ERROR_NONE; +} + + +static void __mmplayer_pipeline_complete(GstElement *decodebin, gpointer data) // @ +{ + mm_player_t* player = (mm_player_t*)data; + + debug_fenter(); + + return_if_fail( player ); + + /* remove fakesink */ + if ( ! __mmplayer_gst_remove_fakesink( player, + &player->pipeline->mainbin[MMPLAYER_M_SRC_FAKESINK]) ) + { + /* NOTE : __mmplayer_pipeline_complete() can be called several time. because + * signaling mechanism ( pad-added, no-more-pad, new-decoded-pad ) from various + * source element are not same. To overcome this situation, this function will called + * several places and several times. Therefore, this is not an error case. + */ + return; + } + debug_log("pipeline has completely constructed\n"); + + player->pipeline_is_constructed = TRUE; + + if ( ( PLAYER_INI()->async_start ) && + ( player->posted_msg == FALSE ) && + ( player->cmd >= MMPLAYER_COMMAND_START )) + { + __mmplayer_post_missed_plugin( player ); + } + + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-complete" ); +} + +static gboolean __mmplayer_configure_audio_callback(mm_player_t* player) +{ + debug_fenter(); + + return_val_if_fail ( player, FALSE ); + + + if ( MMPLAYER_IS_STREAMING(player) ) + return FALSE; + + /* This callback can be set to music player only. */ + if((player->can_support_codec & 0x02) == FOUND_PLUGIN_VIDEO) + { + debug_warning("audio callback is not supported for video"); + return FALSE; + } + + if (player->audio_stream_cb) + { + { + GstPad *pad = NULL; + + pad = gst_element_get_static_pad (player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "sink"); + + if ( !pad ) + { + debug_error("failed to get sink pad from audiosink to probe data\n"); + return FALSE; + } + + player->audio_cb_probe_id = gst_pad_add_buffer_probe (pad, + G_CALLBACK (__mmplayer_audio_stream_probe), player); + + gst_object_unref (pad); + + pad = NULL; + } + } + else + { + debug_error("There is no audio callback to configure.\n"); + return FALSE; + } + + debug_fleave(); + + return TRUE; +} + +static void +__mmplayer_init_factories(mm_player_t* player) // @ +{ + debug_fenter(); + + return_if_fail ( player ); + + player->factories = gst_registry_feature_filter(gst_registry_get_default(), + (GstPluginFeatureFilter)__mmplayer_feature_filter, FALSE, NULL); + + player->factories = g_list_sort(player->factories, (GCompareFunc)util_factory_rank_compare); + + debug_fleave(); +} + +static void +__mmplayer_release_factories(mm_player_t* player) // @ +{ + debug_fenter(); + + return_if_fail ( player ); + + if (player->factories) + { + gst_plugin_feature_list_free (player->factories); + player->factories = NULL; + } + + debug_fleave(); +} + +static void +__mmplayer_release_misc(mm_player_t* player) +{ + int i; + debug_fenter(); + + return_if_fail ( player ); + + player->use_video_stream = FALSE; + player->video_stream_cb = NULL; + player->video_stream_cb_user_param = NULL; + + player->audio_stream_cb = NULL; + player->audio_stream_cb_user_param = NULL; + + player->audio_buffer_cb = NULL; + player->audio_buffer_cb_user_param = NULL; + + player->sent_bos = FALSE; + player->playback_rate = DEFAULT_PLAYBACK_RATE; + + player->doing_seek = FALSE; + + player->streamer = NULL; + player->updated_bitrate_count = 0; + player->total_bitrate = 0; + player->updated_maximum_bitrate_count = 0; + player->total_maximum_bitrate = 0; + + player->not_found_demuxer = 0; + + player->last_position = 0; + player->duration = 0; + player->http_content_size = 0; + player->not_supported_codec = MISSING_PLUGIN_NONE; + player->can_support_codec = FOUND_PLUGIN_NONE; + player->need_update_content_dur = FALSE; + player->pending_seek.is_pending = FALSE; + player->pending_seek.format = MM_PLAYER_POS_FORMAT_TIME; + player->pending_seek.pos = 0; + player->posted_msg = FALSE; + player->has_many_types = FALSE; + + for (i = 0; i < MM_PLAYER_STREAM_COUNT_MAX; i++) + { + player->bitrate[i] = 0; + player->maximum_bitrate[i] = 0; + } + + /* clean found parsers */ + if (player->parsers) + { + g_list_free(player->parsers); + player->parsers = NULL; + } + + MMPLAYER_FREEIF(player->album_art); + + /* free memory related to audio effect */ + if(player->audio_effect_info.custom_ext_level_for_plugin) + { + free(player->audio_effect_info.custom_ext_level_for_plugin); + } + + debug_fleave(); +} + +static GstElement *__mmplayer_element_create_and_link(mm_player_t *player, GstPad* pad, const char* name) +{ + GstElement *element = NULL; + GstPad *sinkpad; + + debug_log("creating %s to plug\n", name); + + element = gst_element_factory_make(name, NULL); + if ( ! element ) + { + debug_error("failed to create queue\n"); + return NULL; + } + + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(element, GST_STATE_READY) ) + { + debug_error("failed to set state READY to %s\n", name); + return NULL; + } + + if ( ! gst_bin_add(GST_BIN(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst), element)) + { + debug_error("failed to add %s\n", name); + return NULL; + } + + sinkpad = gst_element_get_static_pad(element, "sink"); + + if ( GST_PAD_LINK_OK != gst_pad_link(pad, sinkpad) ) + { + debug_error("failed to link %s\n", name); + gst_object_unref (sinkpad); + + return NULL; + } + + debug_log("linked %s to pipeline successfully\n", name); + + gst_object_unref (sinkpad); + + return element; +} + +static gboolean +__mmplayer_close_link(mm_player_t* player, GstPad *srcpad, GstElement *sinkelement, +const char *padname, const GList *templlist) +{ + GstPad *pad = NULL; + gboolean has_dynamic_pads = FALSE; + gboolean has_many_types = FALSE; + const char *klass = NULL; + GstStaticPadTemplate *padtemplate = NULL; + GstElementFactory *factory = NULL; + GstElement* queue = NULL; + GstElement* parser = NULL; + GstPad *pssrcpad = NULL; + GstPad *qsrcpad = NULL, *qsinkpad = NULL; + MMPlayerGstElement *mainbin = NULL; + GstStructure* str = NULL; + GstCaps* srccaps = NULL; + GstState warmup = GST_STATE_READY; + gboolean isvideo_decoder = FALSE; + guint q_max_size_time = 0; + + debug_fenter(); + + return_val_if_fail ( player && + player->pipeline && + player->pipeline->mainbin, + FALSE ); + + mainbin = player->pipeline->mainbin; + + debug_log("plugging pad %s:%s to newly create %s:%s\n", + GST_ELEMENT_NAME( GST_PAD_PARENT ( srcpad ) ), + GST_PAD_NAME( srcpad ), + GST_ELEMENT_NAME( sinkelement ), + padname); + + factory = gst_element_get_factory(sinkelement); + klass = gst_element_factory_get_klass(factory); + + /* check if player can do start continually */ + MMPLAYER_CHECK_CMD_IF_EXIT(player); + + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(sinkelement, warmup) ) + { + if (isvideo_decoder) + player->keep_detecting_vcodec = TRUE; + + debug_error("failed to set %d state to %s\n", warmup, GST_ELEMENT_NAME( sinkelement )); + goto ERROR; + } + + /* add to pipeline */ + if ( ! gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), sinkelement) ) + { + debug_error("failed to add %s to mainbin\n", GST_ELEMENT_NAME( sinkelement )); + goto ERROR; + } + + debug_log("element klass : %s\n", klass); + + /* added to support multi track files */ + /* only decoder case and any of the video/audio still need to link*/ + if(g_strrstr(klass, "Decoder") && __mmplayer_link_decoder(player,srcpad)) + { + gchar *name = NULL; + + name = g_strdup(GST_ELEMENT_NAME( GST_PAD_PARENT ( srcpad ))); + + if (g_strrstr(name, "mpegtsdemux")) + { + gchar *demux_caps = NULL; + gchar *parser_name = NULL; + GstCaps *dcaps = NULL; + + dcaps = gst_pad_get_caps(srcpad); + demux_caps = gst_caps_to_string(dcaps); + + if (g_strrstr(demux_caps, "video/x-h264")) + { + parser_name = g_strdup("h264parse"); + } + else if (g_strrstr(demux_caps, "video/mpeg")) + { + parser_name = g_strdup("mpeg4videoparse"); + } + + gst_caps_unref(dcaps); + MMPLAYER_FREEIF( demux_caps ); + + if (parser_name) + { + parser = __mmplayer_element_create_and_link(player, srcpad, parser_name); + + MMPLAYER_FREEIF(parser_name); + + if ( ! parser ) + { + debug_error("failed to create parser\n"); + } + else + { + /* update srcpad if parser is created */ + pssrcpad = gst_element_get_static_pad(parser, "src"); + srcpad = pssrcpad; + } + } + } + MMPLAYER_FREEIF(name); + + queue = __mmplayer_element_create_and_link(player, srcpad, "queue"); // parser - queue or demuxer - queue + if ( ! queue ) + { + debug_error("failed to create queue\n"); + goto ERROR; + } + + /* update srcpad to link with decoder */ + qsrcpad = gst_element_get_static_pad(queue, "src"); + srcpad = qsrcpad; + + q_max_size_time = GST_QUEUE_DEFAULT_TIME; + + /* assigning queue handle for futher manipulation purpose */ + /* FIXIT : make it some kind of list so that msl can support more then two stream (text, data, etc...) */ + if(mainbin[MMPLAYER_M_Q1].gst == NULL) + { + mainbin[MMPLAYER_M_Q1].id = MMPLAYER_M_Q1; + mainbin[MMPLAYER_M_Q1].gst = queue; + + g_object_set (G_OBJECT (mainbin[MMPLAYER_M_Q1].gst), "max-size-time", q_max_size_time * GST_SECOND, NULL); + } + else if(mainbin[MMPLAYER_M_Q2].gst == NULL) + { + mainbin[MMPLAYER_M_Q2].id = MMPLAYER_M_Q2; + mainbin[MMPLAYER_M_Q2].gst = queue; + + g_object_set (G_OBJECT (mainbin[MMPLAYER_M_Q2].gst), "max-size-time", q_max_size_time * GST_SECOND, NULL); + } + else + { + debug_critical("Not supporting more then two elementary stream\n"); + g_assert(1); + } + + pad = gst_element_get_static_pad(sinkelement, padname); + + if ( ! pad ) + { + debug_warning("failed to get pad(%s) from %s. retrying with [sink]\n", + padname, GST_ELEMENT_NAME(sinkelement) ); + + pad = gst_element_get_static_pad(sinkelement, "sink"); + if ( ! pad ) + { + debug_error("failed to get pad(sink) from %s. \n", + GST_ELEMENT_NAME(sinkelement) ); + goto ERROR; + } + } + + /* to check the video/audio type set the proper flag*/ + { + srccaps = gst_pad_get_caps( srcpad ); + if ( !srccaps ) + goto ERROR; + + str = gst_caps_get_structure( srccaps, 0 ); + if ( ! str ) + goto ERROR; + + name = gst_structure_get_name(str); + if ( ! name ) + goto ERROR; + } + + /* link queue and decoder. so, it will be queue - decoder. */ + if ( GST_PAD_LINK_OK != gst_pad_link(srcpad, pad) ) + { + gst_object_unref(GST_OBJECT(pad)); + debug_error("failed to link (%s) to pad(%s)\n", GST_ELEMENT_NAME( sinkelement ), padname ); + + /* reconstitute supportable codec */ + if (strstr(name, "video")) + { + player->can_support_codec ^= FOUND_PLUGIN_VIDEO; + } + else if (strstr(name, "audio")) + { + player->can_support_codec ^= FOUND_PLUGIN_AUDIO; + } + goto ERROR; + } + + if (strstr(name, "video")) + { + player->videodec_linked = 1; + debug_msg("player->videodec_linked set to 1\n"); + + } + else if (strstr(name, "audio")) + { + player->audiodec_linked = 1; + debug_msg("player->auddiodec_linked set to 1\n"); + } + + gst_object_unref(GST_OBJECT(pad)); + gst_caps_unref(GST_CAPS(srccaps)); + srccaps = NULL; + } + + if ( !MMPLAYER_IS_HTTP_PD(player) ) + { + if( (g_strrstr(klass, "Demux") && !g_strrstr(klass, "Metadata")) || (g_strrstr(klass, "Parser") ) ) + { + if (MMPLAYER_IS_HTTP_STREAMING(player)) + { + GstFormat fmt = GST_FORMAT_BYTES; + gint64 dur_bytes = 0L; + gchar *file_buffering_path = NULL; + gboolean use_file_buffer = FALSE; + + if ( !mainbin[MMPLAYER_M_S_BUFFER].gst) + { + debug_log("creating http streaming buffering queue\n"); + + queue = gst_element_factory_make("queue2", "http_streaming_buffer"); + if ( ! queue ) + { + debug_critical ( "failed to create buffering queue element\n" ); + goto ERROR; + } + + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(queue, GST_STATE_READY) ) + { + debug_error("failed to set state READY to buffering queue\n"); + goto ERROR; + } + + if ( !gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), queue) ) + { + debug_error("failed to add buffering queue\n"); + goto ERROR; + } + + qsinkpad = gst_element_get_static_pad(queue, "sink"); + qsrcpad = gst_element_get_static_pad(queue, "src"); + + if ( GST_PAD_LINK_OK != gst_pad_link(srcpad, qsinkpad) ) + { + debug_error("failed to link buffering queue\n"); + goto ERROR; + } + srcpad = qsrcpad; + + + mainbin[MMPLAYER_M_S_BUFFER].id = MMPLAYER_M_S_BUFFER; + mainbin[MMPLAYER_M_S_BUFFER].gst = queue; + + if ( !MMPLAYER_IS_HTTP_LIVE_STREAMING(player)) + { + if ( !gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_SRC].gst, &fmt, &dur_bytes)) + debug_error("fail to get duration.\n"); + + if (dur_bytes>0) + { + use_file_buffer = MMPLAYER_USE_FILE_FOR_BUFFERING(player); + file_buffering_path = g_strdup(PLAYER_INI()->http_file_buffer_path); + } + } + + __mm_player_streaming_set_buffer(player->streamer, + queue, + TRUE, + PLAYER_INI()->http_max_size_bytes, + 1.0, + PLAYER_INI()->http_buffering_limit, + PLAYER_INI()->http_buffering_time, + use_file_buffer, + file_buffering_path, + dur_bytes); + + MMPLAYER_FREEIF(file_buffering_path); + } + } + } + } + /* if it is not decoder or */ + /* in decoder case any of the video/audio still need to link*/ + if(!g_strrstr(klass, "Decoder")) + { + + pad = gst_element_get_static_pad(sinkelement, padname); + if ( ! pad ) + { + debug_warning("failed to get pad(%s) from %s. retrying with [sink]\n", + padname, GST_ELEMENT_NAME(sinkelement) ); + + pad = gst_element_get_static_pad(sinkelement, "sink"); + + if ( ! pad ) + { + debug_error("failed to get pad(sink) from %s. \n", + GST_ELEMENT_NAME(sinkelement) ); + goto ERROR; + } + } + + if ( GST_PAD_LINK_OK != gst_pad_link(srcpad, pad) ) + { + gst_object_unref(GST_OBJECT(pad)); + debug_error("failed to link (%s) to pad(%s)\n", GST_ELEMENT_NAME( sinkelement ), padname ); + goto ERROR; + } + + gst_object_unref(GST_OBJECT(pad)); + } + + for(;templlist != NULL; templlist = templlist->next) + { + padtemplate = templlist->data; + + debug_log ("director = [%d], presence = [%d]\n", padtemplate->direction, padtemplate->presence); + + if( padtemplate->direction != GST_PAD_SRC || + padtemplate->presence == GST_PAD_REQUEST ) + continue; + + switch(padtemplate->presence) + { + case GST_PAD_ALWAYS: + { + GstPad *srcpad = gst_element_get_static_pad(sinkelement, "src"); + GstCaps *caps = gst_pad_get_caps(srcpad); + + /* Check whether caps has many types */ + if ( gst_caps_get_size (caps) > 1 && g_strrstr(klass, "Parser")) { + debug_log ("has_many_types for this caps [%s]\n", gst_caps_to_string(caps)); + has_many_types = TRUE; + break; + } + + if ( ! __mmplayer_try_to_plug(player, srcpad, caps) ) + { + gst_object_unref(GST_OBJECT(srcpad)); + gst_caps_unref(GST_CAPS(caps)); + + debug_error("failed to plug something after %s\n", GST_ELEMENT_NAME( sinkelement )); + goto ERROR; + } + + gst_caps_unref(GST_CAPS(caps)); + gst_object_unref(GST_OBJECT(srcpad)); + + } + break; + + + case GST_PAD_SOMETIMES: + has_dynamic_pads = TRUE; + break; + + default: + break; + } + } + + /* check if player can do start continually */ + MMPLAYER_CHECK_CMD_IF_EXIT(player); + + if( has_dynamic_pads ) + { + player->have_dynamic_pad = TRUE; + MMPLAYER_SIGNAL_CONNECT ( player, sinkelement, "pad-added", + G_CALLBACK(__mmplayer_add_new_pad), player); + + /* for streaming, more then one typefind will used for each elementary stream + * so this doesn't mean the whole pipeline completion + */ + if ( ! MMPLAYER_IS_RTSP_STREAMING( player ) ) + { + MMPLAYER_SIGNAL_CONNECT( player, sinkelement, "no-more-pads", + G_CALLBACK(__mmplayer_pipeline_complete), player); + } + } + + if (has_many_types) + { + GstPad *pad = NULL; + + player->has_many_types = has_many_types; + + pad = gst_element_get_static_pad(sinkelement, "src"); + MMPLAYER_SIGNAL_CONNECT (player, pad, "notify::caps", G_CALLBACK(__mmplayer_add_new_caps), player); + gst_object_unref (GST_OBJECT(pad)); + } + + + /* check if player can do start continually */ + MMPLAYER_CHECK_CMD_IF_EXIT(player); + + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(sinkelement, GST_STATE_PAUSED) ) + { + debug_error("failed to set state PAUSED to %s\n", GST_ELEMENT_NAME( sinkelement )); + goto ERROR; + } + + if ( queue ) + { + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state (queue, GST_STATE_PAUSED) ) + { + debug_error("failed to set state PAUSED to queue\n"); + goto ERROR; + } + + queue = NULL; + + gst_object_unref (GST_OBJECT(qsrcpad)); + qsrcpad = NULL; + } + + if ( parser ) + { + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state (parser, GST_STATE_PAUSED) ) + { + debug_error("failed to set state PAUSED to queue\n"); + goto ERROR; + } + + parser = NULL; + + gst_object_unref (GST_OBJECT(pssrcpad)); + pssrcpad = NULL; + } + + debug_fleave(); + + return TRUE; + +ERROR: + + if ( queue ) + { + gst_object_unref(GST_OBJECT(qsrcpad)); + + /* NOTE : Trying to dispose element queue0, but it is in READY instead of the NULL state. + * You need to explicitly set elements to the NULL state before + * dropping the final reference, to allow them to clean up. + */ + gst_element_set_state(queue, GST_STATE_NULL); + /* And, it still has a parent "player". + * You need to let the parent manage the object instead of unreffing the object directly. + */ + + gst_bin_remove (GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), queue); + //gst_object_unref( queue ); + } + + if ( srccaps ) + gst_caps_unref(GST_CAPS(srccaps)); + + return FALSE; +} + +static gboolean __mmplayer_feature_filter(GstPluginFeature *feature, gpointer data) // @ +{ + const gchar *klass; + //const gchar *name; + + /* we only care about element factories */ + if (!GST_IS_ELEMENT_FACTORY(feature)) + return FALSE; + + /* only parsers, demuxers and decoders */ + klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature)); + //name = gst_element_factory_get_longname(GST_ELEMENT_FACTORY(feature)); + + if( g_strrstr(klass, "Demux") == NULL && + g_strrstr(klass, "Codec/Decoder") == NULL && + g_strrstr(klass, "Depayloader") == NULL && + g_strrstr(klass, "Parse") == NULL) + { + return FALSE; + } + return TRUE; +} + + +static void __mmplayer_add_new_caps(GstPad* pad, GParamSpec* unused, gpointer data) +{ + mm_player_t* player = (mm_player_t*) data; + GstCaps *caps = NULL; + GstStructure *str = NULL; + const char *name; + + debug_fenter(); + + return_if_fail ( pad ) + return_if_fail ( unused ) + return_if_fail ( data ) + + caps = gst_pad_get_caps(pad); + if ( !caps ) + return; + + str = gst_caps_get_structure(caps, 0); + if ( !str ) + return; + + name = gst_structure_get_name(str); + if ( !name ) + return; + debug_log("name=%s\n", name); + + if ( ! __mmplayer_try_to_plug(player, pad, caps) ) + { + debug_error("failed to autoplug for type (%s)\n", name); + gst_caps_unref(caps); + return; + } + + gst_caps_unref(caps); + + __mmplayer_pipeline_complete( NULL, (gpointer)player ); + + debug_fleave(); + + return; +} + +static void __mmplayer_set_unlinked_mime_type(mm_player_t* player, GstCaps *caps) +{ + GstStructure *str; + gint version = 0; + const char *stream_type; + gchar *version_field = NULL; + + debug_fenter(); + + return_if_fail ( player ); + return_if_fail ( caps ); + + str = gst_caps_get_structure(caps, 0); + if ( !str ) + return; + + stream_type = gst_structure_get_name(str); + if ( !stream_type ) + return; + + + /* set unlinked mime type for downloadable codec */ + if (g_str_has_prefix(stream_type, "video/")) + { + if (g_str_has_prefix(stream_type, "video/mpeg")) + { + gst_structure_get_int (str, MM_PLAYER_MPEG_VNAME, &version); + version_field = MM_PLAYER_MPEG_VNAME; + } + else if (g_str_has_prefix(stream_type, "video/x-wmv")) + { + gst_structure_get_int (str, MM_PLAYER_WMV_VNAME, &version); + version_field = MM_PLAYER_WMV_VNAME; + + } + else if (g_str_has_prefix(stream_type, "video/x-divx")) + { + gst_structure_get_int (str, MM_PLAYER_DIVX_VNAME, &version); + version_field = MM_PLAYER_DIVX_VNAME; + } + + if (version) + { + player->unlinked_video_mime = g_strdup_printf("%s, %s=%d", stream_type, version_field, version); + } + else + { + player->unlinked_video_mime = g_strdup_printf("%s", stream_type); + } + } + else if (g_str_has_prefix(stream_type, "audio/")) + { + if (g_str_has_prefix(stream_type, "audio/mpeg")) // mp3 or aac + { + gst_structure_get_int (str, MM_PLAYER_MPEG_VNAME, &version); + version_field = MM_PLAYER_MPEG_VNAME; + } + else if (g_str_has_prefix(stream_type, "audio/x-wma")) + { + gst_structure_get_int (str, MM_PLAYER_WMA_VNAME, &version); + version_field = MM_PLAYER_WMA_VNAME; + } + + if (version) + { + player->unlinked_audio_mime = g_strdup_printf("%s, %s=%d", stream_type, version_field, version); + } + else + { + player->unlinked_audio_mime = g_strdup_printf("%s", stream_type); + } + } + + debug_fleave(); +} + +static void __mmplayer_add_new_pad(GstElement *element, GstPad *pad, gpointer data) +{ + mm_player_t* player = (mm_player_t*) data; + GstCaps *caps = NULL; + GstStructure *str = NULL; + const char *name; + + debug_fenter(); + return_if_fail ( player ); + return_if_fail ( pad ); + + GST_OBJECT_LOCK (pad); + if ((caps = GST_PAD_CAPS(pad))) + gst_caps_ref(caps); + GST_OBJECT_UNLOCK (pad); + + if ( NULL == caps ) + { + caps = gst_pad_get_caps(pad); + if ( !caps ) return; + } + + //MMPLAYER_LOG_GST_CAPS_TYPE(caps); + + str = gst_caps_get_structure(caps, 0); + if ( !str ) + return; + + name = gst_structure_get_name(str); + if ( !name ) + return; + + player->num_dynamic_pad++; + debug_log("stream count inc : %d\n", player->num_dynamic_pad); + + /* Note : If the stream is the subtitle, we try not to play it. Just close the demuxer subtitle pad. + * If want to play it, remove this code. + */ + if (g_strrstr(name, "application")) + { + if (g_strrstr(name, "x-id3") || g_strrstr(name, "x-apetag")) + { + /* If id3/ape tag comes, keep going */ + debug_log("application mime exception : id3/ape tag"); + } + else + { + /* Otherwise, we assume that this stream is subtile. */ + debug_log(" application mime type pad is closed."); + return; + } + } + else if (g_strrstr(name, "audio")) + { + gint samplerate = 0, channels = 0; + + /* set stream information */ + /* if possible, set it here because the caps is not distrubed by resampler. */ + gst_structure_get_int (str, "rate", &samplerate); + mm_attrs_set_int_by_name(player->attrs, "content_audio_samplerate", samplerate); + + gst_structure_get_int (str, "channels", &channels); + mm_attrs_set_int_by_name(player->attrs, "content_audio_channels", channels); + + debug_log("audio samplerate : %d channels : %d", samplerate, channels); + + /* validate all */ + if ( mmf_attrs_commit ( player->attrs ) ) + { + debug_error("failed to update attributes"); + return; + } + } + else if (g_strrstr(name, "video")) + { + gint stype; + mm_attrs_get_int_by_name (player->attrs, "display_surface_type", &stype); + + /* don't make video because of not required */ + if (stype == MM_DISPLAY_SURFACE_NULL) + { + debug_log("no video because it's not required"); + return; + } + + player->v_stream_caps = gst_caps_copy(caps); //if needed, video caps is required when videobin is created + } + + if ( ! __mmplayer_try_to_plug(player, pad, caps) ) + { + debug_error("failed to autoplug for type (%s)", name); + + __mmplayer_set_unlinked_mime_type(player, caps); + } + + gst_caps_unref(caps); + + debug_fleave(); + return; +} + +/* test API for tuning audio gain. this API should be + * deprecated before the day of final release + */ +int +_mmplayer_set_volume_tune(MMHandleType hplayer, MMPlayerVolumeType volume) +{ + mm_player_t* player = (mm_player_t*) hplayer; + gint error = MM_ERROR_NONE; + gint vol_max = 0; + gboolean isMidi = FALSE; + gint i = 0; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail( player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ) + + debug_log("clip type=%d(1-midi, 0-others), volume [L]=%d:[R]=%d\n", + player->profile.play_mode, volume.level[0], volume.level[1]); + + isMidi = ( player->profile.play_mode == MM_PLAYER_MODE_MIDI ) ? TRUE : FALSE; + + if ( isMidi ) + vol_max = 1000; + else + vol_max = 100; + + /* is it proper volume level? */ + for (i = 0; i < MM_VOLUME_CHANNEL_NUM; ++i) + { + if (volume.level[i] < 0 || volume.level[i] > vol_max) { + debug_log("Invalid Volume level!!!! \n"); + return MM_ERROR_INVALID_ARGUMENT; + } + } + + if ( isMidi ) + { + if ( player->pipeline->mainbin ) + { + GstElement *midi_element = player->pipeline->mainbin[MMPLAYER_M_DEMUX].gst; + + if ( midi_element && ( strstr(GST_ELEMENT_NAME(midi_element), "midiparse")) ) + { + debug_log("setting volume (%d) level to midi plugin\n", volume.level[0]); + + g_object_set(midi_element, "volume", volume.level[0], NULL); + } + } + } + else + { + if ( player->pipeline->audiobin ) + { + GstElement *sink_element = player->pipeline->audiobin[MMPLAYER_A_SINK].gst; + + /* Set to Avsysaudiosink element */ + if ( sink_element ) + { + gint vol_value = 0; + gboolean mute = FALSE; + vol_value = volume.level[0]; + + g_object_set(G_OBJECT(sink_element), "tuningvolume", vol_value, NULL); + + mute = (vol_value == 0)? TRUE:FALSE; + + g_object_set(G_OBJECT(sink_element), "mute", mute, NULL); + } + + } + } + + debug_fleave(); + + return error; +} + +gboolean +__mmplayer_dump_pipeline_state( mm_player_t* player ) +{ + GstIterator*iter = NULL; + gboolean done = FALSE; + + GstElement *item = NULL; + GstElementFactory *factory = NULL; + + GstState state = GST_STATE_VOID_PENDING; + GstState pending = GST_STATE_VOID_PENDING; + GstClockTime time = 200*GST_MSECOND; + + debug_fenter(); + + return_val_if_fail ( player && + player->pipeline && + player->pipeline->mainbin, + FALSE ); + + + iter = gst_bin_iterate_recurse(GST_BIN(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst) ); + + if ( iter != NULL ) + { + while (!done) { + switch ( gst_iterator_next (iter, (gpointer)&item) ) + { + case GST_ITERATOR_OK: + gst_element_get_state(GST_ELEMENT (item),&state, &pending,time); + + factory = gst_element_get_factory (item) ; + debug_error("%s:%s : From:%s To:%s refcount : %d\n", GST_OBJECT_NAME(factory) , GST_ELEMENT_NAME(item) , + gst_element_state_get_name(state), gst_element_state_get_name(pending) , GST_OBJECT_REFCOUNT_VALUE(item)); + + + gst_object_unref (item); + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + } + + item = GST_ELEMENT(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst); + + gst_element_get_state(GST_ELEMENT (item),&state, &pending,time); + + factory = gst_element_get_factory (item) ; + + debug_error("%s:%s : From:%s To:%s refcount : %d\n", + GST_OBJECT_NAME(factory), + GST_ELEMENT_NAME(item), + gst_element_state_get_name(state), + gst_element_state_get_name(pending), + GST_OBJECT_REFCOUNT_VALUE(item) ); + + if ( iter ) + gst_iterator_free (iter); + + debug_fleave(); + + return FALSE; +} + + +gboolean +__mmplayer_check_subtitle( mm_player_t* player ) +{ + MMHandleType attrs = 0; + char *subtitle_uri = NULL; + + debug_fenter(); + + return_val_if_fail( player, FALSE ); + + /* get subtitle attribute */ + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + return FALSE; + + mm_attrs_get_string_by_name(attrs, "subtitle_uri", &subtitle_uri); + if ( !subtitle_uri || !strlen(subtitle_uri)) + return FALSE; + + debug_log ("subtite uri is %s[%d]\n", subtitle_uri, strlen(subtitle_uri)); + + debug_fleave(); + + return TRUE; +} + +static gboolean +__mmplayer_can_extract_pcm( mm_player_t* player ) +{ + MMHandleType attrs = 0; + gboolean is_drm = FALSE; + gboolean sound_extraction = FALSE; + + debug_fenter(); + + return_val_if_fail ( player, FALSE ); + + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("fail to get attributes."); + return FALSE; + } + + /* check file is drm or not */ + g_object_get(G_OBJECT(player->pipeline->mainbin[MMPLAYER_M_SRC].gst), "is-drm", &is_drm, NULL); + + /* get sound_extraction property */ + mm_attrs_get_int_by_name(attrs, "pcm_extraction", &sound_extraction); + + if ( ! sound_extraction || is_drm ) + { + debug_log("pcm extraction param.. is drm = %d, extraction mode = %d", is_drm, sound_extraction); + return FALSE; + } + + debug_fleave(); + + return TRUE; +} + +static gboolean +__mmplayer_handle_gst_error ( mm_player_t* player, GstMessage * message, GError* error ) +{ + MMMessageParamType msg_param; + gchar *msg_src_element; + + debug_fenter(); + + return_val_if_fail( player, FALSE ); + return_val_if_fail( error, FALSE ); + + /* NOTE : do somthing necessary inside of __gst_handle_XXX_error. not here */ + + memset (&msg_param, 0, sizeof(MMMessageParamType)); + + if ( error->domain == GST_CORE_ERROR ) + { + msg_param.code = __gst_handle_core_error( player, error->code ); + } + else if ( error->domain == GST_LIBRARY_ERROR ) + { + msg_param.code = __gst_handle_library_error( player, error->code ); + } + else if ( error->domain == GST_RESOURCE_ERROR ) + { + msg_param.code = __gst_handle_resource_error( player, error->code ); + } + else if ( error->domain == GST_STREAM_ERROR ) + { + msg_param.code = __gst_handle_stream_error( player, error, message ); + } + else + { + debug_warning("This error domain is not defined.\n"); + + /* we treat system error as an internal error */ + msg_param.code = MM_ERROR_PLAYER_INVALID_STREAM; + } + + if ( message->src ) + { + msg_src_element = GST_ELEMENT_NAME( GST_ELEMENT_CAST( message->src ) ); + + msg_param.data = (void *) error->message; + + debug_error("-Msg src : [%s] Domain : [%s] Error : [%s] Code : [%d] is tranlated to error code : [0x%x]\n", + msg_src_element, g_quark_to_string (error->domain), error->message, error->code, msg_param.code); + } + + /* post error to application */ + if ( ! player->posted_msg ) + { + if (msg_param.code == MM_MESSAGE_DRM_NOT_AUTHORIZED ) + { + MMPLAYER_POST_MSG( player, MM_MESSAGE_DRM_NOT_AUTHORIZED, NULL ); + } + else + { + MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); + } + + /* don't post more if one was sent already */ + player->posted_msg = TRUE; + } + else + { + debug_log("skip error post because it's sent already.\n"); + } + + debug_fleave(); + + return TRUE; +} + +static gboolean +__mmplayer_handle_streaming_error ( mm_player_t* player, GstMessage * message ) +{ + debug_log("\n"); + MMMessageParamType msg_param; + gchar *msg_src_element = NULL; + GstStructure *s = NULL; + guint error_id = 0; + gchar *error_string = NULL; + + debug_fenter(); + + return_val_if_fail ( player, FALSE ); + return_val_if_fail ( message, FALSE ); + + s = malloc( sizeof(GstStructure) ); + memcpy ( s, gst_message_get_structure ( message ), sizeof(GstStructure)); + + if ( !gst_structure_get_uint (s, "error_id", &error_id) ) + error_id = MMPLAYER_STREAMING_ERROR_NONE; + + switch ( error_id ) + { + case MMPLAYER_STREAMING_ERROR_UNSUPPORTED_AUDIO: + msg_param.code = MM_ERROR_PLAYER_STREAMING_UNSUPPORTED_AUDIO; + break; + case MMPLAYER_STREAMING_ERROR_UNSUPPORTED_VIDEO: + msg_param.code = MM_ERROR_PLAYER_STREAMING_UNSUPPORTED_VIDEO; + break; + case MMPLAYER_STREAMING_ERROR_CONNECTION_FAIL: + msg_param.code = MM_ERROR_PLAYER_STREAMING_CONNECTION_FAIL; + break; + case MMPLAYER_STREAMING_ERROR_DNS_FAIL: + msg_param.code = MM_ERROR_PLAYER_STREAMING_DNS_FAIL; + break; + case MMPLAYER_STREAMING_ERROR_SERVER_DISCONNECTED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_SERVER_DISCONNECTED; + break; + case MMPLAYER_STREAMING_ERROR_BAD_SERVER: + msg_param.code = MM_ERROR_PLAYER_STREAMING_BAD_SERVER; + break; + case MMPLAYER_STREAMING_ERROR_INVALID_PROTOCOL: + msg_param.code = MM_ERROR_PLAYER_STREAMING_INVALID_PROTOCOL; + break; + case MMPLAYER_STREAMING_ERROR_INVALID_URL: + msg_param.code = MM_ERROR_PLAYER_STREAMING_INVALID_URL; + break; + case MMPLAYER_STREAMING_ERROR_UNEXPECTED_MSG: + msg_param.code = MM_ERROR_PLAYER_STREAMING_UNEXPECTED_MSG; + break; + case MMPLAYER_STREAMING_ERROR_OUT_OF_MEMORIES: + msg_param.code = MM_ERROR_PLAYER_STREAMING_OUT_OF_MEMORIES; + break; + case MMPLAYER_STREAMING_ERROR_RTSP_TIMEOUT: + msg_param.code = MM_ERROR_PLAYER_STREAMING_RTSP_TIMEOUT; + break; + case MMPLAYER_STREAMING_ERROR_BAD_REQUEST: + msg_param.code = MM_ERROR_PLAYER_STREAMING_BAD_REQUEST; + break; + case MMPLAYER_STREAMING_ERROR_NOT_AUTHORIZED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_NOT_AUTHORIZED; + break; + case MMPLAYER_STREAMING_ERROR_PAYMENT_REQUIRED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_PAYMENT_REQUIRED; + break; + case MMPLAYER_STREAMING_ERROR_FORBIDDEN: + msg_param.code = MM_ERROR_PLAYER_STREAMING_FORBIDDEN; + break; + case MMPLAYER_STREAMING_ERROR_CONTENT_NOT_FOUND: + msg_param.code = MM_ERROR_PLAYER_STREAMING_CONTENT_NOT_FOUND; + break; + case MMPLAYER_STREAMING_ERROR_METHOD_NOT_ALLOWED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_METHOD_NOT_ALLOWED; + break; + case MMPLAYER_STREAMING_ERROR_NOT_ACCEPTABLE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_NOT_ACCEPTABLE; + break; + case MMPLAYER_STREAMING_ERROR_PROXY_AUTHENTICATION_REQUIRED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_PROXY_AUTHENTICATION_REQUIRED; + break; + case MMPLAYER_STREAMING_ERROR_SERVER_TIMEOUT: + msg_param.code = MM_ERROR_PLAYER_STREAMING_SERVER_TIMEOUT; + break; + case MMPLAYER_STREAMING_ERROR_GONE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_GONE; + break; + case MMPLAYER_STREAMING_ERROR_LENGTH_REQUIRED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_LENGTH_REQUIRED; + break; + case MMPLAYER_STREAMING_ERROR_PRECONDITION_FAILED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_PRECONDITION_FAILED; + break; + case MMPLAYER_STREAMING_ERROR_REQUEST_ENTITY_TOO_LARGE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_REQUEST_ENTITY_TOO_LARGE; + break; + case MMPLAYER_STREAMING_ERROR_REQUEST_URI_TOO_LARGE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_REQUEST_URI_TOO_LARGE; + break; + case MMPLAYER_STREAMING_ERROR_UNSUPPORTED_MEDIA_TYPE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_UNSUPPORTED_MEDIA_TYPE; + break; + case MMPLAYER_STREAMING_ERROR_PARAMETER_NOT_UNDERSTOOD: + msg_param.code = MM_ERROR_PLAYER_STREAMING_PARAMETER_NOT_UNDERSTOOD; + break; + case MMPLAYER_STREAMING_ERROR_CONFERENCE_NOT_FOUND: + msg_param.code = MM_ERROR_PLAYER_STREAMING_CONFERENCE_NOT_FOUND; + break; + case MMPLAYER_STREAMING_ERROR_NOT_ENOUGH_BANDWIDTH: + msg_param.code = MM_ERROR_PLAYER_STREAMING_NOT_ENOUGH_BANDWIDTH; + break; + case MMPLAYER_STREAMING_ERROR_NO_SESSION_ID: + msg_param.code = MM_ERROR_PLAYER_STREAMING_NO_SESSION_ID; + break; + case MMPLAYER_STREAMING_ERROR_METHOD_NOT_VALID_IN_THIS_STATE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_METHOD_NOT_VALID_IN_THIS_STATE; + break; + case MMPLAYER_STREAMING_ERROR_HEADER_FIELD_NOT_VALID_FOR_SOURCE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_HEADER_FIELD_NOT_VALID_FOR_SOURCE; + break; + case MMPLAYER_STREAMING_ERROR_INVALID_RANGE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_INVALID_RANGE; + break; + case MMPLAYER_STREAMING_ERROR_PARAMETER_IS_READONLY: + msg_param.code = MM_ERROR_PLAYER_STREAMING_PARAMETER_IS_READONLY; + break; + case MMPLAYER_STREAMING_ERROR_AGGREGATE_OP_NOT_ALLOWED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_AGGREGATE_OP_NOT_ALLOWED; + break; + case MMPLAYER_STREAMING_ERROR_ONLY_AGGREGATE_OP_ALLOWED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_ONLY_AGGREGATE_OP_ALLOWED; + break; + case MMPLAYER_STREAMING_ERROR_BAD_TRANSPORT: + msg_param.code = MM_ERROR_PLAYER_STREAMING_BAD_TRANSPORT; + break; + case MMPLAYER_STREAMING_ERROR_DESTINATION_UNREACHABLE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_DESTINATION_UNREACHABLE; + break; + case MMPLAYER_STREAMING_ERROR_INTERNAL_SERVER_ERROR: + msg_param.code = MM_ERROR_PLAYER_STREAMING_INTERNAL_SERVER_ERROR; + break; + case MMPLAYER_STREAMING_ERROR_NOT_IMPLEMENTED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_NOT_IMPLEMENTED; + break; + case MMPLAYER_STREAMING_ERROR_BAD_GATEWAY: + msg_param.code = MM_ERROR_PLAYER_STREAMING_BAD_GATEWAY; + break; + case MMPLAYER_STREAMING_ERROR_SERVICE_UNAVAILABLE: + msg_param.code = MM_ERROR_PLAYER_STREAMING_SERVICE_UNAVAILABLE; + break; + case MMPLAYER_STREAMING_ERROR_GATEWAY_TIME_OUT: + msg_param.code = MM_ERROR_PLAYER_STREAMING_GATEWAY_TIME_OUT; + break; + case MMPLAYER_STREAMING_ERROR_RTSP_VERSION_NOT_SUPPORTED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_RTSP_VERSION_NOT_SUPPORTED; + break; + case MMPLAYER_STREAMING_ERROR_OPTION_NOT_SUPPORTED: + msg_param.code = MM_ERROR_PLAYER_STREAMING_OPTION_NOT_SUPPORTED; + break; + default: + return MM_ERROR_PLAYER_STREAMING_FAIL; + } + + error_string = g_strdup(gst_structure_get_string (s, "error_string")); + if ( error_string ) + msg_param.data = (void *) error_string; + + if ( message->src ) + { + msg_src_element = GST_ELEMENT_NAME( GST_ELEMENT_CAST( message->src ) ); + + debug_error("-Msg src : [%s] Code : [%x] Error : [%s] \n", + msg_src_element, msg_param.code, (char*)msg_param.data ); + } + + /* post error to application */ + if ( ! player->posted_msg ) + { + MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); + + /* don't post more if one was sent already */ + player->posted_msg = TRUE; + } + else + { + debug_log("skip error post because it's sent already.\n"); + } + + debug_fleave(); + + return TRUE; + +} + +static gint +__gst_handle_core_error( mm_player_t* player, int code ) +{ + gint trans_err = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + switch ( code ) + { + case GST_CORE_ERROR_STATE_CHANGE: + case GST_CORE_ERROR_MISSING_PLUGIN: + 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 = MM_ERROR_PLAYER_INVALID_STREAM; + break; + } + + debug_fleave(); + + return trans_err; +} + +static gint +__gst_handle_library_error( mm_player_t* player, int code ) +{ + gint trans_err = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + switch ( code ) + { + case GST_LIBRARY_ERROR_FAILED: + case GST_LIBRARY_ERROR_TOO_LAZY: + case GST_LIBRARY_ERROR_INIT: + case GST_LIBRARY_ERROR_SHUTDOWN: + case GST_LIBRARY_ERROR_SETTINGS: + case GST_LIBRARY_ERROR_ENCODE: + default: + trans_err = MM_ERROR_PLAYER_INVALID_STREAM; + break; + } + + debug_fleave(); + + return trans_err; +} + + +static gint +__gst_handle_resource_error( mm_player_t* player, int code ) +{ + gint trans_err = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + switch ( code ) + { + case GST_RESOURCE_ERROR_NO_SPACE_LEFT: + trans_err = MM_ERROR_PLAYER_NO_FREE_SPACE; + break; + case GST_RESOURCE_ERROR_NOT_FOUND: + case GST_RESOURCE_ERROR_OPEN_READ: + if ( MMPLAYER_IS_HTTP_STREAMING(player) || MMPLAYER_IS_HTTP_LIVE_STREAMING ( player ) ) + { + trans_err = MM_ERROR_PLAYER_STREAMING_CONNECTION_FAIL; + break; + } + case GST_RESOURCE_ERROR_READ: + if ( MMPLAYER_IS_HTTP_STREAMING(player) || MMPLAYER_IS_HTTP_LIVE_STREAMING ( player )) + { + trans_err = MM_ERROR_PLAYER_STREAMING_FAIL; + break; + } + case GST_RESOURCE_ERROR_SEEK: + case GST_RESOURCE_ERROR_FAILED: + 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_WRITE: + case GST_RESOURCE_ERROR_SYNC: + case GST_RESOURCE_ERROR_SETTINGS: + default: + trans_err = MM_ERROR_PLAYER_FILE_NOT_FOUND; + break; + } + + debug_fleave(); + + return trans_err; +} + + +static gint +__gst_handle_stream_error( mm_player_t* player, GError* error, GstMessage * message ) +{ + gint trans_err = MM_ERROR_NONE; + + debug_fenter(); + + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail( error, MM_ERROR_INVALID_ARGUMENT ); + return_val_if_fail ( message, MM_ERROR_INVALID_ARGUMENT ); + + switch ( error->code ) + { + case GST_STREAM_ERROR_FAILED: + case GST_STREAM_ERROR_TYPE_NOT_FOUND: + case GST_STREAM_ERROR_DECODE: + case GST_STREAM_ERROR_WRONG_TYPE: + case GST_STREAM_ERROR_DECRYPT: + case GST_STREAM_ERROR_DECRYPT_NOKEY: + trans_err = __gst_transform_gsterror( player, message, error ); + break; + + case GST_STREAM_ERROR_CODEC_NOT_FOUND: + case GST_STREAM_ERROR_NOT_IMPLEMENTED: + case GST_STREAM_ERROR_TOO_LAZY: + case GST_STREAM_ERROR_ENCODE: + case GST_STREAM_ERROR_DEMUX: + case GST_STREAM_ERROR_MUX: + case GST_STREAM_ERROR_FORMAT: + default: + trans_err = MM_ERROR_PLAYER_INVALID_STREAM; + break; + } + + debug_fleave(); + + return trans_err; +} + + +/* NOTE : decide gstreamer state whether there is some playable track or not. */ +static gint +__gst_transform_gsterror( mm_player_t* player, GstMessage * message, GError* error ) +{ + gchar *src_element_name = NULL; + GstElement *src_element = NULL; + GstElementFactory *factory = NULL; + const gchar* klass = NULL; + + debug_fenter(); + + /* FIXIT */ + return_val_if_fail ( message, MM_ERROR_INVALID_ARGUMENT ); + return_val_if_fail ( message->src, MM_ERROR_INVALID_ARGUMENT ); + return_val_if_fail ( error, MM_ERROR_INVALID_ARGUMENT ); + + src_element = GST_ELEMENT_CAST(message->src); + if ( !src_element ) + goto INTERNAL_ERROR; + + src_element_name = GST_ELEMENT_NAME(src_element); + if ( !src_element_name ) + goto INTERNAL_ERROR; + + factory = gst_element_get_factory(src_element); + if ( !factory ) + goto INTERNAL_ERROR; + + klass = gst_element_factory_get_klass(factory); + if ( !klass ) + goto INTERNAL_ERROR; + + debug_log("error code=%d, msg=%s, src element=%s, class=%s\n", + error->code, error->message, src_element_name, klass); + + + switch ( error->code ) + { + case GST_STREAM_ERROR_DECODE: + { + /* NOTE : Delay is needed because gst callback is sometime sent + * before completing autoplugging. + * Timer is more better than usleep. + * But, transformed msg value should be stored in player handle + * for function to call by timer. + */ + if ( PLAYER_INI()->async_start ) + usleep(500000); + + /* Demuxer can't parse one track because it's corrupted. + * So, the decoder for it is not linked. + * But, it has one playable track. + */ + if ( g_strrstr(klass, "Demux") ) + { + if ( player->can_support_codec == FOUND_PLUGIN_VIDEO ) + { + return MM_ERROR_PLAYER_AUDIO_CODEC_NOT_FOUND; + } + else if ( player->can_support_codec == FOUND_PLUGIN_AUDIO ) + { + return MM_ERROR_PLAYER_VIDEO_CODEC_NOT_FOUND; + } + else + { + if ( player->pipeline->audiobin ) // PCM + { + return MM_ERROR_PLAYER_VIDEO_CODEC_NOT_FOUND; + } + else + { + goto CODEC_NOT_FOUND; + } + } + } + return MM_ERROR_PLAYER_INVALID_STREAM; + } + break; + + case GST_STREAM_ERROR_WRONG_TYPE: + { + return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT; + } + break; + + case GST_STREAM_ERROR_FAILED: + { + /* Decoder Custom Message */ + if ( strstr(error->message, "ongoing") ) + { + if ( strcasestr(klass, "audio") ) + { + if ( ( player->can_support_codec & FOUND_PLUGIN_VIDEO ) ) + { + debug_log("Video can keep playing.\n"); + return MM_ERROR_PLAYER_AUDIO_CODEC_NOT_FOUND; + } + else + { + goto CODEC_NOT_FOUND; + } + + } + else if ( strcasestr(klass, "video") ) + { + if ( ( player->can_support_codec & FOUND_PLUGIN_AUDIO ) ) + { + debug_log("Audio can keep playing.\n"); + return MM_ERROR_PLAYER_VIDEO_CODEC_NOT_FOUND; + } + else + { + goto CODEC_NOT_FOUND; + } + } + } + return MM_ERROR_PLAYER_INVALID_STREAM; + } + break; + + case GST_STREAM_ERROR_TYPE_NOT_FOUND: + { + goto CODEC_NOT_FOUND; + } + break; + + case GST_STREAM_ERROR_DECRYPT: + case GST_STREAM_ERROR_DECRYPT_NOKEY: + { + debug_error("decryption error, [%s] failed, reason : [%s]\n", src_element_name, error->message); + return MM_MESSAGE_DRM_NOT_AUTHORIZED; + } + break; + + default: + break; + } + + debug_fleave(); + + return MM_ERROR_PLAYER_INVALID_STREAM; + +INTERNAL_ERROR: + return MM_ERROR_PLAYER_INTERNAL; + +CODEC_NOT_FOUND: + debug_log("not found any available codec. Player should be destroyed.\n"); + return MM_ERROR_PLAYER_CODEC_NOT_FOUND; +} + +static void +__mmplayer_post_delayed_eos( mm_player_t* player, int delay_in_ms ) +{ + debug_fenter(); + + return_if_fail( player ); + + /* cancel if existing */ + __mmplayer_cancel_delayed_eos( player ); + + + /* post now if delay is zero */ + if ( delay_in_ms == 0 || player->is_sound_extraction) + { + debug_log("eos delay is zero. posting EOS now\n"); + MMPLAYER_POST_MSG( player, MM_MESSAGE_END_OF_STREAM, NULL ); + + if ( player->is_sound_extraction ) + __mmplayer_cancel_delayed_eos(player); + + return; + } + + /* init new timeout */ + /* NOTE : consider give high priority to this timer */ + + debug_log("posting EOS message after [%d] msec\n", delay_in_ms); + player->eos_timer = g_timeout_add( delay_in_ms, + __mmplayer_eos_timer_cb, player ); + + + /* check timer is valid. if not, send EOS now */ + if ( player->eos_timer == 0 ) + { + debug_warning("creating timer for delayed EOS has failed. sending EOS now\n"); + MMPLAYER_POST_MSG( player, MM_MESSAGE_END_OF_STREAM, NULL ); + } + + debug_fleave(); +} + +static void +__mmplayer_cancel_delayed_eos( mm_player_t* player ) +{ + debug_fenter(); + + return_if_fail( player ); + + if ( player->eos_timer ) + { + g_source_remove( player->eos_timer ); + } + + player->eos_timer = 0; + + debug_fleave(); + + return; +} + +static gboolean +__mmplayer_eos_timer_cb(gpointer u_data) +{ + mm_player_t* player = NULL; + player = (mm_player_t*) u_data; + + debug_fenter(); + + return_val_if_fail( player, FALSE ); + + /* posting eos */ + MMPLAYER_POST_MSG( player, MM_MESSAGE_END_OF_STREAM, NULL ); + + /* cleare timer id */ + player->eos_timer = 0; + + debug_fleave(); + + /* we are returning FALSE as we need only one posting */ + return FALSE; +} + +static void __mmplayer_set_antishock( mm_player_t* player, gboolean disable_by_force) +{ + gint antishock = FALSE; + MMHandleType attrs = 0; + + debug_fenter(); + + return_if_fail ( player && player->pipeline ); + + /* It should be passed for video only clip */ + if ( ! player->pipeline->audiobin ) + return; + + if ( ( g_strrstr(PLAYER_INI()->name_of_audiosink, "avsysaudiosink")) ) + { + attrs = MMPLAYER_GET_ATTRS(player); + if ( ! attrs ) + { + debug_error("fail to get attributes.\n"); + return; + } + + mm_attrs_get_int_by_name(attrs, "sound_fadeup", &antishock); + + debug_log("setting antishock as (%d)\n", antishock); + + if ( disable_by_force ) + { + debug_log("but, antishock is disabled by force when is seeked\n"); + + antishock = FALSE; + } + + g_object_set(G_OBJECT(player->pipeline->audiobin[MMPLAYER_A_SINK].gst), "fadeup", antishock, NULL); + } + + debug_fleave(); + + return; +} + + +static gboolean +__mmplayer_link_decoder( mm_player_t* player, GstPad *srcpad) +{ + const gchar* name = NULL; + GstStructure* str = NULL; + GstCaps* srccaps = NULL; + + debug_fenter(); + + return_val_if_fail( player, FALSE ); + return_val_if_fail ( srcpad, FALSE ); + + /* to check any of the decoder (video/audio) need to be linked to parser*/ + srccaps = gst_pad_get_caps( srcpad ); + if ( !srccaps ) + goto ERROR; + + str = gst_caps_get_structure( srccaps, 0 ); + if ( ! str ) + goto ERROR; + + name = gst_structure_get_name(str); + if ( ! name ) + goto ERROR; + + if (strstr(name, "video")) + { + if(player->videodec_linked) + { + debug_msg("Video decoder already linked\n"); + return FALSE; + } + } + if (strstr(name, "audio")) + { + if(player->audiodec_linked) + { + debug_msg("Audio decoder already linked\n"); + return FALSE; + } + } + + gst_caps_unref( srccaps ); + + debug_fleave(); + + return TRUE; + +ERROR: + if ( srccaps ) + gst_caps_unref( srccaps ); + + return FALSE; +} + +static gboolean +__mmplayer_link_sink( mm_player_t* player , GstPad *srcpad) +{ + const gchar* name = NULL; + GstStructure* str = NULL; + GstCaps* srccaps = NULL; + + debug_fenter(); + + return_val_if_fail ( player, FALSE ); + return_val_if_fail ( srcpad, FALSE ); + + /* to check any of the decoder (video/audio) need to be linked to parser*/ + srccaps = gst_pad_get_caps( srcpad ); + if ( !srccaps ) + goto ERROR; + + str = gst_caps_get_structure( srccaps, 0 ); + if ( ! str ) + goto ERROR; + + name = gst_structure_get_name(str); + if ( ! name ) + goto ERROR; + + if (strstr(name, "video")) + { + if(player->videosink_linked) + { + debug_msg("Video Sink already linked\n"); + return FALSE; + } + } + if (strstr(name, "audio")) + { + if(player->audiosink_linked) + { + debug_msg("Audio Sink already linked\n"); + return FALSE; + } + } + if (strstr(name, "text")) + { + if(player->textsink_linked) + { + debug_msg("Text Sink already linked\n"); + return FALSE; + } + } + + gst_caps_unref( srccaps ); + + debug_fleave(); + + return TRUE; + //return (!player->videosink_linked || !player->audiosink_linked); + +ERROR: + if ( srccaps ) + gst_caps_unref( srccaps ); + + return FALSE; +} + + +/* sending event to one of sinkelements */ +static gboolean +__gst_send_event_to_sink( mm_player_t* player, GstEvent* event ) +{ + GstEvent * event2 = NULL; + GList *sinks = NULL; + gboolean res = FALSE; + + debug_fenter(); + + return_val_if_fail( player, FALSE ); + return_val_if_fail ( event, FALSE ); + + if ( player->play_subtitle && !player->use_textoverlay) + event2 = gst_event_copy((const GstEvent *)event); + + sinks = player->sink_elements; + while (sinks) + { + GstElement *sink = GST_ELEMENT_CAST (sinks->data); + + if (GST_IS_ELEMENT(sink)) + { + /* keep ref to the event */ + gst_event_ref (event); + + if ( (res = gst_element_send_event (sink, event)) ) + { + debug_log("sending event[%s] to sink element [%s] success!\n", + GST_EVENT_TYPE_NAME(event), GST_ELEMENT_NAME(sink) ); + break; + } + + debug_log("sending event[%s] to sink element [%s] failed. try with next one.\n", + GST_EVENT_TYPE_NAME(event), GST_ELEMENT_NAME(sink) ); + } + + sinks = g_list_next (sinks); + } + + /* Note : Textbin is not linked to the video or audio bin. + * It needs to send the event to the text sink seperatelly. + */ + if ( player->play_subtitle && !player->use_textoverlay) + { + GstElement *text_sink = GST_ELEMENT_CAST (player->pipeline->textbin[MMPLAYER_T_SINK].gst); + + if (GST_IS_ELEMENT(text_sink)) + { + /* keep ref to the event */ + gst_event_ref (event2); + + if ( (res != gst_element_send_event (text_sink, event2)) ) + { + debug_error("sending event[%s] to subtitle sink element [%s] failed!\n", + GST_EVENT_TYPE_NAME(event2), GST_ELEMENT_NAME(text_sink) ); + } + else + { + debug_log("sending event[%s] to subtitle sink element [%s] success!\n", + GST_EVENT_TYPE_NAME(event2), GST_ELEMENT_NAME(text_sink) ); + } + + gst_event_unref (event2); + } + } + + gst_event_unref (event); + + debug_fleave(); + + return res; +} + +static void +__mmplayer_add_sink( mm_player_t* player, GstElement* sink ) +{ + debug_fenter(); + + return_if_fail ( player ); + return_if_fail ( sink ); + + player->sink_elements = + g_list_append(player->sink_elements, sink); + + debug_fleave(); +} + +static void +__mmplayer_del_sink( mm_player_t* player, GstElement* sink ) +{ + debug_fenter(); + + return_if_fail ( player ); + return_if_fail ( sink ); + + player->sink_elements = + g_list_remove(player->sink_elements, sink); + + debug_fleave(); +} + +static gboolean +__gst_seek(mm_player_t* player, GstElement * element, gdouble rate, + GstFormat format, GstSeekFlags flags, GstSeekType cur_type, + gint64 cur, GstSeekType stop_type, gint64 stop ) +{ + GstEvent* event = NULL; + gboolean result = FALSE; + + debug_fenter(); + + return_val_if_fail( player, FALSE ); + + event = gst_event_new_seek (rate, format, flags, cur_type, + cur, stop_type, stop); + + result = __gst_send_event_to_sink( player, event ); + + debug_fleave(); + + return result; +} + +/* NOTE : be careful with calling this api. please refer to below glib comment + * glib comment : Note that there is a bug in GObject that makes this function much + * less useful than it might seem otherwise. Once gobject is disposed, the callback + * will no longer be called, but, the signal handler is not currently disconnected. + * If the instance is itself being freed at the same time than this doesn't matter, + * since the signal will automatically be removed, but if instance persists, + * then the signal handler will leak. You should not remove the signal yourself + * because in a future versions of GObject, the handler will automatically be + * disconnected. + * + * It's possible to work around this problem in a way that will continue to work + * with future versions of GObject by checking that the signal handler is still + * connected before disconnected it: + * + * if (g_signal_handler_is_connected (instance, id)) + * g_signal_handler_disconnect (instance, id); + */ +static void +__mmplayer_release_signal_connection(mm_player_t* player) +{ + GList* sig_list = player->signals; + MMPlayerSignalItem* item = NULL; + + debug_fenter(); + + return_if_fail( player ); + + for ( ; sig_list; sig_list = sig_list->next ) + { + item = sig_list->data; + + if ( item && item->obj && GST_IS_ELEMENT(item->obj) ) + { + debug_log("checking signal connection : [%lud] from [%s]\n", item->sig, GST_OBJECT_NAME( item->obj )); + + if ( g_signal_handler_is_connected ( item->obj, item->sig ) ) + { + debug_log("signal disconnecting : [%lud] from [%s]\n", item->sig, GST_OBJECT_NAME( item->obj )); + g_signal_handler_disconnect ( item->obj, item->sig ); + } + } + + MMPLAYER_FREEIF( item ); + + } + g_list_free ( player->signals ); + player->signals = NULL; + + debug_fleave(); + + return; +} + + +/* Note : if silent is true, then subtitle would not be displayed. :*/ +int _mmplayer_set_subtitle_silent (MMHandleType hplayer, int silent) +{ + mm_player_t* player = (mm_player_t*) hplayer; + + debug_fenter(); + + /* check player handle */ + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + player->is_subtitle_off = silent; + + debug_log("subtitle is %s.\n", player->is_subtitle_off ? "ON" : "OFF"); + + debug_fleave(); + + return MM_ERROR_NONE; +} + + +int _mmplayer_get_subtitle_silent (MMHandleType hplayer, int* silent) +{ + mm_player_t* player = (mm_player_t*) hplayer; + + debug_fenter(); + + /* check player handle */ + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + *silent = player->is_subtitle_off; + + debug_log("subtitle is %s.\n", silent ? "ON" : "OFF"); + + debug_fleave(); + + return MM_ERROR_NONE; +} + +int _mmplayer_get_track_count(MMHandleType hplayer, MMPlayerTrackType track_type, int *count) +{ + mm_player_t* player = (mm_player_t*) hplayer; + MMHandleType attrs = 0; + int ret = MM_ERROR_NONE; + + debug_fenter(); + + /* check player handle */ + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail(count, MM_ERROR_COMMON_INVALID_ARGUMENT); + return_val_if_fail((MMPLAYER_CURRENT_STATE(player) != MM_PLAYER_STATE_PAUSED) + ||(MMPLAYER_CURRENT_STATE(player) != MM_PLAYER_STATE_PLAYING), + MM_ERROR_PLAYER_INVALID_STATE); + + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute"); + return MM_ERROR_PLAYER_INTERNAL; + } + + switch (track_type) + { + case MM_PLAYER_TRACK_TYPE_AUDIO: + ret = mm_attrs_get_int_by_name(attrs, "content_audio_track_num", count); + break; + case MM_PLAYER_TRACK_TYPE_VIDEO: + ret = mm_attrs_get_int_by_name(attrs, "content_video_track_num", count); + break; + case MM_PLAYER_TRACK_TYPE_TEXT: + ret = mm_attrs_get_int_by_name(attrs, "content_text_track_num", count); + break; + default: + ret = MM_ERROR_COMMON_INVALID_ARGUMENT; + break; + } + + debug_log ("%d track num is %d\n", track_type, *count); + + debug_fleave(); + + return ret; +} + + + +const gchar * +__get_state_name ( int state ) +{ + switch ( state ) + { + case MM_PLAYER_STATE_NULL: + return "NULL"; + case MM_PLAYER_STATE_READY: + return "READY"; + case MM_PLAYER_STATE_PAUSED: + return "PAUSED"; + case MM_PLAYER_STATE_PLAYING: + return "PLAYING"; + case MM_PLAYER_STATE_NONE: + return "NONE"; + default: + return "INVAID"; + } +} +gboolean +__is_rtsp_streaming ( mm_player_t* player ) +{ + return_val_if_fail ( player, FALSE ); + + return ( player->profile.uri_type == MM_PLAYER_URI_TYPE_URL_RTSP ) ? TRUE : FALSE; +} + +static gboolean +__is_http_streaming ( mm_player_t* player ) +{ + return_val_if_fail ( player, FALSE ); + + return ( player->profile.uri_type == MM_PLAYER_URI_TYPE_URL_HTTP ) ? TRUE : FALSE; +} + +static gboolean +__is_streaming ( mm_player_t* player ) +{ + return_val_if_fail ( player, FALSE ); + + return ( __is_rtsp_streaming ( player ) || __is_http_streaming ( player ) || __is_http_live_streaming ( player )) ? TRUE : FALSE; +} + +gboolean +__is_live_streaming ( mm_player_t* player ) +{ + return_val_if_fail ( player, FALSE ); + + return ( __is_rtsp_streaming ( player ) && player->streaming_type == STREAMING_SERVICE_LIVE ) ? TRUE : FALSE; +} + +static gboolean +__is_http_live_streaming( mm_player_t* player ) +{ + return_val_if_fail( player, FALSE ); + + return ( player->profile.uri_type == MM_PLAYER_URI_TYPE_HLS ) ? TRUE : FALSE; +} + +static gboolean +__is_http_progressive_down(mm_player_t* player) +{ + return_val_if_fail( player, FALSE ); + + return ((player->pd_mode) ? TRUE:FALSE); +} diff --git a/src/mm_player_streaming.c b/src/mm_player_streaming.c new file mode 100644 index 0000000..3f76b87 --- /dev/null +++ b/src/mm_player_streaming.c @@ -0,0 +1,433 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 "mm_player_utils.h" + +#include "mm_player_streaming.h" + +static void streaming_set_buffer_size(mm_player_streaming_t* streamer, guint buffer_size); +static void streaming_set_buffer_percent(mm_player_streaming_t* streamer, gdouble low_percent, gdouble high_percent); +static void streaming_set_buffer_type (mm_player_streaming_t* streamer, gboolean use_file, gchar * file_path, guint64 content_size); +static void streaming_set_buffering_time(mm_player_streaming_t* streamer, gdouble buffering_time); + + +mm_player_streaming_t * +__mm_player_streaming_create () +{ + mm_player_streaming_t *streamer = NULL; + + debug_fenter(); + + streamer = (mm_player_streaming_t *) malloc (sizeof (mm_player_streaming_t)); + if (!streamer) + { + debug_error ("fail to create streaming player handle..\n"); + return NULL; + } + + debug_fleave(); + + return streamer; +} + +void __mm_player_streaming_initialize (mm_player_streaming_t* streamer) +{ + debug_fenter(); + + streamer->buffer = NULL; + streamer->buffer_size = DEFAULT_BUFFER_SIZE; + streamer->buffer_low_percent = DEFAULT_BUFFER_LOW_PERCENT; + streamer->buffer_high_percent = DEFAULT_BUFFER_HIGH_PERCENT; + streamer->buffer_avg_bitrate = 0; + streamer->buffer_max_bitrate = 0; + streamer->need_update = FALSE; + + streamer->is_buffering = FALSE; + streamer->buffering_percent = -1; + streamer->buffering_time = DEFAULT_BUFFERING_TIME; + + debug_fleave(); + + return; +} + +void __mm_player_streaming_deinitialize (mm_player_streaming_t* streamer) +{ + debug_fenter(); + + return_if_fail(streamer); + + streamer->buffer_size = DEFAULT_BUFFER_SIZE; + streamer->buffer_low_percent = DEFAULT_BUFFER_LOW_PERCENT; + streamer->buffer_high_percent = DEFAULT_BUFFER_HIGH_PERCENT; + streamer->buffer_avg_bitrate = 0; + streamer->buffer_max_bitrate = 0; + streamer->need_update = FALSE; + + streamer->is_buffering = FALSE; + streamer->buffering_percent = -1; + streamer->buffering_time = DEFAULT_BUFFERING_TIME; + + debug_fleave(); + + return; +} + +void __mm_player_streaming_destroy (mm_player_streaming_t* streamer) +{ + debug_fenter(); + + if(streamer) + { + g_free (streamer); + streamer = NULL; + } + + debug_fleave(); + + return; +} + +void __mm_player_streaming_set_buffer(mm_player_streaming_t* streamer, GstElement * buffer, + gboolean use_buffering, guint buffer_size, gdouble low_percent, gdouble high_percent, gdouble buffering_time, + gboolean use_file, gchar * file_path, guint64 content_size) +{ + debug_fenter(); + + return_if_fail(streamer); + + if (buffer) + { + streamer->buffer = buffer; + + debug_log("buffer element is %s.", GST_ELEMENT_NAME(buffer)); + + g_object_set ( G_OBJECT (streamer->buffer), "use-buffering", use_buffering, NULL ); + } + + streaming_set_buffer_size(streamer, buffer_size); + streaming_set_buffer_percent(streamer, low_percent, high_percent); + streaming_set_buffer_type (streamer, use_file, file_path, content_size); + streaming_set_buffering_time(streamer, buffering_time); + + debug_fleave(); + + return; +} + +void __mm_player_streaming_set_content_bitrate(mm_player_streaming_t* streamer, guint max_bitrate, guint avg_bitrate) +{ + debug_fenter(); + + return_if_fail(streamer); + + /* Note : Update buffering criterion bytes + * 1. maximum bitrate is considered first. + * 2. average bitrage * 3 is next. + * 3. if there are no updated bitrate, use default buffering limit. + */ + if (max_bitrate > 0 && streamer->buffer_max_bitrate != max_bitrate) + { + debug_log("set maximum bitrate(%dbps).\n", max_bitrate); + streamer->buffer_max_bitrate = max_bitrate; + + streamer->need_update = TRUE; + } + + if (avg_bitrate > 0 && streamer->buffer_avg_bitrate != avg_bitrate) + { + debug_log("set averate bitrate(%dbps).\n", avg_bitrate); + streamer->buffer_avg_bitrate = avg_bitrate; + + streamer->need_update = TRUE; + } + + debug_fleave(); + + return; +} + +static void +streaming_set_buffer_size(mm_player_streaming_t* streamer, guint buffer_size) +{ + debug_fenter(); + + return_if_fail(streamer); + return_if_fail(buffer_size>0); + + debug_log("set buffer size to %d.", buffer_size); + + streamer->buffer_size = buffer_size; + + if (streamer->buffer) + g_object_set (G_OBJECT(streamer->buffer), "max-size-bytes", buffer_size, NULL); + + debug_fleave(); + + return; +} + +static void +streaming_set_buffer_percent(mm_player_streaming_t* streamer, gdouble low_percent, gdouble high_percent) +{ + gdouble buffer_low_percent = DEFAULT_BUFFER_LOW_PERCENT; + gdouble buffer_high_percent = DEFAULT_BUFFER_HIGH_PERCENT; + + debug_fenter(); + + return_if_fail(streamer); + + if (low_percent <= MIN_BUFFER_PERCENT || low_percent >= MAX_BUFFER_PERCENT) + { + debug_warning("buffer low percent is out of range. use defaut value."); + buffer_low_percent = DEFAULT_BUFFER_LOW_PERCENT; + } + else + { + buffer_low_percent = low_percent; + } + + if (high_percent <= MIN_BUFFER_PERCENT || high_percent >= MAX_BUFFER_PERCENT) + { + debug_warning("buffer high percent is out of range. use defaut value."); + buffer_high_percent = DEFAULT_BUFFER_HIGH_PERCENT; + } + else + { + buffer_high_percent = high_percent; + } + + if (buffer_high_percent <= buffer_low_percent) + buffer_high_percent = buffer_low_percent + 1.0; + + debug_log("set buffer percent to %2.3f ~ %2.3f.", streamer->buffer_low_percent, streamer->buffer_high_percent); + + if (streamer->buffer) + { + if ( streamer->buffer_low_percent != buffer_low_percent ) + g_object_set (G_OBJECT(streamer->buffer), "low-percent", streamer->buffer_low_percent, NULL); + + if ( streamer->buffer_high_percent != buffer_high_percent ) + g_object_set (G_OBJECT(streamer->buffer), "high-percent", streamer->buffer_high_percent, NULL); + } + + streamer->buffer_low_percent = buffer_low_percent; + streamer->buffer_high_percent = buffer_high_percent; + + debug_fleave(); + + return; +} + +static void +streaming_set_buffering_time(mm_player_streaming_t* streamer, gdouble buffering_time) +{ + gdouble buffer_buffering_time = DEFAULT_BUFFERING_TIME; + + debug_fenter(); + + return_if_fail(streamer); + + if (buffering_time < MIN_BUFFERING_TIME) + buffer_buffering_time = MIN_BUFFERING_TIME; + else if (buffering_time > MAX_BUFFERING_TIME) + buffer_buffering_time = MAX_BUFFERING_TIME; + else + buffer_buffering_time = buffering_time; + + if (streamer->buffering_time != buffer_buffering_time) + { + debug_log("set buffer buffering time from %2.1f to %2.1f.", streamer->buffering_time, buffer_buffering_time); + + streamer->buffering_time = buffer_buffering_time; + } + + debug_fleave(); + + return; +} + +static void +streaming_set_buffer_type (mm_player_streaming_t* streamer, gboolean use_file, gchar * file_path, guint64 content_size) +{ + guint64 storage_available_size = 0L; //bytes + guint64 file_buffer_size = 0L; //bytes + gchar file_buffer_name[MAX_FILE_BUFFER_NAME_LEN] = {0}; + struct statfs buf = {0}; + + debug_fenter(); + + return_if_fail(streamer && streamer->buffer); + + if (!use_file) + { + debug_log("use memory for buffering. streaming is played on push-based. \n" + "buffering position would not be updated.\n" + "buffered data would be flushed after played.\n" + "seeking and getting duration could be failed due to file format."); + return; + } + + debug_log("use file for buffering. streaming is played on pull-based. \n"); + + if (!file_path || strlen(file_path) <= 0) + file_path = g_strdup(DEFAULT_FILE_BUFFER_PATH); + + g_snprintf(file_buffer_name, MM_MAX_URL_LEN, "%s/XXXXXX", file_path); + debug_log("the buffering file name is %s.\n", file_buffer_name); + + if (statfs((const char *)file_path, &buf) < 0) + { + debug_warning ("fail to get availabe storage capacity. just use file buffer.\n"); + file_buffer_size = 0L; + } + else + { + storage_available_size = (guint64)buf.f_bavail * (guint64)buf.f_bsize; //bytes + + debug_log ("the number of available blocks : %"G_GUINT64_FORMAT", the block size is %"G_GUINT64_FORMAT".\n", + (guint64)buf.f_bavail, (guint64)buf.f_bsize); + debug_log ("calculated availabe storage size is %"G_GUINT64_FORMAT" Bytes.\n", storage_available_size); + + if (content_size <= 0 || content_size >= storage_available_size) + file_buffer_size = storage_available_size; + else + file_buffer_size = 0L; + } + + if (file_buffer_size>0) + debug_log("use file ring buffer for buffering."); + + g_object_set (G_OBJECT(streamer->buffer), "temp-template", file_buffer_name, NULL); + g_object_set (G_OBJECT(streamer->buffer), "file-buffer-max-size", file_buffer_size, NULL); + + debug_fleave(); + + return; +} + +#define GET_BYTE_FROM_BIT(bit) (bit/8) +void __mm_player_streaming_buffering(mm_player_streaming_t* streamer, GstMessage *buffering_msg) +{ + GstBufferingMode mode = GST_BUFFERING_STREAM; + gint byte_in_rate = 0; + gint byte_out_rate = 0; + gint64 buffering_left = -1; + gdouble buffering_time = DEFAULT_BUFFERING_TIME; + gdouble low_percent = 0.0; + gdouble high_percent = 0.0; + guint high_percent_byte = 0; + gint buffer_percent = 0; + guint buffer_criteria = 0; + + return_if_fail ( streamer ); + return_if_fail ( buffering_msg ); + return_if_fail ( GST_IS_MESSAGE ( buffering_msg ) ); + return_if_fail ( GST_MESSAGE_TYPE ( buffering_msg ) == GST_MESSAGE_BUFFERING ); + + /* update when buffering has started. */ + if ( !streamer->is_buffering ) + { + debug_log ( "buffering has started.\n" ); + + streamer->is_buffering = TRUE; + streamer->buffering_percent = -1; + streamer->need_update = TRUE; + } + + /* update buffer percent */ + gst_message_parse_buffering ( buffering_msg, &buffer_percent ); + + if ( streamer->buffering_percent < buffer_percent ) + { + debug_log ( "buffering %d%%....\n", buffer_percent ); + streamer->buffering_percent = buffer_percent; + } + + if ( streamer->buffering_percent == MAX_BUFFER_PERCENT ) + { + debug_log ( "buffering had done.\n" ); + streamer->is_buffering = FALSE; + } + + if (!streamer->need_update) + { + debug_log ( "don't need to update buffering stats during buffering.\n" ); + return; + } + + /* Note : Parse the buffering message to get the in/out throughput. + * avg_in is the network throughput and avg_out is the consumed throughtput by the linkded element. + */ + gst_message_parse_buffering_stats ( buffering_msg, &mode, &byte_in_rate, &byte_out_rate, &buffering_left ); + + if (streamer->buffer_max_bitrate > 0) + { + buffer_criteria = GET_BYTE_FROM_BIT(streamer->buffer_max_bitrate); + byte_out_rate = GET_BYTE_FROM_BIT(streamer->buffer_max_bitrate /3); + } + else if (streamer->buffer_avg_bitrate > 0) + { + buffer_criteria = GET_BYTE_FROM_BIT(streamer->buffer_avg_bitrate * 3); + byte_out_rate = GET_BYTE_FROM_BIT(streamer->buffer_avg_bitrate); + } + + debug_log ( "in rate is %d, out rate is %d (bytes/sec).\n", byte_in_rate, byte_out_rate ); + + if ( byte_in_rate > 0 && byte_out_rate > 0) + buffering_time = byte_out_rate / byte_in_rate; + else if (byte_in_rate <= 0 && byte_out_rate > 0) + buffering_time = MAX_BUFFERING_TIME; + else + buffering_time = DEFAULT_BUFFERING_TIME; + + streaming_set_buffering_time(streamer, buffering_time); + + /* calculate buffer low/high percent */ + low_percent = DEFAULT_BUFFER_LOW_PERCENT; + + if ( buffer_criteria > 0 ) + { + high_percent_byte = buffer_criteria * streamer->buffering_time; + high_percent = ( (gdouble)high_percent_byte * 100.0 ) / (gdouble)streamer->buffer_size; + } + else + { + high_percent_byte = streamer->buffer_high_percent * streamer->buffer_size / 100; + high_percent= streamer->buffer_high_percent; + } + + if ( streamer->buffer_size < high_percent_byte ) + { + debug_log ( "buffer size[%d bytes] is smaller than high threshold[%d bytes]. update it. \n", + streamer->buffer_size, high_percent_byte ); + + streaming_set_buffer_size(streamer, high_percent_byte * 1.1); + } + + streaming_set_buffer_percent(streamer, low_percent, high_percent); + + streamer->need_update = FALSE; + + return; +} + diff --git a/src/mm_player_utils.c b/src/mm_player_utils.c new file mode 100644 index 0000000..261d7d4 --- /dev/null +++ b/src/mm_player_utils.c @@ -0,0 +1,447 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * + * 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 +#include "mm_player_utils.h" + +bool util_exist_file_path(const char *file_path) +{ + debug_log("\n"); + + if (!file_path || !strlen(file_path)) + return FALSE; + + int res = access(file_path, R_OK); + if (res) + return FALSE; + + return TRUE; +} + +bool util_write_file_backup(const char *backup_path, char *data_ptr, int data_size) +{ + debug_log("\n"); + + FILE *fp = NULL; + int wsize = 0; + + fp = fopen(backup_path, "wb"); + if (!fp) + return FALSE; + + wsize = fwrite(data_ptr, sizeof(char), data_size, fp); + + fclose(fp); + + if (wsize != data_size) { + if (!access(backup_path, R_OK)) + remove(backup_path); + + debug_error("No space to write!\n"); + + return FALSE; + } + + return TRUE; +} + +bool util_remove_file_backup(const char *backup_path) +{ + debug_log("\n"); + + if (!backup_path || !strlen(backup_path)) + return FALSE; + + int res = access(backup_path, R_OK); + if (!res) + { + if (remove(backup_path) == -1) + return FALSE; + } + + return TRUE; +} + +#define DETECTION_PREFIX_SIZE 20 +//bool util_is_midi_type_by_mem(void *mem, int size) +int util_is_midi_type_by_mem(void *mem, int size) +{ + debug_log("\n"); + + const char *p = (const char *)mem; + + if (size < DETECTION_PREFIX_SIZE) + return MM_AUDIO_CODEC_INVALID; + + /* mmf file detection */ + if (p[0] == 'M' && p[1] == 'M' && p[2] == 'M' && p[3] == 'D') { + debug_log("MM_AUDIO_CODEC_MMF\n"); + return MM_AUDIO_CODEC_MMF; + } + + /* midi file detection */ + if (p[0] == 'M' && p[1] == 'T' && p[2] == 'h' && p[3] == 'd') { + debug_log ("MM_AUDIO_CODEC_MIDI, %d\n", MM_AUDIO_CODEC_MIDI); + return MM_AUDIO_CODEC_MIDI; + } + /* mxmf file detection */ + if (p[0] == 'X' && p[1] == 'M' && p[2] == 'F' && p[3] == '_') { + debug_log ("MM_AUDIO_CODEC_MXMF\n"); + return MM_AUDIO_CODEC_MXMF; + } + + /* wave file detection */ + if (p[0] == 'R' && p[1] == 'I' && p[2] == 'F' && p[3] == 'F' && + p[8] == 'W' && p[9] == 'A' && p[10] == 'V' && p[11] == 'E' && + p[12] == 'f' && p[13] == 'm' && p[14] == 't') { + debug_log ("MM_AUDIO_CODEC_WAVE\n"); + return MM_AUDIO_CODEC_WAVE; + } + /* i-melody file detection */ + if (memcmp(p, "BEGIN:IMELODY", 13) == 0) + { + debug_log ("MM_AUDIO_CODEC_IMELODY\n"); + return MM_AUDIO_CODEC_IMELODY; + } + + return MM_AUDIO_CODEC_INVALID; +} + +//bool util_is_midi_type_by_file(const char *file_path) +int util_is_midi_type_by_file(const char *file_path) +{ + debug_log("\n"); + + struct stat file_attrib; + FILE *fp = NULL; + char prefix[DETECTION_PREFIX_SIZE] = {0,}; + int size; + + if (!file_path) + return FALSE; + + fp = fopen(file_path, "r"); + + if (!fp) + return FALSE; + + memset(&file_attrib, 0, sizeof(file_attrib)); + + if (stat(file_path, &file_attrib) != 0) + { + fclose(fp); + return FALSE; + } + + size = (int) file_attrib.st_size; + + if (size < DETECTION_PREFIX_SIZE) + { + fclose(fp); + return FALSE; + } + + size = fread(prefix, sizeof(char), DETECTION_PREFIX_SIZE, fp); + + fclose(fp); + + return util_is_midi_type_by_mem(prefix, size); +} + +/* messages are treated as warnings bcz those code should not be checked in. + * and no error handling will supported for same manner. + */ +gboolean +__util_gst_pad_probe(GstPad *pad, GstBuffer *buffer, gpointer u_data) +{ + gint flag = (gint) u_data; + GstElement* parent = NULL; + gboolean ret = TRUE; + + /* show name as default */ + parent = (GstElement*)gst_object_get_parent(GST_OBJECT(pad)); + debug_warning("PAD PROBE : %s:%s\n", GST_ELEMENT_NAME(parent), GST_PAD_NAME(pad)); + + /* show time stamp */ + if ( flag & MM_PROBE_TIMESTAMP ) + { + debug_warning("ts : %u:%02u:%02u.%09u\n", GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer))); + } + + /* show buffer size */ + if ( flag & MM_PROBE_BUFFERSIZE ) + { + debug_warning("buffer size : %ud\n", GST_BUFFER_SIZE(buffer)); + } + + /* show buffer duration */ + if ( flag & MM_PROBE_BUFFER_DURATION ) + { + debug_warning("dur : %lld\n", GST_BUFFER_DURATION(buffer)); + } + + /* show buffer caps */ + if ( flag & MM_PROBE_CAPS ) + { + debug_warning("caps : %s\n", gst_caps_to_string(GST_BUFFER_CAPS(buffer))); + } + + /* drop buffer if flag is on */ + if ( flag & MM_PROBE_DROP_BUFFER ) + { + debug_warning("dropping\n"); + ret = FALSE; + } + + /* show clock time */ + if ( flag & MM_PROBE_CLOCK_TIME ) + { + GstClock* clock = NULL; + GstClockTime now = GST_CLOCK_TIME_NONE; + + clock = GST_ELEMENT_CLOCK ( parent ); + + if ( clock ) + { + now = gst_clock_get_time( clock ); + debug_warning("clock time : %" GST_TIME_FORMAT "\n", GST_TIME_ARGS( now )); + } + } + + if ( parent ) + gst_object_unref(parent); + + return ret; +} + +char** +util_get_cookie_list ( const char *cookies ) +{ + char **cookie_list = NULL; + char *temp = NULL; + gint i = 0; + + if ( !cookies || !strlen(cookies) ) + return NULL; + + debug_log("cookies : %d[bytes] - %s \n", strlen(cookies), cookies); + + temp = g_strdup(cookies); + + /* trimming. it works inplace */ + g_strstrip(temp); + + /* split */ + cookie_list = g_strsplit(temp, ";", 100); + + for ( i = 0; i < g_strv_length(cookie_list); i++ ) + { + if ( cookie_list[i] && strlen(cookie_list[i]) ) + { + g_strstrip(cookie_list[i]); + debug_log("cookie_list[%d] : %d[bytes] - %s \n", i, strlen(cookie_list[i]), cookie_list[i]); + } + else + { + cookie_list[i][0]='\0'; + } + } + + if (temp) + g_free (temp); + temp=NULL; + + return cookie_list; +} + +bool util_check_valid_url ( const char *proxy ) +{ + struct in_addr proxy_addr; + bool ret = TRUE; + + return_val_if_fail ( proxy, FALSE ); + return_val_if_fail ( strlen(proxy), FALSE ); + + if ( inet_aton(proxy, &proxy_addr) != 0 ) + { + debug_warning("invalid proxy is set. \n"); + ret = FALSE; + } + + return ret; +} + +/* check the given path is indicating sdp file */ +bool +util_is_sdp_file ( const char *path ) +{ + gboolean ret = FALSE; + gchar* uri = NULL; + + debug_fenter(); + + return_val_if_fail ( path, FALSE ); + + uri = g_ascii_strdown ( path, -1 ); + + if ( uri == -1) + { + return FALSE; + } + + /* trimming */ + g_strstrip( uri ); + + /* strlen(".sdp") == 4 */ + if ( strlen( uri ) <= 4 ) + { + debug_warning ( "path is too short.\n" ); + return ret; + } + + /* first, check extension name */ + ret = g_str_has_suffix ( uri, "sdp" ); + + /* second, if no suffix is there, check it's contents */ + if ( ! ret ) + { + /* FIXIT : do it soon */ + debug_warning("determining whether it's sdp or not with it's content is not implemented yet. ;)\n"); + } + + g_free( uri); + uri = NULL; + + return ret; +} + +int64_t +util_get_time ( void ) +{ + struct timeval tv; + gettimeofday(&tv,NULL); + return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; +} + +int +util_get_rank_increase ( const char *factory_class ) +{ + gint rank_pri_inc = 20; + gint rank_sec_inc = 10; + gint ret = 0; + + if ( g_strrstr(factory_class,"Dsp") ) + ret = rank_pri_inc; + else if ( g_strrstr(factory_class,"HW") ) + ret = rank_pri_inc; + else if ( g_strrstr(factory_class,"Arm") ) + ret = rank_sec_inc; + + return ret; +} + +int +util_factory_rank_compare(GstPluginFeature *f1, GstPluginFeature *f2) // @ +{ + const gchar *klass; + int f1_rank_inc=0, f2_rank_inc=0; + + klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(f1)); + f1_rank_inc = util_get_rank_increase ( klass ); + + klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(f2)); + f2_rank_inc = util_get_rank_increase ( klass ); + + return (gst_plugin_feature_get_rank(f2)+f2_rank_inc) - (gst_plugin_feature_get_rank(f1)+f1_rank_inc ); +} + +char* +util_get_charset(const char *file_path) +{ + UCharsetDetector* ucsd; + const UCharsetMatch* ucm; + UErrorCode status = U_ZERO_ERROR; + + const char* charset = NULL; + char *buf = NULL; + FILE* fin; + + fin = fopen(file_path, "r"); + if (!fin) + { + debug_error("fail to open file %s\n", file_path); + return NULL; + } + + ucsd = ucsdet_open( &status ); + if( U_FAILURE(status) ) { + debug_error("fail to ucsdet_open\n"); + return NULL; + } + + ucsdet_enableInputFilter( ucsd, TRUE ); + + buf = g_malloc(1024*1024); + if (!buf) + { + debug_error("fail to alloc\n"); + goto done; + } + + fread( buf, 1, 1024*1024, fin ); + fclose(fin); + + ucsdet_setText( ucsd, buf, strlen(buf), &status ); + if( U_FAILURE(status) ) { + debug_error("fail to ucsdet_setText\n"); + goto done; + } + + ucm = ucsdet_detect( ucsd, &status ); + if( U_FAILURE(status) ) { + debug_error("fail to ucsdet_detect\n"); + goto done; + } + + charset = ucsdet_getName( ucm, &status ); + if( U_FAILURE(status) ) { + debug_error("fail to ucsdet_getName\n"); + goto done; + } + +done: + ucsdet_close( ucsd ); + + if (buf) + g_free(buf); + + return charset; +} +