/* GStreamer Editing Services * Copyright (C) 2015 Mathieu Duponchelle * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include "utils.h" #include "../ges/ges-internal.h" #undef GST_CAT_DEFAULT /* Copy of GST_ASCII_IS_STRING */ #define ASCII_IS_STRING(c) (g_ascii_isalnum((c)) || ((c) == '_') || \ ((c) == '-') || ((c) == '+') || ((c) == '/') || ((c) == ':') || \ ((c) == '.')) /* g_free after usage */ static gchar * _sanitize_argument (gchar * arg, const gchar * prev_arg) { gboolean expect_equal = !(arg[0] == '+' || g_str_has_prefix (arg, "set-") || prev_arg == NULL || prev_arg[0] == '+' || g_str_has_prefix (prev_arg, "set-")); gboolean need_wrap = FALSE; gchar *first_equal = NULL; gchar *wrap_start; gchar *new_string, *tmp_string; gsize num_escape; for (tmp_string = arg; *tmp_string != '\0'; tmp_string++) { if (expect_equal && first_equal == NULL && *tmp_string == '=') { first_equal = tmp_string; /* if this is the first equal, then don't count it as necessarily * needing a wrap */ } else if (!ASCII_IS_STRING (*tmp_string)) { need_wrap = TRUE; break; } } if (!need_wrap) return g_strdup (arg); if (first_equal) wrap_start = first_equal + 1; else wrap_start = arg; /* need to escape any '"' or '\\' to correctly parse in as a structure */ num_escape = 0; for (tmp_string = wrap_start; *tmp_string != '\0'; tmp_string++) { if (*tmp_string == '"' || *tmp_string == '\\') num_escape++; } tmp_string = new_string = g_malloc (sizeof (gchar) * (strlen (arg) + num_escape + 3)); while (arg != wrap_start) *(tmp_string++) = *(arg++); (*tmp_string++) = '"'; while (*arg != '\0') { if (*arg == '"' || *arg == '\\') (*tmp_string++) = '\\'; *(tmp_string++) = *(arg++); } *(tmp_string++) = '"'; *tmp_string = '\0'; return new_string; } gchar * sanitize_timeline_description (gchar ** args, GESLauncherParsedOptions * opts) { gint i; gchar *prev_arg = NULL; GString *track_def; GString *timeline_str; gchar *string = g_strdup (" "); for (i = 1; args[i]; i++) { gchar *new_string; gchar *sanitized = _sanitize_argument (args[i], prev_arg); new_string = g_strconcat (string, " ", sanitized, NULL); g_free (sanitized); g_free (string); string = new_string; prev_arg = args[i]; } if (i == 1) { g_free (string); return NULL; } if (strstr (string, "+track")) { gchar *res = g_strconcat ("ges:", string, NULL); g_free (string); return res; } timeline_str = g_string_new (string); g_free (string); if (opts->track_types & GES_TRACK_TYPE_VIDEO) { track_def = g_string_new (" +track video "); if (opts->video_track_caps) g_string_append_printf (track_def, " restrictions=[%s] ", opts->video_track_caps); g_string_prepend (timeline_str, track_def->str); g_string_free (track_def, TRUE); } if (opts->track_types & GES_TRACK_TYPE_AUDIO) { track_def = g_string_new (" +track audio "); if (opts->audio_track_caps) g_string_append_printf (track_def, " restrictions=[%s] ", opts->audio_track_caps); g_string_prepend (timeline_str, track_def->str); g_string_free (track_def, TRUE); } g_string_prepend (timeline_str, "ges:"); return g_string_free (timeline_str, FALSE); } gboolean get_flags_from_string (GType type, const gchar * str_flags, guint * flags) { GValue value = G_VALUE_INIT; g_value_init (&value, type); if (!gst_value_deserialize (&value, str_flags)) { g_value_unset (&value); return FALSE; } *flags = g_value_get_flags (&value); g_value_unset (&value); return TRUE; } gchar * ensure_uri (const gchar * location) { if (gst_uri_is_valid (location)) return g_strdup (location); else return gst_filename_to_uri (location, NULL); } GstEncodingProfile * parse_encoding_profile (const gchar * format) { GstEncodingProfile *profile; GValue value = G_VALUE_INIT; g_value_init (&value, GST_TYPE_ENCODING_PROFILE); if (!gst_value_deserialize (&value, format)) { g_value_reset (&value); return NULL; } profile = g_value_dup_object (&value); g_value_reset (&value); return profile; } void print_enum (GType enum_type) { GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (enum_type)); guint i; for (i = 0; i < enum_class->n_values; i++) { gst_print ("%s\n", enum_class->values[i].value_nick); } g_type_class_unref (enum_class); } void ges_print (GstDebugColorFlags c, gboolean err, gboolean nline, const gchar * format, va_list var_args) { GString *str = g_string_new (NULL); GstDebugColorMode color_mode; gchar *color = NULL; const gchar *clear = NULL; color_mode = gst_debug_get_color_mode (); #ifdef G_OS_WIN32 if (color_mode == GST_DEBUG_COLOR_MODE_UNIX) { #else if (color_mode != GST_DEBUG_COLOR_MODE_OFF) { #endif clear = "\033[00m"; color = gst_debug_construct_term_color (c); } if (color) { g_string_append (str, color); g_free (color); } g_string_append_vprintf (str, format, var_args); if (nline) g_string_append_c (str, '\n'); if (clear) g_string_append (str, clear); if (err) gst_printerr ("%s", str->str); else gst_print ("%s", str->str); g_string_free (str, TRUE); } void ges_ok (const gchar * format, ...) { va_list var_args; va_start (var_args, format); ges_print (GST_DEBUG_FG_GREEN, FALSE, TRUE, format, var_args); va_end (var_args); } void ges_warn (const gchar * format, ...) { va_list var_args; va_start (var_args, format); ges_print (GST_DEBUG_FG_YELLOW, TRUE, TRUE, format, var_args); va_end (var_args); } void ges_printerr (const gchar * format, ...) { va_list var_args; va_start (var_args, format); ges_print (GST_DEBUG_FG_RED, TRUE, TRUE, format, var_args); va_end (var_args); } gchar * get_file_extension (gchar * uri) { size_t len; gint find; len = strlen (uri); find = len - 1; while (find >= 0) { if (uri[find] == '.') break; find--; } if (find <= 0) return NULL; return g_strdup (&uri[find + 1]); } static const gchar * get_type_icon (gpointer obj) { if (GST_IS_ENCODING_AUDIO_PROFILE (obj) || GST_IS_DISCOVERER_AUDIO_INFO (obj)) return "♫"; else if (GST_IS_ENCODING_VIDEO_PROFILE (obj) || GST_IS_DISCOVERER_VIDEO_INFO (obj)) return "▶"; else if (GST_IS_ENCODING_CONTAINER_PROFILE (obj) || GST_IS_DISCOVERER_CONTAINER_INFO (obj)) return "∋"; else return ""; } static void print_profile (GstEncodingProfile * profile, const gchar * prefix) { const gchar *name = gst_encoding_profile_get_name (profile); const gchar *desc = gst_encoding_profile_get_description (profile); GstCaps *format = gst_encoding_profile_get_format (profile); gchar *capsdesc = NULL; if (gst_caps_is_fixed (format)) capsdesc = gst_pb_utils_get_codec_description (format); if (!capsdesc) capsdesc = gst_caps_to_string (format); if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) { gst_print ("%s> %s %s: %s%s%s%s\n", prefix, get_type_icon (profile), capsdesc, name ? name : "", desc ? " (" : "", desc ? desc : "", desc ? ")" : ""); } else { gst_print ("%s%s %s%s%s%s%s%s", prefix, get_type_icon (profile), name ? name : capsdesc, desc ? ": " : "", desc ? desc : "", name ? " (" : "", name ? capsdesc : "", name ? ")" : ""); if (GST_IS_ENCODING_VIDEO_PROFILE (profile)) { GstCaps *caps = gst_encoding_profile_get_restriction (profile); if (!caps && gst_caps_is_fixed (format)) caps = gst_caps_ref (format); if (caps) { GstVideoInfo info; if (gst_video_info_from_caps (&info, caps)) { gst_print (" (%dx%d", info.width, info.height); if (info.fps_n) gst_print ("@%d/%dfps", info.fps_n, info.fps_d); gst_print (")"); } gst_caps_unref (caps); } } else if (GST_IS_ENCODING_AUDIO_PROFILE (profile)) { GstCaps *caps = gst_encoding_profile_get_restriction (profile); if (!caps && gst_caps_is_fixed (format)) caps = gst_caps_ref (format); if (caps) { GstAudioInfo info; if (gst_caps_is_fixed (caps) && gst_audio_info_from_caps (&info, caps)) gst_print (" (%d channels @ %dhz)", info.channels, info.rate); gst_caps_unref (caps); } } gst_print ("\n"); } gst_caps_unref (format); g_free (capsdesc); } void describe_encoding_profile (GstEncodingProfile * profile) { g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); print_profile (profile, " "); if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) { const GList *tmp; for (tmp = gst_encoding_container_profile_get_profiles (GST_ENCODING_CONTAINER_PROFILE (profile)); tmp; tmp = tmp->next) print_profile (tmp->data, " - "); } } static void describe_stream_info (GstDiscovererStreamInfo * sinfo, GString * desc) { gchar *capsdesc; GstCaps *caps; caps = gst_discoverer_stream_info_get_caps (sinfo); capsdesc = gst_pb_utils_get_codec_description (caps); if (!capsdesc) capsdesc = gst_caps_to_string (caps); gst_caps_unref (caps); g_string_append_printf (desc, "%s%s%s", desc->len ? ", " : "", get_type_icon (sinfo), capsdesc); g_free (capsdesc); if (GST_IS_DISCOVERER_CONTAINER_INFO (sinfo)) { GList *tmp, *streams; streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (sinfo)); for (tmp = streams; tmp; tmp = tmp->next) describe_stream_info (tmp->data, desc); gst_discoverer_stream_info_list_free (streams); } } static gchar * describe_discoverer (GstDiscovererInfo * info) { GString *desc = g_string_new (NULL); GstDiscovererStreamInfo *sinfo = gst_discoverer_info_get_stream_info (info); describe_stream_info (sinfo, desc); gst_discoverer_stream_info_unref (sinfo); return g_string_free (desc, FALSE); } void print_timeline (GESTimeline * timeline) { gchar *uri; GList *layer, *clip, *clips; if (!timeline->layers) return; uri = ges_command_line_formatter_get_timeline_uri (timeline); gst_print ("\nTimeline description: `%s`\n", &uri[5]); g_free (uri); gst_print ("====================\n\n"); for (layer = timeline->layers; layer; layer = layer->next) { clips = ges_layer_get_clips (layer->data); if (!clips) continue; gst_printerr (" layer %d: \n", ges_layer_get_priority (layer->data)); gst_printerr (" --------\n"); for (clip = clips; clip; clip = clip->next) { gchar *name; if (GES_IS_URI_CLIP (clip->data)) { GESUriClipAsset *asset = GES_URI_CLIP_ASSET (ges_extractable_get_asset (clip->data)); gchar *asset_desc = describe_discoverer (ges_uri_clip_asset_get_info (asset)); name = g_strdup_printf ("Clip from: '%s' [%s]", ges_asset_get_id (GES_ASSET (asset)), asset_desc); g_free (asset_desc); } else { name = g_strdup (GES_TIMELINE_ELEMENT_NAME (clip->data)); } gst_print (" - %s\n start=%" GST_TIME_FORMAT, name, GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (clip->data))); g_free (name); if (GES_TIMELINE_ELEMENT_INPOINT (clip->data)) gst_print (" inpoint=%" GST_TIME_FORMAT, GST_TIME_ARGS (GES_TIMELINE_ELEMENT_INPOINT (clip->data))); gst_print (" duration=%" GST_TIME_FORMAT "\n", GST_TIME_ARGS (GES_TIMELINE_ELEMENT_END (clip->data))); } if (layer->next) gst_printerr ("\n"); g_list_free_full (clips, gst_object_unref); } gst_print ("\n"); }