static gsize status_output_size (gchar *job) { gsize size; gint i; gchar *pipeline; size = (strlen (job) / 8 + 1) * 8; /* job description, 64 bit alignment */ size += sizeof (guint64); /* state */ size += sizeof (gint64); /* duration for transcode */ size += jobdesc_streams_count (job, "source") * sizeof (struct _SourceStreamState); for (i = 0; i < jobdesc_encoders_count (job); i++) { size += sizeof (GstClockTime); /* encoder output heartbeat */ size += sizeof (gboolean); /* end of stream */ pipeline = g_strdup_printf ("encoder.%d", i); size += jobdesc_streams_count (job, pipeline) * sizeof (struct _EncoderStreamState); /* encoder state */ g_free (pipeline); size += sizeof (guint64); /* cache head */ size += sizeof (guint64); /* cache tail */ size += sizeof (guint64); /* last rap (random access point) */ size += sizeof (guint64); /* total count */ /* nonlive job has no output */ if (!jobdesc_is_live (job)) { continue; } /* output share memory */ size += SHM_SIZE; } return size; }
/** * gstreamill_job_start: * @job: (in): json type of job description. * * Returns: json type of job execution result. */ gchar * gstreamill_job_start (Gstreamill *gstreamill, gchar *job_desc) { gchar *p, *name; Job *job; if (!jobdesc_is_valid (job_desc)) { p = g_strdup_printf ("{\n \"result\": \"failure\",\n \"reason\": \"invalid job\"\n}"); return p; } if (jobdesc_is_live (job_desc)) { GST_ERROR ("live job arrived:\n%s", job_desc); } else { GST_ERROR ("transcode job arrived:\n%s", job_desc); } /* create job object */ name = jobdesc_get_name (job_desc); if (get_job (gstreamill, name) != NULL) { GST_ERROR ("start job failure, duplicated name %s.", name); p = g_strdup_printf ("{\n \"result\": \"failure\",\n \"reason\": \"duplicated name\"\n}"); g_free (name); return p; } job = job_new ("job", job_desc, "name", name, "exe_path", gstreamill->exe_path, NULL); g_free (name); /* job initialize */ job->log_dir = gstreamill->log_dir; g_mutex_init (&(job->access_mutex)); job->is_live = jobdesc_is_live (job_desc); job->eos = FALSE; job->current_access = 0; job->age = 0; job->last_start_time = NULL; if (job_initialize (job, gstreamill->daemon) != 0) { p = g_strdup_printf ("{\n \"result\": \"failure\",\n \"reason\": \"initialize job failure\"\n}"); g_object_unref (job); return p; } if (job->is_live && (job_output_initialize (job) != 0)) { p = g_strdup_printf ("{\n \"result\": \"failure\",\n \"reason\": \"initialize job output failure\"\n}"); g_object_unref (job); return p; } /* reset and start job */ job_reset (job); if (gstreamill->daemon) { guint64 stat; stat = create_job_process (job); if (stat == JOB_STATE_PLAYING) { GST_ERROR ("Start job %s success", job->name); g_mutex_lock (&(gstreamill->job_list_mutex)); gstreamill->job_list = g_slist_append (gstreamill->job_list, job); g_mutex_unlock (&(gstreamill->job_list_mutex)); p = g_strdup_printf ("{\n \"name\": \"%s\",\n\"result\": \"success\"\n}", job->name); } else { GST_ERROR ("Start job %s failure, return stat: %s", job->name, job_state_get_name (stat)); g_object_unref (job); p = g_strdup_printf ("{\n \"result\": \"failure\",\n \"reason\": \"create process failure\"\n}"); } } else { job_encoders_output_initialize (job); if (job_start (job) == 0) { g_mutex_lock (&(gstreamill->job_list_mutex)); gstreamill->job_list = g_slist_append (gstreamill->job_list, job); g_mutex_unlock (&(gstreamill->job_list_mutex)); p = g_strdup ("{\n \"result\": \"success\"\n}"); } else { p = g_strdup_printf ("{\n \"result\": \"failure\",\n \"reason\": \"unknown\"\n}"); } } return p; }
int main (int argc, char *argv[]) { HTTPMgmt *httpmgmt; HTTPStreaming *httpstreaming; GMainLoop *loop; GOptionContext *ctx; GError *err = NULL; gboolean foreground; struct rlimit rlim; GDateTime *datetime; gchar exe_path[512], *date; ctx = g_option_context_new (NULL); g_option_context_add_main_entries (ctx, options, NULL); g_option_context_add_group (ctx, gst_init_get_option_group ()); if (!g_option_context_parse (ctx, &argc, &argv, &err)) { g_print ("Error initializing: %s\n", GST_STR_NULL (err->message)); exit (1); } g_option_context_free (ctx); GST_DEBUG_CATEGORY_INIT (GSTREAMILL, "gstreamill", 0, "gstreamill log"); if (version) { print_version_info (); exit (0); } /* stop gstreamill. */ if (stop) { gchar *pid_str; gint pid; g_file_get_contents (PID_FILE, &pid_str, NULL, NULL); if (pid_str == NULL) { g_print ("File %s not found, check if gstreamill is running.\n", PID_FILE); exit (1); } pid = atoi (pid_str); g_free (pid_str); g_print ("stoping gstreamill with pid %d ...\n", pid); kill (pid, SIGTERM); exit (0); } /* readlink exe path before setuid, on CentOS, readlink exe path after setgid/setuid failure on permission denied */ memset (exe_path, '\0', sizeof (exe_path)); if (readlink ("/proc/self/exe", exe_path, sizeof (exe_path)) == -1) { g_print ("Read /proc/self/exe error: %s", g_strerror (errno)); exit (2); } if (prepare_gstreamill_run_dir () != 0) { g_print ("Can't create gstreamill run directory\n"); exit (3); } /* if (set_user_and_group () != 0) { g_print ("set user and group failure\n"); exit (4); } */ if (job_file != NULL) { /* gstreamill command with job, run in foreground */ foreground = TRUE; } else { /* gstreamill command without job, run in background */ foreground = FALSE; } if (gst_debug_get_default_threshold () < GST_LEVEL_WARNING) { gst_debug_set_default_threshold (GST_LEVEL_WARNING); } /* initialize ts segment static plugin */ if (!gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR, "tssegment", "ts segment plugin", ts_segment_plugin_init, "0.1.0", "GPL", "GStreamer", "GStreamer", "http://gstreamer.net/")) { GST_ERROR ("registe tssegment error"); exit (17); } /* subprocess, create_job_process */ if (shm_name != NULL) { gint fd; gchar *job_desc, *p; Job *job; gchar *log_path, *name; gint ret; /* set subprocess maximum of core file */ rlim.rlim_cur = 0; rlim.rlim_max = 0; if (setrlimit (RLIMIT_CORE, &rlim) == -1) { GST_ERROR ("setrlimit error: %s", g_strerror (errno)); } /* read job description from share memory */ job_desc = NULL; fd = shm_open (shm_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); if (ftruncate (fd, job_length) == -1) { exit (5); } p = mmap (NULL, job_length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); job_desc = g_strdup (p); if ((job_desc != NULL) && (!jobdesc_is_valid (job_desc))) { exit (6); } /* initialize log */ name = (gchar *)jobdesc_get_name (job_desc); if (!jobdesc_is_live (job_desc)) { gchar *p; p = jobdesc_get_log_path (job_desc); log_path = g_build_filename (p, "gstreamill.log", NULL); g_free (p); } else { log_path = g_build_filename (log_dir, name, "gstreamill.log", NULL); } ret = init_log (log_path); g_free (log_path); if (ret != 0) { exit (7); } /* launch a job. */ datetime = g_date_time_new_now_local (); date = g_date_time_format (datetime, "%b %d %H:%M:%S"); fprintf (_log->log_hd, "\n*** %s : job %s starting ***\n\n", date, name); g_date_time_unref (datetime); g_free (date); job = job_new ("name", name, "job", job_desc, NULL); job->is_live = jobdesc_is_live (job_desc); job->eos = FALSE; loop = g_main_loop_new (NULL, FALSE); GST_INFO ("Initializing job ..."); if (job_initialize (job, TRUE) != 0) { GST_ERROR ("initialize job failure, exit"); exit (8); } GST_INFO ("Initializing job done"); GST_INFO ("Initializing job's encoders output ..."); if (job_encoders_output_initialize (job) != 0) { GST_ERROR ("initialize job encoders' output failure, exit"); exit (8); } GST_INFO ("Initializing job's encoders output done"); GST_INFO ("Starting job ..."); if (job_start (job) != 0) { GST_ERROR ("start livejob failure, exit"); exit (9); } datetime = g_date_time_new_now_local (); date = g_date_time_format (datetime, "%b %d %H:%M:%S"); fprintf (_log->log_hd, "\n*** %s : job %s started ***\n\n", date, name); g_date_time_unref (datetime); g_free (date); g_free (name); g_free (job_desc); signal (SIGPIPE, SIG_IGN); signal (SIGUSR1, sighandler); signal (SIGTERM, stop_job); g_main_loop_run (loop); } else { /* set parent process maximum of core file */ rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; if (setrlimit (RLIMIT_CORE, &rlim) == -1) { GST_ERROR ("setrlimit error: %s", g_strerror (errno)); } } /* run in background? */ if (!foreground) { gchar *path; gint ret; /* pid file exist? */ if (g_file_test (PID_FILE, G_FILE_TEST_EXISTS)) { g_print ("file %s found, gstreamill already running !!!\n", PID_FILE); exit (10); } /* media directory */ path = g_strdup_printf ("%s/dvr", MEDIA_LOCATION); if (!g_file_test (path, G_FILE_TEST_EXISTS)) { g_printf ("Create DVR directory: %s", path); if (g_mkdir_with_parents (path, 0755) != 0) { g_printf ("Create DVR directory failure: %s", path); } } g_free (path); path = g_strdup_printf ("%s/transcode/in", MEDIA_LOCATION); if (!g_file_test (path, G_FILE_TEST_EXISTS)) { g_printf ("Create transcode directory: %s", path); if (g_mkdir_with_parents (path, 0755) != 0) { g_printf ("Create transcode directory failure: %s", path); } } g_free (path); path = g_strdup_printf ("%s/transcode/out", MEDIA_LOCATION); if (!g_file_test (path, G_FILE_TEST_EXISTS)) { g_printf ("Create transcode directory: %s", path); if (g_mkdir_with_parents (path, 0755) != 0) { g_printf ("Create transcode directory failure: %s", path); } } g_free (path); /* log to file */ path = g_build_filename (log_dir, "gstreamill.log", NULL); ret = init_log (path); g_free (path); if (ret != 0) { g_print ("Init log error, ret %d.\n", ret); exit (11); } /* daemonize */ if (daemon (0, 0) != 0) { fprintf (_log->log_hd, "Failed to daemonize"); remove_pid_file (); exit (1); } /* create pid file */ if (create_pid_file () != 0) { exit (1); } /* customize signal */ signal (SIGUSR1, sighandler); signal (SIGTERM, stop_gstreamill); datetime = g_date_time_new_now_local (); date = g_date_time_format (datetime, "%b %d %H:%M:%S"); fprintf (_log->log_hd, "\n*** %s : gstreamill started ***\n\n", date); g_free (date); g_date_time_unref (datetime); } /* ignore SIGPIPE */ signal (SIGPIPE, SIG_IGN); loop = g_main_loop_new (NULL, FALSE); /* gstreamill */ gstreamill = gstreamill_new ("daemon", !foreground, "log_dir", log_dir, "exe_path", exe_path, NULL); if (gstreamill_start (gstreamill) != 0) { GST_ERROR ("start gstreamill error, exit."); remove_pid_file (); exit (12); } /* httpstreaming, pull */ httpstreaming = httpstreaming_new ("gstreamill", gstreamill, "address", http_streaming, NULL); if (httpstreaming_start (httpstreaming, 10) != 0) { GST_ERROR ("start httpstreaming error, exit."); remove_pid_file (); exit (13); } if (!foreground) { /* run in background, management via http */ httpmgmt = httpmgmt_new ("gstreamill", gstreamill, "address", http_mgmt, NULL); if (httpmgmt_start (httpmgmt) != 0) { GST_ERROR ("start http mangment error, exit."); remove_pid_file (); exit (14); } } else { /* run in foreground, start job */ gchar *job, *p, *result; JSON_Value *val; JSON_Object *obj; /* ctrl-c, stop gstreamill */ signal (SIGINT, stop_gstreamill); /* ctrl-\, stop gstreamill */ signal (SIGQUIT, stop_gstreamill); if (!g_file_get_contents (job_file, &job, NULL, NULL)) { GST_ERROR ("Read job file %s error.", job_file); exit (15); } p = gstreamill_job_start (gstreamill, job); val = json_parse_string (p); obj = json_value_get_object (val); result = (gchar *)json_object_get_string (obj, "result"); GST_INFO ("start job result: %s.", result); if (g_strcmp0 (result, "success") != 0) { exit (16); } json_value_free (val); g_free (p); } g_main_loop_run (loop); return 0; }
guint encoder_initialize (GArray *earray, gchar *job, EncoderOutput *encoders, Source *source) { gint i, j, k; gchar *job_name, *pipeline; Encoder *encoder; EncoderStream *estream; SourceStream *sstream; gchar **bins; gsize count; job_name = jobdesc_get_name (job); count = jobdesc_encoders_count (job); for (i = 0; i < count; i++) { pipeline = g_strdup_printf ("encoder.%d", i); encoder = encoder_new ("name", pipeline, NULL); encoder->job_name = g_strdup (job_name); encoder->id = i; encoder->last_running_time = GST_CLOCK_TIME_NONE; encoder->output = &(encoders[i]); encoder->segment_duration = jobdesc_m3u8streaming_segment_duration (job); encoder->duration_accumulation = 0; encoder->last_segment_duration = 0; encoder->force_key_count = 0; encoder->has_video = FALSE; encoder->has_audio_only = FALSE; encoder->has_tssegment = FALSE; bins = jobdesc_bins (job, pipeline); if (encoder_extract_streams (encoder, bins) != 0) { GST_ERROR ("extract encoder %s streams failure", encoder->name); g_free (job_name); g_free (pipeline); g_strfreev (bins); return 1; } g_strfreev (bins); for (j = 0; j < encoder->streams->len; j++) { estream = g_array_index (encoder->streams, gpointer, j); estream->state = &(encoders[i].streams[j]); g_strlcpy (encoders[i].streams[j].name, estream->name, STREAM_NAME_LEN); estream->encoder = encoder; estream->source = NULL; for (k = 0; k < source->streams->len; k++) { sstream = g_array_index (source->streams, gpointer, k); if (g_strcmp0 (sstream->name, estream->name) == 0) { estream->source = sstream; estream->current_position = -1; estream->system_clock = encoder->system_clock; g_array_append_val (sstream->encoders, estream); break; } } if (estream->source == NULL) { GST_ERROR ("cant find job %s source %s.", job_name, estream->name); g_free (job_name); g_free (pipeline); return 1; } } /* mkdir for transcode job. */ if (!jobdesc_is_live (job)) { gchar *locations[] = {"%s.elements.filesink.property.location", "%s.elements.hlssink.property.location", NULL}; gchar *p, *value, **location; location = locations; while (*location != NULL) { p = g_strdup_printf (*location, pipeline); value = jobdesc_element_property_value (job, p); g_free (p); if (value != NULL) { break; } location += 1; } if (*location == NULL) { GST_ERROR ("No location found for transcode"); return 1; } p = g_path_get_dirname (value); g_free (value); if (g_mkdir_with_parents (p, 0755) != 0) { GST_ERROR ("Can't open or create directory: %s.", p); g_free (p); return 1; } g_free (p); } /* parse bins and create pipeline. */ encoder->bins = bins_parse (job, pipeline); if (encoder->bins == NULL) { GST_ERROR ("parse job %s bins error", job_name); g_free (job_name); g_free (pipeline); return 1; } complete_request_element (encoder->bins); if (create_encoder_pipeline (encoder) != 0) { GST_ERROR ("create encoder %s pipeline failure", encoder->name); g_free (job_name); g_free (pipeline); return 1; } /* parse udpstreaming */ udpstreaming_parse (job, encoder); /* m3u8 playlist */ encoder->is_first_key = TRUE; if (jobdesc_m3u8streaming (job)) { memset (&(encoder->msg_sock_addr), 0, sizeof (struct sockaddr_un)); encoder->msg_sock_addr.sun_family = AF_UNIX; strncpy (encoder->msg_sock_addr.sun_path, MSG_SOCK_PATH, sizeof (encoder->msg_sock_addr.sun_path) - 1); encoder->msg_sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); encoder->has_m3u8_output = TRUE; } else { encoder->has_m3u8_output = FALSE; } g_free (pipeline); g_array_append_val (earray, encoder); } g_free (job_name); return 0; }
/** * gstreamill_job_start: * @job: (in): json type of job description. * * Returns: json type of job execution result. */ gchar * gstreamill_job_start (Gstreamill *gstreamill, gchar *job_desc) { gchar *p, *name; Job *job; if (!jobdesc_is_valid (job_desc)) { p = g_strdup ("Invalid job"); return p; } if (jobdesc_is_live (job_desc)) { GST_ERROR ("live job arrived"); } else { GST_ERROR ("transcode job arrived"); } /* create job object */ name = jobdesc_get_name (job_desc); if (get_job (gstreamill, name) != NULL) { GST_ERROR ("start live job failure, duplicated name %s.", name); p = g_strdup_printf ("start live job failure, duplicated name %s.", name); g_free (name); return p; } job = job_new ("job", job_desc, "name", name, NULL); g_free (name); /* job initialize */ job->log_dir = gstreamill->log_dir; g_mutex_init (&(job->access_mutex)); job->is_live = jobdesc_is_live (job_desc); job->eos = FALSE; job->current_access = 0; job->age = 0; job->last_start_time = NULL; if (job_initialize (job, gstreamill->daemon) != 0) { p = g_strdup ("initialize job failure"); g_object_unref (job); return p; } /* reset and start job */ job_reset (job); if (gstreamill->daemon) { p = create_job_process (job); GST_ERROR ("%s: %s", p, job->name); if (g_str_has_suffix (p, "success")) { g_mutex_lock (&(gstreamill->job_list_mutex)); gstreamill->job_list = g_slist_append (gstreamill->job_list, job); g_mutex_unlock (&(gstreamill->job_list_mutex)); } else { g_object_unref (job); } } else { if (job_start (job) == 0) { g_mutex_lock (&(gstreamill->job_list_mutex)); gstreamill->job_list = g_slist_append (gstreamill->job_list, job); g_mutex_unlock (&(gstreamill->job_list_mutex)); p = g_strdup ("success"); } else { p = g_strdup ("failure"); } } return p; }