/* * Copyright (c) Linux community. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "rec-window.h" #include "rec-manager.h" #include "log.h" #include "dconf.h" #include "utility.h" #include "support.h" #include "gst-recorder.h" #include "dbus-player.h" // Command queue static GAsyncQueue *g_cmd_queue = NULL; // Set this to TRUE and the queue thread will stop and exit static gboolean g_rec_manager_stop = FALSE; // Last used/saved command static RecorderCommand *g_last_rec_cmd = NULL; static void rec_manager_start_command_thread(); static void rec_manager_free_command(RecorderCommand *cmd); static void *rec_manager_command_thread(gpointer user_data); // This is called from media players on the DBus void dbus_player_data_function(MediaPlayerRec *media_player); void rec_manager_init() { LOG_DEBUG("Init rec-manager.c.\n"); // Create a message queue // Ref: http://www.gtk.org/api/2.6/glib/glib-Asynchronous-Queues.html g_cmd_queue = g_async_queue_new(); g_rec_manager_stop = FALSE; rec_manager_start_command_thread(); // Init recorder.c rec_module_init(); // Set callback function so we receive messages from DBus/Media Players dbus_player_set_callback_func(dbus_player_data_function/*Function in this module*/); } void rec_manager_exit() { LOG_DEBUG("Clean up rec-manager.c.\n"); // Clean up recorder.c rec_module_exit(); // Stop the command thread g_rec_manager_stop = TRUE; rec_manager_send_command_ex(RECORDING_QUIT_LOOP, NULL, NULL, NULL, 0, 0, 0); g_usleep(1000000/5); // Unref message queue if (g_cmd_queue) g_async_queue_unref(g_cmd_queue); // Free the saved g_last_rec_cmd rec_manager_free_command(g_last_rec_cmd); g_last_rec_cmd = NULL; } void rec_manager_print_command(RecorderCommand *cmd) { if (!cmd) return; const gchar *type_str = NULL; switch (cmd->type) { case RECORDING_STOP: type_str = "RECORDING_STOP"; break; case RECORDING_START: type_str = "RECORDING_START"; break; case RECORDING_PAUSE: type_str = "RECORDING_PAUSE"; break; case RECORDING_NOTIFY_MSG: type_str = "RECORDING_NOTIFY_MSG"; break; case RECORDING_SHOW_WINDOW: type_str = "RECORDING_SHOW_WINDOW"; break; case RECORDING_HIDE_WINDOW: type_str = "RECORDING_HIDE_WINDOW"; break; case RECORDING_QUIT_LOOP: type_str = "RECORDING_QUIT_LOOP"; break; case RECORDING_QUIT_APP: type_str = "RECORDING_QUIT_APP"; break; default: type_str = "Unknown recording command"; } // Suppress "not used" message if (type_str) { ; } if (cmd->type != RECORDING_NOTIFY_MSG) { LOG_DEBUG("%s: %s, %s, %s, time=%d/%d flags=%d\n", type_str, cmd->track, cmd->artist, cmd->album, cmd->track_pos, cmd->track_len, cmd->flags); } else { LOG_DEBUG("%s: %s\n", type_str, cmd->track); } } void dbus_player_data_function(MediaPlayerRec *media_player) { // This is called from media players on the DBus (see dbus_player.c module). // Map the MediaPlayerRec data to this module. if (!media_player) return; dbus_player_debug_print(media_player); /* TODO: Check this. Removed by moma 17.04.2012 // MediaPlayer is active? if (!media_player->active) { // No. // Stop recording and return rec_manager_stop_recording(); return; } */ // Create a message and send it to the queue. // Pointer to TrackInfo TrackInfo *track = &media_player->track; // MediaPlayer changed status. // Check its status. if (track->status == PLAYER_STATUS_PAUSED) { // Send RECORDING_PAUSE message to the queue rec_manager_send_command_ex(RECORDING_PAUSE, track->track/*track*/, track->artist/*artist*/, track->album/*album*/, track->current_secs/*track_pos*/, track->total_secs/*track_len*/, track->flags/*flags*/); } else if (track->status == PLAYER_STATUS_PLAYING) { // Send RECORDING_START message to the queue rec_manager_send_command_ex(RECORDING_START, track->track/*track*/, track->artist/*artist*/, track->album/*album*/, track->current_secs/*track_pos*/, track->total_secs/*track_len*/, track->flags/*flags*/); } else if (track->status == PLAYER_STATUS_NOTIFY_MSG) { // Send RECORDING_NOTIFY_MSG message to the queue rec_manager_send_command_ex(RECORDING_NOTIFY_MSG, track->track/*track*/, track->artist/*artist*/, track->album/*album*/, track->current_secs/*track_pos*/, track->total_secs/*track_len*/, track->flags/*flags*/); } else { // track.status == PLAYER_STATUS_CLOSED || // track.status == PLAYER_STATUS_STOPPED // Send RECORDING_STOP message to the queue rec_manager_send_command_ex(RECORDING_STOP, track->track/*track*/, track->artist/*artist*/, track->album/*album*/, track->current_secs/*track_pos*/, track->total_secs/*track_len*/, track->flags/*flags*/); } } gint64 rec_manager_get_stream_time() { // Get and return current recording time (stream time) in seconds. return rec_get_stream_time(); } void rec_manager_update_gui() { // Update GUI to reflect the status of recording win_update_gui(); } void rec_manager_update_level_bar(gdouble peak, gdouble peak_dB) { // Update amplitude/level bar win_update_level_bar(peak, peak_dB); } const gchar *rec_manager_get_state_name(gint state) { // Return name of the recording state, state of pipeline. switch (state) { case GST_STATE_PAUSED: return "PAUSED"; case GST_STATE_PLAYING: return "PLAYING"; case GST_STATE_READY: return "READY"; case GST_STATE_NULL: return "NULL"; default: return "UNKNOWN STATE"; } } void rec_manager_flip_recording() { // Start, continue or stop recording // Get recording status gint old_status = -1; gint pending = -1; rec_manager_get_state(&old_status, &pending); switch (old_status) { case GST_STATE_PAUSED: // Continue recording rec_manager_continue_recording(); break; case GST_STATE_PLAYING: // Stop recording rec_manager_stop_recording(); break; default: // And start recording rec_manager_start_recording(); } } void rec_manager_set_filename_label(gchar *filename) { // Set filename label win_set_filename(filename); } void rec_manager_set_time_label(gchar *label_txt) { // Set time label win_set_time_label(label_txt); } void rec_manager_set_size_label(gchar *label_txt) { // Set file size label win_set_size_label(label_txt); } void rec_manager_set_error_text(gchar *error_txt) { // Set error label win_set_error_text(error_txt); } void rec_manager_get_state(gint *status, gint *pending) { // Return recording status rec_get_state(status, pending); } gchar *rec_manager_get_output_filename() { // Return output filename return rec_get_output_filename(); } void rec_manager_show_window(gboolean show) { // Show or hide application window if (show) // Send RECORDING_SHOW_WINDOW message to the queue rec_manager_send_command_ex(RECORDING_SHOW_WINDOW, NULL/*track*/, NULL/*artist*/, NULL/*album*/, 0/*track_pos*/, 0/*track_len*/, 0/*flags*/); else // Send RECORDING_HIDE_WINDOW message to the queue rec_manager_send_command_ex(RECORDING_HIDE_WINDOW, NULL/*track*/, NULL/*artist*/, NULL/*album*/, 0/*track_pos*/, 0/*track_len*/, 0/*flags*/); } void rec_manager_quit_application() { // Quit application // Send RECORDING_QUIT_APP message to the queue rec_manager_send_command_ex(RECORDING_QUIT_APP, NULL/*track*/, NULL/*artist*/, NULL/*album*/, 0/*track_pos*/, 0/*track_len*/, 0/*flags*/); } void rec_manager_start_recording() { // Start recording // Send RECORDING_START message to the queue // The filename will be auto-generated because the track name is NULL. rec_manager_send_command_ex(RECORDING_START, NULL/*track*/, NULL/*artist*/, NULL/*album*/, 0/*track_pos*/, 0/*track_len*/, 0/*flags*/); } void rec_manager_stop_recording() { // Stop recording // Send RECORDING_STOP message to the queue rec_manager_send_command_ex(RECORDING_STOP, NULL/*track*/, NULL/*artist*/, NULL/*album*/, 0/*track_pos*/, 0/*track_len*/, 0/*flags*/); } void rec_manager_continue_recording() { // Send RECORDING_CONTINUE message to the queue (Continues paused recording. This does not restart if recording is off) rec_manager_send_command_ex(RECORDING_CONTINUE, NULL/*track*/, NULL/*artist*/, NULL/*album*/, 0/*track_pos*/, 0/*track_len*/, 0/*flags*/); } void rec_manager_pause_recording() { // Pause recording // Send RECORDING_PAUSE message to the queue rec_manager_send_command_ex(RECORDING_PAUSE, NULL/*track*/, NULL/*artist*/, NULL/*album*/, 0/*track_pos*/, 0/*track_len*/, 0/*flags*/); } gboolean rec_manager_is_recording() { // Is recording? return rec_is_recording(); } void rec_manager_send_gui_msg(gchar *msg) { // Display a message in the GUI (normally a red GtkLabel in the GUI) // Send RECORDING_NOTIFY_MSG to the queue rec_manager_send_command_ex(RECORDING_NOTIFY_MSG, msg/*the msg*/, NULL/*artist*/, NULL/*album*/, 0/*track_pos*/, 0/*track_len*/, 0/*flags*/); } static void rec_manager_free_command(RecorderCommand *cmd) { // Free command if (!cmd) return; g_free(cmd->track); g_free(cmd->artist); g_free(cmd->album); g_free(cmd); } void rec_manager_send_command_ex(enum CommandType type, gchar *track, gchar *artist, gchar *album, gint track_pos, gint track_len, guint flags) { // Build the command RecorderCommand *cmd = g_malloc0(sizeof(RecorderCommand)); cmd->type = type; cmd->track = g_strdup(track); cmd->artist = g_strdup(artist); cmd->album = g_strdup(album); cmd->track_pos = track_pos; cmd->track_len = track_len; cmd->flags = flags; // Send the command. The command que will free the cmd after usage. rec_manager_send_command(cmd); } void rec_manager_send_command(RecorderCommand *cmd) { // Push command to the queue g_async_queue_push(g_cmd_queue, (gpointer)cmd); } static void rec_manager_start_command_thread() { // Create a work thread to handle all incoming messages/commands GError *error = NULL; GThread *gthread = g_thread_create(rec_manager_command_thread, NULL, FALSE, &error); if (!gthread || error) { LOG_ERROR("Cannot start command thread. This program has terminated. %s\n", (error ? error->message : "")); if (error) g_error_free(error); // This is a fatal error exit(0); } } void *rec_manager_command_thread(gpointer user_data) { #define TIMEOUT_USECONDS (5*G_USEC_PER_SEC) while (!g_rec_manager_stop) { RecorderCommand *cmd = NULL; // Glib version is < 2.31.20? // Ref: http://developer.gnome.org/glib/2.30/glib-Version-Information.html // http://ftp.gnome.org/pub/gnome/sources/glib/ // // $ pkg-config --modversion glib-2.0 // #if (!GLIB_CHECK_VERSION(2, 31, 18)) // Glib version is < 2.31.18. // We must use the older g_async_queue_timed_pop(...) function. // Get current time GTimeVal time; g_get_current_time(&time); g_time_val_add(&time, TIMEOUT_USECONDS/*in micro seconds*/); // Read next command from the queue cmd = (RecorderCommand*)g_async_queue_timed_pop(g_cmd_queue, &time); #else // Glib version is >= 2.31.18. // We use the newer g_async_queue_timeout_pop(...) function. // Read next command from the queue cmd = (RecorderCommand*)g_async_queue_timeout_pop(g_cmd_queue, TIMEOUT_USECONDS/*in micro seconds*/); #endif if (!cmd) continue; // Debug print #ifdef ACTIVE_DEBUGGING rec_manager_print_command(cmd); #endif if (cmd->type == RECORDING_START) { // Save the values so gst-recorder.c can grab them conf_save_string_value("track/track-name", check_null(cmd->track)); conf_save_int_value("track/track-pos", cmd->track_pos); conf_save_int_value("track/track-len", cmd->track_len); conf_save_string_value("track/artist-name", check_null(cmd->artist)); conf_save_string_value("track/album-name", check_null(cmd->album)); } // Verify the delete flag and filename gboolean del_flag = FALSE; if (cmd->flags == RECORDING_DELETE_FILE) { gchar *filename = NULL; conf_get_string_value("track/last-track-name", &filename); // Remove path and file extension gchar *path=NULL; gchar *base = NULL; gchar *ext = NULL; split_filename3(filename, &path, &base, &ext); // Filenames do match? del_flag = (filename && !g_strcmp0(base, cmd->track)); g_free(path); g_free(base); g_free(ext); g_free(filename); } switch (cmd->type) { case RECORDING_STOP: rec_stop_recording(del_flag/*delete file?*/); break; case RECORDING_START: rec_start_recording(); break; case RECORDING_PAUSE: rec_pause_recording(); break; case RECORDING_CONTINUE: rec_continue_recording(); break; case RECORDING_NOTIFY_MSG: win_set_error_text(cmd->track); break; case RECORDING_SHOW_WINDOW: win_show_window(TRUE); break; case RECORDING_HIDE_WINDOW: win_show_window(FALSE); break; case RECORDING_QUIT_LOOP: rec_stop_recording(FALSE); g_rec_manager_stop = TRUE; break; case RECORDING_QUIT_APP: rec_stop_recording(FALSE); // Quit application win_quit_application(); break; } // switch ... // Free the lastly saved command rec_manager_free_command(g_last_rec_cmd); // Save this command g_last_rec_cmd = cmd; } // while ... LOG_DEBUG("rec_manager_command_thread: Command thread has terminated.\n"); g_thread_exit(0); return 0; }