2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <glib/gstdio.h>
20 #include <tizen-extension-client-protocol.h>
21 #include <Ecore_Evas.h>
22 #include <Ecore_Wl2.h>
23 #include "media_streamer.h"
24 #include "media_streamer_priv.h"
25 #include "media_streamer_util.h"
27 static const format_s format_table[] = {
29 {MEDIA_FORMAT_L16, "audio/x-raw"},
30 {MEDIA_FORMAT_ALAW, "audio/x-alaw"},
31 {MEDIA_FORMAT_ULAW, "audio/x-mulaw"},
32 {MEDIA_FORMAT_AMR, "audio/AMR"},
33 {MEDIA_FORMAT_AMR_NB, "audio/AMR"},
34 {MEDIA_FORMAT_AMR_WB, "audio/AMR-WB"},
35 {MEDIA_FORMAT_G729, "audio/G729"},
36 {MEDIA_FORMAT_AAC, "audio/mpeg"},
37 {MEDIA_FORMAT_AAC_LC, "audio/mpeg"},
38 {MEDIA_FORMAT_AAC_HE, "audio/mpeg"},
39 {MEDIA_FORMAT_AAC_HE_PS, "audio/mpeg"},
40 {MEDIA_FORMAT_MP3, "audio/mpeg"},
42 {MEDIA_FORMAT_PCM, "S16LE"},
43 {MEDIA_FORMAT_PCM_S24LE, "S24LE"},
44 {MEDIA_FORMAT_PCM_S32LE, "S32LE"},
45 {MEDIA_FORMAT_PCM_S16BE, "S16BE"},
46 {MEDIA_FORMAT_PCM_S24BE, "S24BE"},
47 {MEDIA_FORMAT_PCM_S32BE, "S32BE"},
48 {MEDIA_FORMAT_PCM_F32LE, "F32LE"},
49 {MEDIA_FORMAT_PCM_F32BE, "F32BE"},
50 /* {MEDIA_FORMAT_PCMA, "audio/x-alaw"}, */
51 /* {MEDIA_FORMAT_PCMU, "audio/x-mulaw"}, */
53 {MEDIA_FORMAT_H261, "video/x-h261"},
54 {MEDIA_FORMAT_H263, "video/x-h263"},
55 {MEDIA_FORMAT_H263P, "video/x-h263"},
56 {MEDIA_FORMAT_H264_SP, "video/x-h264"},
57 {MEDIA_FORMAT_H264_MP, "video/x-h264"},
58 {MEDIA_FORMAT_H264_HP, "video/x-h264"},
59 {MEDIA_FORMAT_MJPEG, "image/jpeg"},
60 {MEDIA_FORMAT_MPEG1, "video/mpeg"},
61 {MEDIA_FORMAT_MPEG2_SP, "video/mpeg"},
62 {MEDIA_FORMAT_MPEG2_MP, "video/mpeg"},
63 {MEDIA_FORMAT_MPEG2_HP, "video/mpeg"},
64 {MEDIA_FORMAT_MPEG4_SP, "video/mpeg"},
65 {MEDIA_FORMAT_MPEG4_ASP, "video/mpeg"},
66 {MEDIA_FORMAT_HEVC, "video/x-h265"},
67 {MEDIA_FORMAT_VP8, "video/x-vp8"},
68 {MEDIA_FORMAT_VP9, "video/x-vp9"},
69 {MEDIA_FORMAT_VC1, "video/x-wmv"},
71 {MEDIA_FORMAT_I420, "I420"},
72 {MEDIA_FORMAT_NV12, "NV12"},
73 {MEDIA_FORMAT_NV12T, "NV12T"},
74 {MEDIA_FORMAT_YV12, "YV12"},
75 {MEDIA_FORMAT_NV21, "NV21"},
76 {MEDIA_FORMAT_NV16, "NV16"},
77 {MEDIA_FORMAT_YUYV, "YUYV"},
78 {MEDIA_FORMAT_UYVY, "UYVY"},
79 {MEDIA_FORMAT_422P, "422P"},
80 {MEDIA_FORMAT_RGB565, "RGB565"},
81 {MEDIA_FORMAT_RGB888, "RGB888"},
82 {MEDIA_FORMAT_RGBA, "RGBA"},
83 {MEDIA_FORMAT_ARGB, "ARGB"},
84 {MEDIA_FORMAT_NATIVE_VIDEO, "NATIVE_VIDEO"},
86 {MEDIA_FORMAT_CONTAINER_MP4, "video/quicktime"},
87 {MEDIA_FORMAT_CONTAINER_MPEG2TS, "video/mpegts"},
88 {MEDIA_FORMAT_MAX, NULL}
91 static dictionary *__ms_get_ini_instance(void)
93 static dictionary *instance = NULL;
94 dictionary *ms_dict = NULL;
99 /* loading existing ini file */
100 ms_dict = iniparser_load(MEDIA_STREAMER_INI_PATH);
103 ms_warning("Could not open ini [%s]. Media-streamer will use default values.", MEDIA_STREAMER_INI_PATH);
105 ms_debug("Open ini file [%s].", MEDIA_STREAMER_INI_PATH);
114 gchar *ms_ini_get_string(const char *ini_path, const char *default_str)
116 const char *result_str = default_str;
120 ms_retvm_if(ini_path == NULL, NULL, "Invalid ini path");
122 if (__ms_get_ini_instance()) {
123 const char *str = iniparser_getstring(__ms_get_ini_instance(), ini_path, default_str);
124 if (str && (strlen(str) > 0) && (strlen(str) < INI_MAX_STRLEN))
130 return g_strdup(result_str);
133 void ms_ini_read_list(const char *key, gchar ***list)
139 ms_retm_if(!__ms_get_ini_instance() || !list || !key, "Handle is NULL");
141 /* Read exclude elements list */
142 str = iniparser_getstring(__ms_get_ini_instance(), key, NULL);
143 if (str && strlen(str) > 0) {
144 gchar *strtmp = g_strdup(str);
147 *list = g_strsplit(strtmp, ",", 10);
148 MS_SAFE_GFREE(strtmp);
155 void ms_load_ini_settings(media_streamer_ini_t *ini)
161 memset(ini, 0, sizeof(media_streamer_ini_t));
163 if (__ms_get_ini_instance()) {
165 ini->generate_dot = iniparser_getboolean(__ms_get_ini_instance(), "general:generate dot", DEFAULT_GENERATE_DOT);
166 if (ini->generate_dot == TRUE) {
167 const gchar *dot_path = iniparser_getstring(__ms_get_ini_instance(), "general:dot dir", MEDIA_STREAMER_DEFAULT_DOT_DIR);
168 ms_debug("generate_dot is TRUE, dot file will be stored into %s", dot_path);
169 g_setenv("GST_DEBUG_DUMP_DOT_DIR", dot_path, FALSE);
172 ini->use_decodebin = iniparser_getboolean(__ms_get_ini_instance(), "general:use decodebin", DEFAULT_USE_DECODEBIN);
174 /* Read exclude elements list */
175 ms_ini_read_list("general:exclude elements", &ini->exclude_elem_names);
176 /* Read resource require elements list */
177 ms_ini_read_list("general:resource elements", &ini->resource_required_elem_names);
178 /* Read gstreamer arguments list */
179 ms_ini_read_list("general:gstreamer arguments", &ini->gst_args);
182 /* if dict is not available just fill the structure with default values */
183 ini->generate_dot = DEFAULT_GENERATE_DOT;
184 ini->use_decodebin = DEFAULT_USE_DECODEBIN;
188 ms_debug("Media Streamer param [generate_dot] : %d", ini->generate_dot);
189 ms_debug("Media Streamer param [use_decodebin] : %d", ini->use_decodebin);
195 const gchar *ms_convert_mime_to_string_format(media_format_mimetype_e mime)
197 gchar *format_name = NULL;
202 for (it_format = 0; format_table[it_format].format != MEDIA_FORMAT_MAX; it_format++) {
203 if (mime == format_table[it_format].format) {
204 format_name = format_table[it_format].format_name;
215 const gchar *ms_convert_mime_to_rtp_format(media_format_mimetype_e mime)
218 case MEDIA_FORMAT_I420:
219 case MEDIA_FORMAT_YV12:
220 case MEDIA_FORMAT_ARGB:
222 case MEDIA_FORMAT_H263:
224 case MEDIA_FORMAT_H264_HP:
225 case MEDIA_FORMAT_H264_MP:
226 case MEDIA_FORMAT_H264_SP:
228 case MEDIA_FORMAT_PCM:
231 ms_error("Invalid or Unsupported media format [%d].", mime);
237 media_format_mimetype_e ms_convert_string_format_to_media_format(const char *format_type)
239 media_format_mimetype_e mime = MEDIA_FORMAT_NATIVE_VIDEO;
244 for (it_format = 0; format_table[it_format].format != MEDIA_FORMAT_MAX; it_format++) {
245 if (g_strrstr(format_type, format_table[it_format].format_name)) {
246 mime = format_table[it_format].format;
256 void ms_signal_create(GList **sig_list, GstElement *obj, const char *sig_name, GCallback cb, gpointer user_data)
258 media_streamer_signal_s *sig_data;
262 ms_retm_if(!sig_list || !obj || !sig_name, "Empty signal data!");
264 sig_data = (media_streamer_signal_s *) g_try_malloc(sizeof(media_streamer_signal_s));
266 ms_error("Failed to create signal [%s] for object [%s]", sig_name, GST_OBJECT_NAME(obj));
270 sig_data->obj = G_OBJECT(obj);
271 sig_data->signal_id = g_signal_connect(sig_data->obj, sig_name, cb, user_data);
273 if (sig_data->signal_id > 0) {
274 *sig_list = g_list_append(*sig_list, sig_data);
275 ms_debug("Signal [%s] with id[%lu] connected to object [%s].", sig_name, sig_data->signal_id, GST_OBJECT_NAME(sig_data->obj));
277 ms_error("Failed to connect signal [%s] for object [%s]", sig_name, GST_OBJECT_NAME(obj));
278 MS_SAFE_GFREE(sig_data);
284 void ms_signal_destroy(void *data)
286 media_streamer_signal_s *sig_data = (media_streamer_signal_s *) data;
290 ms_retm_if(!sig_data, "Empty signal data!");
292 if (GST_IS_ELEMENT(sig_data->obj)) {
293 if (g_signal_handler_is_connected(sig_data->obj, sig_data->signal_id)) {
294 g_signal_handler_disconnect(sig_data->obj, sig_data->signal_id);
295 ms_debug("Signal with id[%lu] disconnected from object [%s].", sig_data->signal_id, GST_OBJECT_NAME(sig_data->obj));
298 MS_SAFE_GFREE(sig_data);
303 void ms_param_value_destroy(gpointer data)
305 GValue *val = (GValue *)data;
309 ms_retm_if(!data, "Empty object data!");
311 if (GST_VALUE_HOLDS_CAPS(val)) {
312 gst_caps_unref(GST_CAPS(gst_value_get_caps(val)));
322 int ms_util_uri_path_check(const char *file_uri)
324 struct stat stat_results = {0, };
331 if (!file_uri || !strlen(file_uri))
332 return MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
334 file_open = open(file_uri, O_RDONLY);
337 strerror_r(errnum, mes_error, sizeof(mes_error));
338 ms_error("Couldn`t open file [%s] according to [%s]. Error N [%d]", file_uri, mes_error, errnum);
340 if (EACCES == errnum)
341 return MEDIA_STREAMER_ERROR_PERMISSION_DENIED;
343 return MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
346 if (fstat(file_open, &stat_results) < 0) {
347 ms_error("Couldn`t get status of the file [%s]", file_uri);
348 } else if (stat_results.st_size == 0) {
349 ms_error("The size of file is 0");
351 return MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
353 ms_debug("Size of file [%lld] bytes", (long long)stat_results.st_size);
360 return MEDIA_STREAMER_ERROR_NONE;
363 int ms_bin_foreach_elements(GstBin *bin, GstIteratorFoldFunction func, void *user_data)
365 media_streamer_s *streamer = (media_streamer_s *) user_data;
369 if (!GST_BIN_NUMCHILDREN(bin)) {
370 ms_debug("No elements were added to bin [%s]. Skipping... ",
371 GST_ELEMENT_NAME(bin));
372 return MEDIA_STREAMER_STATE_NONE;
375 GstIterator *iter = gst_bin_iterate_recurse(bin);
376 GValue ret = G_VALUE_INIT;
377 GstIteratorResult result = GST_ITERATOR_DONE;
379 g_value_init(&ret, G_TYPE_BOOLEAN);
380 result = gst_iterator_fold(iter, func, &ret, streamer);
381 gst_iterator_free(iter);
384 case GST_ITERATOR_RESYNC:
385 case GST_ITERATOR_ERROR:
386 ms_error("Error while iterating elements in bin [%s]!",
387 GST_ELEMENT_NAME(bin));
388 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
390 case GST_ITERATOR_DONE:
391 case GST_ITERATOR_OK:
392 /* Check for last return value */
393 if (!g_value_get_boolean(&ret)) {
394 ms_error("Failed to prepare one of nodes");
395 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
404 return MEDIA_STREAMER_ERROR_NONE;
407 /* Use g_free() to free the return value. */
408 gchar* ms_get_string_from_json_object(JsonObject *object)
411 JsonGenerator *generator;
414 ms_retvm_if(object == NULL, NULL, "object is NULL");
416 root = json_node_init_object(json_node_alloc(), object);
417 generator = json_generator_new();
418 json_generator_set_root(generator, root);
419 text = json_generator_to_data(generator, NULL);
421 g_object_unref(generator);
422 json_node_free(root);
427 /* Use g_free() to free the sdp and type parameter. */
428 int ms_webrtc_get_sdp_from_message(const char *sdp_msg, gchar **sdp, gchar **type)
430 int ret = MEDIA_STREAMER_ERROR_NONE;
435 const gchar *member_sdp;
436 const gchar *member_type;
438 ms_retvm_if(sdp_msg == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "sdp_msg is NULL");
439 ms_retvm_if(sdp == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "sdp is NULL");
441 parser = json_parser_new();
442 if (!JSON_IS_PARSER(parser))
443 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
445 if (!json_parser_load_from_data(parser, sdp_msg, -1, NULL)) {
446 ms_error("Unknown message: %s", sdp_msg);
447 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
451 root = json_parser_get_root(parser);
452 if (!JSON_NODE_HOLDS_OBJECT(root)) {
453 ms_error("It does not contain a JsonObject: %s", sdp_msg);
454 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
458 object = json_node_get_object(root);
459 if (!json_object_has_member(object, "sdp")) {
460 ms_error("It does not contain 'sdp' member: %s", sdp_msg);
461 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
465 child = json_object_get_object_member(object, "sdp");
467 member_type = json_object_get_string_member(child, "type");
468 if (!member_type || !(g_str_equal(member_type, "answer") || g_str_equal(member_type, "offer"))) {
469 ms_error("Could not find valid type member: %s", sdp_msg);
470 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
473 *type = g_strdup(member_type);
475 member_sdp = json_object_get_string_member(child, "sdp");
477 ms_error("Could not find sdb member: %s", sdp_msg);
478 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
482 *sdp = g_strdup(member_sdp);
484 ms_info("type: %s", *type);
485 ms_info("sdp:\n%s", *sdp);
487 g_object_unref (parser);
491 /* Use g_free() to free the candidate parameter. */
492 int ms_webrtc_get_ice_candidate_from_message(const char *ice_msg, gchar **candidate, gint *mlineindex)
494 int ret = MEDIA_STREAMER_ERROR_NONE;
499 const gchar *_candidate;
501 ms_retvm_if(ice_msg == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ice_msg is NULL");
502 ms_retvm_if(candidate == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "candidate is NULL");
503 ms_retvm_if(mlineindex == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "mlineindex is NULL");
505 parser = json_parser_new();
506 if (!JSON_IS_PARSER(parser))
507 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
509 if (!json_parser_load_from_data(parser, ice_msg, -1, NULL)) {
510 ms_error("Unknown message: %s", ice_msg);
511 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
515 root = json_parser_get_root(parser);
516 if (!JSON_NODE_HOLDS_OBJECT(root)) {
517 ms_error("It does not contain a JsonObject: %s", ice_msg);
518 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
522 object = json_node_get_object(root);
523 if (!json_object_has_member(object, "ice")) {
524 ms_error("It does not contain 'ice' member: %s", ice_msg);
525 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
529 child = json_object_get_object_member(object, "ice");
531 _candidate = json_object_get_string_member(child, "candidate");
533 ms_error("Could not find candidate member: %s", ice_msg);
534 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
538 *candidate = g_strdup(_candidate);
539 *mlineindex = json_object_get_int_member(child, "sdpMLineIndex");
541 ms_debug("candidate: %s", *candidate);
542 ms_debug("sdpMLineIndex: %d", *mlineindex);
544 g_object_unref(parser);
549 static void __global(void *data, struct wl_registry *registry,
550 uint32_t name, const char *interface, uint32_t version)
552 struct tizen_surface **tz_surface = NULL;
559 tz_surface = (struct tizen_surface **)data;
562 LOGW("NULL interface");
566 if (strcmp(interface, "tizen_surface") == 0) {
567 LOGD("binding tizen surface for wayland");
569 *tz_surface = wl_registry_bind(registry, name, &tizen_surface_interface, 1);
570 if (*tz_surface == NULL)
571 LOGE("failed to bind");
579 static void __global_remove(void *data, struct wl_registry *wl_registry, uint32_t name)
585 static const struct wl_registry_listener _media_streamer_wl_registry_listener = {
590 static void __parent_id_getter(void *data, struct tizen_resource *tizen_resource, uint32_t id)
597 *((unsigned int *)data) = id;
599 LOGD("[CLIENT] got parent_id [%u] from server", id);
604 static const struct tizen_resource_listener _media_streamer_tz_resource_listener = {
608 int ms_get_wl_info(Evas_Object *obj, media_streamer_wl_info_s *wl_info)
610 int ret = MEDIA_STREAMER_ERROR_NONE;
611 Ecore_Wl2_Window *window = NULL;
612 Ecore_Evas *ee = NULL;
613 Ecore_Wl2_Display *e_wl2_display = NULL;
615 struct wl_display *display = NULL;
616 struct wl_display *display_wrapper = NULL;
617 struct wl_surface *surface = NULL;
618 struct wl_registry *registry = NULL;
619 struct wl_event_queue *queue = NULL;
620 struct tizen_surface *tz_surface = NULL;
621 struct tizen_resource *tz_resource = NULL;
623 if (!obj || !wl_info) {
624 LOGE("NULL parameter %p %p", obj, wl_info);
625 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
628 e = evas_object_evas_get(obj);
630 LOGE("failed to get evas object");
631 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
635 ee = ecore_evas_ecore_evas_get(e);
637 LOGE("failed to get ecore evas object");
638 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
642 window = ecore_evas_wayland2_window_get(ee);
644 LOGE("failed to get wayland window");
645 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
649 /* set video_has flag to a video application window */
650 ecore_wl2_window_video_has(window, EINA_TRUE);
652 surface = (struct wl_surface *)ecore_wl2_window_surface_get(window);
654 LOGE("failed to get wayland surface");
655 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
659 e_wl2_display = ecore_wl2_connected_display_get(NULL);
660 if (!e_wl2_display) {
661 LOGE("failed to get ecore wl2 display");
662 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
666 display = (struct wl_display *)ecore_wl2_display_get(e_wl2_display);
668 LOGE("failed to get wayland display");
669 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
673 display_wrapper = wl_proxy_create_wrapper(display);
674 if (!display_wrapper) {
675 LOGE("failed to create wl display wrapper");
676 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
680 queue = wl_display_create_queue(display);
682 LOGE("failed to create wl display queue");
683 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
687 wl_proxy_set_queue((struct wl_proxy *)display_wrapper, queue);
689 registry = wl_display_get_registry(display_wrapper);
691 LOGE("failed to get wayland registry");
692 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
696 wl_registry_add_listener(registry, &_media_streamer_wl_registry_listener, &tz_surface);
698 wl_display_dispatch_queue(display, queue);
699 wl_display_roundtrip_queue(display, queue);
702 LOGE("failed to get tizen surface");
703 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
707 tz_resource = tizen_surface_get_tizen_resource(tz_surface, surface);
709 LOGE("failed to get tizen resource");
710 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
714 wl_info->parent_id = 0;
716 tizen_resource_add_listener(tz_resource, &_media_streamer_tz_resource_listener, &wl_info->parent_id);
718 wl_display_roundtrip_queue(display, queue);
720 if (wl_info->parent_id > 0) {
721 LOGD("parent id : %u", wl_info->parent_id);
723 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
724 LOGE("failed to get parent id");
729 tizen_surface_destroy(tz_surface);
734 tizen_resource_destroy(tz_resource);
739 wl_registry_destroy(registry);
744 wl_event_queue_destroy(queue);
748 if (display_wrapper) {
749 wl_proxy_wrapper_destroy(display_wrapper);
750 display_wrapper = NULL;