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 parameter. */
428 int ms_webrtc_get_sdp_from_message(const char *sdp_msg, gchar **sdp)
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;
474 member_sdp = json_object_get_string_member(child, "sdp");
476 ms_error("Could not find sdb member: %s", sdp_msg);
477 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
481 *sdp = g_strdup(member_sdp);
483 ms_debug("sdp: %s", *sdp);
485 g_object_unref (parser);
489 /* Use g_free() to free the candidate parameter. */
490 int ms_webrtc_get_ice_candidate_from_message(const char *ice_msg, gchar **candidate, gint *mlineindex)
492 int ret = MEDIA_STREAMER_ERROR_NONE;
497 const gchar *_candidate;
499 ms_retvm_if(ice_msg == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ice_msg is NULL");
500 ms_retvm_if(candidate == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "candidate is NULL");
501 ms_retvm_if(mlineindex == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "mlineindex is NULL");
503 parser = json_parser_new();
504 if (!JSON_IS_PARSER(parser))
505 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
507 if (!json_parser_load_from_data(parser, ice_msg, -1, NULL)) {
508 ms_error("Unknown message: %s", ice_msg);
509 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
513 root = json_parser_get_root(parser);
514 if (!JSON_NODE_HOLDS_OBJECT(root)) {
515 ms_error("It does not contain a JsonObject: %s", ice_msg);
516 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
520 object = json_node_get_object(root);
521 if (!json_object_has_member(object, "ice")) {
522 ms_error("It does not contain 'ice' member: %s", ice_msg);
523 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
527 child = json_object_get_object_member(object, "ice");
529 _candidate = json_object_get_string_member(child, "candidate");
531 ms_error("Could not find candidate member: %s", ice_msg);
532 ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER;
536 *candidate = g_strdup(_candidate);
537 *mlineindex = json_object_get_int_member(child, "sdpMLineIndex");
539 ms_debug("candidate: %s", *candidate);
540 ms_debug("sdpMLineIndex: %d", *mlineindex);
542 g_object_unref(parser);
547 static void __global(void *data, struct wl_registry *registry,
548 uint32_t name, const char *interface, uint32_t version)
550 struct tizen_surface **tz_surface = NULL;
557 tz_surface = (struct tizen_surface **)data;
560 LOGW("NULL interface");
564 if (strcmp(interface, "tizen_surface") == 0) {
565 LOGD("binding tizen surface for wayland");
567 *tz_surface = wl_registry_bind(registry, name, &tizen_surface_interface, 1);
568 if (*tz_surface == NULL)
569 LOGE("failed to bind");
577 static void __global_remove(void *data, struct wl_registry *wl_registry, uint32_t name)
583 static const struct wl_registry_listener _media_streamer_wl_registry_listener = {
588 static void __parent_id_getter(void *data, struct tizen_resource *tizen_resource, uint32_t id)
595 *((unsigned int *)data) = id;
597 LOGD("[CLIENT] got parent_id [%u] from server", id);
602 static const struct tizen_resource_listener _media_streamer_tz_resource_listener = {
606 int ms_get_wl_info(Evas_Object *obj, media_streamer_wl_info_s *wl_info)
608 int ret = MEDIA_STREAMER_ERROR_NONE;
609 Ecore_Wl2_Window *window = NULL;
610 Ecore_Evas *ee = NULL;
611 Ecore_Wl2_Display *e_wl2_display = NULL;
613 struct wl_display *display = NULL;
614 struct wl_display *display_wrapper = NULL;
615 struct wl_surface *surface = NULL;
616 struct wl_registry *registry = NULL;
617 struct wl_event_queue *queue = NULL;
618 struct tizen_surface *tz_surface = NULL;
619 struct tizen_resource *tz_resource = NULL;
621 if (!obj || !wl_info) {
622 LOGE("NULL parameter %p %p", obj, wl_info);
623 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
626 e = evas_object_evas_get(obj);
628 LOGE("failed to get evas object");
629 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
633 ee = ecore_evas_ecore_evas_get(e);
635 LOGE("failed to get ecore evas object");
636 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
640 window = ecore_evas_wayland2_window_get(ee);
642 LOGE("failed to get wayland window");
643 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
647 /* set video_has flag to a video application window */
648 ecore_wl2_window_video_has(window, EINA_TRUE);
650 surface = (struct wl_surface *)ecore_wl2_window_surface_get(window);
652 LOGE("failed to get wayland surface");
653 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
657 e_wl2_display = ecore_wl2_connected_display_get(NULL);
658 if (!e_wl2_display) {
659 LOGE("failed to get ecore wl2 display");
660 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
664 display = (struct wl_display *)ecore_wl2_display_get(e_wl2_display);
666 LOGE("failed to get wayland display");
667 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
671 display_wrapper = wl_proxy_create_wrapper(display);
672 if (!display_wrapper) {
673 LOGE("failed to create wl display wrapper");
674 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
678 queue = wl_display_create_queue(display);
680 LOGE("failed to create wl display queue");
681 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
685 wl_proxy_set_queue((struct wl_proxy *)display_wrapper, queue);
687 registry = wl_display_get_registry(display_wrapper);
689 LOGE("failed to get wayland registry");
690 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
694 wl_registry_add_listener(registry, &_media_streamer_wl_registry_listener, &tz_surface);
696 wl_display_dispatch_queue(display, queue);
697 wl_display_roundtrip_queue(display, queue);
700 LOGE("failed to get tizen surface");
701 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
705 tz_resource = tizen_surface_get_tizen_resource(tz_surface, surface);
707 LOGE("failed to get tizen resource");
708 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
712 wl_info->parent_id = 0;
714 tizen_resource_add_listener(tz_resource, &_media_streamer_tz_resource_listener, &wl_info->parent_id);
716 wl_display_roundtrip_queue(display, queue);
718 if (wl_info->parent_id > 0) {
719 LOGD("parent id : %u", wl_info->parent_id);
721 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
722 LOGE("failed to get parent id");
727 tizen_surface_destroy(tz_surface);
732 tizen_resource_destroy(tz_resource);
737 wl_registry_destroy(registry);
742 wl_event_queue_destroy(queue);
746 if (display_wrapper) {
747 wl_proxy_wrapper_destroy(display_wrapper);
748 display_wrapper = NULL;