From 712066a6e8c39ebe8a15843edb836214c8703220 Mon Sep 17 00:00:00 2001 From: "eunhae1.choi" Date: Mon, 26 May 2014 18:27:10 +0900 Subject: [PATCH] devide files to resolve build break Change-Id: I35cc8556b93e91798dc13ce7ea17c8d44bad2a00 --- src/Makefile.am | 3 + src/include/mm_player_priv_internal.h | 55 + src/include/mm_player_priv_locl_func.h | 78 + src/mm_player_priv.c | 11321 +++++++++---------------------- src/mm_player_priv_gst.c | 1145 ++++ src/mm_player_priv_gst_wrapper.c | 3029 +++++++++ src/mm_player_priv_internal.c | 1201 ++++ src/mm_player_streaming.c | 2 +- src/mm_player_utils.c | 42 +- 9 files changed, 8594 insertions(+), 8282 deletions(-) mode change 100644 => 100755 src/Makefile.am create mode 100755 src/include/mm_player_priv_internal.h create mode 100755 src/include/mm_player_priv_locl_func.h create mode 100755 src/mm_player_priv_gst.c create mode 100755 src/mm_player_priv_gst_wrapper.c create mode 100755 src/mm_player_priv_internal.c mode change 100644 => 100755 src/mm_player_streaming.c mode change 100644 => 100755 src/mm_player_utils.c diff --git a/src/Makefile.am b/src/Makefile.am old mode 100644 new mode 100755 index 8e1523e..4081fe4 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,6 +8,9 @@ includelibmmfplayer_HEADERS = include/mm_player.h \ libmmfplayer_la_SOURCES = mm_player.c \ mm_player_priv.c \ + mm_player_priv_gst.c \ + mm_player_priv_internal.c \ + mm_player_priv_gst_wrapper.c \ mm_player_ini.c \ mm_player_utils.c \ mm_player_asm.c \ diff --git a/src/include/mm_player_priv_internal.h b/src/include/mm_player_priv_internal.h new file mode 100755 index 0000000..40b9dc4 --- /dev/null +++ b/src/include/mm_player_priv_internal.h @@ -0,0 +1,55 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , Heechul Jeon + * + * 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_INTERNAL_H__ +#define __MM_PLAYER_PRIV_INTERNAL_H__ + +#define MM_PLAYER_FADEOUT_TIME_DEFAULT 700000 // 700 msec + + +/*--------------------------------------------------------------------------- +| LOCAL FUNCTION PROTOTYPES: | +---------------------------------------------------------------------------*/ +void __mmplayer_release_signal_connection(mm_player_t* player); +gboolean __mmplayer_dump_pipeline_state(mm_player_t* player); +int __mmplayer_gst_set_state (mm_player_t* player, GstElement * element, GstState state, gboolean async, gint timeout); +gboolean __mmplayer_check_subtitle(mm_player_t* player); +int __mmplayer_handle_missed_plugin(mm_player_t* player); +gboolean __mmplayer_link_decoder(mm_player_t* player, GstPad *srcpad); +gboolean __mmplayer_link_sink(mm_player_t* player , GstPad *srcpad); +gint __gst_handle_core_error(mm_player_t* player, int code); +gint __gst_handle_library_error(mm_player_t* player, int code); +gint __gst_handle_resource_error(mm_player_t* player, int code); +gint __gst_handle_stream_error(mm_player_t* player, GError* error, GstMessage * message); +gint __gst_transform_gsterror( mm_player_t* player, GstMessage * message, GError* error ); +gboolean __mmplayer_handle_gst_error ( mm_player_t* player, GstMessage * message, GError* error ); +gboolean __mmplayer_handle_streaming_error ( mm_player_t* player, GstMessage * message ); +void __mmplayer_add_sink( mm_player_t* player, GstElement* sink ); +void __mmplayer_del_sink( mm_player_t* player, GstElement* sink ); +gboolean __is_rtsp_streaming ( mm_player_t* player ); +gboolean __is_http_streaming ( mm_player_t* player ); +gboolean __is_streaming ( mm_player_t* player ); +gboolean __is_live_streaming ( mm_player_t* player ); +gboolean __is_http_live_streaming( mm_player_t* player ); +gboolean __is_http_progressive_down(mm_player_t* player); + +#endif /* __MM_PLAYER_PRIV_INTERNAL_H__ */ + diff --git a/src/include/mm_player_priv_locl_func.h b/src/include/mm_player_priv_locl_func.h new file mode 100755 index 0000000..c63c0ce --- /dev/null +++ b/src/include/mm_player_priv_locl_func.h @@ -0,0 +1,78 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , Heechul Jeon + * + * 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_LOCAL_FUNCTION_DEF_H_ +#define __MM_PLAYER_LOCAL_FUNCTION_DEF_H_ + + +/*--------------------------------------------------------------------------- +| LOCAL FUNCTION PROTOTYPES: | +---------------------------------------------------------------------------*/ +/* mm_player_priv.c */ +gboolean __mmplayer_eos_timer_cb(gpointer u_data); +void __mmplayer_typefind_have_type( GstElement *tf, guint probability, GstCaps *caps, gpointer data); +GstBusSyncReply __mmplayer_bus_sync_callback (GstBus * bus, GstMessage * message, gpointer data); +gboolean __mmplayer_update_subtitle( GstElement* object, GstBuffer *buffer, GstPad *pad, gpointer data); +void __mmplayer_videoframe_render_error_cb(GstElement *element, void *error_id, gpointer data); +void __mmplayer_videostream_cb(GstElement *element, void *stream, int width, int height, gpointer data); + +/* mm_player_priv_wrapper.c */ +gboolean __mmplayer_gst_callback(GstBus *bus, GstMessage *msg, gpointer data); +gboolean __mmplayer_gst_handle_duration(mm_player_t* player, GstMessage* msg); +gboolean __mmplayer_gst_extract_tag_from_msg(mm_player_t* player, GstMessage* msg); +void __mmplayer_gst_rtp_no_more_pads (GstElement *element, gpointer data); +gboolean __mmplayer_gst_remove_fakesink(mm_player_t* player, MMPlayerGstElement* fakesink); +void __mmplayer_gst_rtp_dynamic_pad (GstElement *element, GstPad *pad, gpointer data); +void __mmplayer_gst_decode_callback(GstElement *decodebin, GstPad *pad, gboolean last, gpointer data); +int __mmplayer_gst_element_link_bucket(GList* element_bucket); +int __mmplayer_gst_element_add_bucket_to_bin(GstBin* bin, GList* element_bucket); +int __mmplayer_gst_create_audio_pipeline(mm_player_t* player); +int __mmplayer_gst_create_video_pipeline(mm_player_t* player, GstCaps* caps, MMDisplaySurfaceType surface_type); +int __mmplayer_gst_create_text_pipeline(mm_player_t* player); +int __mmplayer_gst_create_subtitle_src(mm_player_t* player); +int __mmplayer_gst_create_pipeline(mm_player_t* player); +int __mmplayer_gst_destroy_pipeline(mm_player_t* player); + +/* mm_player_priv_gst.c */ +int __gst_realize(mm_player_t* player); +int __gst_unrealize(mm_player_t* player); +int __gst_pending_seek(mm_player_t* player); +int __gst_start(mm_player_t* player); +int __gst_stop(mm_player_t* player); +int __gst_pause(mm_player_t* player, gboolean async); +int __gst_resume(mm_player_t* player, gboolean async); +int __gst_set_position(mm_player_t* player, int format, unsigned long position, gboolean internal_called); +int __gst_get_position(mm_player_t* player, int format, unsigned long* position); +int __gst_get_buffer_position(mm_player_t* player, int format, unsigned long* start_pos, unsigned long* stop_pos); +int __gst_set_message_callback(mm_player_t* player, MMMessageCallback callback, gpointer user_param); +gboolean __gst_send_event_to_sink( mm_player_t* player, GstEvent* event ); +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); +int __gst_adjust_subtitle_position(mm_player_t* player, int format, int position); +void __gst_appsrc_feed_data_mem(GstElement *element, guint size, gpointer user_data); +gboolean __gst_appsrc_seek_data_mem(GstElement *element, guint64 size, gpointer user_data); +void __gst_appsrc_feed_data(GstElement *element, guint size, gpointer user_data); +gboolean __gst_appsrc_seek_data(GstElement *element, guint64 offset, gpointer user_data); +gboolean __gst_appsrc_enough_data(GstElement *element, gpointer user_data); + +#endif /*#ifndef __MM_PLAYER_LOCAL_FUNCTION_DEF_H_*/ + diff --git a/src/mm_player_priv.c b/src/mm_player_priv.c index 0a6a1ea..ede7896 100755 --- a/src/mm_player_priv.c +++ b/src/mm_player_priv.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include @@ -47,6 +47,8 @@ #include "mm_player_ini.h" #include "mm_player_attrs.h" #include "mm_player_capture.h" +#include "mm_player_priv_internal.h" +#include "mm_player_priv_locl_func.h" /*=========================================================================================== | | @@ -76,8 +78,6 @@ #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" @@ -88,9 +88,6 @@ #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 @@ -116,18 +113,6 @@ gulong ahs_appsrc_cb_probe_id = 0; ---------------------------------------------------------------------------*/ 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); @@ -138,73 +123,26 @@ static gboolean __mmplayer_close_link(mm_player_t* player, GstPad *srcpad, GstEl 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_get_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); -static gboolean __mmplayer_gst_handle_duration(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_handle_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 gboolean _mmplayer_update_content_attrs(mm_player_t* player, enum content_attr_flag flag); - - -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 ); @@ -217,15 +155,8 @@ 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); @@ -444,70 +375,7 @@ ALREADY_GOING: 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); - - /* dump state of all element */ - __mmplayer_dump_pipeline_state( player ); - - 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) ); - - /* dump state of all element */ - __mmplayer_dump_pipeline_state( player ); - - 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 +void __mmplayer_videostream_cb(GstElement *element, void *stream, int width, int height, gpointer data) // @ { @@ -527,7 +395,7 @@ int width, int height, gpointer data) // @ debug_fleave(); } -static void +void __mmplayer_videoframe_render_error_cb(GstElement *element, void *error_id, gpointer data) { mm_player_t* player = (mm_player_t*)data; @@ -1414,5926 +1282,1518 @@ __mmplayer_handle_buffering_message ( mm_player_t* player ) } } + static gboolean -__mmplayer_gst_callback(GstBus *bus, GstMessage *msg, gpointer data) // @ +__mmplayer_get_property_value_for_rotation(mm_player_t* player, int rotation_angle, int *value) { - mm_player_t* player = (mm_player_t*) data; - gboolean ret = TRUE; - static gboolean async_done = FALSE; + int pro_value = 0; // in the case of expection, default will be returned. + int dest_angle = rotation_angle; + int rotation_using_type = -1; + #define ROTATION_USING_X 0 + #define ROTATION_USING_FIMC 1 + #define ROTATION_USING_FLIP 2 - return_val_if_fail ( player, FALSE ); - return_val_if_fail ( msg && GST_IS_MESSAGE(msg), FALSE ); + return_val_if_fail(player, FALSE); + return_val_if_fail(value, FALSE); + return_val_if_fail(rotation_angle >= 0, FALSE); -#ifdef GST_API_VERSION_1 - const GstStructure *structure; - structure = gst_message_get_structure (msg); -#endif + if (rotation_angle >= 360) + { + dest_angle = rotation_angle - 360; + } - switch ( GST_MESSAGE_TYPE( msg ) ) + /* chech if supported or not */ + if ( dest_angle % 90 ) { - case GST_MESSAGE_UNKNOWN: - debug_warning("unknown message received\n"); - break; + debug_log("not supported rotation angle = %d", rotation_angle); + return FALSE; + } + + if (player->use_video_stream) + { + if (player->is_nv12_tiled) + { + rotation_using_type = ROTATION_USING_FIMC; + } + else + { + 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); - case GST_MESSAGE_EOS: + switch (surface_type) { - MMHandleType attrs = 0; - gint count = 0; + case MM_DISPLAY_SURFACE_X: + rotation_using_type = ROTATION_USING_X; + break; + case MM_DISPLAY_SURFACE_EVAS: + if (player->is_nv12_tiled && !strcmp(PLAYER_INI()->videosink_element_evas,"evasimagesink")) + { + rotation_using_type = ROTATION_USING_FIMC; + } + else if (!player->is_nv12_tiled) + { + rotation_using_type = ROTATION_USING_FLIP; + } + else + { + debug_error("it should not be here.."); + return FALSE; + } + break; + default: + rotation_using_type = ROTATION_USING_FLIP; + break; + } + } - debug_log("GST_MESSAGE_EOS received\n"); + debug_log("using %d type for rotation", rotation_using_type); - /* 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) ) + /* get property value for setting */ + switch(rotation_using_type) + { + case ROTATION_USING_X: // xvimagesink { - debug_warning("EOS received on non-playing state. ignoring it\n"); - break; + 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; + } } - - if ( (player->audio_stream_cb) && (player->is_sound_extraction) ) + break; + case ROTATION_USING_FIMC: // fimcconvert { - 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 */ -#ifdef GST_API_VERSION_1 - gst_pad_remove_probe (pad, player->audio_cb_probe_id); -#else - gst_pad_remove_buffer_probe (pad, player->audio_cb_probe_id); -#endif - 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; - + switch (dest_angle) + { + case 0: + break; + case 90: + pro_value = 90; // clockwise 90 + break; + case 180: + pro_value = 180; + break; + case 270: + pro_value = 270; // counter-clockwise 90 + break; + } } - - /* rewind if repeat count is greater then zero */ - /* get play count */ - attrs = MMPLAYER_GET_ATTRS(player); - - if ( attrs ) + break; + case ROTATION_USING_FLIP: // videoflip { - 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 ) + switch (dest_angle) { - debug_log("smooth repeat enabled. seeking operation will be excuted in new thread\n"); - g_cond_signal( player->repeat_thread_cond ); - - break; + 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; } - else - { - gint ret_value = 0; + } + break; + } - if ( player->section_repeat ) - { - ret_value = _mmplayer_activate_section_repeat((MMHandleType)player, player->section_repeat_start, player->section_repeat_end); - } - else - { + debug_log("setting rotation property value : %d", pro_value); - 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 ); - } + *value = pro_value; - ret_value = __gst_set_position( player, MM_PLAYER_POS_FORMAT_TIME, 0, TRUE); + return TRUE; +} - /* initialize */ - player->sent_bos = FALSE; - } +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; - 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--; + debug_fenter(); - mm_attrs_set_int_by_name(attrs, "profile_play_count", count); + /* 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 ); - if ( mmf_attrs_commit ( attrs ) ) - debug_error("failed to commit attrs\n"); - } - } + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("cannot get content attribute"); + return MM_ERROR_PLAYER_INTERNAL; + } - break; - } - } - } + /* update user roation */ + mm_attrs_get_int_by_name(attrs, "display_rotation", &user_angle_type); - MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-eos" ); + /* 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; + } - /* post eos message to application */ - __mmplayer_post_delayed_eos( player, PLAYER_INI()->eos_delay ); + /* get original orientation */ + if (player->v_stream_caps) + { + GstStructure *str = NULL; - /* reset last position */ - player->last_position = 0; + 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"); } - break; - - case GST_MESSAGE_ERROR: + else { - GError *error = NULL; - gchar* debug = NULL; - gchar *msg_src_element = NULL; + debug_log("origianl video orientation = %d", org_angle); + } + } - /* generating debug info before returning error */ - MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-error" ); + debug_log("check user angle: %d, org angle: %d", user_angle, org_angle); - /* get error code */ - gst_message_parse_error( msg, &error, &debug ); + /* get rotation value to set */ + __mmplayer_get_property_value_for_rotation(player, org_angle+user_angle, &rotation_value); - msg_src_element = GST_ELEMENT_NAME( GST_ELEMENT_CAST( msg->src ) ); -#ifdef GST_API_VERSION_1 - if ( gst_structure_has_name ( 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 ); + /* check video stream callback is used */ + if( player->use_video_stream ) + { + if (player->is_nv12_tiled) + { + gchar *ename = NULL; + int width = 0; + int height = 0; - /* dump state of all element */ - __mmplayer_dump_pipeline_state( player ); + mm_attrs_get_int_by_name(attrs, "display_width", &width); + mm_attrs_get_int_by_name(attrs, "display_height", &height); - } + /* resize video frame with requested values for fimcconvert */ +#ifdef GST_API_VERSION_1 + ename = GST_OBJECT (gst_element_get_factory(player->pipeline->videobin[MMPLAYER_V_CONV].gst)); #else - 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 ); + ename = GST_PLUGIN_FEATURE_NAME(gst_element_get_factory(player->pipeline->videobin[MMPLAYER_V_CONV].gst)); +#endif - /* dump state of all element */ - __mmplayer_dump_pipeline_state( player ); - } - else + if (g_strrstr(ename, "fimcconvert")) { - /* traslate gst error code to msl error code. then post it - * to application if needed - */ - __mmplayer_handle_gst_error( player, msg, error ); + if (width) + g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-width", width, NULL); - /* dump state of all element */ - __mmplayer_dump_pipeline_state( player ); + if (height) + g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-height", height, NULL); + g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "rotate", rotation_value, NULL); + debug_log("updating fimcconvert - r[%d], w[%d], h[%d]", rotation_value, width, height); } - -#endif - - - if (MMPLAYER_IS_HTTP_PD(player)) + else { - _mmplayer_unrealize_pd_downloader ((MMHandleType)player); + debug_error("no available video converter"); + return MM_ERROR_PLAYER_INTERNAL; } - - MMPLAYER_FREEIF( debug ); - g_error_free( error ); } - break; - - case GST_MESSAGE_WARNING: + else { - char* debug = NULL; - GError* error = NULL; + debug_log("using video stream callback with memsink. player handle : [%p]", player); - gst_message_parse_warning(msg, &error, &debug); + g_object_set(player->pipeline->videobin[MMPLAYER_V_FLIP].gst, "method", rotation_value, NULL); + } - debug_warning("warning : %s\n", error->message); - debug_warning("debug : %s\n", debug); + return MM_ERROR_NONE; + } - MMPLAYER_POST_MSG( player, MM_MESSAGE_WARNING, NULL ); + /* 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); - MMPLAYER_FREEIF( debug ); - g_error_free( error ); - } - break; + /* 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; - case GST_MESSAGE_INFO: debug_log("GST_MESSAGE_STATE_DIRTY\n"); break; + /* common case if using x surface */ + mm_attrs_get_data_by_name(attrs, "display_overlay", &xid); + if ( xid ) + { +#define GST_VAAPI_DISPLAY_TYPE_X11 1 + if (!strncmp(PLAYER_INI()->videosink_element_x,"vaapisink", strlen("vaapisink"))){ + debug_log("set video param: vaapisink display %d", GST_VAAPI_DISPLAY_TYPE_X11); + g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, + "display", GST_VAAPI_DISPLAY_TYPE_X11, + NULL); + } - case GST_MESSAGE_TAG: - { - debug_log("GST_MESSAGE_TAG\n"); - if ( ! __mmplayer_gst_extract_tag_from_msg( player, msg ) ) + debug_log("set video param : xid %d", *(int*)xid); +#ifdef GST_API_VERSION_1 + gst_video_overlay_set_window_handle( GST_VIDEO_OVERLAY( player->pipeline->videobin[MMPLAYER_V_SINK].gst ), *(int*)xid ); +#else + gst_x_overlay_set_xwindow_id( GST_X_OVERLAY( player->pipeline->videobin[MMPLAYER_V_SINK].gst ), *(int*)xid ); +#endif + } + else { - debug_warning("failed to extract tags from gstmessage\n"); + /* FIXIT : is it error case? */ + debug_warning("still we don't have xid on player attribute. create it's own surface."); } - } - 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); + /* 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); - __mmplayer_handle_buffering_message ( player ); + 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 ); - 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 ); + 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); } + + /* if vaapisink */ + if (!strncmp(PLAYER_INI()->videosink_element_x, "vaapisink", strlen("vaapisink"))) + { + g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, + "rotation", rotation_value, + NULL); + debug_log("set video param: vaapisink rotation %d", rotation_value); + } } break; - - case GST_MESSAGE_STATE_CHANGED: + case MM_DISPLAY_SURFACE_EVAS: { - MMPlayerGstElement *mainbin; - const GValue *voldstate, *vnewstate, *vpending; - GstState oldstate, newstate, pending; + void *object = NULL; + int scaling = 0; + gboolean visible = TRUE; - if ( ! ( player->pipeline && player->pipeline->mainbin ) ) + /* 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) { - debug_error("player pipeline handle is null"); - break; + 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); } - - mainbin = player->pipeline->mainbin; - - /* we only handle messages from pipeline */ - if( msg->src != (GstObject *)mainbin[MMPLAYER_M_PIPE].gst ) - break; - - /* get state info from msg */ -#ifdef GST_API_VERSION_1 - voldstate = gst_structure_get_value (structure, "old-state"); - vnewstate = gst_structure_get_value (structure, "new-state"); - vpending = gst_structure_get_value (structure, "pending-state"); -#else - 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"); -#endif - - oldstate = (GstState)voldstate->data[0].v_int; - newstate = (GstState)vnewstate->data[0].v_int; - pending = (GstState)vpending->data[0].v_int; - - 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 ) ); - - if (oldstate == newstate) + else { - debug_warning("pipeline reports state transition to old state"); - break; + debug_error("no evas object"); + return MM_ERROR_PLAYER_INTERNAL; } - switch(newstate) + /* if evasimagesink */ + if (!strcmp(PLAYER_INI()->videosink_element_evas,"evasimagesink") && player->is_nv12_tiled) { - case GST_STATE_VOID_PENDING: - break; + int width = 0; + int height = 0; + int no_scaling = !scaling; - case GST_STATE_NULL: - break; + mm_attrs_get_int_by_name(attrs, "display_width", &width); + mm_attrs_get_int_by_name(attrs, "display_height", &height); - case GST_STATE_READY: - break; + /* NOTE: fimcconvert does not manage index of src buffer from upstream src-plugin, decoder gives frame information in output buffer with no ordering */ + g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-buffer-num", 5, NULL); - case GST_STATE_PAUSED: + if (no_scaling) { - gboolean prepare_async = FALSE; - - 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); - } - } + /* no-scaling order to fimcconvert, original width, height size of media src will be passed to sink plugin */ + g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, + "dst-width", 0, /* setting 0, output video width will be media src's width */ + "dst-height", 0, /* setting 0, output video height will be media src's height */ + NULL); } - break; - - case GST_STATE_PLAYING: + else { - if (player->doing_seek && async_done) + /* scaling order to fimcconvert */ + if (width) { - player->doing_seek = FALSE; - async_done = FALSE; - MMPLAYER_POST_MSG ( player, MM_MESSAGE_SEEK_COMPLETED, NULL ); + g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-width", width, NULL); } - - if ( MMPLAYER_IS_STREAMING(player) ) // managed prepare async case when buffering is completed + if (height) { - // 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); + g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-height", height, NULL); } + debug_log("set video param : video frame scaling down to width(%d) height(%d)", width, height); } - break; - - default: - break; + debug_log("set video param : display_evas_do_scaling %d", scaling); } - } - 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: + /* if evaspixmapsink */ + if (!strcmp(PLAYER_INI()->videosink_element_evas,"evaspixmapsink")) { - 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; + int display_method = 0; + int roi_x = 0; + int roi_y = 0; + int roi_w = 0; + int roi_h = 0; + int origin_size = !scaling; - 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; + 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); - 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; + g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, + "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 ); - case GST_MESSAGE_ELEMENT: - { - debug_log("GST_MESSAGE_ELEMENT\n"); + 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 : 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 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: + case MM_DISPLAY_SURFACE_X_EXT: /* NOTE : this surface type is used for the videoTexture(canvasTexture) overlay */ { - debug_log("GST_MESSAGE_DURATION\n"); - ret = __mmplayer_gst_handle_duration(player, msg); - if (!ret) + 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_warning("failed to update duration"); + debug_error("videosink is not xvimagesink"); + return MM_ERROR_PLAYER_INTERNAL; } - } - - 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))); + /* 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 (player->doing_seek) + if ( pixmap_id_cb ) { - 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) + debug_log("set video param : display_overlay(0x%x)", pixmap_id_cb); + if (pixmap_id_cb_user_data) { - async_done = TRUE; + 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, + "rotate", rotation_value, + "pixmap-id-callback", pixmap_id_cb, + "pixmap-id-callback-userdata", pixmap_id_cb_user_data, + NULL ); } 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"); + case MM_DISPLAY_SURFACE_NULL: + { + /* do nothing */ + } 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 ); + debug_fleave(); - return ret; + return MM_ERROR_NONE; } static gboolean -__mmplayer_gst_handle_duration(mm_player_t* player, GstMessage* msg) +__mmplayer_audio_stream_probe (GstPad *pad, GstBuffer *buffer, gpointer u_data) { - GstFormat format; - gint64 bytes = 0; - - debug_fenter(); + mm_player_t* player = (mm_player_t*) u_data; + gint size; + guint8 *data; - return_val_if_fail(player, FALSE); - return_val_if_fail(msg, FALSE); +#ifdef GST_API_VERSION_1 + GstMapInfo info; + gst_buffer_map (buffer, &info, GST_MAP_WRITE); + data = info.data; + size = gst_buffer_get_size(buffer); - gst_message_parse_duration (msg, &format, &bytes); + if (player->audio_stream_cb && size && data) + player->audio_stream_cb((void *)data, size, player->audio_stream_cb_user_param); - if (MMPLAYER_IS_HTTP_STREAMING(player) && format == GST_FORMAT_BYTES ) - { - debug_log("data total size of http content: %lld", bytes); - player->http_content_size = bytes; - } - else if (format == GST_FORMAT_TIME) - { - /* handling audio clip which has vbr. means duration is keep changing */ - _mmplayer_update_content_attrs (player, ATTR_DURATION ); - } - else - { - debug_warning("duration is neither BYTES or TIME"); - return FALSE; - } + gst_buffer_unmap (buffer, &info); +#else + data = GST_BUFFER_DATA(buffer); + size = GST_BUFFER_SIZE(buffer); - debug_fleave(); + if (player->audio_stream_cb && size && data) + player->audio_stream_cb((void *)data, size, player->audio_stream_cb_user_param); +#endif return TRUE; } -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;\ - }\ -} - #ifdef GST_API_VERSION_1 -#define MMPLAYER_UPDATE_TAG_IMAGE(gsttag, attribute, playertag) \ -value = gst_tag_list_get_value_index(tag_list, gsttag, index); \ -if (value) \ -{\ - GstMapInfo info; \ - gst_buffer_map (buffer, &info, GST_MAP_WRITE); \ - buffer = gst_value_get_buffer (value); \ - debug_log ( "update album cover data : %p, size : %d\n", info.data, gst_buffer_get_size(buffer)); \ - player->album_art = (gchar *)g_malloc(gst_buffer_get_size(buffer)); \ - if (player->album_art); \ - { \ - memcpy(player->album_art, info.data, gst_buffer_get_size(buffer)); \ - mm_attrs_set_data_by_name(attribute, playertag, (void *)player->album_art, gst_buffer_get_size(buffer)); \ - } \ -gst_buffer_unmap (buffer, &info); \ -} -#else -#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)); \ - } \ -} -#endif - -#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;\ - }\ -} +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; + GstMapInfo info; -#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);\ - }\ -} + debug_fenter(); -#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;\ - }\ -} + return_val_if_fail ( player, FALSE ); + return_val_if_fail ( buffer, FALSE ); -#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;\ - }\ -} + gst_buffer_map (buffer, &info, GST_MAP_WRITE); + text = info.data; + gst_buffer_unmap (buffer, &info); - /* function start */ - GstTagList* tag_list = NULL; + duration = GST_BUFFER_DURATION(buffer); - MMHandleType attrs = 0; + if ( player->is_subtitle_off ) + { + debug_log("subtitle is OFF.\n" ); + return TRUE; + } - char *string = NULL; - guint v_uint = 0; - GDate *date = NULL; - /* album cover */ - GstBuffer *buffer = NULL; - gint index = 0; - const GValue *value; + if ( !text ) + { + debug_log("There is no subtitle to be displayed.\n" ); + return TRUE; + } - /* currently not used. but those are needed for above macro */ - //guint64 v_uint64 = 0; - //gdouble v_double = 0; + msg.data = (void *) text; + msg.subtitle.duration = GST_TIME_AS_MSECONDS(duration); - return_val_if_fail( player && msg, FALSE ); + debug_warning("update subtitle : [%ld msec] %s\n'", msg.subtitle.duration, (char*)msg.data ); - attrs = MMPLAYER_GET_ATTRS(player); + MMPLAYER_POST_MSG( player, MM_MESSAGE_UPDATE_SUBTITLE, &msg ); - 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); + debug_fleave(); - return TRUE; + return ret; } - -static void -__mmplayer_gst_rtp_no_more_pads (GstElement *element, gpointer data) // @ +#else +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(); - /* 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 + return_val_if_fail ( player, FALSE ); + return_val_if_fail ( buffer, FALSE ); - * [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 ) + text = GST_BUFFER_DATA(buffer); + duration = GST_BUFFER_DURATION(buffer); + + if ( player->is_subtitle_off ) { - debug_log("it seems pad caps is directely used for autoplugging. removing fakesink now\n"); + debug_log("subtitle is OFF.\n" ); + return TRUE; + } - 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; - } + if ( !text ) + { + debug_log("There is no subtitle to be displayed.\n" ); + return TRUE; } - /* create dot before error-return. for debugging */ - MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-no-more-pad" ); + msg.data = (void *) text; + msg.subtitle.duration = GST_TIME_AS_MSECONDS(duration); - /* 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 ); + debug_warning("update subtitle : [%ld msec] %s\n'", msg.subtitle.duration, (char*)msg.data ); - player->no_more_pad = TRUE; + MMPLAYER_POST_MSG( player, MM_MESSAGE_UPDATE_SUBTITLE, &msg ); debug_fleave(); + + return ret; } +#endif -static gboolean -__mmplayer_gst_remove_fakesink(mm_player_t* player, MMPlayerGstElement* fakesink) // @ +#ifdef GST_API_VERSION_1 +int +_mmplayer_push_buffer(MMHandleType hplayer, unsigned char *buf, int size) // @ { - GstElement* parent = NULL; - - return_val_if_fail(player && player->pipeline && fakesink, FALSE); + mm_player_t* player = (mm_player_t*)hplayer; + GstBuffer *buffer = NULL; + GstMapInfo info; + GstFlowReturn gst_ret = GST_FLOW_OK; + int ret = MM_ERROR_NONE; - /* lock */ - g_mutex_lock( player->fsink_lock ); + debug_fenter(); - if ( ! fakesink->gst ) - { - goto ERROR; - } + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - /* get parent of fakesink */ - parent = (GstElement*)gst_object_get_parent( (GstObject*)fakesink->gst ); - if ( ! parent ) - { - debug_log("fakesink already removed\n"); - goto ERROR; - } + /* check current state */ +// MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_START ); - 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 + /* NOTE : we should check and create pipeline again if not created as we destroy + * whole pipeline when stopping in streamming playback */ - if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state (fakesink->gst, GST_STATE_NULL) ) + if ( ! player->pipeline ) { - debug_error("fakesink state change failure!\n"); - - /* FIXIT : should I return here? or try to proceed to next? */ - /* return FALSE; */ + if ( MM_ERROR_NONE != __gst_realize( player ) ) + { + debug_error("failed to realize before starting. only in streamming\n"); + return MM_ERROR_PLAYER_INTERNAL; + } } - /* 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 ); + debug_msg("app-src: pushing data\n"); - goto ERROR; + if ( buf == NULL ) + { + debug_error("buf is null\n"); + return MM_ERROR_NONE; } - gst_object_unref( parent ); - - debug_log("state-holder removed\n"); + buffer = gst_buffer_new (); - gst_element_set_locked_state( fakesink->gst, FALSE ); - - g_mutex_unlock( player->fsink_lock ); - return TRUE; - -ERROR: - if ( fakesink->gst ) + if (size <= 0) { - gst_element_set_locked_state( fakesink->gst, FALSE ); + 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; } - g_mutex_unlock( player->fsink_lock ); - return FALSE; -} + info.data = (guint8*)(buf); + gst_buffer_set_size(buffer, size); + gst_buffer_map (buffer, &info, GST_MAP_WRITE); + 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); -static void -__mmplayer_gst_rtp_dynamic_pad (GstElement *element, GstPad *pad, gpointer data) // @ -{ - GstPad *sinkpad = NULL; - GstCaps* caps = NULL; - GstElement* new_element = NULL; + debug_fleave(); - mm_player_t* player = (mm_player_t*) data; + return ret; +} +#else +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_if_fail( element && pad ); - return_if_fail( player && - player->pipeline && - player->pipeline->mainbin ); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + + /* check current state */ +// MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_START ); - /* payload type is recognizable. increase num_dynamic and wait for sinkbin creation. - * num_dynamic_pad will decreased after creating a sinkbin. + /* NOTE : we should check and create pipeline again if not created as we destroy + * whole pipeline when stopping in streamming playback */ - 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 ) + if ( ! player->pipeline ) { - /* create typefind */ - new_element = gst_element_factory_make( "typefind", NULL ); - if ( ! new_element ) + if ( MM_ERROR_NONE != __gst_realize( player ) ) { - debug_error("failed to create typefind\n"); - goto ERROR; + debug_error("failed to realize before starting. only in streamming\n"); + return MM_ERROR_PLAYER_INTERNAL; } - - 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"); -#ifdef GST_API_VERSION_1 - caps = gst_pad_get_current_caps( pad ); -#else - caps = gst_pad_get_caps( pad ); -#endif - MMPLAYER_CHECK_NULL( caps ); + debug_msg("app-src: pushing data\n"); - /* 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; - } + if ( buf == NULL ) + { + debug_error("buf is null\n"); + return MM_ERROR_NONE; + } - /* 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; - } + buffer = gst_buffer_new (); - gst_caps_unref( caps ); - caps = NULL; + 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; } - /* excute new_element if created*/ - if ( new_element ) - { - debug_log("adding new element to pipeline\n"); + GST_BUFFER_DATA(buffer) = (guint8*)(buf); + GST_BUFFER_SIZE(buffer) = size; - /* set state to READY before add to bin */ - MMPLAYER_ELEMENT_SET_STATE( new_element, GST_STATE_READY ); + 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); - /* 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; - } + debug_fleave(); - /* 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; - } + return ret; +} +#endif - /* link it */ - if ( GST_PAD_LINK_OK != GST_PAD_LINK(pad, sinkpad) ) - { - debug_error("failed to link autoplug element\n"); - goto ERROR; - } +GstBusSyncReply +__mmplayer_bus_sync_callback (GstBus * bus, GstMessage * message, gpointer data) +{ + mm_player_t *player = (mm_player_t *)data; - gst_object_unref (sinkpad); - sinkpad = NULL; + switch (GST_MESSAGE_TYPE (message)) + { + case GST_MESSAGE_TAG: + __mmplayer_gst_extract_tag_from_msg(player, message); + break; + case GST_MESSAGE_DURATION: + __mmplayer_gst_handle_duration(player, message); + break; - /* run. setting PLAYING here since streamming source is live source */ - MMPLAYER_ELEMENT_SET_STATE( new_element, GST_STATE_PLAYING ); + default: + return GST_BUS_PASS; } + gst_message_unref (message); - debug_fleave(); + return GST_BUS_DROP; +} - return; +static void __mmplayer_do_sound_fadedown(mm_player_t* player, unsigned int time) +{ + debug_fenter(); -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)); + return_if_fail(player + && player->pipeline + && player->pipeline->audiobin + && player->pipeline->audiobin[MMPLAYER_A_SINK].gst); - if ( sinkpad ) - gst_object_unref(GST_OBJECT(sinkpad)); + g_object_set(G_OBJECT(player->pipeline->audiobin[MMPLAYER_A_SINK].gst), "mute", 2, NULL); - if ( caps ) - gst_object_unref(GST_OBJECT(caps)); + usleep(time); - /* 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 - */ + debug_fleave(); } - -static void -__mmplayer_gst_decode_callback(GstElement *decodebin, GstPad *pad, gboolean last, gpointer data) // @ +static void __mmplayer_undo_sound_fadedown(mm_player_t* player) { - 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; - } + debug_fenter(); - /* get mimetype from caps */ -#ifdef GST_API_VERSION_1 - caps = gst_pad_get_current_caps( pad ); -#else - caps = gst_pad_get_caps( pad ); -#endif - if ( !caps ) - { - debug_error("cannot get caps from pad.\n"); - goto ERROR; - } + return_if_fail(player + && player->pipeline + && player->pipeline->audiobin + && player->pipeline->audiobin[MMPLAYER_A_SINK].gst); - str = gst_caps_get_structure( caps, 0 ); - if ( ! str ) - { - debug_error("cannot get structure from capse.\n"); - goto ERROR; - } + g_object_set(G_OBJECT(player->pipeline->audiobin[MMPLAYER_A_SINK].gst), "mute", 0, NULL); - name = gst_structure_get_name(str); - if ( ! name ) - { - debug_error("cannot get mimetype from structure.\n"); - goto ERROR; - } + debug_fleave(); +} - debug_log("detected mimetype : %s\n", name); +static gboolean __mmfplayer_parse_profile(const char *uri, void *param, MMPlayerParseProfile* data) // @ +{ + gboolean ret = FALSE; + char *path = NULL; - 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; - } - ) + debug_fenter(); - 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"); - } + return_val_if_fail ( uri , FALSE); + return_val_if_fail ( data , FALSE); + return_val_if_fail ( ( strlen(uri) <= MM_MAX_URL_LEN ), FALSE ); - /* FIXIT : track number shouldn't be hardcoded */ - mm_attrs_set_int_by_name(attrs, "content_audio_track_num", 1); - player->audiosink_linked = 1; + memset(data, 0, sizeof(MMPlayerParseProfile)); - 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 ((path = strstr(uri, "file://"))) { - 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 (util_exist_file_path(path + 7)) { + strncpy(data->uri, path, MM_MAX_URL_LEN-1); - if (surface_type == MM_DISPLAY_SURFACE_NULL) + if ( util_is_sdp_file ( path ) ) { - debug_log("not make videobin because it dose not want\n"); - goto ERROR; + 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; } - - __ta__("__mmplayer_gst_create_video_pipeline", - if (MM_ERROR_NONE != __mmplayer_gst_create_video_pipeline(player, caps, surface_type) ) + else { - debug_error("failed to create videobin. continuing without video\n"); - goto ERROR; + data->uri_type = MM_PLAYER_URI_TYPE_FILE; } - ) - - sinkbin = player->pipeline->videobin[MMPLAYER_V_BIN].gst; - debug_log("creating videosink bin success\n"); + ret = TRUE; } else { - sinkbin = player->pipeline->videobin[MMPLAYER_V_BIN].gst; - debug_log("re-using videobin\n"); + debug_warning("could access %s.\n", path); } - - /* FIXIT : track number shouldn't be hardcoded */ - mm_attrs_set_int_by_name(attrs, "content_video_track_num", 1); - player->videosink_linked = 1; - - 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 ((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 (strstr(name, "text")) + else if ((path = strstr(uri, "http://"))) { - 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; - } - ) + if (strlen(path)) { + strcpy(data->uri, uri); + data->uri_type = MM_PLAYER_URI_TYPE_URL_HTTP; - 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"); + ret = TRUE; } + } + else if ((path = strstr(uri, "https://"))) + { + if (strlen(path)) { + strcpy(data->uri, uri); + data->uri_type = MM_PLAYER_URI_TYPE_URL_HTTP; - /* 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; + ret = TRUE; } } - else + else if ((path = strstr(uri, "rtspu://"))) { - debug_warning("unknown type of elementary stream! ignoring it...\n"); - goto ERROR; + if (strlen(path)) { + strcpy(data->uri, uri); + data->uri_type = MM_PLAYER_URI_TYPE_URL_RTSP; + ret = TRUE; + } } - - if ( sinkbin ) + else if ((path = strstr(uri, "rtspr://"))) { - /* 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; - } + strcpy(data->uri, path); + char *separater =strstr(path, "*"); - /* add */ - if ( FALSE == gst_bin_add( GST_BIN(pipeline), sinkbin ) ) - { - debug_error("failed to add sinkbin to pipeline\n"); - goto ERROR; - } + if (separater) { + int urgent_len = 0; + char *urgent = separater + strlen("*"); - /* link */ - if ( GST_PAD_LINK_OK != GST_PAD_LINK(pad, sinkpad) ) - { - debug_error("failed to get pad from sinkbin\n"); - goto ERROR; + 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; + } } - - /* 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; + } + else if ((path = strstr(uri, "mms://"))) + { + if (strlen(path)) { + strcpy(data->uri, uri); + data->uri_type = MM_PLAYER_URI_TYPE_URL_MMS; + ret = TRUE; } - - gst_object_unref( sinkpad ); - sinkpad = NULL; } + 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,}; - debug_log("linking sink bin success\n"); + if (seperator) { + if ((buffer = strstr(path, "ext="))) { + buffer += strlen("ext="); - /* 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--; + if (strlen(buffer)) { + strcpy(ext, buffer); - debug_log("stream count dec : %d (num of dynamic pad)\n", player->num_dynamic_pad); + if ((seperator = strchr(ext, ',')) + || (seperator = strchr(ext, ' ')) + || (seperator = strchr(ext, '\0'))) { + seperator[0] = '\0'; + } + } + } - if ( ( player->no_more_pad ) && ( player->num_dynamic_pad == 0 ) ) - { - __mmplayer_pipeline_complete( NULL, player ); - } + if ((buffer = strstr(path, "size="))) { + buffer += strlen("size="); -ERROR: - if ( caps ) - gst_caps_unref( caps ); + if (strlen(buffer) > 0) { + strcpy(size, buffer); - if ( sinkpad ) - gst_object_unref(GST_OBJECT(sinkpad)); + if ((seperator = strchr(size, ',')) + || (seperator = strchr(size, ' ')) + || (seperator = strchr(size, '\0'))) { + seperator[0] = '\0'; + } - /* flusing out new attributes */ - if ( mmf_attrs_commit ( attrs ) ) - { - debug_error("failed to comit attributes\n"); - } + mem_size = atoi(size); + } + } + } - return; -} + 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); -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; - int rotation_using_type = -1; - #define ROTATION_USING_X 0 - #define ROTATION_USING_FIMC 1 - #define ROTATION_USING_FLIP 2 - - 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) - { - if (player->is_nv12_tiled) - { - rotation_using_type = ROTATION_USING_FIMC; - } - else - { - 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: - if (player->is_nv12_tiled && !strcmp(PLAYER_INI()->videosink_element_evas,"evasimagesink")) - { - rotation_using_type = ROTATION_USING_FIMC; - } - else if (!player->is_nv12_tiled) - { - rotation_using_type = ROTATION_USING_FLIP; - } - else - { - debug_error("it should not be here.."); - return FALSE; - } - break; - 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_FIMC: // fimcconvert - { - switch (dest_angle) - { - case 0: - break; - case 90: - pro_value = 90; // clockwise 90 - break; - case 180: - pro_value = 180; - break; - case 270: - pro_value = 270; // 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 ) - { - if (player->is_nv12_tiled) - { - gchar *ename = NULL; - int width = 0; - int height = 0; - - mm_attrs_get_int_by_name(attrs, "display_width", &width); - mm_attrs_get_int_by_name(attrs, "display_height", &height); - - /* resize video frame with requested values for fimcconvert */ -#ifdef GST_API_VERSION_1 - ename = GST_OBJECT (gst_element_get_factory(player->pipeline->videobin[MMPLAYER_V_CONV].gst)); -#else - ename = GST_PLUGIN_FEATURE_NAME(gst_element_get_factory(player->pipeline->videobin[MMPLAYER_V_CONV].gst)); -#endif - - if (g_strrstr(ename, "fimcconvert")) - { - if (width) - g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-width", width, NULL); - - if (height) - g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-height", height, NULL); - - g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "rotate", rotation_value, NULL); - debug_log("updating fimcconvert - r[%d], w[%d], h[%d]", rotation_value, width, height); - } - else - { - debug_error("no available video converter"); - return MM_ERROR_PLAYER_INTERNAL; - } - } - else - { - debug_log("using video stream callback with memsink. player handle : [%p]", player); - - 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 ) - { -#define GST_VAAPI_DISPLAY_TYPE_X11 1 - if (!strncmp(PLAYER_INI()->videosink_element_x,"vaapisink", strlen("vaapisink"))){ - debug_log("set video param: vaapisink display %d", GST_VAAPI_DISPLAY_TYPE_X11); - g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, - "display", GST_VAAPI_DISPLAY_TYPE_X11, - NULL); - } - - debug_log("set video param : xid %d", *(int*)xid); -#ifdef GST_API_VERSION_1 - gst_video_overlay_set_window_handle( GST_VIDEO_OVERLAY( player->pipeline->videobin[MMPLAYER_V_SINK].gst ), *(int*)xid ); -#else - gst_x_overlay_set_xwindow_id( GST_X_OVERLAY( player->pipeline->videobin[MMPLAYER_V_SINK].gst ), *(int*)xid ); -#endif - } - 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); - } - - /* if vaapisink */ - if (!strncmp(PLAYER_INI()->videosink_element_x, "vaapisink", strlen("vaapisink"))) - { - g_object_set(player->pipeline->videobin[MMPLAYER_V_SINK].gst, - "rotation", rotation_value, - NULL); - debug_log("set video param: vaapisink rotation %d", rotation_value); - } - } - 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 evasimagesink */ - if (!strcmp(PLAYER_INI()->videosink_element_evas,"evasimagesink") && player->is_nv12_tiled) - { - int width = 0; - int height = 0; - int no_scaling = !scaling; - - mm_attrs_get_int_by_name(attrs, "display_width", &width); - mm_attrs_get_int_by_name(attrs, "display_height", &height); - - /* NOTE: fimcconvert does not manage index of src buffer from upstream src-plugin, decoder gives frame information in output buffer with no ordering */ - g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-buffer-num", 5, NULL); - - if (no_scaling) - { - /* no-scaling order to fimcconvert, original width, height size of media src will be passed to sink plugin */ - g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, - "dst-width", 0, /* setting 0, output video width will be media src's width */ - "dst-height", 0, /* setting 0, output video height will be media src's height */ - NULL); - } - else - { - /* scaling order to fimcconvert */ - if (width) - { - g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-width", width, NULL); - } - if (height) - { - g_object_set(player->pipeline->videobin[MMPLAYER_V_CONV].gst, "dst-height", height, NULL); - } - debug_log("set video param : video frame scaling down to width(%d) height(%d)", width, height); - } - debug_log("set video param : display_evas_do_scaling %d", scaling); - } - - /* 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 origin_size = !scaling; - - 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, - "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 : 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, - "rotate", rotation_value, - "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; - gint 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"))) - { -#ifdef GST_API_VERSION_1 - if ((caps = gst_pad_get_current_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); - } -#else - 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); - } -#endif - 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); - - 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; - -#ifdef GST_API_VERSION_1 - GstMapInfo info; - gst_buffer_map (buffer, &info, GST_MAP_WRITE); - data = info.data; - size = gst_buffer_get_size(buffer); - - if (player->audio_stream_cb && size && data) - player->audio_stream_cb((void *)data, size, player->audio_stream_cb_user_param); - - gst_buffer_unmap (buffer, &info); -#else - 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); -#endif - - 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) : fimcconvert ! 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; - gint ret = 0; - - debug_log("using memsink\n"); - - /* first, create colorspace convert */ - if (player->is_nv12_tiled) - { - vconv_factory = "fimcconvert"; - } - else // get video converter from player ini file - { - 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); - } - - if ( !player->is_nv12_tiled) - { - gint width = 0; //width of video - gint height = 0; //height of video - GstCaps* video_caps = NULL; - - /* rotator, scaler and capsfilter */ - if (strncmp(PLAYER_INI()->videosink_element_x, "vaapisink", strlen("vaapisink"))){ - 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 ( (player->is_nv12_tiled && (surface_type == MM_DISPLAY_SURFACE_EVAS) && - !strcmp(PLAYER_INI()->videosink_element_evas, "evasimagesink") ) ) - { - vconv_factory = "fimcconvert"; - } - else if (player->is_nv12_tiled) - { - vconv_factory = NULL; - } - - if (vconv_factory) - { - MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_CONV, vconv_factory, "video converter", TRUE); - debug_log("using video converter: %s", vconv_factory); - } - } - - if (strncmp(PLAYER_INI()->videosink_element_x,"vaapisink", strlen("vaapisink"))){ - /* set video rotator */ - if ( !player->is_nv12_tiled ) - 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; - } - 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); - - 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; -} - -#ifdef GST_API_VERSION_1 -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; - GstMapInfo info; - - debug_fenter(); - - return_val_if_fail ( player, FALSE ); - return_val_if_fail ( buffer, FALSE ); - - gst_buffer_map (buffer, &info, GST_MAP_WRITE); - text = info.data; - gst_buffer_unmap (buffer, &info); - - 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; -} -#else -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; -} -#endif - -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: - { - 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; -} - -#ifdef GST_API_VERSION_1 -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; - } - - buffer = gst_buffer_new(); - GstMapInfo info; - - info.data = (guint8*)(buf->buf + buf->offset); - gst_buffer_set_size(buffer, len); - - //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; - gst_buffer_map (buffer, &info, GST_MAP_WRITE); - - 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; -} -#else -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; -} -#endif - -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; -} - -#ifdef GST_API_VERSION_1 -int -_mmplayer_push_buffer(MMHandleType hplayer, unsigned char *buf, int size) // @ -{ - mm_player_t* player = (mm_player_t*)hplayer; - GstBuffer *buffer = NULL; - GstMapInfo info; - 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; - } - - info.data = (guint8*)(buf); - gst_buffer_set_size(buffer, size); - gst_buffer_map (buffer, &info, GST_MAP_WRITE); - - 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; -} -#else -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; -} -#endif - -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; - case GST_MESSAGE_DURATION: - __mmplayer_gst_handle_duration(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; - } -#ifdef GST_API_VERSION_1 - GST_OBJECT_FLAG_UNSET (mainbin[MMPLAYER_M_SRC_FAKESINK].gst, GST_ELEMENT_FLAG_SINK); -#else - GST_OBJECT_FLAG_UNSET (mainbin[MMPLAYER_M_SRC_FAKESINK].gst, GST_ELEMENT_IS_SINK); -#endif - - /* 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 */ -#ifdef GST_API_VERSION_1 - gst_bus_set_sync_handler(bus, __mmplayer_bus_sync_callback, player, NULL); -#else - gst_bus_set_sync_handler(bus, __mmplayer_bus_sync_callback, player); -#endif - /* 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" ); - -#ifdef GST_API_VERSION_1 - gst_pad_remove_probe (pad, ahs_appsrc_cb_probe_id); -#else - gst_pad_remove_buffer_probe (pad, ahs_appsrc_cb_probe_id); -#endif - 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)); - -#ifdef GST_API_VERSION_1 - gst_bus_set_sync_handler (bus, NULL, NULL, NULL); -#else - gst_bus_set_sync_handler (bus, NULL, NULL); -#endif - gst_object_unref(bus); - - 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 READY state"); - 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"); - return ret; - } - - /* 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"); - return ret; - } - - /* rewind */ - if ( rewind ) - { - if ( ! __gst_seek( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, player->playback_rate, - 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; - __mmplayer_dump_pipeline_state(player); - } - - /* 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 ( FALSE == async && ret != MM_ERROR_NONE ) - { - GstMessage *msg = NULL; - GTimer *timer = NULL; - gdouble MAX_TIMEOUT_SEC = 3; - - debug_error("failed to set state to PAUSED"); - - timer = g_timer_new(); - g_timer_start(timer); - - GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst)); - /* check if gst error posted or not */ - do - { - msg = gst_bus_timed_pop(bus, GST_SECOND /2); - if (msg) - { - if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) - { - GError *error = NULL; - - debug_error("paring error posted from bus"); - /* parse error code */ - gst_message_parse_error(msg, &error, NULL); - - if (error->domain == GST_STREAM_ERROR) - { - ret = __gst_handle_stream_error( player, error, msg ); - } - else if (error->domain == GST_RESOURCE_ERROR) - { - ret = __gst_handle_resource_error( player, error->code ); - } - else if (error->domain == GST_LIBRARY_ERROR) - { - ret = __gst_handle_library_error( player, error->code ); - } - else if (error->domain == GST_CORE_ERROR) - { - ret = __gst_handle_core_error( player, error->code ); - } - player->msg_posted = TRUE; - } - gst_message_unref(msg); - } - } while (g_timer_elapsed(timer, NULL) < MAX_TIMEOUT_SEC); - - /* clean */ - gst_object_unref(bus); - g_timer_stop (timer); - g_timer_destroy (timer); - - 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; - GstBus *bus = NULL; - - 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"); - - /* clean bus sync handler because it's not needed any more */ - bus = gst_pipeline_get_bus (GST_PIPELINE(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst)); -#ifdef GST_API_VERSION_1 - gst_bus_set_sync_handler (bus, NULL, NULL, NULL); -#else - gst_bus_set_sync_handler (bus, NULL, NULL); -#endif - gst_object_unref(bus); - - /* 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"); - - 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) // @ -{ -#ifndef GST_API_VERSION_1 - GstFormat fmt = GST_FORMAT_TIME; -#endif - 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 ) - { -#ifdef GST_API_VERSION_1 - if ( !gst_element_query_duration( player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &dur_nsec )) - { - goto SEEK_ERROR; - } -#else - if ( !gst_element_query_duration( player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &dur_nsec )) - { - goto SEEK_ERROR; - } -#endif - 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, player->playback_rate, - 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, player->playback_rate, - 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; -#ifndef GST_API_VERSION_1 - GstFormat fmt = GST_FORMAT_TIME; -#endif - 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 ) - { -#ifdef GST_API_VERSION_1 - ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &pos_msec); -#else - ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &pos_msec); -#endif - } - - /* 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_warning("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_warning("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_warning ("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_warning ("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_warning("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); - - 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->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) + if ( util_is_sdp_file( (char*)uri ) ) { - g_error_free (err); + 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; + } + } - 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_unregister(&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; + 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 MM_ERROR_NONE; + return ret; } -int -__mmplayer_realize_streaming_ext(mm_player_t* player) +gboolean _asm_postmsg(gpointer *data) { - int ret = MM_ERROR_NONE; + mm_player_t* player = (mm_player_t*)data; + MMMessageParamType msg = {0, }; 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; - } + return_val_if_fail ( player, FALSE ); - bret = _mmplayer_realize_pd_downloader((MMHandleType)player, player->profile.uri, player->pd_file_save_path, player->pipeline->mainbin[MMPLAYER_M_SRC].gst); + msg.union_type = MM_MSG_UNION_CODE; + msg.code = player->sm.event_src; - if (FALSE == bret) - { - debug_error ("Unable to create PD Downloader..."); - ret = MM_ERROR_PLAYER_NOT_INITIALIZED; - } - } + MMPLAYER_POST_MSG( player, MM_MESSAGE_READY_TO_RESUME, &msg); - debug_fleave(); - return ret; + return FALSE; } -int -_mmplayer_realize(MMHandleType hplayer) // @ +gboolean _asm_lazy_pause(gpointer *data) { - 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; + mm_player_t* player = (mm_player_t*)data; 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 ); + return_val_if_fail ( player, FALSE ); - attrs = MMPLAYER_GET_ATTRS(player); - if ( !attrs ) + if (MMPLAYER_CURRENT_STATE(player) == MM_PLAYER_STATE_PLAYING) { - debug_error("fail to get attributes.\n"); - return MM_ERROR_PLAYER_INTERNAL; + 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"); + } } - - 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) ) + else { - debug_error("failed to parse profile\n"); - return MM_ERROR_PLAYER_INVALID_URI; + debug_log ("Invalid state to proceed lazy pause\n"); } - /* 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; - } + /* unset mute */ + if (player->pipeline && player->pipeline->audiobin) + g_object_set(G_OBJECT(player->pipeline->audiobin[MMPLAYER_A_SINK].gst), "mute", 0, NULL); - 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; - } + player->sm.by_asm_cb = 0; //should be reset here - 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; + debug_fleave(); - player->videodec_linked = 0; - player->videosink_linked = 0; - player->audiodec_linked = 0; - player->audiosink_linked = 0; - player->textsink_linked = 0; + return FALSE; +} - /* set the subtitle ON default */ - player->is_subtitle_off = 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; - /* registry should be updated for downloadable codec */ - mm_attrs_get_int_by_name(attrs, "profile_update_registry", &update_registry); + debug_fenter(); - if ( update_registry ) - { - debug_log("updating registry...\n"); - gst_update_registry(); + return_val_if_fail ( player && player->pipeline, ASM_CB_RES_IGNORE ); + return_val_if_fail ( player->attrs, MM_ERROR_PLAYER_INTERNAL ); - /* then we have to rebuild factories */ - __mmplayer_release_factories( player ); - __mmplayer_init_factories(player); + if (player->is_sound_extraction) + { + debug_log("sound extraction is working...so, asm command is ignored.\n"); + return result; } - /* realize pipeline */ - ret = __gst_realize( player ); - if ( ret != MM_ERROR_NONE ) + 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 ) { - debug_error("fail to realize the player.\n"); + 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 + else if (event_src == ASM_EVENT_SOURCE_RESOURCE_CONFLICT) { - ret = __mmplayer_realize_streaming_ext(player); + /* 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; } - 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)) + switch(command) { - _mmplayer_unrealize_pd_downloader ((MMHandleType)player); - player->pd_downloader = NULL; - } + case ASM_COMMAND_PLAY: + debug_warning ("Got unexpected asm command (%d)", command); + break; - debug_fleave(); - return MM_ERROR_NONE; -} + case ASM_COMMAND_STOP: // notification case + { + debug_warning("Got msg from asm to stop"); -int -_mmplayer_unrealize(MMHandleType hplayer) // @ -{ - mm_player_t* player = (mm_player_t*)hplayer; - int ret = MM_ERROR_NONE; + 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; - debug_fenter(); + case ASM_COMMAND_PAUSE: + { + debug_warning("Got msg from asm to Pause"); - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ) + 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_warning ("do fade down->pause->undo fade down"); - /* check current state */ - MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_UNREALIZE ); + __mmplayer_do_sound_fadedown(player, MM_PLAYER_FADEOUT_TIME_DEFAULT); - __mmplayer_unrealize_streaming_ext(player); + 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 - /* unrealize pipeline */ - ret = __gst_unrealize( player ); + if ( player->pipeline->audiobin && player->pipeline->audiobin[MMPLAYER_A_SINK].gst ) + g_object_set( player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "mute", 2, NULL); - /* set player state if success */ - if ( MM_ERROR_NONE == ret ) - { - if (player->sm.state != ASM_STATE_STOP) { - ret = _mmplayer_asm_set_state(hplayer, ASM_STATE_STOP); - if ( ret ) + player->lazy_pause_event_id = g_timeout_add(LAZY_PAUSE_TIMEOUT_MSEC, (GSourceFunc)_asm_lazy_pause, (gpointer)player); + debug_warning ("set lazy pause timer (id=[%d], timeout=[%d ms])", player->lazy_pause_event_id, LAZY_PAUSE_TIMEOUT_MSEC); + } + else { - debug_error("failed to set asm state to STOP\n"); - return ret; + //immediate pause + debug_log ("immediate pause"); + result = _mmplayer_pause((MMHandleType)player); } + cb_res = ASM_CB_RES_PAUSE; } - } + break; - debug_fleave(); + case ASM_COMMAND_RESUME: + { + debug_warning("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; - return ret; -} + default: + break; + } -int -_mmplayer_set_message_callback(MMHandleType hplayer, MMMessageCallback callback, gpointer user_param) // @ -{ - mm_player_t* player = (mm_player_t*)hplayer; + if (!lazy_pause) + player->sm.by_asm_cb = 0; - return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + debug_fleave(); - return __gst_set_message_callback(player, callback, user_param); + return cb_res; } int -_mmplayer_get_state(MMHandleType hplayer, int* state) // @ +_mmplayer_create_player(MMHandleType handle) // @ { - mm_player_t *player = (mm_player_t*)hplayer; - - return_val_if_fail(state, MM_ERROR_INVALID_ARGUMENT); - - *state = MMPLAYER_CURRENT_STATE(player); + mm_player_t* player = MM_PLAYER_CAST(handle); - return MM_ERROR_NONE; -} + debug_fenter(); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); -int -_mmplayer_set_volume(MMHandleType hplayer, MMPlayerVolumeType volume) // @ -{ - mm_player_t* player = (mm_player_t*) hplayer; - GstElement* vol_element = NULL; - int i = 0; + MMTA_ACUM_ITEM_BEGIN("[KPI] media player service create->playing", FALSE); - debug_fenter(); + /* 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; - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL ( player, MMPLAYER_COMMAND_CREATE ); - debug_log("volume [L]=%f:[R]=%f\n", - volume.level[MM_VOLUME_CHANNEL_LEFT], volume.level[MM_VOLUME_CHANNEL_RIGHT]); + /* construct attributes */ + player->attrs = _mmplayer_construct_attribute(handle); - /* invalid factor range or not */ - for ( i = 0; i < MM_VOLUME_CHANNEL_NUM; i++ ) + if ( !player->attrs ) { - 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; - } + debug_critical("Failed to construct attributes\n"); + goto ERROR; } - /* Save volume to handle. Currently the first array element will be saved. */ - player->sound.volume = volume.level[0]; + /* initialize gstreamer with configured parameter */ + if ( ! __mmplayer_gstreamer_init() ) + { + debug_critical("Initializing gstreamer failed\n"); + goto ERROR; + } - /* check pipeline handle */ - if ( ! player->pipeline || ! player->pipeline->audiobin ) + /* initialize factories if not using decodebin */ + if ( FALSE == PLAYER_INI()->use_decodebin ) { - debug_log("audiobin is not created yet\n"); - debug_log("but, current stored volume will be set when it's created.\n"); + if( player->factories == NULL ) + __mmplayer_init_factories(player); + } - /* 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; + /* 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; } - /* setting volume to volume element */ - vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst; + /* create repeat mutex */ + player->repeat_thread_mutex = g_mutex_new(); + if ( ! player->repeat_thread_mutex ) + { + debug_critical("Cannot create repeat mutex\n"); + goto ERROR; + } - if ( vol_element ) + /* create repeat cond */ + player->repeat_thread_cond = g_cond_new(); + if ( ! player->repeat_thread_cond ) { - debug_log("volume is set [%f]\n", player->sound.volume); - g_object_set(vol_element, "volume", player->sound.volume, NULL); + debug_critical("Cannot create repeat cond\n"); + goto ERROR; } - debug_fleave(); + /* create repeat thread */ + player->repeat_thread = + g_thread_create (__mmplayer_repeat_thread, (gpointer)player, TRUE, NULL); + if ( ! player->repeat_thread ) + { + goto ERROR; + } - return MM_ERROR_NONE; -} + 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; + } -int -_mmplayer_get_volume(MMHandleType hplayer, MMPlayerVolumeType* volume) -{ - mm_player_t* player = (mm_player_t*) hplayer; - int i = 0; + if (MMPLAYER_IS_HTTP_PD(player)) + { + player->pd_downloader = NULL; + player->pd_file_save_path = NULL; + } - debug_fenter(); + /* 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; - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - return_val_if_fail( volume, MM_ERROR_INVALID_ARGUMENT ); + player->play_subtitle = FALSE; + player->use_textoverlay = FALSE; - /* returning stored volume */ - for (i = 0; i < MM_VOLUME_CHANNEL_NUM; i++) - volume->level[i] = player->sound.volume; + /* 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; -} - - -int -_mmplayer_set_mute(MMHandleType hplayer, int mute) // @ -{ - mm_player_t* player = (mm_player_t*) hplayer; - GstElement* vol_element = NULL; - - debug_fenter(); +ERROR: + /* free lock */ + if ( player->fsink_lock ) + g_mutex_free( player->fsink_lock ); + player->fsink_lock = NULL; - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + /* 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 ); - debug_log("mute : %d\n", mute); + g_thread_join( player->repeat_thread ); + player->repeat_thread = NULL; - /* mute value shoud 0 or 1 */ - if ( mute != 0 && mute != 1 ) - { - debug_error("bad mute value\n"); + g_mutex_free ( player->repeat_thread_mutex ); + player->repeat_thread_mutex = NULL; - /* FIXIT : definitly, we need _BAD_PARAM error code */ - return MM_ERROR_INVALID_ARGUMENT; + 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); - /* 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; - } + 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; - vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst; + debug_fenter(); - /* NOTE : volume will only created when the bt is enabled */ - if ( vol_element ) - { - g_object_set(vol_element, "mute", mute, NULL); - } - else + if ( initialized ) { - debug_log("volume elemnet is not created. using volume in audiosink\n"); + debug_log("gstreamer already initialized.\n"); + return TRUE; } - player->sound.mute = mute; - - debug_fleave(); - - return MM_ERROR_NONE; -} + /* alloc */ + argc = malloc( sizeof(int) ); + argv = malloc( sizeof(gchar*) * max_argc ); -int -_mmplayer_get_mute(MMHandleType hplayer, int* pmute) // @ -{ - mm_player_t* player = (mm_player_t*) hplayer; - GstElement* vol_element = NULL; + if ( !argc || !argv ) + goto ERROR; - debug_fenter(); + memset( argv, 0, sizeof(gchar*) * max_argc ); - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - return_val_if_fail ( pmute, MM_ERROR_INVALID_ARGUMENT ); + /* add initial */ + *argc = 1; + argv[0] = g_strdup( "mmplayer" ); - /* just hold mute value if pipeline is not ready */ - if ( !player->pipeline || !player->pipeline->audiobin ) + /* add gst_param */ + for ( i = 0; i < 5; i++ ) /* FIXIT : num of param is now fixed to 5. make it dynamic */ { - debug_log("pipeline is not ready. returning stored value\n"); - *pmute = player->sound.mute; - return MM_ERROR_NONE; + 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)++; - vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst; - - if ( vol_element ) + /* check disable registry scan */ + if ( PLAYER_INI()->skip_rescan ) { - g_object_get(vol_element, "mute", pmute, NULL); - debug_log("mute=%d\n\n", *pmute); + argv[*argc] = g_strdup("--gst-disable-registry-update"); + (*argc)++; } - else + + /* check disable segtrap */ + if ( PLAYER_INI()->disable_segtrap ) { - *pmute = player->sound.mute; + argv[*argc] = g_strdup("--gst-disable-segtrap"); + (*argc)++; } - 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(); + debug_log("initializing gstreamer with following parameter\n"); + debug_log("argc : %d\n", *argc); - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - return_val_if_fail ( callback, MM_ERROR_INVALID_ARGUMENT ); + for ( i = 0; i < *argc; i++ ) + { + debug_log("argv[%d] : %s\n", i, argv[i]); + } - 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(); + /* initializing gstreamer */ + __ta__("gst_init time", - return MM_ERROR_NONE; -} + 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); + } -int -_mmplayer_set_audiostream_cb(MMHandleType hplayer, mm_player_audio_stream_callback callback, void *user_param) // @ -{ - mm_player_t* player = (mm_player_t*) hplayer; + goto ERROR; + } + ); - debug_fenter(); + /* release */ + for ( i = 0; i < *argc; i++ ) + { + MMPLAYER_FREEIF( argv[i] ); + } - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - return_val_if_fail(callback, MM_ERROR_INVALID_ARGUMENT); + MMPLAYER_FREEIF( argv ); + MMPLAYER_FREEIF( argc ); - 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); + /* done */ + initialized = TRUE; 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); + return TRUE; - 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); +ERROR: - debug_fleave(); + MMPLAYER_FREEIF( argv ); + MMPLAYER_FREEIF( argc ); - return MM_ERROR_NONE; + return FALSE; } int -_mmplayer_set_buffer_need_data_cb(MMHandleType hplayer, mm_player_buffer_need_data_callback callback, void *user_param) // @ +__mmplayer_destroy_streaming_ext(mm_player_t* player) { - 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; + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - debug_log("buffer need dataHandle value is %p : %p\n", player, player->need_data_cb); + if (player->pd_downloader) + _mmplayer_unrealize_pd_downloader((MMHandleType)player); - debug_fleave(); + 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_set_buffer_enough_data_cb(MMHandleType hplayer, mm_player_buffer_enough_data_callback callback, void *user_param) // @ +_mmplayer_destroy(MMHandleType handle) // @ { - mm_player_t* player = (mm_player_t*) hplayer; + mm_player_t* player = MM_PLAYER_CAST(handle); 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; -} + /* check player handle */ + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); -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; + /* destroy can called at anytime */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL ( player, MMPLAYER_COMMAND_DESTROY ); - debug_fenter(); + __mmplayer_destroy_streaming_ext(player); - return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); - return_val_if_fail(callback, MM_ERROR_INVALID_ARGUMENT); + /* 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 ); - player->seek_data_cb = callback; - player->buffer_cb_user_param = user_param; + 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"); + } - debug_log("buffer seek data cb Handle value is %p : %p\n", player, player->seek_data_cb); + if (MM_ERROR_NONE != _mmplayer_release_video_capture(player)) + { + debug_error("failed to release video capture\n"); + return MM_ERROR_PLAYER_INTERNAL; + } - debug_fleave(); + /* withdraw asm */ + if ( MM_ERROR_NONE != _mmplayer_asm_unregister(&player->sm) ) + { + debug_error("failed to deregister asm server\n"); + return MM_ERROR_PLAYER_INTERNAL; + } - return MM_ERROR_NONE; -} + /* release pipeline */ + if ( MM_ERROR_NONE != __mmplayer_gst_destroy_pipeline( player ) ) + { + debug_error("failed to destory pipeline\n"); + return MM_ERROR_PLAYER_INTERNAL; + } -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; + /* release attributes */ + _mmplayer_deconstruct_attribute( handle ); - debug_fenter(); + /* release factories */ + __mmplayer_release_factories( player ); - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - return_val_if_fail ( callback, MM_ERROR_INVALID_ARGUMENT ); + /* release lock */ + if ( player->fsink_lock ) + g_mutex_free( player->fsink_lock ); - player->video_frame_render_error_cb = callback; - player->video_frame_render_error_cb_user_param = user_param; + if ( player->msg_cb_lock ) + g_mutex_free( player->msg_cb_lock ); - debug_log("Video frame render error cb Handle value is %p : %p\n", player, player->video_frame_render_error_cb); + if (player->lazy_pause_event_id) + { + g_source_remove (player->lazy_pause_event_id); + player->lazy_pause_event_id = 0; + } debug_fleave(); @@ -7341,35 +2801,30 @@ _mmplayer_set_videoframe_render_error_cb(MMHandleType hplayer, mm_player_video_f } int -__mmplayer_start_streaming_ext(mm_player_t *player) +__mmplayer_realize_streaming_ext(mm_player_t* player) { - gint ret = MM_ERROR_NONE; + 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 ) { - ret = __mmplayer_realize_streaming_ext(player); - - if ( ret != MM_ERROR_NONE) - { - debug_error ("failed to realize streaming ext\n"); - return ret; - } + debug_error ("Unable to create PD Downloader..."); + ret = MM_ERROR_PLAYER_NO_FREE_SPACE; } - if (player->pd_downloader && player->pd_mode == MM_PLAYER_PD_MODE_URI) + 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) { - 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_error ("Unable to create PD Downloader..."); + ret = MM_ERROR_PLAYER_NOT_INITIALIZED; } } @@ -7378,176 +2833,146 @@ __mmplayer_start_streaming_ext(mm_player_t *player) } int -_mmplayer_start(MMHandleType hplayer) // @ +_mmplayer_realize(MMHandleType hplayer) // @ { - mm_player_t* player = (mm_player_t*) hplayer; - gint ret = MM_ERROR_NONE; + 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(); - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + /* 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_START ); + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_REALIZE ); - ret = _mmplayer_asm_set_state(hplayer, ASM_STATE_PLAYING); - if ( ret != MM_ERROR_NONE ) + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) { - debug_error("failed to set asm state to PLAYING\n"); - return ret; + debug_error("fail to get attributes.\n"); + return MM_ERROR_PLAYER_INTERNAL; } - /* NOTE : we should check and create pipeline again if not created as we destroy - * whole pipeline when stopping in streamming playback - */ - if ( ! player->pipeline ) + 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) ) { - ret = __gst_realize( player ); - if ( MM_ERROR_NONE != ret ) - { - debug_error("failed to realize before starting. only in streamming\n"); - return ret; - } + debug_error("failed to parse profile\n"); + return MM_ERROR_PLAYER_INVALID_URI; } - ret = __mmplayer_start_streaming_ext(player); - if ( ret != MM_ERROR_NONE ) + /* FIXIT : we can use thouse in player->profile directly */ + if (player->profile.uri_type == MM_PLAYER_URI_TYPE_MEM) { - debug_error("failed to start streaming ext \n"); + player->mem_buf.buf = (char *)player->profile.mem; + player->mem_buf.len = player->profile.mem_size; + player->mem_buf.offset = 0; } - /* start pipeline */ - ret = __gst_start( player ); - if ( ret != MM_ERROR_NONE ) + if (player->profile.uri_type == MM_PLAYER_URI_TYPE_URL_MMS) { - debug_error("failed to start player.\n"); + debug_warning("mms protocol is not supported format.\n"); + return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT; } - 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_handle_missed_plugin(mm_player_t* player) -{ - MMMessageParamType msg_param; - memset (&msg_param, 0, sizeof(MMMessageParamType)); - gboolean post_msg_direct = FALSE; + 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; - debug_fenter(); + player->videodec_linked = 0; + player->videosink_linked = 0; + player->audiodec_linked = 0; + player->audiosink_linked = 0; + player->textsink_linked = 0; - return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); + /* set the subtitle ON default */ + player->is_subtitle_off = FALSE; - debug_log("not_supported_codec = 0x%02x, can_support_codec = 0x%02x\n", - player->not_supported_codec, player->can_support_codec); + /* registry should be updated for downloadable codec */ + mm_attrs_get_int_by_name(attrs, "profile_update_registry", &update_registry); - if( player->not_found_demuxer ) + if ( update_registry ) { - 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); + debug_log("updating registry...\n"); + gst_update_registry(); - return MM_ERROR_NONE; + /* then we have to rebuild factories */ + __mmplayer_release_factories( player ); + __mmplayer_init_factories(player); } - if (player->not_supported_codec) + /* realize pipeline */ + ret = __gst_realize( player ); + if ( ret != MM_ERROR_NONE ) { - 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); + debug_error("fail to realize the player.\n"); + } + else + { + ret = __mmplayer_realize_streaming_ext(player); + } - return MM_ERROR_NONE; - } - else // no any supported codec case - { - debug_warning("not found any codec, posting error code to application.\n"); + debug_fleave(); - 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); - } + return ret; +} - MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); +int +__mmplayer_unrealize_streaming_ext(mm_player_t *player) +{ + debug_fenter(); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - MMPLAYER_FREEIF(msg_param.data); - } + /* 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; } -/* NOTE : it should be able to call 'stop' anytime*/ int -_mmplayer_stop(MMHandleType hplayer) // @ +_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 ); + 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_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_UNREALIZE ); __mmplayer_unrealize_streaming_ext(player); - /* stop pipeline */ - ret = __gst_stop( player ); + /* unrealize pipeline */ + ret = __gst_unrealize( player ); - if ( ret != MM_ERROR_NONE ) + /* set player state if success */ + if ( MM_ERROR_NONE == ret ) { - debug_error("failed to stop player.\n"); + if (player->sm.state != ASM_STATE_STOP) { + 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(); @@ -7556,241 +2981,148 @@ _mmplayer_stop(MMHandleType hplayer) // @ } int -_mmplayer_pause(MMHandleType hplayer) // @ +_mmplayer_set_message_callback(MMHandleType hplayer, MMMessageCallback callback, gpointer user_param) // @ { mm_player_t* player = (mm_player_t*)hplayer; -#ifndef GST_API_VERSION_1 - GstFormat fmt = GST_FORMAT_TIME; -#endif - 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 - */ -#ifdef GST_API_VERSION_1 - ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &pos_msec); -#else - ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &pos_msec); -#endif - if ( ! ret ) - debug_warning("getting current position failed in paused\n"); + return_val_if_fail(player, MM_ERROR_PLAYER_NOT_INITIALIZED); - player->last_position = pos_msec; - } - break; - } + return __gst_set_message_callback(player, callback, user_param); +} - /* pause pipeline */ - ret = __gst_pause( player, async ); +int +_mmplayer_get_state(MMHandleType hplayer, int* state) // @ +{ + mm_player_t *player = (mm_player_t*)hplayer; - if ( ret != MM_ERROR_NONE ) - { - debug_error("failed to pause player. ret : 0x%x\n", ret); - } + return_val_if_fail(state, MM_ERROR_INVALID_ARGUMENT); - debug_fleave(); + *state = MMPLAYER_CURRENT_STATE(player); - return ret; + return MM_ERROR_NONE; } + int -_mmplayer_resume(MMHandleType hplayer) +_mmplayer_set_volume(MMHandleType hplayer, MMPlayerVolumeType volume) // @ { - mm_player_t* player = (mm_player_t*)hplayer; - int ret = MM_ERROR_NONE; - gboolean async = FALSE; + 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 ); - ret = _mmplayer_asm_set_state(hplayer, ASM_STATE_PLAYING); - if ( ret ) + 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++ ) { - debug_error("failed to set asm state to PLAYING\n"); - return ret; + 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; + } } - /* check current state */ - MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_RESUME ); + /* 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; + } - /* resume pipeline */ - ret = __gst_resume( player, FALSE ); + /* setting volume to volume element */ + vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst; - if ( ret != MM_ERROR_NONE ) + if ( vol_element ) { - debug_error("failed to resume player.\n"); + debug_log("volume is set [%f]\n", player->sound.volume); + g_object_set(vol_element, "volume", player->sound.volume, NULL); } - debug_fleave(); - return ret; + return MM_ERROR_NONE; } + int -__mmplayer_set_play_count(mm_player_t* player, gint count) +_mmplayer_get_volume(MMHandleType hplayer, MMPlayerVolumeType* volume) { - MMHandleType attrs = 0; + 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 ); - 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"); + /* returning stored volume */ + for (i = 0; i < MM_VOLUME_CHANNEL_NUM; i++) + volume->level[i] = player->sound.volume; debug_fleave(); - return MM_ERROR_NONE; + return MM_ERROR_NONE; } + + int -_mmplayer_activate_section_repeat(MMHandleType hplayer, unsigned long start, unsigned long end) +_mmplayer_set_mute(MMHandleType hplayer, int mute) // @ { - mm_player_t* player = (mm_player_t*)hplayer; - gint64 start_pos = 0; - gint64 end_pos = 0; - gint infinity = -1; + 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 ( 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 ); + debug_log("mute : %d\n", mute); - if ( (!__gst_seek( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, - player->playback_rate, - GST_FORMAT_TIME, - ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ), - GST_SEEK_TYPE_SET, start_pos, - GST_SEEK_TYPE_SET, end_pos))) + /* mute value shoud 0 or 1 */ + if ( mute != 0 && mute != 1 ) { - debug_error("failed to activate section repeat\n"); + debug_error("bad mute value\n"); - return MM_ERROR_PLAYER_SEEK; + /* FIXIT : definitly, we need _BAD_PARAM error code */ + return MM_ERROR_INVALID_ARGUMENT; } - 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; -#ifndef GST_API_VERSION_1 - GstFormat fmt = GST_FORMAT_TIME; -#endif - 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) + /* just hold mute value if pipeline is not ready */ + if ( !player->pipeline || !player->pipeline->audiobin ) { - debug_log("extracting entire stream"); + debug_log("pipeline is not ready. holding mute value\n"); + player->sound.mute = mute; 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 */ -#ifdef GST_API_VERSION_1 - ret = gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &dur_nsec); -#else - ret = gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &dur_nsec); -#endif - 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 + vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst; + + /* NOTE : volume will only created when the bt is enabled */ + if ( vol_element ) { - debug_log("invalid end pos for pcm extraction"); - return MM_ERROR_INVALID_ARGUMENT; + g_object_set(vol_element, "mute", mute, NULL); } - - 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))) + else { - debug_error("failed to seek for pcm extraction\n"); - - return MM_ERROR_PLAYER_SEEK; + debug_log("volume elemnet is not created. using volume in audiosink\n"); } - debug_log("succeeded to set up segment extraction from [%llu] to [%llu] (nsec)\n", start_nsec, end_nsec); + player->sound.mute = mute; debug_fleave(); @@ -7798,2558 +3130,2382 @@ __mmplayer_set_pcm_extraction(mm_player_t* player) } int -_mmplayer_deactivate_section_repeat(MMHandleType hplayer) +_mmplayer_get_mute(MMHandleType hplayer, int* pmute) // @ { - mm_player_t* player = (mm_player_t*)hplayer; - gint64 cur_pos = 0; -#ifndef GST_API_VERSION_1 - GstFormat fmt = GST_FORMAT_TIME; -#endif - gint onetime = 1; + 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 ); - player->section_repeat = FALSE; - - __mmplayer_set_play_count( player, onetime ); -#ifdef GST_API_VERSION_1 - gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &cur_pos); -#else - gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &cur_pos); -#endif - - 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; -#ifndef GST_API_VERSION_1 - GstFormat format =GST_FORMAT_TIME; -#endif - 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) + /* just hold mute value if pipeline is not ready */ + if ( !player->pipeline || !player->pipeline->audiobin ) { - if (player->can_support_codec & FOUND_PLUGIN_VIDEO) - mute = TRUE; - } - _mmplayer_set_mute(hplayer, mute); - - if (player->playback_rate == rate) + debug_log("pipeline is not ready. returning stored value\n"); + *pmute = player->sound.mute; 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); -#ifdef GST_API_VERSION_1 - if ( current_state != MM_PLAYER_STATE_PAUSED ) - ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &pos_msec); -#else - if ( current_state != MM_PLAYER_STATE_PAUSED ) - ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &format, &pos_msec); -#endif - debug_log ("pos_msec = %"GST_TIME_FORMAT" and ret = %d and state = %d", GST_TIME_ARGS (pos_msec), ret, current_state); + vol_element = player->pipeline->audiobin[MMPLAYER_A_VOL].gst; - if ( ( current_state == MM_PLAYER_STATE_PAUSED ) - || ( ! ret )) - //|| ( player->last_position != 0 && pos_msec == 0 ) ) + if ( vol_element ) { - debug_warning("returning last point : %lld\n", player->last_position ); - pos_msec = player->last_position; + g_object_get(vol_element, "mute", pmute, NULL); + debug_log("mute=%d\n\n", *pmute); } - - 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))) + else { - debug_error("failed to set speed playback\n"); - return MM_ERROR_PLAYER_SEEK; + *pmute = player->sound.mute; } - debug_log("succeeded to set speed playback as %fl\n", rate); - debug_fleave(); - return MM_ERROR_NONE;; + return MM_ERROR_NONE; } int -_mmplayer_set_position(MMHandleType hplayer, int format, int position) // @ +_mmplayer_set_videostream_cb(MMHandleType hplayer, mm_player_video_stream_callback callback, void *user_param) // @ { - mm_player_t* player = (mm_player_t*)hplayer; - int ret = MM_ERROR_NONE; + 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 ); - ret = __gst_set_position ( player, format, (unsigned long)position, FALSE ); + 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 ret; + return MM_ERROR_NONE; } int -_mmplayer_get_position(MMHandleType hplayer, int format, unsigned long *position) // @ +_mmplayer_set_audiostream_cb(MMHandleType hplayer, mm_player_audio_stream_callback callback, void *user_param) // @ { - mm_player_t* player = (mm_player_t*)hplayer; - int ret = MM_ERROR_NONE; + 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); - ret = __gst_get_position ( player, format, position ); + 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); - return ret; + debug_fleave(); + + return MM_ERROR_NONE; } int -_mmplayer_get_buffer_position(MMHandleType hplayer, int format, unsigned long* start_pos, unsigned long* stop_pos) // @ +_mmplayer_set_audiobuffer_cb(MMHandleType hplayer, mm_player_audio_stream_callback callback, void *user_param) // @ { - mm_player_t* player = (mm_player_t*)hplayer; - int ret = MM_ERROR_NONE; + 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); - ret = __gst_get_buffer_position ( player, format, start_pos, stop_pos ); + 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); - return ret; + debug_fleave(); + + return MM_ERROR_NONE; } int -_mmplayer_adjust_subtitle_postion(MMHandleType hplayer, int format, int position) // @ +_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; - int ret = MM_ERROR_NONE; + mm_player_t* player = (mm_player_t*) hplayer; debug_fenter(); - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail ( player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail(callback, MM_ERROR_INVALID_ARGUMENT); - ret = __gst_adjust_subtitle_position(player, format, position); + 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 ret; + return MM_ERROR_NONE; } -static gboolean -__mmplayer_is_midi_type( gchar* str_caps) +int +_mmplayer_set_buffer_enough_data_cb(MMHandleType hplayer, mm_player_buffer_enough_data_callback callback, void *user_param) // @ { - 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"); + mm_player_t* player = (mm_player_t*) hplayer; - return TRUE; - } + debug_fenter(); - return FALSE; + 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; } -static gboolean -__mmplayer_is_amr_type (gchar *str_caps) +int +_mmplayer_set_buffer_seek_data_cb(MMHandleType hplayer, mm_player_buffer_seek_data_callback callback, void *user_param) // @ { - if ((g_strrstr(str_caps, "AMR")) || - (g_strrstr(str_caps, "amr"))) - { - return TRUE; - } - return FALSE; + 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; } -static gboolean -__mmplayer_is_only_mp3_type (gchar *str_caps) +int +_mmplayer_set_videoframe_render_error_cb(MMHandleType hplayer, mm_player_video_frame_render_error_callback callback, void *user_param) // @ { - if (g_strrstr(str_caps, "application/x-id3") || - (g_strrstr(str_caps, "audio/mpeg") && g_strrstr(str_caps, "mpegversion=(int)1"))) + 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)) { - return TRUE; + 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; + } } - return FALSE; + + debug_fleave(); + return ret; } -static void -__mmplayer_typefind_have_type( GstElement *tf, guint probability, // @ -GstCaps *caps, gpointer data) +int +_mmplayer_start(MMHandleType hplayer) // @ { - mm_player_t* player = (mm_player_t*)data; - GstPad* pad = NULL; + mm_player_t* player = (mm_player_t*) hplayer; + gint ret = MM_ERROR_NONE; debug_fenter(); - return_if_fail( player && tf && caps ); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - /* 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)); + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_START ); - /* midi type should be stored because it will be used to set audio gain in avsysaudiosink */ - if ( __mmplayer_is_midi_type(player->type)) + ret = _mmplayer_asm_set_state(hplayer, ASM_STATE_PLAYING); + if ( ret != MM_ERROR_NONE ) { - player->profile.play_mode = MM_PLAYER_MODE_MIDI; + debug_error("failed to set asm state to PLAYING\n"); + return ret; } - else if (__mmplayer_is_amr_type(player->type)) + + /* NOTE : we should check and create pipeline again if not created as we destroy + * whole pipeline when stopping in streamming playback + */ + if ( ! player->pipeline ) { - player->bypass_audio_effect = FALSE; - if ( (PLAYER_INI()->use_audio_effect_preset || PLAYER_INI()->use_audio_effect_custom) ) + ret = __gst_realize( player ); + if ( MM_ERROR_NONE != ret ) { - 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"); - } - } + debug_error("failed to realize before starting. only in streamming\n"); + return ret; } } - else if ( g_strrstr(player->type, "application/x-hls")) + + ret = __mmplayer_start_streaming_ext(player); + if ( ret != MM_ERROR_NONE ) { - /* 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; + debug_error("failed to start streaming ext \n"); } - pad = gst_element_get_static_pad(tf, "src"); - if ( !pad ) + /* start pipeline */ + ret = __gst_start( player ); + if ( ret != MM_ERROR_NONE ) { - debug_error("fail to get typefind src pad.\n"); - return; + debug_error("failed to start player.\n"); } - /* try to plug */ - if ( ! __mmplayer_try_to_plug( player, pad, caps ) ) - { - gboolean async = FALSE; + debug_fleave(); - debug_error("failed to autoplug %s\n", player->type); - mm_attrs_get_int_by_name(player->attrs, "profile_prepare_async", &async); + return ret; +} - if ( async && player->msg_posted == FALSE ) - { - __mmplayer_handle_missed_plugin( player ); - } +/* 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 ); - goto DONE; - } + __mmplayer_unrealize_streaming_ext(player); - /* finish autopluging if no dynamic pad waiting */ - if( ( ! player->have_dynamic_pad) && ( ! player->has_many_types) ) + /* stop pipeline */ + ret = __gst_stop( player ); + + if ( ret != MM_ERROR_NONE ) { - if ( ! MMPLAYER_IS_RTSP_STREAMING( player ) ) - { - __mmplayer_pipeline_complete( NULL, (gpointer)player ); - } + debug_error("failed to stop player.\n"); } -DONE: - gst_object_unref( GST_OBJECT(pad) ); - debug_fleave(); - return; + return ret; } -static gboolean -__mmplayer_warm_up_video_codec( mm_player_t* player, GstElementFactory *factory) +int +_mmplayer_pause(MMHandleType hplayer) // @ { - GstElement *element; - GstStateChangeReturn ret; - gboolean usable = TRUE; + mm_player_t* player = (mm_player_t*)hplayer; +#ifndef GST_API_VERSION_1 + GstFormat fmt = GST_FORMAT_TIME; +#endif + gint64 pos_msec = 0; + gboolean async = FALSE; + gint ret = MM_ERROR_NONE; - return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - return_val_if_fail ( factory, MM_ERROR_COMMON_INVALID_ARGUMENT ); + debug_fenter(); - element = gst_element_factory_create (factory, NULL); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - ret = gst_element_set_state (element, GST_STATE_READY); + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_PAUSE ); - if (ret != GST_STATE_CHANGE_SUCCESS) + 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 + */ #ifdef GST_API_VERSION_1 - debug_error ("resource conflict so, %s unusable\n", gst_object_get_name (GST_OBJECT (factory))); + ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &pos_msec); #else - debug_error ("resource conflict so, %s unusable\n", GST_PLUGIN_FEATURE_NAME (factory)); + ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &pos_msec); #endif - usable = FALSE; + if ( ! ret ) + debug_warning("getting current position failed in paused\n"); + + player->last_position = pos_msec; + } + break; } - gst_element_set_state (element, GST_STATE_NULL); - gst_object_unref (element); + /* pause pipeline */ + ret = __gst_pause( player, async ); - return usable; + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to pause player. ret : 0x%x\n", ret); + } + + debug_fleave(); + + return ret; } -/* it will return first created element */ -static gboolean -__mmplayer_try_to_plug(mm_player_t* player, GstPad *pad, const GstCaps *caps) // @ +int +_mmplayer_resume(MMHandleType hplayer) { - 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; + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; + gboolean async = FALSE; debug_fenter(); - return_val_if_fail( player && player->pipeline && player->pipeline->mainbin, FALSE ); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - mainbin = player->pipeline->mainbin; + ret = _mmplayer_asm_set_state(hplayer, ASM_STATE_PLAYING); + if ( ret ) + { + debug_error("failed to set asm state to PLAYING\n"); + return ret; + } - mime = gst_structure_get_name(gst_caps_get_structure(caps, 0)); + /* check current state */ + MMPLAYER_CHECK_STATE_RETURN_IF_FAIL( player, MMPLAYER_COMMAND_RESUME ); - /* 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, "video/x-surface") - || g_str_has_prefix(mime, "text/plain") ||g_str_has_prefix(mime, "text/x-pango-markup")) - { + /* resume pipeline */ + ret = __gst_resume( player, FALSE ); - 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) ); + if ( ret != MM_ERROR_NONE ) + { + debug_error("failed to resume player.\n"); + } - /* 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; - } + debug_fleave(); - /* 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; - } + return ret; +} - /* add to pipeline */ - if ( ! gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), queue) ) - { - debug_warning("failed to add queue\n"); - goto ERROR; - } +int +__mmplayer_set_play_count(mm_player_t* player, gint count) +{ + MMHandleType attrs = 0; - /* link queue */ - queue_pad = gst_element_get_static_pad(queue, "sink"); + debug_fenter(); - 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; + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - /* 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; - } + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) + { + debug_error("fail to get attributes.\n"); + return MM_ERROR_PLAYER_INTERNAL; + } - /* 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); + 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"); - if(__mmplayer_link_sink(player,pad)) - __mmplayer_gst_decode_callback(element, pad, FALSE, player); + debug_fleave(); - gst_object_unref( GST_OBJECT(element)); - element = NULL; + return MM_ERROR_NONE; +} - return TRUE; - } +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; - item = player->factories; - for(; item != NULL ; item = item->next) - { - GstElementFactory *factory = GST_ELEMENT_FACTORY(item->data); - const GList *pads; - gint idx = 0; + debug_fenter(); - skip = FALSE; + 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 ); - /* filtering exclude keyword */ -#ifdef GST_API_VERSION_1 - for ( idx = 0; PLAYER_INI()->exclude_element_keyword[idx][0] != '\0'; idx++ ) - { - if ( g_strrstr(gst_object_get_name (GST_OBJECT (factory)), - PLAYER_INI()->exclude_element_keyword[idx]) ) - { - debug_warning("skipping [%s] by exculde keyword [%s]\n", - gst_object_get_name (GST_OBJECT (factory)), - PLAYER_INI()->exclude_element_keyword[idx] ); + player->section_repeat = TRUE; + player->section_repeat_start = start; + player->section_repeat_end = end; - skip = TRUE; - break; - } - } -#else - 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] ); + 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, + player->playback_rate, + 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; + } - skip = TRUE; - break; - } - } + 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; +#ifndef GST_API_VERSION_1 + GstFormat fmt = GST_FORMAT_TIME; #endif + int required_start = 0; + int required_end = 0; + int ret = 0; - if ( skip ) continue; + debug_fenter(); - /* check factory class for filtering */ - klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(factory)); + return_val_if_fail( player, FALSE ); - /* 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") ) - { + 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 */ #ifdef GST_API_VERSION_1 - debug_log("skipping [%s] by not required\n", - gst_object_get_name (GST_OBJECT (factory)) ); + ret = gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &dur_nsec); #else - debug_log("skipping [%s] by not required\n", - GST_PLUGIN_FEATURE_NAME (factory) ); + ret = gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &dur_nsec); #endif - continue; - } + if ( !ret ) + { + debug_error("failed to get duration"); + return MM_ERROR_PLAYER_INTERNAL; + } + dur_msec = GST_TIME_AS_MSECONDS(dur_nsec); - /* 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 (dur_msec < required_end) // FIXME + { + debug_log("invalid end pos for pcm extraction"); + return MM_ERROR_INVALID_ARGUMENT; + } - if( temp1->direction != GST_PAD_SINK || - temp1->presence != GST_PAD_ALWAYS) - continue; + 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"); - 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 ); - } + return MM_ERROR_PLAYER_SEEK; + } - res = gst_caps_intersect(caps, static_caps); + debug_log("succeeded to set up segment extraction from [%llu] to [%llu] (nsec)\n", start_nsec, end_nsec); - gst_caps_unref( static_caps ); - static_caps = NULL; + debug_fleave(); - if( res && !gst_caps_is_empty(res) ) - { -#ifdef GST_API_VERSION_1 - GstElement *new_element; - GList *elements = player->parsers; - char *name_template = g_strdup(temp1->name_template); - gchar *name_to_plug = gst_object_get_name (GST_OBJECT (factory)); + return MM_ERROR_NONE; +} - gst_caps_unref(res); +int +_mmplayer_deactivate_section_repeat(MMHandleType hplayer) +{ + mm_player_t* player = (mm_player_t*)hplayer; + gint64 cur_pos = 0; +#ifndef GST_API_VERSION_1 + GstFormat fmt = GST_FORMAT_TIME; +#endif + gint onetime = 1; - debug_log("found %s to plug\n", name_to_plug); + debug_fenter(); - 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_object_get_name (GST_OBJECT (factory))); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - MMPLAYER_FREEIF(name_template); + player->section_repeat = FALSE; - continue; - } + __mmplayer_set_play_count( player, onetime ); +#ifdef GST_API_VERSION_1 + gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &cur_pos); #else - 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_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &cur_pos); +#endif - gst_caps_unref(res); + 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"); - debug_log("found %s to plug\n", name_to_plug); + return MM_ERROR_PLAYER_SEEK; + } - 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)); + debug_fenter(); - MMPLAYER_FREEIF(name_template); + return MM_ERROR_NONE; +} - continue; - } +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; +#ifndef GST_API_VERSION_1 + GstFormat format =GST_FORMAT_TIME; #endif + MMPlayerStateType current_state = MM_PLAYER_STATE_NONE; + debug_fenter(); - /* 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; + 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 ); - for ( ; elements; elements = g_list_next(elements)) - { - gchar *element_name = elements->data; + /* 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 (g_strrstr(element_name, name_to_plug)) - { - debug_log("but, %s already linked, so skipping it\n", name_to_plug); - skip = TRUE; - } - } + if (player->playback_rate == rate) + return MM_ERROR_NONE; - if (skip) continue; + /* 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; - selected = g_strdup(name_to_plug); + current_state = MMPLAYER_CURRENT_STATE(player); +#ifdef GST_API_VERSION_1 + if ( current_state != MM_PLAYER_STATE_PAUSED ) + ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &pos_msec); +#else + if ( current_state != MM_PLAYER_STATE_PAUSED ) + ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &format, &pos_msec); +#endif - player->parsers = g_list_append(player->parsers, selected); - } + debug_log ("pos_msec = %"GST_TIME_FORMAT" and ret = %d and state = %d", GST_TIME_ARGS (pos_msec), ret, current_state); - /* 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; + 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; + } - debug_log("found VIDEO decoder\n"); - player->not_supported_codec &= MISSING_PLUGIN_AUDIO; - player->can_support_codec |= FOUND_PLUGIN_VIDEO; + 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; + } - 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); + debug_log("succeeded to set speed playback as %fl\n", rate); - caps_type = gst_caps_to_string(caps); + debug_fleave(); - if ( g_strrstr( caps_type, "ST12") ) - player->is_nv12_tiled = TRUE; + return MM_ERROR_NONE;; +} - /* 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; +int +_mmplayer_set_position(MMHandleType hplayer, int format, int position) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; - /* Link is failed even though a supportable codec is found. */ - __mmplayer_check_not_supported_codec(player, (gchar *)mime); + debug_fenter(); - MMPLAYER_FREEIF(name_template); - debug_error("failed to call _close_link\n"); - return FALSE; - } + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - MMPLAYER_FREEIF(name_template); - return TRUE; - } + ret = __gst_set_position ( player, format, (unsigned long)position, FALSE ); - gst_caps_unref(res); + debug_fleave(); - break; - } - } + return ret; +} - /* There is no available codec. */ - __mmplayer_check_not_supported_codec(player,(gchar *)mime); +int +_mmplayer_get_position(MMHandleType hplayer, int format, unsigned long *position) // @ +{ + mm_player_t* player = (mm_player_t*)hplayer; + int ret = MM_ERROR_NONE; - debug_fleave(); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - return FALSE; + ret = __gst_get_position ( player, format, position ); -ERROR: - /* release */ - if ( queue ) - gst_object_unref( queue ); + 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; - if ( queue_pad ) - gst_object_unref( queue_pad ); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - if ( element ) - gst_object_unref ( element ); + ret = __gst_get_buffer_position ( player, format, start_pos, stop_pos ); - return FALSE; + return ret; } - -static -int __mmplayer_check_not_supported_codec(mm_player_t* player, gchar* mime) +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 && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED); - return_val_if_fail ( mime, MM_ERROR_INVALID_ARGUMENT ); + return_val_if_fail ( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - debug_log("mimetype to check: %s\n", mime ); + ret = __gst_adjust_subtitle_position(player, format, position); - /* 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 ); + debug_fleave(); - goto DONE; - } - } + return ret; +} - if( ( g_str_has_prefix(mime, "video") ) ||( g_str_has_prefix(mime, "image") ) ) +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("can support codec=%d, vdec_linked=%d, adec_linked=%d\n", - player->can_support_codec, player->videodec_linked, player->audiodec_linked); + debug_log("midi\n"); - /* 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; - } + return TRUE; } -DONE: - debug_fleave(); - - return MM_ERROR_NONE; + return FALSE; } - -static void __mmplayer_pipeline_complete(GstElement *decodebin, gpointer data) // @ +static gboolean +__mmplayer_is_amr_type (gchar *str_caps) { - 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]) ) + if ((g_strrstr(str_caps, "AMR")) || + (g_strrstr(str_caps, "amr"))) { - /* 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; + return TRUE; } - debug_log("pipeline has completely constructed\n"); - - player->pipeline_is_constructed = TRUE; + return FALSE; +} - if ( ( PLAYER_INI()->async_start ) && - ( player->msg_posted == FALSE ) && - ( player->cmd >= MMPLAYER_COMMAND_START )) +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"))) { - __mmplayer_handle_missed_plugin( player ); + return TRUE; } - - MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-complete" ); + return FALSE; } -static gboolean __mmplayer_configure_audio_callback(mm_player_t* player) +void +__mmplayer_typefind_have_type( GstElement *tf, guint probability, // @ +GstCaps *caps, gpointer data) { - debug_fenter(); + mm_player_t* player = (mm_player_t*)data; + GstPad* pad = NULL; - return_val_if_fail ( player, FALSE ); + debug_fenter(); + return_if_fail( player && tf && caps ); - if ( MMPLAYER_IS_STREAMING(player) ) - return FALSE; + /* 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)); - /* This callback can be set to music player only. */ - if((player->can_support_codec & 0x02) == FOUND_PLUGIN_VIDEO) + /* midi type should be stored because it will be used to set audio gain in avsysaudiosink */ + if ( __mmplayer_is_midi_type(player->type)) { - debug_warning("audio callback is not supported for video"); - return FALSE; + player->profile.play_mode = MM_PLAYER_MODE_MIDI; } - - if (player->audio_stream_cb) + 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) ) { - GstPad *pad = NULL; - -#ifdef GST_API_VERSION_1 - pad = gst_element_get_static_pad (player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "sink"); -#else - pad = gst_element_get_pad (player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "sink"); -#endif - - if ( !pad ) + if ( player->audio_effect_info.effect_type == MM_AUDIO_EFFECT_TYPE_PRESET ) { - debug_error("failed to get sink pad from audiosink to probe data\n"); - return FALSE; + 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; + } -#ifdef GST_API_VERSION_1 - player->audio_cb_probe_id = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, - __mmplayer_audio_stream_probe, player, NULL); -#else - player->audio_cb_probe_id = gst_pad_add_buffer_probe (pad, - G_CALLBACK (__mmplayer_audio_stream_probe), player); -#endif + pad = gst_element_get_static_pad(tf, "src"); + if ( !pad ) + { + debug_error("fail to get typefind src pad.\n"); + return; + } - gst_object_unref (pad); + /* try to plug */ + if ( ! __mmplayer_try_to_plug( player, pad, caps ) ) + { + gboolean async = FALSE; - pad = NULL; - } + debug_error("failed to autoplug %s\n", player->type); + mm_attrs_get_int_by_name(player->attrs, "profile_prepare_async", &async); + + if ( async && player->msg_posted == FALSE ) + { + __mmplayer_handle_missed_plugin( player ); + } + + goto DONE; } - else + + /* finish autopluging if no dynamic pad waiting */ + if( ( ! player->have_dynamic_pad) && ( ! player->has_many_types) ) { - debug_error("There is no audio callback to configure.\n"); - return FALSE; + if ( ! MMPLAYER_IS_RTSP_STREAMING( player ) ) + { + __mmplayer_pipeline_complete( NULL, (gpointer)player ); + } } - debug_fleave(); +DONE: + gst_object_unref( GST_OBJECT(pad) ); - return TRUE; + debug_fleave(); + + return; } -static void -__mmplayer_init_factories(mm_player_t* player) // @ +static gboolean +__mmplayer_warm_up_video_codec( mm_player_t* player, GstElementFactory *factory) { - debug_fenter(); + GstElement *element; + GstStateChangeReturn ret; + gboolean usable = TRUE; - return_if_fail ( player ); + 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) + { #ifdef GST_API_VERSION_1 - player->factories = gst_registry_feature_filter(gst_registry_get(), - (GstPluginFeatureFilter)__mmplayer_feature_filter, FALSE, NULL); + debug_error ("resource conflict so, %s unusable\n", gst_object_get_name (GST_OBJECT (factory))); #else - player->factories = gst_registry_feature_filter(gst_registry_get_default(), - (GstPluginFeatureFilter)__mmplayer_feature_filter, FALSE, NULL); + debug_error ("resource conflict so, %s unusable\n", GST_PLUGIN_FEATURE_NAME (factory)); #endif + usable = FALSE; + } - player->factories = g_list_sort(player->factories, (GCompareFunc)util_factory_rank_compare); + gst_element_set_state (element, GST_STATE_NULL); + gst_object_unref (element); - debug_fleave(); + return usable; } -static void -__mmplayer_release_factories(mm_player_t* player) // @ +/* 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_if_fail ( player ); + return_val_if_fail( player && player->pipeline && player->pipeline->mainbin, FALSE ); - if (player->factories) + 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, "video/x-surface") + || g_str_has_prefix(mime, "text/plain") ||g_str_has_prefix(mime, "text/x-pango-markup")) { - gst_plugin_feature_list_free (player->factories); - player->factories = NULL; - } - debug_fleave(); -} + 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) ); -static void -__mmplayer_release_misc(mm_player_t* player) -{ - int i; - debug_fenter(); + /* 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"); - return_if_fail ( player ); + queue = gst_element_factory_make("queue", NULL); + if ( ! queue ) + { + debug_warning("failed to create queue\n"); + goto ERROR; + } - player->use_video_stream = FALSE; - player->video_stream_cb = NULL; - player->video_stream_cb_user_param = NULL; + /* 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; + } - player->audio_stream_cb = NULL; - player->audio_stream_cb_user_param = NULL; + /* add to pipeline */ + if ( ! gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), queue) ) + { + debug_warning("failed to add queue\n"); + goto ERROR; + } - player->audio_buffer_cb = NULL; - player->audio_buffer_cb_user_param = NULL; + /* link queue */ + queue_pad = gst_element_get_static_pad(queue, "sink"); - player->sent_bos = FALSE; - player->playback_rate = DEFAULT_PLAYBACK_RATE; + 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; - player->doing_seek = FALSE; + /* 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; + } - player->streamer = NULL; - player->updated_bitrate_count = 0; - player->total_bitrate = 0; - player->updated_maximum_bitrate_count = 0; - player->total_maximum_bitrate = 0; + /* 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); - player->not_found_demuxer = 0; + if(__mmplayer_link_sink(player,pad)) + __mmplayer_gst_decode_callback(element, pad, FALSE, player); - 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->pending_seek.is_pending = FALSE; - player->pending_seek.format = MM_PLAYER_POS_FORMAT_TIME; - player->pending_seek.pos = 0; - player->msg_posted = FALSE; - player->has_many_types = FALSE; + gst_object_unref( GST_OBJECT(element)); + element = NULL; - for (i = 0; i < MM_PLAYER_STREAM_COUNT_MAX; i++) - { - player->bitrate[i] = 0; - player->maximum_bitrate[i] = 0; + return TRUE; } - /* clean found parsers */ - if (player->parsers) + item = player->factories; + for(; item != NULL ; item = item->next) { - g_list_free(player->parsers); - player->parsers = NULL; - } + GstElementFactory *factory = GST_ELEMENT_FACTORY(item->data); + const GList *pads; + gint idx = 0; - MMPLAYER_FREEIF(player->album_art); + skip = FALSE; - /* 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); - } + /* filtering exclude keyword */ +#ifdef GST_API_VERSION_1 + for ( idx = 0; PLAYER_INI()->exclude_element_keyword[idx][0] != '\0'; idx++ ) + { + if ( g_strrstr(gst_object_get_name (GST_OBJECT (factory)), + PLAYER_INI()->exclude_element_keyword[idx]) ) + { + debug_warning("skipping [%s] by exculde keyword [%s]\n", + gst_object_get_name (GST_OBJECT (factory)), + PLAYER_INI()->exclude_element_keyword[idx] ); - debug_fleave(); -} + skip = TRUE; + break; + } + } +#else + 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] ); -static GstElement *__mmplayer_element_create_and_link(mm_player_t *player, GstPad* pad, const char* name) -{ - GstElement *element = NULL; - GstPad *sinkpad; + skip = TRUE; + break; + } + } +#endif - debug_log("creating %s to plug\n", name); + if ( skip ) continue; - element = gst_element_factory_make(name, NULL); - if ( ! element ) - { - debug_error("failed to create queue\n"); - return NULL; - } + /* 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") ) + { +#ifdef GST_API_VERSION_1 + debug_log("skipping [%s] by not required\n", + gst_object_get_name (GST_OBJECT (factory)) ); +#else + debug_log("skipping [%s] by not required\n", + GST_PLUGIN_FEATURE_NAME (factory) ); +#endif + continue; + } - 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; - } + /* 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 ( ! gst_bin_add(GST_BIN(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst), element)) - { - debug_error("failed to add %s\n", name); - return NULL; - } + if( temp1->direction != GST_PAD_SINK || + temp1->presence != GST_PAD_ALWAYS) + continue; - sinkpad = gst_element_get_static_pad(element, "sink"); + 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 ); + } - if ( GST_PAD_LINK_OK != gst_pad_link(pad, sinkpad) ) - { - debug_error("failed to link %s\n", name); - gst_object_unref (sinkpad); + res = gst_caps_intersect(caps, static_caps); - return NULL; - } + gst_caps_unref( static_caps ); + static_caps = NULL; - debug_log("linked %s to pipeline successfully\n", name); + if( res && !gst_caps_is_empty(res) ) + { +#ifdef GST_API_VERSION_1 + GstElement *new_element; + GList *elements = player->parsers; + char *name_template = g_strdup(temp1->name_template); + gchar *name_to_plug = gst_object_get_name (GST_OBJECT (factory)); - gst_object_unref (sinkpad); + gst_caps_unref(res); - return element; -} + debug_log("found %s to plug\n", name_to_plug); -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; + 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_object_get_name (GST_OBJECT (factory))); - debug_fenter(); + MMPLAYER_FREEIF(name_template); - return_val_if_fail ( player && - player->pipeline && - player->pipeline->mainbin, - FALSE ); + continue; + } +#else + GstElement *new_element; + GList *elements = player->parsers; + char *name_template = g_strdup(temp1->name_template); + gchar *name_to_plug = GST_PLUGIN_FEATURE_NAME(factory); - mainbin = player->pipeline->mainbin; + gst_caps_unref(res); - 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); + debug_log("found %s to plug\n", name_to_plug); - factory = gst_element_get_factory(sinkelement); - klass = gst_element_factory_get_klass(factory); + 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)); - /* check if player can do start continually */ - MMPLAYER_CHECK_CMD_IF_EXIT(player); + MMPLAYER_FREEIF(name_template); - if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(sinkelement, warmup) ) - { - if (isvideo_decoder) - player->keep_detecting_vcodec = TRUE; + continue; + } +#endif - debug_error("failed to set %d state to %s\n", warmup, GST_ELEMENT_NAME( sinkelement )); - goto ERROR; - } + /* 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; - /* 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; - } + for ( ; elements; elements = g_list_next(elements)) + { + gchar *element_name = elements->data; - debug_log("element klass : %s\n", klass); + if (g_strrstr(element_name, name_to_plug)) + { + debug_log("but, %s already linked, so skipping it\n", name_to_plug); + skip = TRUE; + } + } - /* 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; + if (skip) continue; - name = g_strdup(GST_ELEMENT_NAME( GST_PAD_PARENT ( srcpad ))); + selected = g_strdup(name_to_plug); - if (g_strrstr(name, "mpegtsdemux")) - { - gchar *demux_caps = NULL; - gchar *parser_name = NULL; - GstCaps *dcaps = NULL; + player->parsers = g_list_append(player->parsers, selected); + } -#ifdef GST_API_VERSION_1 - dcaps = gst_pad_get_current_caps(srcpad); -#else - dcaps = gst_pad_get_caps(srcpad); -#endif - demux_caps = gst_caps_to_string(dcaps); + /* 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; - 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"); - } + debug_log("found VIDEO decoder\n"); + player->not_supported_codec &= MISSING_PLUGIN_AUDIO; + player->can_support_codec |= FOUND_PLUGIN_VIDEO; - gst_caps_unref(dcaps); - MMPLAYER_FREEIF( demux_caps ); + 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); - if (parser_name) - { - parser = __mmplayer_element_create_and_link(player, srcpad, parser_name); + caps_type = gst_caps_to_string(caps); - MMPLAYER_FREEIF(parser_name); + if ( g_strrstr( caps_type, "ST12") ) + player->is_nv12_tiled = TRUE; - if ( ! parser ) - { - debug_error("failed to create parser\n"); + /* 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; + } } - else + if ( ! __mmplayer_close_link(player, pad, new_element, + name_template,gst_element_factory_get_static_pad_templates(factory)) ) { - /* update srcpad if parser is created */ - pssrcpad = gst_element_get_static_pad(parser, "src"); - srcpad = pssrcpad; + 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; } - } - 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; + gst_caps_unref(res); + + break; } + } - /* update srcpad to link with decoder */ - qsrcpad = gst_element_get_static_pad(queue, "src"); - srcpad = qsrcpad; + /* There is no available codec. */ + __mmplayer_check_not_supported_codec(player,(gchar *)mime); - q_max_size_time = GST_QUEUE_DEFAULT_TIME; + debug_fleave(); - /* 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; + return FALSE; + +ERROR: + /* release */ + if ( queue ) + gst_object_unref( 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); - } + if ( queue_pad ) + gst_object_unref( queue_pad ); - pad = gst_element_get_static_pad(sinkelement, padname); + if ( element ) + gst_object_unref ( element ); - if ( ! pad ) - { - debug_warning("failed to get pad(%s) from %s. retrying with [sink]\n", - padname, GST_ELEMENT_NAME(sinkelement) ); + return FALSE; +} - 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*/ - { -#ifdef GST_API_VERSION_1 - srccaps = gst_pad_get_current_caps( srcpad ); -#else - srccaps = gst_pad_get_caps( srcpad ); -#endif - if ( !srccaps ) - goto ERROR; +static +int __mmplayer_check_not_supported_codec(mm_player_t* player, gchar* mime) +{ + debug_fenter(); - str = gst_caps_get_structure( srccaps, 0 ); - if ( ! str ) - goto ERROR; + return_val_if_fail(player && player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED); + return_val_if_fail ( mime, MM_ERROR_INVALID_ARGUMENT ); - name = gst_structure_get_name(str); - if ( ! name ) - goto ERROR; - } + debug_log("mimetype to check: %s\n", mime ); - /* link queue and decoder. so, it will be queue - decoder. */ - if ( GST_PAD_LINK_OK != gst_pad_link(srcpad, pad) ) + /* 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 ) ) { - gst_object_unref(GST_OBJECT(pad)); - debug_error("failed to link (%s) to pad(%s)\n", GST_ELEMENT_NAME( sinkelement ), padname ); + debug_log("not found demuxer\n"); + player->not_found_demuxer = TRUE; + player->unlinked_demuxer_mime = g_strdup_printf ( "%s", mime ); - /* 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; + goto DONE; } + } - if (strstr(name, "video")) - { - player->videodec_linked = 1; - debug_msg("player->videodec_linked set to 1\n"); + 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 if (strstr(name, "audio")) + else { - player->audiodec_linked = 1; - debug_msg("player->auddiodec_linked set to 1\n"); + debug_warning("add VIDEO to missing plugin\n"); + player->not_supported_codec |= MISSING_PLUGIN_VIDEO; } - - gst_object_unref(GST_OBJECT(pad)); - gst_caps_unref(GST_CAPS(srccaps)); - srccaps = NULL; } - - if ( !MMPLAYER_IS_HTTP_PD(player) ) + else if ( g_str_has_prefix(mime, "audio") ) { - if( (g_strrstr(klass, "Demux") && !g_strrstr(klass, "Metadata")) || (g_strrstr(klass, "Parser") ) ) + if ( ( player->can_support_codec & FOUND_PLUGIN_AUDIO ) && ( player->audiodec_linked ) ) { - if (MMPLAYER_IS_HTTP_STREAMING(player)) - { -#ifndef GST_API_VERSION_1 - GstFormat fmt = GST_FORMAT_BYTES; -#endif - gint64 dur_bytes = 0L; - gchar *file_buffering_path = NULL; - gboolean use_file_buffer = FALSE; + debug_log("audio plugin is already linked\n"); + } + else + { + debug_warning("add AUDIO to missing plugin\n"); + player->not_supported_codec |= MISSING_PLUGIN_AUDIO; + } + } - if ( !mainbin[MMPLAYER_M_S_BUFFER].gst) - { - debug_log("creating http streaming buffering queue\n"); +DONE: + debug_fleave(); - queue = gst_element_factory_make("queue2", "http_streaming_buffer"); - if ( ! queue ) - { - debug_critical ( "failed to create buffering queue element\n" ); - goto ERROR; - } + return MM_ERROR_NONE; +} - 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; - } +static void __mmplayer_pipeline_complete(GstElement *decodebin, gpointer data) // @ +{ + mm_player_t* player = (mm_player_t*)data; - qsinkpad = gst_element_get_static_pad(queue, "sink"); - qsrcpad = gst_element_get_static_pad(queue, "src"); + debug_fenter(); - if ( GST_PAD_LINK_OK != gst_pad_link(srcpad, qsinkpad) ) - { - debug_error("failed to link buffering queue\n"); - goto ERROR; - } - srcpad = qsrcpad; + 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"); - mainbin[MMPLAYER_M_S_BUFFER].id = MMPLAYER_M_S_BUFFER; - mainbin[MMPLAYER_M_S_BUFFER].gst = queue; + player->pipeline_is_constructed = TRUE; - if ( !MMPLAYER_IS_HTTP_LIVE_STREAMING(player)) - { -#ifdef GST_API_VERSION_1 - if ( !gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_SRC].gst, GST_FORMAT_BYTES, &dur_bytes)) - debug_error("fail to get duration.\n"); -#else - if ( !gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_SRC].gst, &fmt, &dur_bytes)) - debug_error("fail to get duration.\n"); -#endif + if ( ( PLAYER_INI()->async_start ) && + ( player->msg_posted == FALSE ) && + ( player->cmd >= MMPLAYER_COMMAND_START )) + { + __mmplayer_handle_missed_plugin( player ); + } - if (dur_bytes>0) - { - use_file_buffer = MMPLAYER_USE_FILE_FOR_BUFFERING(player); - file_buffering_path = g_strdup(PLAYER_INI()->http_file_buffer_path); - } - } + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-complete" ); +} - __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); +static gboolean __mmplayer_configure_audio_callback(mm_player_t* player) +{ + debug_fenter(); - 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")) + 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; + } - pad = gst_element_get_static_pad(sinkelement, padname); - if ( ! pad ) + if (player->audio_stream_cb) + { { - debug_warning("failed to get pad(%s) from %s. retrying with [sink]\n", - padname, GST_ELEMENT_NAME(sinkelement) ); + GstPad *pad = NULL; - pad = gst_element_get_static_pad(sinkelement, "sink"); +#ifdef GST_API_VERSION_1 + pad = gst_element_get_static_pad (player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "sink"); +#else + pad = gst_element_get_pad (player->pipeline->audiobin[MMPLAYER_A_SINK].gst, "sink"); +#endif - if ( ! pad ) + if ( !pad ) { - debug_error("failed to get pad(sink) from %s. \n", - GST_ELEMENT_NAME(sinkelement) ); - goto ERROR; + debug_error("failed to get sink pad from audiosink to probe data\n"); + return FALSE; } - } - 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; - } +#ifdef GST_API_VERSION_1 + player->audio_cb_probe_id = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, + __mmplayer_audio_stream_probe, player, NULL); +#else + player->audio_cb_probe_id = gst_pad_add_buffer_probe (pad, + G_CALLBACK (__mmplayer_audio_stream_probe), player); +#endif + + gst_object_unref (pad); - gst_object_unref(GST_OBJECT(pad)); + pad = NULL; + } } - - for(;templlist != NULL; templlist = templlist->next) + else { - padtemplate = templlist->data; + debug_error("There is no audio callback to configure.\n"); + return FALSE; + } - debug_log ("director = [%d], presence = [%d]\n", padtemplate->direction, padtemplate->presence); + debug_fleave(); - if( padtemplate->direction != GST_PAD_SRC || - padtemplate->presence == GST_PAD_REQUEST ) - continue; + return TRUE; +} + +static void +__mmplayer_init_factories(mm_player_t* player) // @ +{ + debug_fenter(); + + return_if_fail ( player ); - switch(padtemplate->presence) - { - case GST_PAD_ALWAYS: - { - GstPad *srcpad = gst_element_get_static_pad(sinkelement, "src"); #ifdef GST_API_VERSION_1 - GstCaps *caps = gst_pad_get_current_caps(srcpad); + player->factories = gst_registry_feature_filter(gst_registry_get(), + (GstPluginFeatureFilter)__mmplayer_feature_filter, FALSE, NULL); #else - GstCaps *caps = gst_pad_get_caps(srcpad); + player->factories = gst_registry_feature_filter(gst_registry_get_default(), + (GstPluginFeatureFilter)__mmplayer_feature_filter, FALSE, NULL); #endif - /* 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; - } + player->factories = g_list_sort(player->factories, (GCompareFunc)util_factory_rank_compare); - if ( ! __mmplayer_try_to_plug(player, srcpad, caps) ) - { - gst_object_unref(GST_OBJECT(srcpad)); - gst_caps_unref(GST_CAPS(caps)); + debug_fleave(); +} - debug_error("failed to plug something after %s\n", GST_ELEMENT_NAME( sinkelement )); - goto ERROR; - } +static void +__mmplayer_release_factories(mm_player_t* player) // @ +{ + debug_fenter(); - gst_caps_unref(GST_CAPS(caps)); - gst_object_unref(GST_OBJECT(srcpad)); + return_if_fail ( player ); - } - break; + if (player->factories) + { + gst_plugin_feature_list_free (player->factories); + player->factories = NULL; + } + debug_fleave(); +} - case GST_PAD_SOMETIMES: - has_dynamic_pads = TRUE; - break; +static void +__mmplayer_release_misc(mm_player_t* player) +{ + int i; + debug_fenter(); - default: - break; - } - } + return_if_fail ( player ); - /* check if player can do start continually */ - MMPLAYER_CHECK_CMD_IF_EXIT(player); + player->use_video_stream = FALSE; + player->video_stream_cb = NULL; + player->video_stream_cb_user_param = NULL; - if( has_dynamic_pads ) - { - player->have_dynamic_pad = TRUE; - MMPLAYER_SIGNAL_CONNECT ( player, sinkelement, "pad-added", - G_CALLBACK(__mmplayer_add_new_pad), player); + player->audio_stream_cb = NULL; + player->audio_stream_cb_user_param = NULL; - /* 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); - } + 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->pending_seek.is_pending = FALSE; + player->pending_seek.format = MM_PLAYER_POS_FORMAT_TIME; + player->pending_seek.pos = 0; + player->msg_posted = 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; } - if (has_many_types) + /* clean found parsers */ + if (player->parsers) { - GstPad *pad = NULL; + g_list_free(player->parsers); + player->parsers = NULL; + } - player->has_many_types = has_many_types; + MMPLAYER_FREEIF(player->album_art); - 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)); + /* 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(); +} - /* check if player can do start continually */ - MMPLAYER_CHECK_CMD_IF_EXIT(player); +static GstElement *__mmplayer_element_create_and_link(mm_player_t *player, GstPad* pad, const char* name) +{ + GstElement *element = NULL; + GstPad *sinkpad; - if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(sinkelement, GST_STATE_PAUSED) ) + debug_log("creating %s to plug\n", name); + + element = gst_element_factory_make(name, NULL); + if ( ! element ) { - debug_error("failed to set state PAUSED to %s\n", GST_ELEMENT_NAME( sinkelement )); - goto ERROR; + debug_error("failed to create queue\n"); + return NULL; } - if ( queue ) + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(element, GST_STATE_READY) ) { - 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; + debug_error("failed to set state READY to %s\n", name); + return NULL; } - if ( parser ) + if ( ! gst_bin_add(GST_BIN(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst), element)) { - 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; - } + debug_error("failed to add %s\n", name); + return NULL; + } - parser = NULL; + sinkpad = gst_element_get_static_pad(element, "sink"); - gst_object_unref (GST_OBJECT(pssrcpad)); - pssrcpad = NULL; + 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_fleave(); + debug_log("linked %s to pipeline successfully\n", name); - return TRUE; + gst_object_unref (sinkpad); -ERROR: + return element; +} - if ( queue ) - { - gst_object_unref(GST_OBJECT(qsrcpad)); +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; - /* 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. - */ + debug_fenter(); - gst_bin_remove (GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), queue); - //gst_object_unref( queue ); - } + return_val_if_fail ( player && + player->pipeline && + player->pipeline->mainbin, + FALSE ); - if ( srccaps ) - gst_caps_unref(GST_CAPS(srccaps)); + 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); - return FALSE; -} + factory = gst_element_get_factory(sinkelement); + klass = gst_element_factory_get_klass(factory); -static gboolean __mmplayer_feature_filter(GstPluginFeature *feature, gpointer data) // @ -{ - const gchar *klass; - //const gchar *name; + /* check if player can do start continually */ + MMPLAYER_CHECK_CMD_IF_EXIT(player); - /* we only care about element factories */ - if (!GST_IS_ELEMENT_FACTORY(feature)) - return FALSE; + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(sinkelement, warmup) ) + { + if (isvideo_decoder) + player->keep_detecting_vcodec = TRUE; - /* 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)); + debug_error("failed to set %d state to %s\n", warmup, GST_ELEMENT_NAME( sinkelement )); + goto ERROR; + } - 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; -} + /* 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); -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; + /* 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; - debug_fenter(); + name = g_strdup(GST_ELEMENT_NAME( GST_PAD_PARENT ( srcpad ))); - return_if_fail ( pad ) - return_if_fail ( unused ) - return_if_fail ( data ) + if (g_strrstr(name, "mpegtsdemux")) + { + gchar *demux_caps = NULL; + gchar *parser_name = NULL; + GstCaps *dcaps = NULL; #ifdef GST_API_VERSION_1 - caps = gst_pad_get_current_caps(pad); + dcaps = gst_pad_get_current_caps(srcpad); #else - caps = gst_pad_get_caps(pad); + dcaps = gst_pad_get_caps(srcpad); #endif - if ( !caps ) - return; - - str = gst_caps_get_structure(caps, 0); - if ( !str ) - return; + demux_caps = gst_caps_to_string(dcaps); - name = gst_structure_get_name(str); - if ( !name ) - return; - debug_log("name=%s\n", name); + 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"); + } - 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(dcaps); + MMPLAYER_FREEIF( demux_caps ); - gst_caps_unref(caps); + if (parser_name) + { + parser = __mmplayer_element_create_and_link(player, srcpad, parser_name); - __mmplayer_pipeline_complete( NULL, (gpointer)player ); + MMPLAYER_FREEIF(parser_name); - debug_fleave(); + 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); - return; -} + 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; + } -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; + /* update srcpad to link with decoder */ + qsrcpad = gst_element_get_static_pad(queue, "src"); + srcpad = qsrcpad; - debug_fenter(); + q_max_size_time = GST_QUEUE_DEFAULT_TIME; - return_if_fail ( player ); - return_if_fail ( caps ); + /* 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; - str = gst_caps_get_structure(caps, 0); - if ( !str ) - return; + 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; - stream_type = gst_structure_get_name(str); - if ( !stream_type ) - return; + 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); - /* set unlinked mime type for downloadable codec */ - if (g_str_has_prefix(stream_type, "video/")) - { - if (g_str_has_prefix(stream_type, "video/mpeg")) + if ( ! pad ) { - gst_structure_get_int (str, MM_PLAYER_MPEG_VNAME, &version); - version_field = MM_PLAYER_MPEG_VNAME; + 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; + } } - else if (g_str_has_prefix(stream_type, "video/x-wmv")) + + /* to check the video/audio type set the proper flag*/ { - gst_structure_get_int (str, MM_PLAYER_WMV_VNAME, &version); - version_field = MM_PLAYER_WMV_VNAME; +#ifdef GST_API_VERSION_1 + srccaps = gst_pad_get_current_caps( srcpad ); +#else + srccaps = gst_pad_get_caps( srcpad ); +#endif + 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; } - else if (g_str_has_prefix(stream_type, "video/x-divx")) + + /* link queue and decoder. so, it will be queue - decoder. */ + if ( GST_PAD_LINK_OK != gst_pad_link(srcpad, pad) ) { - gst_structure_get_int (str, MM_PLAYER_DIVX_VNAME, &version); - version_field = MM_PLAYER_DIVX_VNAME; + 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 (version) + if (strstr(name, "video")) { - player->unlinked_video_mime = g_strdup_printf("%s, %s=%d", stream_type, version_field, version); + player->videodec_linked = 1; + debug_msg("player->videodec_linked set to 1\n"); + } - else + else if (strstr(name, "audio")) { - player->unlinked_video_mime = g_strdup_printf("%s", stream_type); + 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; } - else if (g_str_has_prefix(stream_type, "audio/")) + + if ( !MMPLAYER_IS_HTTP_PD(player) ) { - 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")) + if( (g_strrstr(klass, "Demux") && !g_strrstr(klass, "Metadata")) || (g_strrstr(klass, "Parser") ) ) { - gst_structure_get_int (str, MM_PLAYER_WMA_VNAME, &version); - version_field = MM_PLAYER_WMA_VNAME; - } + if (MMPLAYER_IS_HTTP_STREAMING(player)) + { +#ifndef GST_API_VERSION_1 + GstFormat fmt = GST_FORMAT_BYTES; +#endif + 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 (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); - } - } + 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; + } - debug_fleave(); -} + if ( !gst_bin_add(GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), queue) ) + { + debug_error("failed to add buffering queue\n"); + goto ERROR; + } -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; + qsinkpad = gst_element_get_static_pad(queue, "sink"); + qsrcpad = gst_element_get_static_pad(queue, "src"); - debug_fenter(); - return_if_fail ( player ); - return_if_fail ( pad ); + if ( GST_PAD_LINK_OK != gst_pad_link(srcpad, qsinkpad) ) + { + debug_error("failed to link buffering queue\n"); + goto ERROR; + } + srcpad = qsrcpad; - GST_OBJECT_LOCK (pad); -#ifdef GST_API_VERSION_1 - if ((caps = gst_pad_get_current_caps (pad))) - gst_caps_ref(caps); -#else - if ((caps = GST_PAD_CAPS(pad))) - gst_caps_ref(caps); -#endif - GST_OBJECT_UNLOCK (pad); - if ( NULL == caps ) - { + mainbin[MMPLAYER_M_S_BUFFER].id = MMPLAYER_M_S_BUFFER; + mainbin[MMPLAYER_M_S_BUFFER].gst = queue; + + if ( !MMPLAYER_IS_HTTP_LIVE_STREAMING(player)) + { #ifdef GST_API_VERSION_1 - caps = gst_pad_get_current_caps(pad); + if ( !gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_SRC].gst, GST_FORMAT_BYTES, &dur_bytes)) + debug_error("fail to get duration.\n"); #else - caps = gst_pad_get_caps(pad); + if ( !gst_element_query_duration(player->pipeline->mainbin[MMPLAYER_M_SRC].gst, &fmt, &dur_bytes)) + debug_error("fail to get duration.\n"); #endif - 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; + if (dur_bytes>0) + { + use_file_buffer = MMPLAYER_USE_FILE_FOR_BUFFERING(player); + file_buffering_path = g_strdup(PLAYER_INI()->http_file_buffer_path); + } + } - player->num_dynamic_pad++; - debug_log("stream count inc : %d\n", player->num_dynamic_pad); + __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); - /* 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; + MMPLAYER_FREEIF(file_buffering_path); + } + } } } - else if (g_strrstr(name, "audio")) + /* if it is not decoder or */ + /* in decoder case any of the video/audio still need to link*/ + if(!g_strrstr(klass, "Decoder")) { - 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); + 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) ); - gst_structure_get_int (str, "channels", &channels); - mm_attrs_set_int_by_name(player->attrs, "content_audio_channels", channels); + pad = gst_element_get_static_pad(sinkelement, "sink"); - debug_log("audio samplerate : %d channels : %d", samplerate, channels); - } - else if (g_strrstr(name, "video")) - { - gint stype; - mm_attrs_get_int_by_name (player->attrs, "display_surface_type", &stype); + if ( ! pad ) + { + debug_error("failed to get pad(sink) from %s. \n", + GST_ELEMENT_NAME(sinkelement) ); + goto ERROR; + } + } - /* don't make video because of not required */ - if (stype == MM_DISPLAY_SURFACE_NULL) + if ( GST_PAD_LINK_OK != gst_pad_link(srcpad, pad) ) { - debug_log("no video because it's not required"); - return; + gst_object_unref(GST_OBJECT(pad)); + debug_error("failed to link (%s) to pad(%s)\n", GST_ELEMENT_NAME( sinkelement ), padname ); + goto ERROR; } - player->v_stream_caps = gst_caps_copy(caps); //if needed, video caps is required when videobin is created + gst_object_unref(GST_OBJECT(pad)); } - if ( ! __mmplayer_try_to_plug(player, pad, caps) ) + for(;templlist != NULL; templlist = templlist->next) { - debug_error("failed to autoplug for type (%s)", name); - - __mmplayer_set_unlinked_mime_type(player, caps); - } - - gst_caps_unref(caps); + padtemplate = templlist->data; - debug_fleave(); - return; -} + debug_log ("director = [%d], presence = [%d]\n", padtemplate->direction, padtemplate->presence); -/* 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; + if( padtemplate->direction != GST_PAD_SRC || + padtemplate->presence == GST_PAD_REQUEST ) + continue; - debug_fenter(); + switch(padtemplate->presence) + { + case GST_PAD_ALWAYS: + { + GstPad *srcpad = gst_element_get_static_pad(sinkelement, "src"); +#ifdef GST_API_VERSION_1 + GstCaps *caps = gst_pad_get_current_caps(srcpad); +#else + GstCaps *caps = gst_pad_get_caps(srcpad); +#endif - return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); - return_val_if_fail( player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ) + /* 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; + } - 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]); + if ( ! __mmplayer_try_to_plug(player, srcpad, caps) ) + { + gst_object_unref(GST_OBJECT(srcpad)); + gst_caps_unref(GST_CAPS(caps)); - isMidi = ( player->profile.play_mode == MM_PLAYER_MODE_MIDI ) ? TRUE : FALSE; + debug_error("failed to plug something after %s\n", GST_ELEMENT_NAME( sinkelement )); + goto ERROR; + } - if ( isMidi ) - vol_max = 1000; - else - vol_max = 100; + gst_caps_unref(GST_CAPS(caps)); + gst_object_unref(GST_OBJECT(srcpad)); - /* 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; - } - } + } + break; - 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]); + case GST_PAD_SOMETIMES: + has_dynamic_pads = TRUE; + break; - g_object_set(midi_element, "volume", volume.level[0], NULL); - } + default: + break; } } - 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; + /* check if player can do start continually */ + MMPLAYER_CHECK_CMD_IF_EXIT(player); - g_object_set(G_OBJECT(sink_element), "mute", mute, NULL); - } + 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); } } - 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; + if (has_many_types) + { + GstPad *pad = NULL; - GstState state = GST_STATE_VOID_PENDING; - GstState pending = GST_STATE_VOID_PENDING; - GstClockTime time = 200*GST_MSECOND; + player->has_many_types = has_many_types; - debug_fenter(); + 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)); + } - 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) ); + /* check if player can do start continually */ + MMPLAYER_CHECK_CMD_IF_EXIT(player); - if ( iter != NULL ) + if ( GST_STATE_CHANGE_FAILURE == gst_element_set_state(sinkelement, GST_STATE_PAUSED) ) { - 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) ; - if (!factory) - { - 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; - } - } + debug_error("failed to set state PAUSED to %s\n", GST_ELEMENT_NAME( sinkelement )); + goto ERROR; } - item = GST_ELEMENT(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst); - - gst_element_get_state(GST_ELEMENT (item),&state, &pending,time); + 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; + } - factory = gst_element_get_factory (item) ; + queue = NULL; - if (!factory) - { - 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 (GST_OBJECT(qsrcpad)); + qsrcpad = NULL; } - if ( iter ) - gst_iterator_free (iter); - - debug_fleave(); + 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; + } - return FALSE; -} + parser = NULL; + gst_object_unref (GST_OBJECT(pssrcpad)); + pssrcpad = NULL; + } -gboolean -__mmplayer_check_subtitle( mm_player_t* player ) -{ - MMHandleType attrs = 0; - char *subtitle_uri = NULL; + debug_fleave(); - debug_fenter(); + return TRUE; - return_val_if_fail( player, FALSE ); +ERROR: - /* get subtitle attribute */ - attrs = MMPLAYER_GET_ATTRS(player); - if ( !attrs ) - return FALSE; + if ( queue ) + { + gst_object_unref(GST_OBJECT(qsrcpad)); - mm_attrs_get_string_by_name(attrs, "subtitle_uri", &subtitle_uri); - if ( !subtitle_uri || !strlen(subtitle_uri)) - return FALSE; + /* 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. + */ - debug_log ("subtite uri is %s[%d]\n", subtitle_uri, strlen(subtitle_uri)); + gst_bin_remove (GST_BIN(mainbin[MMPLAYER_M_PIPE].gst), queue); + //gst_object_unref( queue ); + } - debug_fleave(); + if ( srccaps ) + gst_caps_unref(GST_CAPS(srccaps)); - return TRUE; + return FALSE; } -static gboolean -__mmplayer_can_extract_pcm( mm_player_t* player ) +static gboolean __mmplayer_feature_filter(GstPluginFeature *feature, gpointer data) // @ { - MMHandleType attrs = 0; - gboolean is_drm = FALSE; - gboolean sound_extraction = FALSE; - - return_val_if_fail ( player, FALSE ); + const gchar *klass; + //const gchar *name; - attrs = MMPLAYER_GET_ATTRS(player); - if ( !attrs ) - { - debug_error("fail to get attributes."); + /* we only care about element factories */ + if (!GST_IS_ELEMENT_FACTORY(feature)) 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); + /* 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 ( ! sound_extraction || is_drm ) + if( g_strrstr(klass, "Demux") == NULL && + g_strrstr(klass, "Codec/Decoder") == NULL && + g_strrstr(klass, "Depayloader") == NULL && + g_strrstr(klass, "Parse") == NULL) { - debug_log("checking pcm extraction mode : %d, drm : %d", sound_extraction, is_drm); return FALSE; } - - return TRUE; + return TRUE; } -static gboolean -__mmplayer_handle_gst_error ( mm_player_t* player, GstMessage * message, GError* error ) + +static void __mmplayer_add_new_caps(GstPad* pad, GParamSpec* unused, gpointer data) { - MMMessageParamType msg_param; - gchar *msg_src_element; + mm_player_t* player = (mm_player_t*) data; + GstCaps *caps = NULL; + GstStructure *str = NULL; + const char *name; 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 */ + return_if_fail ( pad ) + return_if_fail ( unused ) + return_if_fail ( data ) - memset (&msg_param, 0, sizeof(MMMessageParamType)); +#ifdef GST_API_VERSION_1 + caps = gst_pad_get_current_caps(pad); +#else + caps = gst_pad_get_caps(pad); +#endif + if ( !caps ) + return; - 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"); + str = gst_caps_get_structure(caps, 0); + if ( !str ) + return; - /* we treat system error as an internal error */ - msg_param.code = MM_ERROR_PLAYER_INVALID_STREAM; - } + name = gst_structure_get_name(str); + if ( !name ) + return; + debug_log("name=%s\n", name); - if ( message->src ) + if ( ! __mmplayer_try_to_plug(player, pad, caps) ) { - 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); + debug_error("failed to autoplug for type (%s)\n", name); + gst_caps_unref(caps); + return; } - /* post error to application */ - if ( ! player->msg_posted ) - { - MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); - /* don't post more if one was sent already */ - player->msg_posted = TRUE; - } - else - { - debug_log("skip error post because it's sent already.\n"); - } + gst_caps_unref(caps); + + __mmplayer_pipeline_complete( NULL, (gpointer)player ); debug_fleave(); - return TRUE; + return; } -static gboolean -__mmplayer_handle_streaming_error ( mm_player_t* player, GstMessage * message ) +static void __mmplayer_set_unlinked_mime_type(mm_player_t* player, GstCaps *caps) { - debug_log("\n"); - MMMessageParamType msg_param; - gchar *msg_src_element = NULL; - GstStructure *s = NULL; - guint error_id = 0; - gchar *error_string = NULL; + GstStructure *str; + gint version = 0; + const char *stream_type; + gchar *version_field = 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)); + return_if_fail ( player ); + return_if_fail ( caps ); - if ( !gst_structure_get_uint (s, "error_id", &error_id) ) - error_id = MMPLAYER_STREAMING_ERROR_NONE; + str = gst_caps_get_structure(caps, 0); + if ( !str ) + return; - 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; - } + stream_type = gst_structure_get_name(str); + if ( !stream_type ) + return; - error_string = g_strdup(gst_structure_get_string (s, "error_string")); - if ( error_string ) - msg_param.data = (void *) error_string; - if ( message->src ) + /* set unlinked mime type for downloadable codec */ + if (g_str_has_prefix(stream_type, "video/")) { - msg_src_element = GST_ELEMENT_NAME( GST_ELEMENT_CAST( message->src ) ); + 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; + } - debug_error("-Msg src : [%s] Code : [%x] Error : [%s] \n", - msg_src_element, msg_param.code, (char*)msg_param.data ); + 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); + } } - - /* post error to application */ - if ( ! player->msg_posted ) + else if (g_str_has_prefix(stream_type, "audio/")) { - MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); + 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; + } - /* don't post more if one was sent already */ - player->msg_posted = TRUE; - } - else - { - debug_log("skip error post because it's sent already.\n"); + 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(); - - return TRUE; - } -static gint -__gst_handle_core_error( mm_player_t* player, int code ) +static void __mmplayer_add_new_pad(GstElement *element, GstPad *pad, gpointer data) { - gint trans_err = MM_ERROR_NONE; + 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 ); - return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + GST_OBJECT_LOCK (pad); +#ifdef GST_API_VERSION_1 + if ((caps = gst_pad_get_current_caps (pad))) + gst_caps_ref(caps); +#else + if ((caps = GST_PAD_CAPS(pad))) + gst_caps_ref(caps); +#endif + GST_OBJECT_UNLOCK (pad); - 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; + if ( NULL == caps ) + { +#ifdef GST_API_VERSION_1 + caps = gst_pad_get_current_caps(pad); +#else + caps = gst_pad_get_caps(pad); +#endif + if ( !caps ) return; } - debug_fleave(); - - return trans_err; -} + //MMPLAYER_LOG_GST_CAPS_TYPE(caps); -static gint -__gst_handle_library_error( mm_player_t* player, int code ) -{ - gint trans_err = MM_ERROR_NONE; + str = gst_caps_get_structure(caps, 0); + if ( !str ) + return; - debug_fenter(); + name = gst_structure_get_name(str); + if ( !name ) + return; - return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + player->num_dynamic_pad++; + debug_log("stream count inc : %d\n", player->num_dynamic_pad); - switch ( code ) + /* 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")) { - 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; + 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; - debug_fleave(); - - return trans_err; -} + /* 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); -static gint -__gst_handle_resource_error( mm_player_t* player, int code ) -{ - gint trans_err = MM_ERROR_NONE; + debug_log("audio samplerate : %d channels : %d", samplerate, channels); + } + else if (g_strrstr(name, "video")) + { + gint stype; + mm_attrs_get_int_by_name (player->attrs, "display_surface_type", &stype); - debug_fenter(); + /* don't make video because of not required */ + if (stype == MM_DISPLAY_SURFACE_NULL) + { + debug_log("no video because it's not required"); + return; + } - return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + player->v_stream_caps = gst_caps_copy(caps); //if needed, video caps is required when videobin is created + } - switch ( code ) + if ( ! __mmplayer_try_to_plug(player, pad, caps) ) { - 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 ) - || MMPLAYER_IS_RTSP_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 ) - || MMPLAYER_IS_RTSP_STREAMING(player)) - { - trans_err = MM_ERROR_PLAYER_STREAMING_FAIL; - break; - } - case GST_RESOURCE_ERROR_WRITE: - case GST_RESOURCE_ERROR_FAILED: - trans_err = MM_ERROR_PLAYER_INTERNAL; - break; + debug_error("failed to autoplug for type (%s)", name); - case GST_RESOURCE_ERROR_SEEK: - case GST_RESOURCE_ERROR_TOO_LAZY: - case GST_RESOURCE_ERROR_BUSY: - case GST_RESOURCE_ERROR_OPEN_WRITE: - case GST_RESOURCE_ERROR_OPEN_READ_WRITE: - case GST_RESOURCE_ERROR_CLOSE: - case GST_RESOURCE_ERROR_SYNC: - case GST_RESOURCE_ERROR_SETTINGS: - default: - trans_err = MM_ERROR_PLAYER_FILE_NOT_FOUND; - break; + __mmplayer_set_unlinked_mime_type(player, caps); } - 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; - } + gst_caps_unref(caps); debug_fleave(); - - return trans_err; + return; } -/* 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 ) +/* 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) { - gchar *src_element_name = NULL; - GstElement *src_element = NULL; - GstElementFactory *factory = NULL; - const gchar* klass = NULL; + 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(); - /* 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; + return_val_if_fail( player, MM_ERROR_PLAYER_NOT_INITIALIZED ); + return_val_if_fail( player->pipeline, MM_ERROR_PLAYER_NOT_INITIALIZED ) - factory = gst_element_get_factory(src_element); - if ( !factory ) - goto INTERNAL_ERROR; + 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]); - klass = gst_element_factory_get_klass(factory); - if ( !klass ) - goto INTERNAL_ERROR; + isMidi = ( player->profile.play_mode == MM_PLAYER_MODE_MIDI ) ? TRUE : FALSE; - debug_log("error code=%d, msg=%s, src element=%s, class=%s\n", - error->code, error->message, src_element_name, klass); + if ( isMidi ) + vol_max = 1000; + else + vol_max = 100; - switch ( error->code ) + /* is it proper volume level? */ + for (i = 0; i < MM_VOLUME_CHANNEL_NUM; ++i) { - case GST_STREAM_ERROR_DECODE: - { - /* 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; + if (volume.level[i] < 0 || volume.level[i] > vol_max) { + debug_log("Invalid Volume level!!!! \n"); + return MM_ERROR_INVALID_ARGUMENT; } - break; + } - case GST_STREAM_ERROR_WRONG_TYPE: + if ( isMidi ) + { + if ( player->pipeline->mainbin ) { - return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT; - } - break; + GstElement *midi_element = player->pipeline->mainbin[MMPLAYER_M_DEMUX].gst; - case GST_STREAM_ERROR_FAILED: - { - /* Decoder Custom Message */ - if ( strstr(error->message, "ongoing") ) + if ( midi_element && ( strstr(GST_ELEMENT_NAME(midi_element), "midiparse")) ) { - if ( strncasecmp(klass, "audio", 5) ) - { - 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; - } + debug_log("setting volume (%d) level to midi plugin\n", volume.level[0]); - } - else if ( strncasecmp(klass, "video", 5) ) - { - 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; - } - } + g_object_set(midi_element, "volume", volume.level[0], NULL); } - return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT; } - break; - - case GST_STREAM_ERROR_TYPE_NOT_FOUND: - return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT; - break; - - case GST_STREAM_ERROR_DECRYPT: - case GST_STREAM_ERROR_DECRYPT_NOKEY: + } + else + { + if ( player->pipeline->audiobin ) { - debug_error("decryption error, [%s] failed, reason : [%s]\n", src_element_name, error->message); + GstElement *sink_element = player->pipeline->audiobin[MMPLAYER_A_SINK].gst; - if ( strstr(error->message, "rights expired") ) - { - return MM_ERROR_PLAYER_DRM_EXPIRED; - } - else if ( strstr(error->message, "no rights") ) - { - return MM_ERROR_PLAYER_DRM_NO_LICENSE; - } - else if ( strstr(error->message, "has future rights") ) - { - return MM_ERROR_PLAYER_DRM_FUTURE_USE; - } - else if ( strstr(error->message, "opl violation") ) + /* Set to Avsysaudiosink element */ + if ( sink_element ) { - return MM_ERROR_PLAYER_DRM_OUTPUT_PROTECTION; - } - return MM_ERROR_PLAYER_DRM_NOT_AUTHORIZED; - } - break; + 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); + } - 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; + return error; } -static void -__mmplayer_post_delayed_eos( mm_player_t* player, int delay_in_ms ) +static gboolean +__mmplayer_can_extract_pcm( mm_player_t* player ) { - debug_fenter(); - - return_if_fail( player ); - - /* cancel if existing */ - __mmplayer_cancel_delayed_eos( player ); + MMHandleType attrs = 0; + gboolean is_drm = FALSE; + gboolean sound_extraction = FALSE; + return_val_if_fail ( player, FALSE ); - /* post now if delay is zero */ - if ( delay_in_ms == 0 || player->is_sound_extraction) + attrs = MMPLAYER_GET_ATTRS(player); + if ( !attrs ) { - 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; + debug_error("fail to get attributes."); + return FALSE; } - /* 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 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); - /* check timer is valid. if not, send EOS now */ - if ( player->eos_timer == 0 ) + if ( ! sound_extraction || is_drm ) { - debug_warning("creating timer for delayed EOS has failed. sending EOS now\n"); - MMPLAYER_POST_MSG( player, MM_MESSAGE_END_OF_STREAM, NULL ); + debug_log("checking pcm extraction mode : %d, drm : %d", sound_extraction, is_drm); + return FALSE; } - debug_fleave(); + return TRUE; } static void @@ -10371,7 +5527,7 @@ __mmplayer_cancel_delayed_eos( mm_player_t* player ) return; } -static gboolean +gboolean __mmplayer_eos_timer_cb(gpointer u_data) { mm_player_t* player = NULL; @@ -10434,313 +5590,6 @@ static void __mmplayer_set_antishock( mm_player_t* player, gboolean disable_by_f 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*/ -#ifdef GST_API_VERSION_1 - srccaps = gst_pad_get_current_caps( srcpad ); -#else - srccaps = gst_pad_get_caps( srcpad ); -#endif - 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*/ -#ifdef GST_API_VERSION_1 - srccaps = gst_pad_get_current_caps( srcpad ); -#else - srccaps = gst_pad_get_caps( srcpad ); -#endif - 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) { @@ -10824,7 +5673,6 @@ int _mmplayer_get_track_count(MMHandleType hplayer, MMPlayerTrackType track_typ return ret; } - const gchar * __get_state_name ( int state ) { @@ -10845,50 +5693,3 @@ __get_state_name ( int state ) } } -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_priv_gst.c b/src/mm_player_priv_gst.c new file mode 100755 index 0000000..8f4fe65 --- /dev/null +++ b/src/mm_player_priv_gst.c @@ -0,0 +1,1145 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , Heechul Jeon + * + * 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_priv.h" +#include "mm_player_priv_internal.h" +#include "mm_player_priv_locl_func.h" + +/*=========================================================================================== +| | +| LOCAL DEFINITIONS AND DECLARATIONS FOR MODULE | +| | +========================================================================================== */ + +/*--------------------------------------------------------------------------- +| GLOBAL CONSTANT DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| IMPORTED VARIABLE DECLARATIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| IMPORTED FUNCTION DECLARATIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL #defines: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL CONSTANT DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL DATA TYPE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| GLOBAL VARIABLE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL VARIABLE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*=========================================================================================== +| | +| FUNCTION DEFINITIONS | +| | +========================================================================================== */ + +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 READY state"); + 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; +} + +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; +} + +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; +} + +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"); + return ret; + } + + /* generating debug info before returning error */ + MMPLAYER_GENERATE_DOT_IF_ENABLED ( player, "pipeline-status-start" ); + + debug_fleave(); + + return ret; +} + +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"); + return ret; + } + + /* rewind */ + if ( rewind ) + { + if ( ! __gst_seek( player, player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, player->playback_rate, + 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; + __mmplayer_dump_pipeline_state(player); + } + + /* 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 ( FALSE == async && ret != MM_ERROR_NONE ) + { + GstMessage *msg = NULL; + GTimer *timer = NULL; + gdouble MAX_TIMEOUT_SEC = 3; + + debug_error("failed to set state to PAUSED"); + + timer = g_timer_new(); + g_timer_start(timer); + + GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst)); + /* check if gst error posted or not */ + do + { + msg = gst_bus_timed_pop(bus, GST_SECOND /2); + if (msg) + { + if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) + { + GError *error = NULL; + + debug_error("paring error posted from bus"); + /* parse error code */ + gst_message_parse_error(msg, &error, NULL); + + if (error->domain == GST_STREAM_ERROR) + { + ret = __gst_handle_stream_error( player, error, msg ); + } + else if (error->domain == GST_RESOURCE_ERROR) + { + ret = __gst_handle_resource_error( player, error->code ); + } + else if (error->domain == GST_LIBRARY_ERROR) + { + ret = __gst_handle_library_error( player, error->code ); + } + else if (error->domain == GST_CORE_ERROR) + { + ret = __gst_handle_core_error( player, error->code ); + } + player->msg_posted = TRUE; + } + gst_message_unref(msg); + } + } while (g_timer_elapsed(timer, NULL) < MAX_TIMEOUT_SEC); + + /* clean */ + gst_object_unref(bus); + g_timer_stop (timer); + g_timer_destroy (timer); + + 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; + GstBus *bus = NULL; + + 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"); + + /* clean bus sync handler because it's not needed any more */ + bus = gst_pipeline_get_bus (GST_PIPELINE(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst)); +#ifdef GST_API_VERSION_1 + gst_bus_set_sync_handler (bus, NULL, NULL, NULL); +#else + gst_bus_set_sync_handler (bus, NULL, NULL); +#endif + gst_object_unref(bus); + + /* 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"); + + 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; +} + +int +__gst_set_position(mm_player_t* player, int format, unsigned long position, gboolean internal_called) // @ +{ +#ifndef GST_API_VERSION_1 + GstFormat fmt = GST_FORMAT_TIME; +#endif + 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 ) + { +#ifdef GST_API_VERSION_1 + if ( !gst_element_query_duration( player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &dur_nsec )) + { + goto SEEK_ERROR; + } +#else + if ( !gst_element_query_duration( player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &dur_nsec )) + { + goto SEEK_ERROR; + } +#endif + 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, player->playback_rate, + 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, player->playback_rate, + 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 + +int +__gst_get_position(mm_player_t* player, int format, unsigned long* position) // @ +{ + MMPlayerStateType current_state = MM_PLAYER_STATE_NONE; +#ifndef GST_API_VERSION_1 + GstFormat fmt = GST_FORMAT_TIME; +#endif + 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 ) + { +#ifdef GST_API_VERSION_1 + ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, GST_FORMAT_TIME, &pos_msec); +#else + ret = gst_element_query_position(player->pipeline->mainbin[MMPLAYER_M_PIPE].gst, &fmt, &pos_msec); +#endif + } + + /* 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; +} + +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; +} + +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; +} + +/* sending event to one of sinkelements */ +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; +} + +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; +} + +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: + { + 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; +} + +#ifdef GST_API_VERSION_1 +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; + } + + buffer = gst_buffer_new(); + GstMapInfo info; + + info.data = (guint8*)(buf->buf + buf->offset); + gst_buffer_set_size(buffer, len); + + //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; + gst_buffer_map (buffer, &info, GST_MAP_WRITE); + + 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; +} +#else +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; +} +#endif + +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; +} + +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); +} + +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; +} + + +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; +} + + + diff --git a/src/mm_player_priv_gst_wrapper.c b/src/mm_player_priv_gst_wrapper.c new file mode 100755 index 0000000..cef6bec --- /dev/null +++ b/src/mm_player_priv_gst_wrapper.c @@ -0,0 +1,3029 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , Heechul Jeon + * + * 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 | +| | +========================================================================================== */ +#if 0 +#include +#include +#ifndef GST_API_VERSION_1 +#include +#else +#include +#endif +#include +#include +#include +#include +#include + +#include +//#include +//#include +#include + +#include "mm_player_ini.h" +#include "mm_player_attrs.h" +#include "mm_player_capture.h" +#endif +#include + +#include "mm_player_priv.h" +#include "mm_player_priv_internal.h" +#include "mm_player_priv_locl_func.h" + +/*=========================================================================================== +| | +| LOCAL DEFINITIONS AND DECLARATIONS FOR MODULE | +| | +========================================================================================== */ + +/*--------------------------------------------------------------------------- +| GLOBAL CONSTANT DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| IMPORTED VARIABLE DECLARATIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| IMPORTED FUNCTION DECLARATIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL #defines: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL CONSTANT DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL DATA TYPE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| GLOBAL VARIABLE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL VARIABLE DEFINITIONS: | +---------------------------------------------------------------------------*/ +/* video capture callback*/ +gulong ahs_appsrc_cb_probe_id = 0; + +/*--------------------------------------------------------------------------- +| LOCAL FUNCTION PROTOTYPES: | +---------------------------------------------------------------------------*/ + +/*=========================================================================================== +| | +| FUNCTION DEFINITIONS | +| | +========================================================================================== */ + +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 ); + +#ifdef GST_API_VERSION_1 + const GstStructure *structure; + structure = gst_message_get_structure (msg); +#endif + + 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 */ +#ifdef GST_API_VERSION_1 + gst_pad_remove_probe (pad, player->audio_cb_probe_id); +#else + gst_pad_remove_buffer_probe (pad, player->audio_cb_probe_id); +#endif + 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 ) ); +#ifdef GST_API_VERSION_1 + if ( gst_structure_has_name ( 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 ); + + } +#else + 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 ); + + } + +#endif + + + 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; + + /* we only handle messages from pipeline */ + if( msg->src != (GstObject *)mainbin[MMPLAYER_M_PIPE].gst ) + break; + + /* get state info from msg */ +#ifdef GST_API_VERSION_1 + voldstate = gst_structure_get_value (structure, "old-state"); + vnewstate = gst_structure_get_value (structure, "new-state"); + vpending = gst_structure_get_value (structure, "pending-state"); +#else + 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"); +#endif + + oldstate = (GstState)voldstate->data[0].v_int; + newstate = (GstState)vnewstate->data[0].v_int; + pending = (GstState)vpending->data[0].v_int; + + 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 ) ); + + if (oldstate == newstate) + { + debug_warning("pipeline reports state transition to old state"); + 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; + + 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"); + ret = __mmplayer_gst_handle_duration(player, msg); + if (!ret) + { + debug_warning("failed to update duration"); + } + } + + 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; +} + +gboolean +__mmplayer_gst_handle_duration(mm_player_t* player, GstMessage* msg) +{ + GstFormat format; + gint64 bytes = 0; + + debug_fenter(); + + return_val_if_fail(player, FALSE); + return_val_if_fail(msg, FALSE); + + gst_message_parse_duration (msg, &format, &bytes); + + if (MMPLAYER_IS_HTTP_STREAMING(player) && format == GST_FORMAT_BYTES ) + { + debug_log("data total size of http content: %lld", bytes); + player->http_content_size = bytes; + } + else if (format == GST_FORMAT_TIME) + { + /* handling audio clip which has vbr. means duration is keep changing */ + _mmplayer_update_content_attrs (player, ATTR_DURATION ); + } + else + { + debug_warning("duration is neither BYTES or TIME"); + return FALSE; + } + + debug_fleave(); + + return TRUE; +} + +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;\ + }\ +} + +#ifdef GST_API_VERSION_1 +#define MMPLAYER_UPDATE_TAG_IMAGE(gsttag, attribute, playertag) \ +value = gst_tag_list_get_value_index(tag_list, gsttag, index); \ +if (value) \ +{\ + GstMapInfo info; \ + gst_buffer_map (buffer, &info, GST_MAP_WRITE); \ + buffer = gst_value_get_buffer (value); \ + debug_log ( "update album cover data : %p, size : %d\n", info.data, gst_buffer_get_size(buffer)); \ + player->album_art = (gchar *)g_malloc(gst_buffer_get_size(buffer)); \ + if (player->album_art); \ + { \ + memcpy(player->album_art, info.data, gst_buffer_get_size(buffer)); \ + mm_attrs_set_data_by_name(attribute, playertag, (void *)player->album_art, gst_buffer_get_size(buffer)); \ + } \ +gst_buffer_unmap (buffer, &info); \ +} +#else +#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)); \ + } \ +} +#endif + +#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; +} + +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(); +} + +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 ); + + 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; +} + +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"); +#ifdef GST_API_VERSION_1 + caps = gst_pad_get_current_caps( pad ); +#else + caps = gst_pad_get_caps( pad ); +#endif + + 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 + */ +} + +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 */ +#ifdef GST_API_VERSION_1 + caps = gst_pad_get_current_caps( pad ); +#else + caps = gst_pad_get_caps( pad ); +#endif + 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; + + 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; + + 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; + } + + 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)); + + /* flusing out new attributes */ + if ( mmf_attrs_commit ( attrs ) ) + { + debug_error("failed to comit attributes\n"); + } + + return; +} + +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; +} + +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 + */ +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; + gint 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"))) + { +#ifdef GST_API_VERSION_1 + if ((caps = gst_pad_get_current_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); + } +#else + 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); + } +#endif + 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); + + 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; +} + +/** + * 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) : fimcconvert ! evasimagesink + * - evas surface (x86) : videoconvertor ! videoflip ! evasimagesink + */ +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; + gint ret = 0; + + debug_log("using memsink\n"); + + /* first, create colorspace convert */ + if (player->is_nv12_tiled) + { + vconv_factory = "fimcconvert"; + } + else // get video converter from player ini file + { + 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); + } + + if ( !player->is_nv12_tiled) + { + gint width = 0; //width of video + gint height = 0; //height of video + GstCaps* video_caps = NULL; + + /* rotator, scaler and capsfilter */ + if (strncmp(PLAYER_INI()->videosink_element_x, "vaapisink", strlen("vaapisink"))){ + 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 ( (player->is_nv12_tiled && (surface_type == MM_DISPLAY_SURFACE_EVAS) && + !strcmp(PLAYER_INI()->videosink_element_evas, "evasimagesink") ) ) + { + vconv_factory = "fimcconvert"; + } + else if (player->is_nv12_tiled) + { + vconv_factory = NULL; + } + + if (vconv_factory) + { + MMPLAYER_CREATE_ELEMENT(videobin, MMPLAYER_V_CONV, vconv_factory, "video converter", TRUE); + debug_log("using video converter: %s", vconv_factory); + } + } + + if (strncmp(PLAYER_INI()->videosink_element_x,"vaapisink", strlen("vaapisink"))){ + /* set video rotator */ + if ( !player->is_nv12_tiled ) + 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; + } + 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); + + 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; +} + +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; +} + +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; +} + +/** + * 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 + */ +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; + } +#ifdef GST_API_VERSION_1 + GST_OBJECT_FLAG_UNSET (mainbin[MMPLAYER_M_SRC_FAKESINK].gst, GST_ELEMENT_FLAG_SINK); +#else + GST_OBJECT_FLAG_UNSET (mainbin[MMPLAYER_M_SRC_FAKESINK].gst, GST_ELEMENT_IS_SINK); +#endif + + /* 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 */ +#ifdef GST_API_VERSION_1 + gst_bus_set_sync_handler(bus, __mmplayer_bus_sync_callback, player, NULL); +#else + gst_bus_set_sync_handler(bus, __mmplayer_bus_sync_callback, player); +#endif + /* 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; +} + +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" ); + +#ifdef GST_API_VERSION_1 + gst_pad_remove_probe (pad, ahs_appsrc_cb_probe_id); +#else + gst_pad_remove_buffer_probe (pad, ahs_appsrc_cb_probe_id); +#endif + 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)); + +#ifdef GST_API_VERSION_1 + gst_bus_set_sync_handler (bus, NULL, NULL, NULL); +#else + gst_bus_set_sync_handler (bus, NULL, NULL); +#endif + gst_object_unref(bus); + + 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; +} + diff --git a/src/mm_player_priv_internal.c b/src/mm_player_priv_internal.c new file mode 100755 index 0000000..c75b6a9 --- /dev/null +++ b/src/mm_player_priv_internal.c @@ -0,0 +1,1201 @@ +/* + * libmm-player + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: JongHyuk Choi , Heechul Jeon + * + * 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_priv.h" +#include "mm_player_priv_internal.h" +#include "mm_player_priv_locl_func.h" + + +/*=========================================================================================== +| | +| LOCAL DEFINITIONS AND DECLARATIONS FOR MODULE | +| | +========================================================================================== */ + +/*--------------------------------------------------------------------------- +| GLOBAL CONSTANT DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| IMPORTED VARIABLE DECLARATIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| IMPORTED FUNCTION DECLARATIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL #defines: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL CONSTANT DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL DATA TYPE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| GLOBAL VARIABLE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL VARIABLE DEFINITIONS: | +---------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------- +| LOCAL FUNCTION PROTOTYPES: | +---------------------------------------------------------------------------*/ + +/*=========================================================================================== +| | +| FUNCTION DEFINITIONS | +| | +========================================================================================== */ +/* 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); + */ +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; +} + +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) ; + if (!factory) + { + 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) ; + + if (!factory) + { + 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; +} + +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); + + /* dump state of all element */ + __mmplayer_dump_pipeline_state( player ); + + 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) ); + + /* dump state of all element */ + __mmplayer_dump_pipeline_state( player ); + + 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; +} + +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; +} + +/* 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_handle_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; +} + +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*/ +#ifdef GST_API_VERSION_1 + srccaps = gst_pad_get_current_caps( srcpad ); +#else + srccaps = gst_pad_get_caps( srcpad ); +#endif + 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; +} + +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*/ +#ifdef GST_API_VERSION_1 + srccaps = gst_pad_get_current_caps( srcpad ); +#else + srccaps = gst_pad_get_caps( srcpad ); +#endif + 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; +} + +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; +} + +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; +} + +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 ) + || MMPLAYER_IS_RTSP_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 ) + || MMPLAYER_IS_RTSP_STREAMING(player)) + { + trans_err = MM_ERROR_PLAYER_STREAMING_FAIL; + break; + } + case GST_RESOURCE_ERROR_WRITE: + case GST_RESOURCE_ERROR_FAILED: + trans_err = MM_ERROR_PLAYER_INTERNAL; + break; + + case GST_RESOURCE_ERROR_SEEK: + case GST_RESOURCE_ERROR_TOO_LAZY: + case GST_RESOURCE_ERROR_BUSY: + case GST_RESOURCE_ERROR_OPEN_WRITE: + case GST_RESOURCE_ERROR_OPEN_READ_WRITE: + case GST_RESOURCE_ERROR_CLOSE: + case GST_RESOURCE_ERROR_SYNC: + case GST_RESOURCE_ERROR_SETTINGS: + default: + trans_err = MM_ERROR_PLAYER_FILE_NOT_FOUND; + break; + } + + debug_fleave(); + + return trans_err; +} + +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. */ +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: + { + /* 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 ( strncasecmp(klass, "audio", 5) ) + { + 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 ( strncasecmp(klass, "video", 5) ) + { + 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_NOT_SUPPORTED_FORMAT; + } + break; + + case GST_STREAM_ERROR_TYPE_NOT_FOUND: + return MM_ERROR_PLAYER_NOT_SUPPORTED_FORMAT; + 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); + + if ( strstr(error->message, "rights expired") ) + { + return MM_ERROR_PLAYER_DRM_EXPIRED; + } + else if ( strstr(error->message, "no rights") ) + { + return MM_ERROR_PLAYER_DRM_NO_LICENSE; + } + else if ( strstr(error->message, "has future rights") ) + { + return MM_ERROR_PLAYER_DRM_FUTURE_USE; + } + else if ( strstr(error->message, "opl violation") ) + { + return MM_ERROR_PLAYER_DRM_OUTPUT_PROTECTION; + } + return MM_ERROR_PLAYER_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(); +} + +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->msg_posted ) + { + MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); + /* don't post more if one was sent already */ + player->msg_posted = TRUE; + } + else + { + debug_log("skip error post because it's sent already.\n"); + } + + debug_fleave(); + + return TRUE; +} + +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->msg_posted ) + { + MMPLAYER_POST_MSG( player, MM_MESSAGE_ERROR, &msg_param ); + + /* don't post more if one was sent already */ + player->msg_posted = TRUE; + } + else + { + debug_log("skip error post because it's sent already.\n"); + } + + debug_fleave(); + + return TRUE; + +} + +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(); +} + +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(); +} + +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; +} + +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; +} + +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; +} + +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; +} + +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 old mode 100644 new mode 100755 index 3f76b87..77eb83f --- a/src/mm_player_streaming.c +++ b/src/mm_player_streaming.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. * - * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * Contact: JongHyuk Choi , Heechul Jeon * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/mm_player_utils.c b/src/mm_player_utils.c old mode 100644 new mode 100755 index 778049f..eae640d --- a/src/mm_player_utils.c +++ b/src/mm_player_utils.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. * - * Contact: JongHyuk Choi , YeJin Cho , YoungHwan An + * Contact: JongHyuk Choi , Heechul Jeon * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,7 +111,7 @@ bool util_remove_file_backup(const char *backup_path) 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) @@ -155,7 +155,7 @@ int util_is_midi_type_by_mem(void *mem, int size) 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,}; @@ -184,7 +184,7 @@ int util_is_midi_type_by_file(const char *file_path) fclose(fp); return FALSE; } - + size = fread(prefix, sizeof(char), DETECTION_PREFIX_SIZE, fp); fclose(fp); @@ -192,20 +192,20 @@ int util_is_midi_type_by_file(const char *file_path) 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. +/* messages are treated as warnings bcz those code should not be checked in. + * and no error handling will supported for same manner. */ -gboolean +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 ) { @@ -242,7 +242,7 @@ __util_gst_pad_probe(GstPad *pad, GstBuffer *buffer, gpointer u_data) debug_warning("dropping\n"); ret = FALSE; } - + /* show clock time */ if ( flag & MM_PROBE_CLOCK_TIME ) { @@ -264,7 +264,7 @@ __util_gst_pad_probe(GstPad *pad, GstBuffer *buffer, gpointer u_data) return ret; } -char** +char** util_get_cookie_list ( const char *cookies ) { char **cookie_list = NULL; @@ -308,7 +308,7 @@ 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 ); @@ -317,19 +317,19 @@ bool util_check_valid_url ( const char *proxy ) debug_warning("invalid proxy is set. \n"); ret = FALSE; } - + return ret; } /* check the given path is indicating sdp file */ -bool +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 ); @@ -365,7 +365,7 @@ util_is_sdp_file ( const char *path ) return ret; } -int64_t +int64_t util_get_time ( void ) { struct timeval tv; @@ -373,16 +373,16 @@ util_get_time ( void ) return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; } -int +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") ) + if ( g_strrstr(factory_class,"Dsp") ) ret = rank_pri_inc; - else if ( g_strrstr(factory_class,"HW") ) + else if ( g_strrstr(factory_class,"HW") ) ret = rank_pri_inc; else if ( g_strrstr(factory_class,"Arm") ) ret = rank_sec_inc; @@ -390,7 +390,7 @@ util_get_rank_increase ( const char *factory_class ) return ret; } -int +int util_factory_rank_compare(GstPluginFeature *f1, GstPluginFeature *f2) // @ { const gchar *klass; -- 2.7.4