gmerlin-1.2.0~dfsg/ 0000755 0001750 0001750 00000000000 12062133312 014062 5 ustar alessio alessio gmerlin-1.2.0~dfsg/lib/ 0000755 0001750 0001750 00000000000 11764363441 014650 5 ustar alessio alessio gmerlin-1.2.0~dfsg/lib/transcoder.c 0000644 0001750 0001750 00000300220 11764363410 017151 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
// mkdir()
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// #define DUMP_VIDEO_TIMESTAMPS
// #define DUMP_AUDIO_TIMESTAMPS
#define LOG_DOMAIN "transcoder"
#define STREAM_STATE_OFF 0
#define STREAM_STATE_ON 1
#define STREAM_STATE_FINISHED 2
#define STREAM_TYPE_AUDIO 0
#define STREAM_TYPE_VIDEO 1
#define STREAM_TYPE_SUBTITLE_TEXT 2
#define STREAM_TYPE_SUBTITLE_OVERLAY 3
#define STREAM_ACTION_FORGET 0
#define STREAM_ACTION_TRANSCODE 1
#define STREAM_ACTION_COPY 2
/* The followings are for subtitles only */
#define STREAM_ACTION_BLEND 3
/* The following is only for text subtitles and means, that they'll be
converted to graphical overlays using the text renderer */
#define STREAM_ACTION_TRANSCODE_OVERLAY 4
#define TRANSCODER_STATE_INIT 0
#define TRANSCODER_STATE_RUNNING 1
#define TRANSCODER_STATE_FINISHED 2
#define TRANSCODER_STATE_ERROR 3
/* Language utilities */
static void copy_language(char * dst, const char * src)
{
if(src)
strncpy(dst, src, 3);
else
*dst = '\0';
}
static void get_language(char * in_lang, char * lang, int force,
gavl_metadata_t * m)
{
char * ret;
if(in_lang)
ret = force ? lang : in_lang;
else
ret = lang;
gavl_metadata_set(m, GAVL_META_LANGUAGE, ret);
}
typedef struct subtitle_stream_s subtitle_stream_t;
typedef struct
{
int status;
int type;
int action;
int in_index;
int out_index;
gavl_time_t time;
bg_plugin_handle_t * in_handle;
bg_input_plugin_t * in_plugin;
int do_encode; /* Whether this stream should be really encoded */
int do_decode; /* Whether this stream should be decoded */
int do_copy; /* Whether this stream should be copied */
gavl_compression_info_t ci;
gavl_packet_t packet;
gavl_metadata_t m;
} stream_t;
static int set_stream_parameters_general(stream_t * s,
const char * name,
const bg_parameter_value_t * val)
{
if(!strcmp(name, "action"))
{
if(!strcmp(val->val_str, "transcode"))
s->action = STREAM_ACTION_TRANSCODE;
else if(!strcmp(val->val_str, "copy"))
s->action = STREAM_ACTION_COPY;
else if(!strcmp(val->val_str, "transcode_overlay"))
s->action = STREAM_ACTION_TRANSCODE_OVERLAY;
else if(!strcmp(val->val_str, "blend"))
s->action = STREAM_ACTION_BLEND;
else
s->action = STREAM_ACTION_FORGET;
return 1;
}
return 0;
}
typedef struct
{
stream_t com;
// int do_convert_in;
int do_convert_out;
gavl_audio_converter_t * cnv_out;
bg_audio_filter_chain_t * fc;
gavl_audio_frame_t * out_frame;
gavl_audio_frame_t * pipe_frame;
gavl_audio_format_t in_format;
gavl_audio_format_t pipe_format;
gavl_audio_format_t out_format;
bg_read_audio_func_t in_func;
void * in_data;
int in_stream;
/* Set by set_parameter */
bg_gavl_audio_options_t options;
/* Do normalization */
int normalize;
int64_t samples_to_read; /* Samples to read (in OUTPUT samplerate) */
int64_t samples_read; /* Samples read so far (in OUTPUT samplerate) */
int initialized;
gavl_peak_detector_t * peak_detector;
gavl_volume_control_t * volume_control;
char in_language[4];
char out_language[4];
int force_language;
} audio_stream_t;
static void set_audio_parameter_general(void * data, const char * name,
const bg_parameter_value_t * val)
{
audio_stream_t * stream;
stream = (audio_stream_t*)data;
if(!name)
return;
if(!strcmp(name, "normalize"))
{
stream->normalize = val->val_i;
return;
}
if(!strcmp(name, "in_language"))
{
copy_language(stream->in_language, val->val_str);
return;
}
if(!strcmp(name, "language"))
{
copy_language(stream->out_language, val->val_str);
return;
}
if(!strcmp(name, "force_language"))
{
stream->force_language = val->val_i;
return;
}
if(set_stream_parameters_general(&stream->com,
name, val))
return;
else if(bg_gavl_audio_set_parameter(&stream->options, name, val))
return;
}
typedef struct
{
stream_t com;
bg_video_filter_chain_t * fc;
gavl_video_frame_t * frame;
gavl_video_format_t in_format;
gavl_video_format_t out_format;
int64_t frames_written;
/* Set by set_parameter */
bg_gavl_video_options_t options;
/* Data source */
bg_read_video_func_t in_func;
void * in_data;
int in_stream;
/* Other stuff */
int initialized;
int64_t start_time_scaled;
/* Whether 2-pass transcoding is requested */
int twopass;
char * stats_file;
/* Subtitle streams for blending */
int num_subtitle_streams;
subtitle_stream_t ** subtitle_streams;
int b_frames_seen;
int flush_b_frames;
} video_stream_t;
static void set_video_parameter_general(void * data,
const char * name,
const bg_parameter_value_t * val)
{
video_stream_t * stream;
stream = (video_stream_t*)data;
if(!name)
return;
if(!strcmp(name, "twopass"))
stream->twopass = val->val_i;
if(set_stream_parameters_general(&stream->com,
name, val))
return;
else if(bg_gavl_video_set_parameter(&stream->options,
name, val))
return;
}
struct subtitle_stream_s
{
stream_t com;
gavl_video_converter_t * cnv;
int do_convert;
gavl_overlay_t ovl1, ovl2;
gavl_overlay_t * current_ovl;
gavl_overlay_t * next_ovl;
int has_current;
int has_next;
gavl_overlay_blend_context_t * blend_context;
int video_stream;
gavl_video_format_t in_format;
gavl_video_format_t out_format;
int do_blend; /* Set by check_video_blend() */
int eof; /* Set STREAM finished not before the last subtitle expired */
char in_language[4];
char out_language[4];
int force_language;
int64_t time_offset;
int64_t time_offset_scaled;
};
typedef struct
{
subtitle_stream_t com;
bg_text_renderer_t * textrenderer;
char * text;
int text_alloc;
gavl_time_t subtitle_start;
gavl_time_t subtitle_duration;
} subtitle_text_stream_t;
static void set_subtitle_parameter_general(void * data,
const char * name,
const bg_parameter_value_t * val)
{
subtitle_stream_t * stream;
stream = (subtitle_stream_t*)data;
if(!name)
return;
if(!strcmp(name, "video_stream"))
stream->video_stream = val->val_i-1;
else if(!strcmp(name, "in_language"))
{
copy_language(stream->in_language, val->val_str);
return;
}
else if(!strcmp(name, "language"))
{
copy_language(stream->out_language, val->val_str);
return;
}
else if(!strcmp(name, "force_language"))
{
stream->force_language = val->val_i;
return;
}
else if(!strcmp(name, "time_offset"))
{
stream->time_offset = (int64_t)(val->val_f * GAVL_TIME_SCALE + 0.5);
return;
}
else if(set_stream_parameters_general(&stream->com,
name, val))
return;
}
struct bg_transcoder_s
{
int num_audio_streams;
int num_video_streams;
int num_subtitle_text_streams;
int num_subtitle_overlay_streams;
int num_audio_streams_real;
int num_video_streams_real;
int num_subtitle_text_streams_real;
int num_subtitle_overlay_streams_real;
audio_stream_t * audio_streams;
video_stream_t * video_streams;
subtitle_text_stream_t * subtitle_text_streams;
subtitle_stream_t * subtitle_overlay_streams;
float percentage_done;
gavl_time_t remaining_time; /* Remaining time (Transcoding time, NOT track time!!!) */
double last_seconds;
int state;
bg_plugin_handle_t * in_handle;
bg_input_plugin_t * in_plugin;
bg_track_info_t * track_info;
bg_plugin_handle_t * out_handle;
/* Set by set_parameter */
char * name;
char * location;
char * plugin;
int track;
gavl_time_t start_time;
gavl_time_t end_time;
int set_start_time;
int set_end_time;
int prefer_edl;
gavl_metadata_t metadata;
/* General configuration stuff */
char * output_directory;
char * output_path;
char * subdir;
int delete_incomplete;
int send_finished;
/* Timing stuff */
gavl_timer_t * timer;
gavl_time_t time;
/* Duration of the section to be transcoded */
gavl_time_t duration;
char * output_filename;
int is_url;
/* Message queues */
bg_msg_queue_list_t * message_queues;
/* Multi threading stuff */
pthread_t thread;
int do_stop;
pthread_mutex_t stop_mutex;
/* Multipass */
int total_passes;
int pass;
bg_plugin_registry_t * plugin_reg;
/* Track we are created from */
bg_transcoder_track_t * transcoder_track;
/* Encoder frontend */
bg_encoder_t * enc;
/* Postprocess only */
int pp_only;
bg_encoder_callbacks_t cb;
char ** output_files;
int num_output_files;
};
static void add_output_file(bg_transcoder_t * t, const char * filename)
{
t->output_files = realloc(t->output_files,
(t->num_output_files+2) * sizeof(*t->output_files));
memset(t->output_files + t->num_output_files, 0, 2 * sizeof(*t->output_files));
t->output_files[t->num_output_files] = bg_strdup(NULL, filename);
t->num_output_files++;
}
static void free_output_files(bg_transcoder_t * t)
{
int i = 0;
for(i = 0; i < t->num_output_files; i++)
free(t->output_files[i]);
if(t->output_files)
free(t->output_files);
}
static void log_transcoding_time(bg_transcoder_t * t)
{
gavl_time_t transcoding_time;
char time_str[GAVL_TIME_STRING_LEN];
transcoding_time = gavl_timer_get(t->timer);
gavl_time_prettyprint(transcoding_time, time_str);
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Transcoding took %s (%.2f %% of realtime duration)",
time_str,
100.0 * gavl_time_to_seconds(transcoding_time) /
gavl_time_to_seconds(t->duration));
}
static const bg_parameter_info_t parameters[] =
{
{
.name = "output_path",
.long_name = TRS("Output Directory"),
.type = BG_PARAMETER_DIRECTORY,
.val_default = { .val_str = "." },
},
{
.name = "delete_incomplete",
.long_name = TRS("Delete incomplete output files"),
.type = BG_PARAMETER_CHECKBUTTON,
.val_default = { .val_i = 1 },
.help_string = TRS("Delete the encoded files if you hit the stop button. \
This option will automatically be disabled, when the track is an URL"),
},
{
.name = "cleanup_pp",
.long_name = TRS("Clean up after postprocessing"),
.type = BG_PARAMETER_CHECKBUTTON,
.help_string = TRS("Clean up all encoded files, which were postprocessed"),
},
{
.name = "send_finished",
.long_name = TRS("Send finished files to player"),
.type = BG_PARAMETER_CHECKBUTTON,
.val_default = { .val_i = 1 },
},
{ /* End of parameters */ }
};
void bg_transcoder_set_parameter(void * data, const char * name, const bg_parameter_value_t * val)
{
bg_transcoder_t * w = (bg_transcoder_t*)data;
if(!name)
return;
if(!strcmp(name, "output_path"))
{
w->output_path = bg_strdup(w->output_path, val->val_str);
}
else if(!strcmp(name, "delete_incomplete"))
{
w->delete_incomplete = val->val_i;
}
else if(!strcmp(name, "send_finished"))
{
w->send_finished = val->val_i;
}
}
const bg_parameter_info_t * bg_transcoder_get_parameters()
{
return parameters;
}
/* Message stuff */
typedef struct
{
int id;
int num;
} message_num_t;
static void set_message_num(bg_msg_t * msg, const void * data)
{
message_num_t * num = (message_num_t *)data;
bg_msg_set_id(msg, num->id);
bg_msg_set_arg_int(msg, 0, num->num);
}
void bg_transcoder_send_msg_num_audio_streams(bg_msg_queue_list_t * l,
int num)
{
message_num_t n;
n.id = BG_TRANSCODER_MSG_NUM_AUDIO_STREAMS;
n.num = num;
bg_msg_queue_list_send(l,
set_message_num, &n);
}
void bg_transcoder_send_msg_num_video_streams(bg_msg_queue_list_t * l,
int num)
{
message_num_t n;
n.id = BG_TRANSCODER_MSG_NUM_VIDEO_STREAMS;
n.num = num;
bg_msg_queue_list_send(l,
set_message_num, &n);
}
typedef struct
{
int id;
int index;
gavl_audio_format_t * ifmt;
gavl_audio_format_t * ofmt;
} message_af_t;
static void set_message_audio_format(bg_msg_t * msg, const void * data)
{
message_af_t * m = (message_af_t *)data;
bg_msg_set_id(msg, m->id);
bg_msg_set_arg_int(msg, 0, m->index);
bg_msg_set_arg_audio_format(msg, 1, m->ifmt);
bg_msg_set_arg_audio_format(msg, 2, m->ofmt);
}
void bg_transcoder_send_msg_audio_format(bg_msg_queue_list_t * l,
int index,
gavl_audio_format_t * input_format,
gavl_audio_format_t * output_format)
{
message_af_t m;
m.id = BG_TRANSCODER_MSG_AUDIO_FORMAT;
m.index = index;
m.ifmt = input_format;
m.ofmt = output_format;
bg_msg_queue_list_send(l,
set_message_audio_format, &m);
}
typedef struct
{
int id;
int index;
gavl_video_format_t * ifmt;
gavl_video_format_t * ofmt;
} message_vf_t;
static void set_message_video_format(bg_msg_t * msg, const void * data)
{
message_vf_t * m = (message_vf_t *)data;
bg_msg_set_id(msg, m->id);
bg_msg_set_arg_int(msg, 0, m->index);
bg_msg_set_arg_video_format(msg, 1, m->ifmt);
bg_msg_set_arg_video_format(msg, 2, m->ofmt);
}
void bg_transcoder_send_msg_video_format(bg_msg_queue_list_t * l,
int index,
gavl_video_format_t * input_format,
gavl_video_format_t * output_format)
{
message_vf_t m;
m.id = BG_TRANSCODER_MSG_VIDEO_FORMAT;
m.index = index;
m.ifmt = input_format;
m.ofmt = output_format;
bg_msg_queue_list_send(l,
set_message_video_format, &m);
}
typedef struct
{
const char * name;
int pp_only;
} message_file_t;
static void set_message_file(bg_msg_t * msg, const void * data)
{
message_file_t * m = (message_file_t *)data;
bg_msg_set_id(msg, BG_TRANSCODER_MSG_FILE);
bg_msg_set_arg_string(msg, 0, m->name);
bg_msg_set_arg_int(msg, 1, m->pp_only);
}
void bg_transcoder_send_msg_file(bg_msg_queue_list_t * l,
const char * filename, int pp_only)
{
message_file_t m;
m.name = filename;
m.pp_only = pp_only;
bg_msg_queue_list_send(l,
set_message_file, &m);
}
typedef struct
{
float perc;
gavl_time_t rem;
} message_progress_t;
static void set_message_progress(bg_msg_t * msg, const void * data)
{
message_progress_t * m = (message_progress_t *)data;
bg_msg_set_id(msg, BG_TRANSCODER_MSG_PROGRESS);
bg_msg_set_arg_float(msg, 0, m->perc);
bg_msg_set_arg_time(msg, 1, m->rem);
}
void bg_transcoder_send_msg_progress(bg_msg_queue_list_t * l,
float percentage_done,
gavl_time_t remaining_time)
{
message_progress_t n;
n.perc = percentage_done;
n.rem = remaining_time;
bg_msg_queue_list_send(l,
set_message_progress, &n);
}
static void set_message_finished(bg_msg_t * msg, const void * data)
{
bg_msg_set_id(msg, BG_TRANSCODER_MSG_FINISHED);
}
void bg_transcoder_send_msg_finished(bg_msg_queue_list_t * l)
{
bg_msg_queue_list_send(l, set_message_finished, NULL);
}
static void set_message_start(bg_msg_t * msg, const void * data)
{
bg_msg_set_id(msg, BG_TRANSCODER_MSG_START);
bg_msg_set_arg_string(msg, 0, (char*)data);
}
void bg_transcoder_send_msg_start(bg_msg_queue_list_t * l, char * what)
{
bg_msg_queue_list_send(l,
set_message_start, what);
}
static void set_message_error(bg_msg_t * msg, const void * data)
{
bg_msg_set_id(msg, BG_TRANSCODER_MSG_ERROR);
}
void bg_transcoder_send_msg_error(bg_msg_queue_list_t * l)
{
bg_msg_queue_list_send(l, set_message_error, NULL);
}
static void set_message_metadata(bg_msg_t * msg, const void * data)
{
bg_msg_set_id(msg, BG_TRANSCODER_MSG_METADATA);
bg_msg_set_arg_metadata(msg, 0, (const gavl_metadata_t*)data);
}
void bg_transcoder_send_msg_metadata(bg_msg_queue_list_t * l, gavl_metadata_t * m)
{
bg_msg_queue_list_send(l,
set_message_metadata, m);
}
/* */
static int decode_video_frame(void * priv, gavl_video_frame_t * f, int stream)
{
video_stream_t * s;
bg_transcoder_t * t;
int result;
t = (bg_transcoder_t *)priv;
s = &t->video_streams[stream];
result = s->com.in_plugin->read_video(s->com.in_handle->priv,
f, s->com.in_index);
if(!result)
return 0;
#ifdef DUMP_VIDEO_TIMESTAMPS
bg_debug("Input timestamp (video): %"PRId64" Offset: %"PRId64"\n",
f->timestamp, s->start_time_scaled);
#endif
/* Check for end of stream */
if((t->end_time != GAVL_TIME_UNDEFINED) &&
(gavl_time_unscale(s->in_format.timescale, f->timestamp) >= t->end_time))
return 0;
/* Correct timestamps */
if(s->start_time_scaled)
{
f->timestamp -= s->start_time_scaled;
if(f->timestamp < 0)
f->timestamp = 0;
}
return 1;
}
static void init_audio_stream(audio_stream_t * ret,
bg_transcoder_track_audio_t * s,
int in_index, bg_plugin_registry_t * plugin_reg)
{
ret->com.type = STREAM_TYPE_AUDIO;
/* Default options */
ret->volume_control = gavl_volume_control_create();
ret->peak_detector = gavl_peak_detector_create();
/* Create converter */
bg_gavl_audio_options_init(&ret->options);
ret->cnv_out = gavl_audio_converter_create();
ret->fc = bg_audio_filter_chain_create(&ret->options, plugin_reg);
/* Apply parameters */
bg_cfg_section_apply(s->general_section,
bg_transcoder_track_audio_get_general_parameters(),
set_audio_parameter_general, ret);
bg_cfg_section_apply(s->filter_section,
s->filter_parameters,
bg_audio_filter_chain_set_parameter, ret->fc);
ret->com.in_index = in_index;
}
static void start_audio_stream_i(audio_stream_t * ret,
bg_plugin_handle_t * in_handle)
{
ret->com.in_handle = in_handle;
ret->com.in_plugin = (bg_input_plugin_t*)(in_handle->plugin);
/* Set stream */
if(ret->com.in_plugin->set_audio_stream)
{
if(ret->com.do_decode)
ret->com.in_plugin->set_audio_stream(ret->com.in_handle->priv,
ret->com.in_index,
BG_STREAM_ACTION_DECODE);
else if(ret->com.do_copy)
ret->com.in_plugin->set_audio_stream(ret->com.in_handle->priv,
ret->com.in_index,
BG_STREAM_ACTION_READRAW);
else
ret->com.in_plugin->set_audio_stream(ret->com.in_handle->priv,
ret->com.in_index,
BG_STREAM_ACTION_OFF);
}
}
static void init_video_stream(video_stream_t * ret,
bg_transcoder_track_video_t * s,
int in_index, bg_plugin_registry_t * plugin_reg)
{
ret->com.type = STREAM_TYPE_VIDEO;
/* Default options */
bg_gavl_video_options_init(&ret->options);
/* Create converter */
ret->fc = bg_video_filter_chain_create(&ret->options, plugin_reg);
/* Apply parameters */
bg_cfg_section_apply(s->general_section,
bg_transcoder_track_video_get_general_parameters(),
set_video_parameter_general, ret);
bg_cfg_section_apply(s->filter_section,
s->filter_parameters,
bg_video_filter_chain_set_parameter, ret->fc);
ret->com.in_index = in_index;
}
static void start_video_stream_i(video_stream_t * ret, bg_plugin_handle_t * in_handle)
{
ret->com.in_handle = in_handle;
ret->com.in_plugin = (bg_input_plugin_t*)(in_handle->plugin);
/* Set stream */
if(ret->com.in_plugin->set_video_stream)
{
if(ret->com.do_decode)
ret->com.in_plugin->set_video_stream(ret->com.in_handle->priv,
ret->com.in_index,
BG_STREAM_ACTION_DECODE);
else if(ret->com.do_copy)
ret->com.in_plugin->set_video_stream(ret->com.in_handle->priv,
ret->com.in_index,
BG_STREAM_ACTION_READRAW);
else
ret->com.in_plugin->set_video_stream(ret->com.in_handle->priv,
ret->com.in_index,
BG_STREAM_ACTION_OFF);
}
}
static void init_subtitle_overlay_stream(subtitle_stream_t * ret,
bg_transcoder_track_subtitle_overlay_t * s)
{
ret->com.type = STREAM_TYPE_SUBTITLE_OVERLAY;
/* Apply parameters */
bg_cfg_section_apply(s->general_section,
s->general_parameters,
set_subtitle_parameter_general, ret);
ret->com.in_index = s->in_index;
if(ret->com.action == STREAM_ACTION_BLEND)
{
ret->blend_context = gavl_overlay_blend_context_create();
}
ret->cnv = gavl_video_converter_create();
}
static void init_subtitle_text_stream(subtitle_text_stream_t * ret,
bg_transcoder_track_subtitle_text_t * s)
{
ret->com.com.type = STREAM_TYPE_SUBTITLE_TEXT;
/* Apply parameters */
bg_cfg_section_apply(s->general_section,
s->general_parameters,
set_subtitle_parameter_general, ret);
ret->com.com.in_index = s->in_index;
if(ret->com.com.action == STREAM_ACTION_BLEND)
{
ret->com.blend_context = gavl_overlay_blend_context_create();
ret->textrenderer = bg_text_renderer_create();
bg_cfg_section_apply(s->textrenderer_section,
bg_text_renderer_get_parameters(),
bg_text_renderer_set_parameter,
ret->textrenderer);
}
else if(ret->com.com.action == STREAM_ACTION_TRANSCODE_OVERLAY)
{
ret->textrenderer = bg_text_renderer_create();
bg_cfg_section_apply(s->textrenderer_section,
bg_text_renderer_get_parameters(),
bg_text_renderer_set_parameter,
ret->textrenderer);
ret->com.cnv = gavl_video_converter_create();
}
}
static void start_subtitle_stream_i(subtitle_stream_t * ret,
bg_plugin_handle_t * in_handle)
{
ret->com.in_handle = in_handle;
ret->com.in_plugin = (bg_input_plugin_t*)(in_handle->plugin);
/* Set stream */
if(ret->com.in_plugin->set_subtitle_stream)
{
if(ret->com.action != STREAM_ACTION_FORGET)
ret->com.in_plugin->set_subtitle_stream(ret->com.in_handle->priv,
ret->com.in_index,
BG_STREAM_ACTION_DECODE);
else
ret->com.in_plugin->set_subtitle_stream(ret->com.in_handle->priv,
ret->com.in_index,
BG_STREAM_ACTION_OFF);
}
}
static void set_input_formats(bg_transcoder_t * ret)
{
int i;
for(i = 0; i < ret->num_audio_streams; i++)
{
if(ret->audio_streams[i].com.do_decode || ret->audio_streams[i].com.do_copy)
gavl_audio_format_copy(&ret->audio_streams[i].in_format,
&ret->track_info->audio_streams[ret->audio_streams[i].com.in_index].format);
}
for(i = 0; i < ret->num_video_streams; i++)
{
if(ret->video_streams[i].com.do_decode || ret->video_streams[i].com.do_copy)
gavl_video_format_copy(&ret->video_streams[i].in_format,
&ret->track_info->video_streams[ret->video_streams[i].com.in_index].format);
}
for(i = 0; i < ret->num_subtitle_overlay_streams; i++)
{
if(ret->subtitle_overlay_streams[i].com.do_decode)
gavl_video_format_copy(&ret->subtitle_overlay_streams[i].in_format,
&ret->track_info->subtitle_streams[ret->subtitle_overlay_streams[i].com.in_index].format);
}
for(i = 0; i < ret->num_subtitle_text_streams; i++)
{
if(ret->subtitle_text_streams[i].com.com.do_decode)
gavl_video_format_copy(&ret->subtitle_text_streams[i].com.in_format,
&ret->track_info->subtitle_streams[ret->subtitle_text_streams[i].com.com.in_index].format);
}
}
static void add_audio_stream(audio_stream_t * ret,
bg_transcoder_t * t)
{
ret->in_func = ret->com.in_plugin->read_audio;
ret->in_data = ret->com.in_handle->priv;
ret->in_stream = ret->com.in_index;
/* We set the frame size so we have roughly half second long audio chunks */
ret->in_format.samples_per_frame =
gavl_time_to_samples(ret->in_format.samplerate,
GAVL_TIME_SCALE/2);
bg_audio_filter_chain_connect_input(ret->fc,
ret->in_func,
ret->in_data,
ret->in_stream);
bg_audio_filter_chain_init(ret->fc, &ret->in_format, &ret->pipe_format);
ret->pipe_format.samples_per_frame =
gavl_time_to_samples(ret->pipe_format.samplerate,
GAVL_TIME_SCALE/2);
ret->in_func = bg_audio_filter_chain_read;
ret->in_data = ret->fc;
ret->in_stream = 0;
gavl_audio_format_copy(&ret->out_format,
&ret->pipe_format);
/* Decide language */
get_language(ret->in_language,
ret->out_language,
ret->force_language,
&ret->com.m);
/* Add the audio stream */
ret->com.out_index =
bg_encoder_add_audio_stream(t->enc,
&ret->com.m,
&ret->out_format,
ret->com.in_index);
}
static void add_audio_stream_compressed(audio_stream_t * ret,
bg_transcoder_t * t)
{
/* Decide language */
get_language(ret->in_language,
ret->out_language,
ret->force_language,
&ret->com.m);
/* Add the audio stream */
ret->com.out_index =
bg_encoder_add_audio_stream_compressed(t->enc,
&ret->com.m,
&ret->in_format,
&ret->com.ci,
ret->com.in_index);
}
static void add_subtitle_text_stream(subtitle_text_stream_t * ret,
bg_transcoder_track_subtitle_text_t * s,
bg_transcoder_t * t)
{
if(ret->com.com.action == STREAM_ACTION_TRANSCODE)
{
/* Decide language */
get_language(ret->com.in_language,
ret->com.out_language,
ret->com.force_language,
&ret->com.com.m);
gavl_video_format_copy(&ret->com.out_format,
&ret->com.in_format);
/* TODO: timescale might get changed by the encoder!!! */
ret->com.com.out_index =
bg_encoder_add_subtitle_text_stream(t->enc,
&ret->com.com.m,
ret->com.out_format.timescale,
ret->com.com.in_index);
}
else if(ret->com.com.action == STREAM_ACTION_TRANSCODE_OVERLAY)
{
/* Get the video format for overlay encoding. This is a bit nasty, since we have no idea yet,
how the format will look like (the video output format isn't known by now). We'll copy the
format, which was passed to the video encoder and hope that the overlay encoder will choose
a proper pixelformat for us. Then, we pass the same format as *frame* format to the
textrenderer */
if(t->video_streams[ret->com.video_stream].com.do_encode)
{
gavl_video_format_copy(&ret->com.out_format,
&t->video_streams[ret->com.video_stream].out_format);
}
else
{
/* Video stream won't get encoded: Use video format associatd with the text subtitle stream */
gavl_video_format_copy(&ret->com.out_format, &ret->com.in_format);
}
/* Decide language */
get_language(ret->com.in_language,
ret->com.out_language,
ret->com.force_language,
&ret->com.com.m);
ret->com.com.out_index =
bg_encoder_add_subtitle_overlay_stream(t->enc,
&ret->com.com.m,
&ret->com.out_format,
ret->com.com.in_index,
BG_STREAM_SUBTITLE_TEXT);
}
}
static void
add_subtitle_overlay_stream(subtitle_stream_t * ret,
bg_transcoder_track_subtitle_overlay_t * s,
bg_transcoder_t * t)
{
gavl_video_format_copy(&ret->out_format, &ret->in_format);
/* Decide language */
get_language(ret->in_language,
ret->out_language,
ret->force_language,
&ret->com.m);
ret->com.out_index =
bg_encoder_add_subtitle_overlay_stream(t->enc,
&ret->com.m,
&ret->out_format, ret->com.in_index,
BG_STREAM_SUBTITLE_OVERLAY);
}
static void add_video_stream(video_stream_t * ret,
bg_transcoder_t * t)
{
ret->in_func = decode_video_frame;
ret->in_data = t;
ret->in_stream = ret->com.in_index;
bg_video_filter_chain_connect_input(ret->fc,
ret->in_func, ret->in_data, ret->in_stream);
bg_video_filter_chain_init(ret->fc, &ret->in_format,
&ret->out_format);
ret->in_func = bg_video_filter_chain_read;
ret->in_data = ret->fc;
ret->in_stream = 0;
/* Add the video stream */
ret->com.out_index =
bg_encoder_add_video_stream(t->enc,
&ret->com.m,
&ret->out_format,
ret->com.in_index);
}
static void add_video_stream_compressed(video_stream_t * ret,
bg_transcoder_t * t)
{
/* Add the video stream */
ret->com.out_index =
bg_encoder_add_video_stream_compressed(t->enc,
&ret->com.m,
&ret->in_format,
&ret->com.ci,
ret->com.in_index);
}
static int set_video_pass(bg_transcoder_t * t, int i)
{
video_stream_t * s;
s = &t->video_streams[i];
if(!s->twopass)
return 1;
if(!s->stats_file)
{
s->stats_file = bg_sprintf("%s/%s_video_%02d.stats", t->output_directory, t->name, i+1);
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Using statistics file: %s", s->stats_file);
}
bg_encoder_set_video_pass(t->enc,
s->com.out_index, t->pass, t->total_passes,
s->stats_file);
return 1;
}
static int audio_iteration(audio_stream_t*s, bg_transcoder_t * t)
{
int ret = 1;
int num_samples;
int samples_decoded;
gavl_audio_frame_t * frame;
/* Get one frame worth of input data */
if(!s->initialized)
{
if((t->start_time != GAVL_TIME_UNDEFINED) &&
(t->end_time != GAVL_TIME_UNDEFINED) &&
(t->end_time > t->start_time))
{
s->samples_to_read = gavl_time_to_samples(s->out_format.samplerate,
t->end_time - t->start_time);
}
else if(t->end_time != GAVL_TIME_UNDEFINED)
{
s->samples_to_read = gavl_time_to_samples(s->out_format.samplerate,
t->end_time);
}
else
s->samples_to_read = 0; /* Zero == Infinite */
s->initialized = 1;
}
if(s->com.do_copy)
{
if(!s->com.in_plugin->read_audio_packet(s->com.in_handle->priv,
s->com.in_index,
&s->com.packet))
{
/* EOF */
s->com.status = STREAM_STATE_FINISHED;
return ret;
}
ret = bg_encoder_write_audio_packet(t->enc, &s->com.packet,
s->com.out_index);
s->samples_read += s->com.packet.duration;
if(s->samples_to_read && (s->samples_to_read <= s->samples_read))
s->com.status = STREAM_STATE_FINISHED;
s->com.time = gavl_samples_to_time(s->in_format.samplerate,
s->samples_read);
}
else
{
if(s->samples_to_read &&
(s->samples_read + s->out_format.samples_per_frame > s->samples_to_read))
num_samples = s->samples_to_read - s->samples_read;
else
num_samples = s->out_format.samples_per_frame;
if(s->do_convert_out)
frame = s->pipe_frame;
else
frame = s->out_frame;
samples_decoded = s->in_func(s->in_data, frame, s->in_stream,
num_samples);
/* Nothing more to transcode */
if(!samples_decoded)
{
s->com.status = STREAM_STATE_FINISHED;
return ret;
}
/* Volume normalization */
if(s->normalize)
{
if(t->pass == t->total_passes)
{
gavl_volume_control_apply(s->volume_control, frame);
}
else if((t->pass > 1) && (t->pass < t->total_passes))
frame = NULL;
}
/* Output conversion */
if(s->do_convert_out)
{
gavl_audio_convert(s->cnv_out, s->pipe_frame, s->out_frame);
frame = s->out_frame;
}
/* Update sample counter before the frame is set to NULL
after peak-detection */
s->samples_read += frame->valid_samples;
/* Peak detection is done for the final frame */
if(s->normalize)
{
if(t->pass == 1)
{
gavl_peak_detector_update(s->peak_detector, frame);
frame = NULL;
}
}
if(frame)
{
#ifdef DUMP_AUDIO_TIMESTAMPS
bg_debug("Output timestamp (audio): %"PRId64"\n",
frame->timestamp);
#endif
ret = bg_encoder_write_audio_frame(t->enc,
frame,
s->com.out_index);
}
s->com.time = gavl_samples_to_time(s->out_format.samplerate,
s->samples_read);
}
/* Last samples */
if(s->samples_to_read && (s->samples_to_read <= s->samples_read))
s->com.status = STREAM_STATE_FINISHED;
if(!ret)
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Encoding audio failed");
return ret;
}
static void correct_subtitle_timestamp(subtitle_stream_t * s,
int64_t * start,
int64_t * duration,
bg_transcoder_t * t)
{
/* Add offset */
*start += gavl_time_scale(s->in_format.timescale, s->time_offset);
/* Correct timestamps */
if(t->start_time != GAVL_TIME_UNDEFINED)
{
*start -= gavl_time_scale(s->in_format.timescale, t->start_time);
if(*start < 0)
{
*duration += *start;
if(*duration < 0)
*duration = 0;
*start = 0;
}
}
/* Rescale */
*start = gavl_time_rescale(s->in_format.timescale, s->out_format.timescale,
*start);
*duration = gavl_time_rescale(s->in_format.timescale, s->out_format.timescale,
*duration);
}
static int decode_subtitle_overlay(subtitle_stream_t * s, bg_transcoder_t * t,
gavl_overlay_t * ovl)
{
int result;
subtitle_text_stream_t * st;
result = s->com.in_plugin->has_subtitle(s->com.in_handle->priv, s->com.in_index);
if(!result)
return 0;
if(s->com.type == STREAM_TYPE_SUBTITLE_TEXT)
{
st = (subtitle_text_stream_t*)s;
result = s->com.in_plugin->read_subtitle_text(s->com.in_handle->priv,
&st->text, &st->text_alloc,
&st->subtitle_start,
&st->subtitle_duration,
s->com.in_index);
if(!result || ((t->end_time != GAVL_TIME_UNDEFINED) &&
(gavl_time_unscale(s->in_format.timescale, st->subtitle_start) >= t->end_time)))
{
s->eof = 1;
return 0;
}
correct_subtitle_timestamp(&st->com, &st->subtitle_start,
&st->subtitle_duration, t);
ovl->frame->timestamp = st->subtitle_start;
ovl->frame->duration = st->subtitle_duration;
bg_text_renderer_render(st->textrenderer, st->text, ovl);
}
else
{
result = s->com.in_plugin->read_subtitle_overlay(s->com.in_handle->priv,
ovl, s->com.in_index);
if(!result ||
((t->end_time != GAVL_TIME_UNDEFINED) &&
(gavl_time_unscale(s->in_format.timescale, ovl->frame->timestamp) >= t->end_time)))
{
s->eof = 1;
return 0;
}
correct_subtitle_timestamp(s, &ovl->frame->timestamp,
&ovl->frame->duration, t);
}
return 1;
}
static int decode_subtitle_text(subtitle_text_stream_t * s, bg_transcoder_t * t)
{
int result;
result =
s->com.com.in_plugin->has_subtitle(s->com.com.in_handle->priv,
s->com.com.in_index);
if(!result)
return 0;
result = s->com.com.in_plugin->read_subtitle_text(s->com.com.in_handle->priv,
&s->text, &s->text_alloc,
&s->subtitle_start,
&s->subtitle_duration,
s->com.com.in_index);
if(!result || ((t->end_time != GAVL_TIME_UNDEFINED) &&
(gavl_time_unscale(s->com.in_format.timescale, s->subtitle_start) >= t->end_time)))
{
s->com.eof = 1;
return 0;
}
correct_subtitle_timestamp(&s->com, &s->subtitle_start,
&s->subtitle_duration, t);
return 1;
}
static int check_video_blend(video_stream_t * vs,
bg_transcoder_t * t, int64_t time)
{
gavl_overlay_t * tmp_ovl;
int i;
subtitle_stream_t * ss;
int ret = 0;
int current_changed = 0;
for(i = 0; i < vs->num_subtitle_streams; i++)
{
ss = vs->subtitle_streams[i];
if(ss->com.status != STREAM_STATE_ON)
continue;
/* Check if the overlay expired */
if(ss->has_current)
{
if(bg_overlay_too_old(time,
ss->current_ovl->frame->timestamp,
ss->current_ovl->frame->duration))
{
tmp_ovl = ss->current_ovl;
ss->current_ovl = ss->next_ovl;
ss->next_ovl = tmp_ovl;
ss->has_current = ss->has_next;
ss->has_next = 0;
if(ss->has_current)
current_changed = 1;
}
}
/* Check if the next overlay replaces the current one */
if(ss->has_next)
{
if(!bg_overlay_too_new(time,
ss->next_ovl->frame->timestamp))
{
tmp_ovl = ss->current_ovl;
ss->current_ovl = ss->next_ovl;
ss->next_ovl = tmp_ovl;
ss->has_current = 1;
ss->has_next = 0;
if(bg_overlay_too_old(time,
ss->current_ovl->frame->timestamp,
ss->current_ovl->frame->duration))
ss->has_current = 0;
else
current_changed = 1;
}
}
if(!ss->has_current && !ss->eof)
{
if(decode_subtitle_overlay(ss, t, ss->current_ovl))
{
ss->has_current = 1;
current_changed = 1;
}
else
continue;
}
if(!ss->has_next && !ss->eof)
{
if(decode_subtitle_overlay(ss, t, ss->next_ovl))
ss->has_next = 1;
}
if(ss->has_current &&
!bg_overlay_too_new(time,
ss->current_ovl->frame->timestamp))
{
ss->do_blend = 1;
ret = 1;
}
else
ss->do_blend = 0;
if(current_changed)
gavl_overlay_blend_context_set_overlay(ss->blend_context,
ss->current_ovl);
if(!ss->has_current && !ss->has_next &&
ss->eof)
ss->com.status = STREAM_STATE_FINISHED;
}
return ret;
}
#define SWAP_FRAMES \
tmp_frame=s->in_frame_1;\
s->in_frame_1=s->in_frame_2;\
s->in_frame_2=tmp_frame
static int video_iteration(video_stream_t * s, bg_transcoder_t * t)
{
int ret = 1;
int i;
int result;
if(!s->initialized)
{
if(t->start_time != GAVL_TIME_UNDEFINED)
{
s->start_time_scaled = gavl_time_scale(s->in_format.timescale,
t->start_time);
// bg_video_converter_reset(s->cnv, s->start_time_scaled);
}
s->initialized = 1;
}
if(s->com.do_copy)
{
if(!s->com.in_plugin->read_video_packet(s->com.in_handle->priv,
s->com.in_index,
&s->com.packet))
{
/* EOF */
s->com.status = STREAM_STATE_FINISHED;
return ret;
}
if((s->com.packet.flags & GAVL_PACKET_TYPE_MASK) != GAVL_PACKET_TYPE_B)
{
if(s->flush_b_frames)
{
s->com.status = STREAM_STATE_FINISHED;
return ret;
}
s->com.time = gavl_time_unscale(s->in_format.timescale,
s->com.packet.pts + s->com.packet.duration);
if((t->end_time != GAVL_TIME_UNDEFINED) &&
(s->com.time >= t->end_time))
{
if(s->b_frames_seen)
s->flush_b_frames = 1;
else
{
s->com.status = STREAM_STATE_FINISHED;
return ret;
}
}
}
else
{
s->b_frames_seen = 1;
}
bg_encoder_write_video_packet(t->enc, &s->com.packet,
s->com.out_index);
}
else
{
result = s->in_func(s->in_data, s->frame, s->in_stream);
if(!result)
{
s->com.status = STREAM_STATE_FINISHED;
/* Set this also for all attached subtitle streams */
for(i = 0; i < s->num_subtitle_streams; i++)
s->subtitle_streams[i]->com.status = STREAM_STATE_FINISHED;
return ret;
}
s->com.time = gavl_time_unscale(s->out_format.timescale,
s->frame->timestamp + s->frame->duration);
if((t->end_time != GAVL_TIME_UNDEFINED) &&
(s->com.time >= t->end_time))
s->com.status = STREAM_STATE_FINISHED;
// if(check_video_blend(s, t, s->com.time))
if(check_video_blend(s, t, s->frame->timestamp))
{
for(i = 0; i < s->num_subtitle_streams; i++)
{
if(s->subtitle_streams[i]->do_blend)
{
gavl_overlay_blend(s->subtitle_streams[i]->blend_context,
s->frame);
}
}
}
#ifdef DUMP_VIDEO_TIMESTAMPS
bg_debug("Output timestamp (video): %"PRId64"\n", s->frame->timestamp);
#endif
ret = bg_encoder_write_video_frame(t->enc,
s->frame,
s->com.out_index);
}
if(!ret)
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Encoding video failed");
s->frames_written++;
return ret;
}
/* Time offset of 0.5 seconds means, that we encode subtitles maximum
0.5 seconds before the subtitle should appear. This is only interesting
for formats, which don't allow random access to subtitles */
#define SUBTITLE_TIME_OFFSET (GAVL_TIME_SCALE/2)
static int subtitle_iteration(bg_transcoder_t * t)
{
int i, ret = 1;
subtitle_text_stream_t * st;
subtitle_stream_t * ss;
video_stream_t * vs;
gavl_video_frame_t * tmp_frame;
for(i = 0; i < t->num_subtitle_text_streams; i++)
{
st = &t->subtitle_text_streams[i];
if(!st->com.com.do_encode)
continue;
if(st->com.eof)
{
st->com.com.status = STREAM_STATE_FINISHED;
continue;
}
/* Check for decoding */
if(!st->com.has_current)
{
if(st->com.com.action == STREAM_ACTION_TRANSCODE)
{
st->com.has_current = decode_subtitle_text(st, t);
}
else if(st->com.com.action == STREAM_ACTION_TRANSCODE_OVERLAY)
{
st->com.has_current = decode_subtitle_overlay((subtitle_stream_t*)st, t, &st->com.ovl1);
if(st->com.has_current && st->com.do_convert)
{
gavl_video_convert(st->com.cnv, st->com.ovl1.frame, st->com.ovl2.frame);
tmp_frame = st->com.ovl1.frame;
st->com.ovl1.frame = st->com.ovl2.frame;
st->com.ovl2.frame = tmp_frame;
}
}
}
/* Check for encoding */
if(st->com.has_current)
{
vs = &t->video_streams[st->com.video_stream];
if(!vs->com.do_encode || (st->subtitle_start - vs->com.time < SUBTITLE_TIME_OFFSET))
{
if(st->com.com.action == STREAM_ACTION_TRANSCODE)
{
ret =
bg_encoder_write_subtitle_text(t->enc,
st->text, st->subtitle_start,
st->subtitle_duration,
st->com.com.out_index);
st->com.com.time = gavl_time_unscale(st->com.out_format.timescale,
st->subtitle_start);
if(st->subtitle_start > t->time)
t->time = st->subtitle_start;
}
else if(st->com.com.action == STREAM_ACTION_TRANSCODE_OVERLAY)
{
ret =
bg_encoder_write_subtitle_overlay(t->enc,
&st->com.ovl1,
st->com.com.out_index);
st->com.com.time = gavl_time_unscale(st->com.out_format.timescale,
st->com.ovl1.frame->timestamp);
if(st->com.com.time > t->time)
t->time = st->com.com.time;
}
st->com.has_current = 0;
}
}
if(!ret)
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Encoding subtitles failed");
}
if(!ret)
return ret;
for(i = 0; i < t->num_subtitle_overlay_streams; i++)
{
ss = &t->subtitle_overlay_streams[i];
if(!ss->com.do_encode)
continue;
if(ss->eof)
{
ss->com.status = STREAM_STATE_FINISHED;
continue;
}
/* Check for decoding */
if(!ss->has_current)
{
ss->has_current = decode_subtitle_overlay(ss, t, &ss->ovl1);
if(ss->has_current && ss->do_convert)
{
gavl_video_convert(ss->cnv, ss->ovl1.frame, ss->ovl2.frame);
tmp_frame = ss->ovl1.frame;
ss->ovl1.frame = ss->ovl2.frame;
ss->ovl2.frame = tmp_frame;
}
}
/* Check for encoding */
if(ss->has_current)
{
vs = &t->video_streams[ss->video_stream];
if(!vs->com.do_encode || (ss->ovl1.frame->timestamp - vs->com.time < SUBTITLE_TIME_OFFSET))
{
ret = bg_encoder_write_subtitle_overlay(t->enc,
&ss->ovl1,
ss->com.out_index);
ss->com.time = gavl_time_unscale(ss->out_format.timescale,
ss->ovl1.frame->timestamp);
if(ss->com.time > t->time)
t->time = ss->com.time;
ss->has_current = 0;
}
}
if(!ret)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Encoding subtitles failed");
break;
}
}
return ret;
}
/* Parameter passing for the Transcoder */
#define SP_STR(s) if(!strcmp(name, # s)) \
{ \
t->s = bg_strdup(t->s, val->val_str); \
return; \
}
#define SP_INT(s) if(!strcmp(name, # s)) \
{ \
t->s = val->val_i; \
return; \
}
#define SP_TIME(s) if(!strcmp(name, # s)) \
{ \
t->s = val->val_time; \
return; \
}
static void
set_parameter_general(void * data, const char * name,
const bg_parameter_value_t * val)
{
int i, name_len;
bg_transcoder_t * t;
t = (bg_transcoder_t *)data;
if(!name)
{
/* Set start and end times */
if(!t->set_start_time)
t->start_time = GAVL_TIME_UNDEFINED;
if(!t->set_end_time)
t->end_time = GAVL_TIME_UNDEFINED;
/* Replace all '/' by '-' */
if(t->name)
{
name_len = strlen(t->name);
for(i = 0; i < name_len; i++)
{
if(t->name[i] == '/')
t->name[i] = '-';
}
}
/* Create the subdirectory if necessary */
if(t->subdir)
{
t->output_directory = bg_sprintf("%s/%s", t->output_path, t->subdir);
if(mkdir(t->output_directory,
S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH) == -1)
{
if(errno != EEXIST)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot create directory %s: %s, using default",
t->output_directory, strerror(errno));
t->output_directory = bg_strdup(t->output_directory, t->output_path);
}
}
else
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Created directory %s", t->output_directory);
}
else
t->output_directory = bg_strdup(t->output_directory, t->output_path);
return;
}
SP_STR(name);
SP_STR(location);
SP_STR(plugin);
SP_STR(subdir);
SP_INT(track);
SP_INT(prefer_edl);
SP_INT(set_start_time);
SP_INT(set_end_time);
SP_TIME(start_time);
SP_TIME(end_time);
SP_INT(pp_only);
}
#undef SP_INT
#undef SP_TIME
#undef SP_STR
void bg_transcoder_add_message_queue(bg_transcoder_t * t,
bg_msg_queue_t * message_queue)
{
bg_msg_queue_list_add(t->message_queues, message_queue);
}
bg_transcoder_t * bg_transcoder_create()
{
bg_transcoder_t * ret;
ret = calloc(1, sizeof(*ret));
ret->timer = gavl_timer_create();
ret->message_queues = bg_msg_queue_list_create();
pthread_mutex_init(&ret->stop_mutex, NULL);
return ret;
}
static void send_init_messages(bg_transcoder_t * t)
{
int i;
char * tmp_string;
if(t->pp_only)
{
tmp_string = bg_sprintf(TR("Postprocessing %s"), t->location);
}
else if(t->total_passes > 1)
{
tmp_string = bg_sprintf(TR("Transcoding %s [Track %d, Pass %d/%d]"),
t->location, t->track+1, t->pass, t->total_passes);
}
else
{
tmp_string = bg_sprintf(TR("Transcoding %s [Track %d]"), t->location, t->track+1);
}
bg_transcoder_send_msg_start(t->message_queues, tmp_string);
bg_log_notranslate(BG_LOG_INFO, LOG_DOMAIN, "%s", tmp_string);
free(tmp_string);
bg_transcoder_send_msg_metadata(t->message_queues, &t->metadata);
tmp_string = bg_metadata_to_string(&t->metadata, 1);
if(tmp_string)
{
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Metadata:\n%s", tmp_string);
free(tmp_string);
}
if(!t->pp_only)
{
bg_transcoder_send_msg_num_audio_streams(t->message_queues, t->num_audio_streams);
for(i = 0; i < t->num_audio_streams; i++)
{
if(t->audio_streams[i].com.do_decode)
{
bg_transcoder_send_msg_audio_format(t->message_queues, i,
&t->audio_streams[i].in_format,
&t->audio_streams[i].out_format);
tmp_string = bg_audio_format_to_string(&t->audio_streams[i].in_format,
1);
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Audio stream %d input format:\n%s",
i+1, tmp_string);
free(tmp_string);
tmp_string = bg_audio_format_to_string(&t->audio_streams[i].out_format,
1);
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Audio stream %d output format:\n%s",
i+1, tmp_string);
free(tmp_string);
}
else if(t->audio_streams[i].com.do_copy)
{
tmp_string = bg_audio_format_to_string(&t->audio_streams[i].in_format,
0);
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Audio stream %d format:\n%s",
i+1, tmp_string);
free(tmp_string);
}
}
bg_transcoder_send_msg_num_video_streams(t->message_queues, t->num_video_streams);
for(i = 0; i < t->num_video_streams; i++)
{
if(t->video_streams[i].com.do_decode)
{
bg_transcoder_send_msg_video_format(t->message_queues, i,
&t->video_streams[i].in_format,
&t->video_streams[i].out_format);
tmp_string = bg_video_format_to_string(&t->video_streams[i].in_format,
0);
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Video stream %d input format:\n%s",
i+1, tmp_string);
free(tmp_string);
tmp_string = bg_video_format_to_string(&t->video_streams[i].out_format, 0);
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Video stream %d output format:\n%s",
i+1, tmp_string);
free(tmp_string);
}
else if(t->video_streams[i].com.do_copy)
{
tmp_string = bg_video_format_to_string(&t->video_streams[i].in_format, 0);
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Video stream %d format:\n%s",
i+1, tmp_string);
free(tmp_string);
}
}
for(i = 0; i < t->num_subtitle_text_streams; i++)
{
if(t->subtitle_text_streams[i].com.com.do_decode)
{
switch(t->subtitle_text_streams[i].com.com.action)
{
case STREAM_ACTION_BLEND:
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Text subtitle stream %d: Blending onto video stream %d",
i+1, t->subtitle_text_streams[i].com.video_stream);
break;
case STREAM_ACTION_TRANSCODE:
case STREAM_ACTION_TRANSCODE_OVERLAY:
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Text subtitle stream %d: Exporting to file", i+1);
break;
}
}
}
for(i = 0; i < t->num_subtitle_overlay_streams; i++)
{
if(t->subtitle_overlay_streams[i].com.do_decode)
{
switch(t->subtitle_overlay_streams[i].com.action)
{
case STREAM_ACTION_BLEND:
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Overlay subtitle stream %d: Blending onto video stream %d",
i+1, t->subtitle_overlay_streams[i].video_stream);
break;
case STREAM_ACTION_TRANSCODE:
case STREAM_ACTION_TRANSCODE_OVERLAY:
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Overlay subtitle stream %d: Exporting to file", i+1);
break;
}
}
}
}
}
static void send_file(const char * name)
{
char * command;
command = bg_sprintf("gmerlin_remote -add \"%s\"\n", name);
bg_system(command);
free(command);
}
static void send_file_messages(bg_transcoder_t * t)
{
int i;
for(i = 0; i < t->num_output_files; i++)
{
bg_transcoder_send_msg_file(t->message_queues,
t->output_files[i],
t->pp_only);
if(t->send_finished)
send_file(t->output_files[i]);
}
}
static int open_input(bg_transcoder_t * ret)
{
bg_cfg_section_t * s;
const bg_plugin_info_t * plugin_info;
plugin_info = bg_plugin_find_by_name(ret->plugin_reg, ret->plugin);
if(!plugin_info)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot find plugin %s", ret->plugin);
goto fail;
}
s = bg_plugin_registry_get_section(ret->plugin_reg,
plugin_info->name);
if(s && ret->transcoder_track->input_section)
{
bg_cfg_section_transfer(ret->transcoder_track->input_section, s);
}
// fprintf(stderr, "open input %d\n", ret->prefer_edl);
if(!bg_input_plugin_load(ret->plugin_reg,
ret->location,
plugin_info,
&ret->in_handle,
NULL, ret->prefer_edl))
goto fail;
ret->in_plugin = (bg_input_plugin_t*)ret->in_handle->plugin;
if(bg_string_is_url(ret->location))
ret->is_url = 1;
/* Select track and get track info */
if(ret->in_plugin->get_num_tracks &&
(ret->track >= ret->in_plugin->get_num_tracks(ret->in_handle->priv)))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Invalid track number %d", ret->track);
goto fail;
}
ret->track_info = ret->in_plugin->get_track_info(ret->in_handle->priv,
ret->track);
if(ret->in_plugin->set_track)
ret->in_plugin->set_track(ret->in_handle->priv, ret->track);
return 1;
fail:
return 0;
}
static void check_compressed(bg_transcoder_t * ret)
{
int i, j;
for(i = 0; i < ret->num_audio_streams; i++)
{
if(!(ret->audio_streams[i].com.action == STREAM_ACTION_COPY))
continue;
/* Check if we can read compressed data */
if(!ret->in_plugin->get_audio_compression_info ||
!ret->in_plugin->get_audio_compression_info(ret->in_handle->priv,
i, &ret->audio_streams[i].com.ci))
{
bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Audio stream %d cannot be read compressed", i+1);
ret->audio_streams[i].com.action = STREAM_ACTION_TRANSCODE;
continue;
}
/* Check if we can write compressed data */
if(!bg_encoder_writes_compressed_audio(ret->enc,
&ret->track_info->audio_streams[i].format,
&ret->audio_streams[i].com.ci))
{
bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Audio stream %d cannot be written compressed", i+1);
ret->audio_streams[i].com.action = STREAM_ACTION_TRANSCODE;
continue;
}
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Copying compressed audio stream %d", i+1);
// bg_dprintf("Copying compressed audio stream %d\n", i+1);
// gavl_compression_info_dump(&ret->audio_streams[i].com.ci);
}
for(i = 0; i < ret->num_video_streams; i++)
{
if(ret->video_streams[i].com.action != STREAM_ACTION_COPY)
continue;
/* Check if we can read compressed data */
if(!ret->in_plugin->get_video_compression_info ||
!ret->in_plugin->get_video_compression_info(ret->in_handle->priv,
i, &ret->video_streams[i].com.ci))
{
bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Video stream %d cannot be read compressed", i+1);
ret->video_streams[i].com.action = STREAM_ACTION_TRANSCODE;
continue;
}
/* Check if we need to blend text subtitles */
for(j = 0; j < ret->num_subtitle_text_streams; j++)
{
if((ret->subtitle_text_streams[i].com.com.action == STREAM_ACTION_BLEND) &&
(ret->subtitle_text_streams[i].com.video_stream == i))
{
bg_log(BG_LOG_WARNING, LOG_DOMAIN,
"Not copying video stream %d: Will blend subtitles", i+1);
ret->video_streams[i].com.action = STREAM_ACTION_TRANSCODE;
break;
}
}
if(ret->video_streams[i].com.action != STREAM_ACTION_COPY)
continue;
/* Check if we need to blend overlay subtitles */
for(j = 0; j < ret->num_subtitle_overlay_streams; j++)
{
if((ret->subtitle_overlay_streams[i].com.action == STREAM_ACTION_BLEND) &&
(ret->subtitle_overlay_streams[i].video_stream == i))
{
bg_log(BG_LOG_WARNING, LOG_DOMAIN,
"Not copying video stream %d: Will blend subtitles", i+1);
ret->video_streams[i].com.action = STREAM_ACTION_TRANSCODE;
}
}
if(ret->video_streams[i].com.action != STREAM_ACTION_COPY)
continue;
/* Check if we can write compressed data */
if(!bg_encoder_writes_compressed_video(ret->enc,
&ret->track_info->video_streams[i].format,
&ret->video_streams[i].com.ci))
{
bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Video stream %d cannot be written compressed", i+1);
ret->video_streams[i].com.action = STREAM_ACTION_TRANSCODE;
continue;
}
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Copying compressed video stream %d", i+1);
// bg_dprintf("Copying compressed video stream %d\n", i+1);
// gavl_compression_info_dump(&ret->video_streams[i].com.ci);
}
}
static void create_streams(bg_transcoder_t * ret,
bg_transcoder_track_t * track)
{
int i;
/* Allocate streams */
ret->num_audio_streams = track->num_audio_streams;
if(ret->num_audio_streams)
ret->audio_streams = calloc(ret->num_audio_streams,
sizeof(*ret->audio_streams));
ret->num_video_streams = track->num_video_streams;
if(ret->num_video_streams)
ret->video_streams = calloc(ret->num_video_streams,
sizeof(*ret->video_streams));
ret->num_subtitle_text_streams = track->num_subtitle_text_streams;
if(ret->num_subtitle_text_streams)
ret->subtitle_text_streams = calloc(ret->num_subtitle_text_streams,
sizeof(*ret->subtitle_text_streams));
ret->num_subtitle_overlay_streams = track->num_subtitle_overlay_streams;
if(ret->num_subtitle_overlay_streams)
ret->subtitle_overlay_streams = calloc(ret->num_subtitle_overlay_streams,
sizeof(*ret->subtitle_overlay_streams));
/* Prepare streams */
ret->num_audio_streams_real = 0;
ret->num_video_streams_real = 0;
ret->num_subtitle_text_streams_real = 0;
ret->num_subtitle_overlay_streams_real = 0;
for(i = 0; i < ret->num_audio_streams; i++)
{
init_audio_stream(&ret->audio_streams[i],
&track->audio_streams[i],
i, ret->plugin_reg);
if(ret->audio_streams[i].com.action == STREAM_ACTION_TRANSCODE)
ret->num_audio_streams_real++;
}
for(i = 0; i < ret->num_video_streams; i++)
{
init_video_stream(&ret->video_streams[i],
&track->video_streams[i],
i, ret->plugin_reg);
if(ret->video_streams[i].com.action == STREAM_ACTION_TRANSCODE)
ret->num_video_streams_real++;
}
for(i = 0; i < ret->num_subtitle_text_streams; i++)
{
init_subtitle_text_stream(&ret->subtitle_text_streams[i],
&track->subtitle_text_streams[i]);
if(ret->subtitle_text_streams[i].com.com.action == STREAM_ACTION_TRANSCODE)
ret->num_subtitle_text_streams_real++;
else if(ret->subtitle_text_streams[i].com.com.action == STREAM_ACTION_TRANSCODE_OVERLAY)
ret->num_subtitle_overlay_streams_real++;
}
for(i = 0; i < ret->num_subtitle_overlay_streams; i++)
{
init_subtitle_overlay_stream(&ret->subtitle_overlay_streams[i],
&track->subtitle_overlay_streams[i]);
if(ret->subtitle_overlay_streams[i].com.action == STREAM_ACTION_TRANSCODE)
ret->num_subtitle_overlay_streams_real++;
}
}
static int start_input(bg_transcoder_t * ret)
{
int i;
for(i = 0; i < ret->num_audio_streams; i++)
{
if(ret->audio_streams[i].com.do_decode || ret->audio_streams[i].com.do_copy)
{
start_audio_stream_i(&ret->audio_streams[i],
ret->in_handle);
}
}
for(i = 0; i < ret->num_video_streams; i++)
{
if(ret->video_streams[i].com.do_decode || ret->video_streams[i].com.do_copy)
{
start_video_stream_i(&ret->video_streams[i],
ret->in_handle);
}
}
for(i = 0; i < ret->num_subtitle_text_streams; i++)
{
if(ret->subtitle_text_streams[i].com.com.do_decode)
{
start_subtitle_stream_i((subtitle_stream_t*)(&ret->subtitle_text_streams[i]),
ret->in_handle);
}
}
for(i = 0; i < ret->num_subtitle_overlay_streams; i++)
{
if(ret->subtitle_overlay_streams[i].com.do_decode)
{
start_subtitle_stream_i(&ret->subtitle_overlay_streams[i],
ret->in_handle);
}
}
if(ret->in_plugin->start)
{
if(!ret->in_plugin->start(ret->in_handle->priv))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Starting input plugin failed");
goto fail;
}
}
/* Check if we must seek to the start position */
if(ret->start_time != GAVL_TIME_UNDEFINED)
{
if(!ret->in_plugin->seek)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot seek to start point");
goto fail;
}
ret->in_plugin->seek(ret->in_handle->priv, &ret->start_time, GAVL_TIME_SCALE);
/* This happens, if the decoder reached EOF during the seek */
if(ret->start_time == GAVL_TIME_UNDEFINED)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot seek to start point");
goto fail;
}
}
/* Check, if the user entered bullshit */
if((ret->start_time != GAVL_TIME_UNDEFINED) &&
(ret->end_time != GAVL_TIME_UNDEFINED) &&
(ret->end_time < ret->start_time))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "End time if before start time");
goto fail;
}
return 1;
fail:
return 0;
}
static void check_passes(bg_transcoder_t * ret)
{
int i;
ret->total_passes = 1;
for(i = 0; i < ret->num_audio_streams; i++)
{
if((ret->audio_streams[i].com.action == STREAM_ACTION_TRANSCODE) &&
ret->audio_streams[i].normalize)
{
ret->total_passes = 2;
return;
}
}
for(i = 0; i < ret->num_video_streams; i++)
{
if((ret->video_streams[i].com.action == STREAM_ACTION_TRANSCODE) &&
ret->video_streams[i].twopass)
{
ret->total_passes = 2;
return;
}
}
}
static int setup_pass(bg_transcoder_t * ret)
{
int i;
int result = 0;
for(i = 0; i < ret->num_audio_streams; i++)
{
/* Reset the samples already decoded */
ret->audio_streams[i].samples_read = 0;
if(ret->audio_streams[i].com.action == STREAM_ACTION_COPY)
{
if(ret->pass == ret->total_passes)
{
ret->audio_streams[i].com.do_copy = 1;
result = 1;
}
else
ret->audio_streams[i].com.do_copy = 0;
}
else if(ret->audio_streams[i].com.action != STREAM_ACTION_TRANSCODE)
{
ret->audio_streams[i].com.do_decode = 0;
ret->audio_streams[i].com.do_encode = 0;
}
else if(ret->pass == ret->total_passes)
{
ret->audio_streams[i].com.do_decode = 1;
ret->audio_streams[i].com.do_encode = 1;
result = 1;
}
else if((ret->pass == 1) && (ret->audio_streams[i].normalize))
{
ret->audio_streams[i].com.do_decode = 1;
ret->audio_streams[i].com.do_encode = 0;
result = 1;
}
else
{
ret->audio_streams[i].com.do_decode = 0;
ret->audio_streams[i].com.do_encode = 0;
}
if(ret->audio_streams[i].com.do_decode ||
ret->audio_streams[i].com.do_copy)
ret->audio_streams[i].com.status = STREAM_STATE_ON;
else
ret->audio_streams[i].com.status = STREAM_STATE_OFF;
}
for(i = 0; i < ret->num_video_streams; i++)
{
if(ret->video_streams[i].com.action == STREAM_ACTION_COPY)
{
if(ret->pass == ret->total_passes)
{
ret->video_streams[i].com.do_copy = 1;
result = 1;
}
else
ret->video_streams[i].com.do_copy = 0;
}
else if(ret->video_streams[i].com.action != STREAM_ACTION_TRANSCODE)
{
ret->video_streams[i].com.do_decode = 0;
ret->video_streams[i].com.do_encode = 0;
}
else if((ret->pass == ret->total_passes) || ret->video_streams[i].twopass)
{
ret->video_streams[i].com.do_decode = 1;
ret->video_streams[i].com.do_encode = 1;
result = 1;
}
else
{
ret->video_streams[i].com.do_decode = 0;
ret->video_streams[i].com.do_encode = 0;
}
if(ret->video_streams[i].com.do_decode ||
ret->video_streams[i].com.do_copy)
ret->video_streams[i].com.status = STREAM_STATE_ON;
else
ret->video_streams[i].com.status = STREAM_STATE_OFF;
}
/* Subtitles */
for(i = 0; i < ret->num_subtitle_text_streams; i++)
{
switch(ret->subtitle_text_streams[i].com.com.action)
{
case STREAM_ACTION_FORGET:
ret->subtitle_text_streams[i].com.com.do_decode = 0;
ret->subtitle_text_streams[i].com.com.do_encode = 0;
break;
case STREAM_ACTION_BLEND:
if((ret->video_streams[ret->subtitle_text_streams[i].com.video_stream].twopass) ||
(ret->pass == ret->total_passes))
{
ret->subtitle_text_streams[i].com.com.do_decode = 1;
ret->subtitle_text_streams[i].com.com.do_encode = 0;
}
else
{
ret->subtitle_text_streams[i].com.com.do_decode = 0;
ret->subtitle_text_streams[i].com.com.do_encode = 0;
}
result = 1;
break;
case STREAM_ACTION_TRANSCODE:
case STREAM_ACTION_TRANSCODE_OVERLAY:
if(ret->pass == ret->total_passes)
{
ret->subtitle_text_streams[i].com.com.do_decode = 1;
ret->subtitle_text_streams[i].com.com.do_encode = 1;
result = 1;
}
else
{
ret->subtitle_text_streams[i].com.com.do_decode = 0;
ret->subtitle_text_streams[i].com.com.do_encode = 0;
}
break;
default:
bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Subtitle stream cannot be handled");
ret->subtitle_text_streams[i].com.com.do_decode = 0;
ret->subtitle_text_streams[i].com.com.do_encode = 0;
ret->subtitle_text_streams[i].com.com.action = STREAM_ACTION_FORGET;
break;
}
if(!ret->subtitle_text_streams[i].com.com.do_decode)
ret->subtitle_text_streams[i].com.com.status = STREAM_STATE_OFF;
else
ret->subtitle_text_streams[i].com.com.status = STREAM_STATE_ON;
}
for(i = 0; i < ret->num_subtitle_overlay_streams; i++)
{
switch(ret->subtitle_overlay_streams[i].com.action)
{
case STREAM_ACTION_FORGET:
ret->subtitle_overlay_streams[i].com.do_decode = 0;
ret->subtitle_overlay_streams[i].com.do_encode = 0;
break;
case STREAM_ACTION_BLEND:
if(ret->video_streams[ret->subtitle_overlay_streams[i].video_stream].com.do_encode)
{
ret->subtitle_overlay_streams[i].com.do_decode = 1;
ret->subtitle_overlay_streams[i].com.do_encode = 1;
result = 1;
}
else
{
ret->subtitle_overlay_streams[i].com.do_decode = 0;
ret->subtitle_overlay_streams[i].com.do_encode = 0;
}
break;
case STREAM_ACTION_TRANSCODE:
if(ret->pass == ret->total_passes)
{
ret->subtitle_overlay_streams[i].com.do_decode = 1;
ret->subtitle_overlay_streams[i].com.do_encode = 1;
result = 1;
}
else
{
ret->subtitle_overlay_streams[i].com.do_decode = 0;
ret->subtitle_overlay_streams[i].com.do_encode = 0;
}
break;
default:
bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Subtitle stream cannot be handled");
ret->subtitle_overlay_streams[i].com.do_decode = 0;
ret->subtitle_overlay_streams[i].com.do_encode = 0;
ret->subtitle_overlay_streams[i].com.action = STREAM_ACTION_FORGET;
break;
}
if(!ret->subtitle_overlay_streams[i].com.do_decode)
ret->subtitle_overlay_streams[i].com.status = STREAM_STATE_OFF;
else
ret->subtitle_overlay_streams[i].com.status = STREAM_STATE_ON;
}
return result;
}
static int create_output_file_cb(void * priv, const char * filename)
{
bg_transcoder_t * t = priv;
if(!strcmp(filename, t->location))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Input and output are the same file");
return 0;
}
if(t->pass == t->total_passes)
add_output_file(t, filename);
return 1;
}
static void create_encoder(bg_transcoder_t * ret)
{
ret->enc = bg_encoder_create(ret->plugin_reg,
NULL,
ret->transcoder_track,
BG_STREAM_AUDIO |
BG_STREAM_VIDEO |
BG_STREAM_SUBTITLE_TEXT |
BG_STREAM_SUBTITLE_OVERLAY,
BG_PLUGIN_FILE);
}
static int init_encoder(bg_transcoder_t * ret)
{
int i;
char * tmp_string;
ret->cb.create_output_file = create_output_file_cb;
ret->cb.data = ret;
bg_encoder_set_callbacks(ret->enc, &ret->cb);
tmp_string = bg_sprintf("%s/%s", ret->output_directory, ret->name);
bg_encoder_open(ret->enc, tmp_string,
&ret->metadata,
ret->transcoder_track->chapter_list);
free(tmp_string);
for(i = 0; i < ret->num_audio_streams; i++)
{
if(ret->audio_streams[i].com.do_decode) /* If we don't encode we still need to open the
plugin to get the final output format */
add_audio_stream(&ret->audio_streams[i], ret);
else if(ret->audio_streams[i].com.do_copy)
add_audio_stream_compressed(&ret->audio_streams[i], ret);
}
/* Video streams: Must be added before the subtitle streams, because we need to know
the output format (at least the output size) of the stream */
for(i = 0; i < ret->num_video_streams; i++)
{
if(ret->video_streams[i].com.do_decode)/* If we don't encode we still need to open the
plugin to get the final output format */
{
add_video_stream(&ret->video_streams[i], ret);
set_video_pass(ret, i);
}
else if(ret->video_streams[i].com.do_copy)
add_video_stream_compressed(&ret->video_streams[i], ret);
}
for(i = 0; i < ret->num_subtitle_text_streams; i++)
{
if(!ret->subtitle_text_streams[i].com.com.do_encode)
continue;
add_subtitle_text_stream(&ret->subtitle_text_streams[i],
&ret->transcoder_track->subtitle_text_streams[i], ret);
}
for(i = 0; i < ret->num_subtitle_overlay_streams; i++)
{
if(!ret->subtitle_overlay_streams[i].com.do_encode)
continue;
add_subtitle_overlay_stream(&ret->subtitle_overlay_streams[i],
&ret->transcoder_track->subtitle_overlay_streams[i], ret);
}
return bg_encoder_start(ret->enc);
}
static int init_audio_converter(audio_stream_t * ret, bg_transcoder_t * t)
{
gavl_audio_options_t * opt;
bg_encoder_get_audio_format(t->enc,
ret->com.out_index,
&ret->out_format);
gavl_audio_format_copy(&ret->pipe_format, &ret->out_format);
if(ret->options.force_format != GAVL_SAMPLE_NONE)
ret->pipe_format.sample_format = ret->options.force_format;
bg_audio_filter_chain_set_out_format(ret->fc, &ret->pipe_format);
/* Initialize output converter */
opt = gavl_audio_converter_get_options(ret->cnv_out);
gavl_audio_options_copy(opt, ret->options.opt);
ret->do_convert_out = gavl_audio_converter_init(ret->cnv_out,
&ret->pipe_format,
&ret->out_format);
/* Create frames (could be left from the previous pass) */
if(ret->do_convert_out && !ret->pipe_frame)
ret->pipe_frame = gavl_audio_frame_create(&ret->pipe_format);
if(!ret->out_frame)
ret->out_frame = gavl_audio_frame_create(&ret->out_format);
return 1;
}
static int init_video_converter(video_stream_t * ret, bg_transcoder_t * t)
{
bg_encoder_get_video_format(t->enc,
ret->com.out_index, &ret->out_format);
/* Initialize converter */
bg_video_filter_chain_set_out_format(ret->fc, &ret->out_format);
/* Create frames */
if(!ret->frame)
{
ret->frame = gavl_video_frame_create(&ret->out_format);
// fprintf(stderr, "Created video frame %p %p\n", ret->frame, ret->frame->planes[0]);
}
gavl_video_frame_clear(ret->frame, &ret->out_format);
return 1;
}
static void subtitle_init_blend(subtitle_stream_t * ss, video_stream_t * vs)
{
subtitle_text_stream_t * sst;
vs->subtitle_streams =
realloc(vs->subtitle_streams,
sizeof(*(vs->subtitle_streams))*(vs->num_subtitle_streams+1));
vs->subtitle_streams[vs->num_subtitle_streams] = ss;
vs->num_subtitle_streams++;
/* Check whether to initialize the text renderer */
if(ss->com.type == STREAM_TYPE_SUBTITLE_TEXT)
{
sst = (subtitle_text_stream_t*)ss;
bg_text_renderer_init(sst->textrenderer,
&vs->out_format,
&ss->in_format);
}
if(!ss->ovl1.frame) ss->ovl1.frame = gavl_video_frame_create(&ss->in_format);
if(!ss->ovl2.frame) ss->ovl2.frame = gavl_video_frame_create(&ss->in_format);
ss->current_ovl = &ss->ovl1;
ss->next_ovl = &ss->ovl2;
gavl_overlay_blend_context_init(ss->blend_context,
&vs->out_format, &ss->in_format);
gavl_video_format_copy(&ss->out_format, &vs->out_format);
}
static void subtitle_init_encode_text(subtitle_text_stream_t * ss,
bg_transcoder_t * t)
{
/* Nothing to do here for now */
bg_encoder_get_subtitle_text_timescale(t->enc,
ss->com.com.out_index,
&ss->com.out_format.timescale);
}
static void subtitle_init_encode_overlay(subtitle_stream_t * ss,
bg_transcoder_t * t)
{
subtitle_text_stream_t * sst;
bg_encoder_get_subtitle_overlay_format(t->enc,
ss->com.out_index,
&ss->out_format);
/* Check whether to initialize the text renderer */
if(ss->com.type == STREAM_TYPE_SUBTITLE_TEXT)
{
sst = (subtitle_text_stream_t*)ss;
bg_text_renderer_init(sst->textrenderer,
&ss->out_format,
&ss->in_format);
// gavl_video_format_dump(&ss->in_format);
// gavl_video_format_dump(&ss->out_format);
}
ss->do_convert = gavl_video_converter_init(ss->cnv, &ss->in_format,
&ss->out_format);
if(!ss->ovl1.frame)
ss->ovl1.frame = gavl_video_frame_create(&ss->in_format);
if(ss->do_convert)
{
if(!ss->ovl2.frame)
ss->ovl2.frame = gavl_video_frame_create(&ss->out_format);
}
}
static int init_converters(bg_transcoder_t * ret)
{
int i;
for(i = 0; i < ret->num_audio_streams; i++)
{
if(ret->audio_streams[i].com.do_copy)
{
gavl_audio_format_copy(&ret->audio_streams[i].out_format,
&ret->audio_streams[i].in_format);
continue;
}
if(!ret->audio_streams[i].com.do_decode)
continue;
if(!init_audio_converter(&ret->audio_streams[i], ret))
return 0;
}
for(i = 0; i < ret->num_video_streams; i++)
{
if(ret->video_streams[i].com.do_copy)
{
gavl_video_format_copy(&ret->video_streams[i].out_format,
&ret->video_streams[i].in_format);
continue;
}
if(!ret->video_streams[i].com.do_decode)
continue;
if(!init_video_converter(&ret->video_streams[i], ret))
return 0;
if(ret->video_streams[i].subtitle_streams)
{
free(ret->video_streams[i].subtitle_streams);
ret->video_streams[i].subtitle_streams = NULL;
ret->video_streams[i].num_subtitle_streams = 0;
}
}
for(i = 0; i < ret->num_subtitle_text_streams; i++)
{
if(!ret->subtitle_text_streams[i].com.com.do_decode)
continue;
switch(ret->subtitle_text_streams[i].com.com.action)
{
case STREAM_ACTION_BLEND:
subtitle_init_blend((subtitle_stream_t*)(&ret->subtitle_text_streams[i]),
&ret->video_streams[ret->subtitle_text_streams[i].com.video_stream]);
break;
case STREAM_ACTION_TRANSCODE:
subtitle_init_encode_text((&ret->subtitle_text_streams[i]), ret);
break;
case STREAM_ACTION_TRANSCODE_OVERLAY:
subtitle_init_encode_overlay((subtitle_stream_t*)&ret->subtitle_text_streams[i], ret);
break;
}
}
for(i = 0; i < ret->num_subtitle_overlay_streams; i++)
{
if(!ret->subtitle_overlay_streams[i].com.do_decode)
continue;
switch(ret->subtitle_overlay_streams[i].com.action)
{
case STREAM_ACTION_BLEND:
subtitle_init_blend(&ret->subtitle_overlay_streams[i],
&ret->video_streams[ret->subtitle_overlay_streams[i].video_stream]);
break;
case STREAM_ACTION_TRANSCODE:
subtitle_init_encode_overlay(&ret->subtitle_overlay_streams[i], ret);
break;
}
}
return 1;
}
static void init_normalize(bg_transcoder_t * ret)
{
int i;
double absolute;
double volume_dB = 0.0;
for(i = 0; i < ret->num_audio_streams; i++)
{
if(!ret->audio_streams[i].normalize)
continue;
if(ret->pass == 1)
{
gavl_peak_detector_set_format(ret->audio_streams[i].peak_detector,
&ret->audio_streams[i].out_format);
}
else if(ret->pass == ret->total_passes)
{
gavl_volume_control_set_format(ret->audio_streams[i].volume_control,
&ret->audio_streams[i].pipe_format);
/* Set the volume */
gavl_peak_detector_get_peak(ret->audio_streams[i].peak_detector,
NULL, NULL, &absolute);
if(absolute == 0.0)
{
gavl_volume_control_set_volume(ret->audio_streams[i].volume_control, volume_dB);
bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Zero peaks detected (silent file?). Disabling normalization.");
}
else
{
volume_dB = - 20.0 * log10(absolute);
gavl_volume_control_set_volume(ret->audio_streams[i].volume_control, volume_dB);
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Correcting volume by %.2f dB", volume_dB);
}
}
}
}
int bg_transcoder_init(bg_transcoder_t * ret,
bg_plugin_registry_t * plugin_reg,
bg_transcoder_track_t * track)
{
ret->plugin_reg = plugin_reg;
ret->transcoder_track = track;
/* Initialize encoder info */
/* Set general parameter */
bg_cfg_section_apply(track->general_section,
track->general_parameters,
set_parameter_general,
ret);
/* Set Metadata */
bg_cfg_section_apply(track->metadata_section,
track->metadata_parameters,
bg_metadata_set_parameter,
&ret->metadata);
/* Postprocess only: Send messages and return */
if(ret->pp_only)
{
ret->output_filename = bg_strdup(ret->output_filename, ret->location);
send_init_messages(ret);
ret->state = STREAM_STATE_FINISHED;
return 1;
}
/* Open input plugin */
if(!open_input(ret))
goto fail;
create_encoder(ret);
create_streams(ret, track);
check_compressed(ret);
/* Check how many passes we must do */
check_passes(ret);
ret->pass = 1;
/* Set up this pass */
if(!setup_pass(ret))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "No stream to encode");
goto fail;
}
/* Start input plugin */
if(!start_input(ret))
goto fail;
set_input_formats(ret);
/* Set up the streams in the encoders */
if(!init_encoder(ret))
goto fail;
/* Set formats */
if(!init_converters(ret))
goto fail;
/* Init normalizing */
init_normalize(ret);
/* Send messages */
send_init_messages(ret);
/* Set duration */
if(ret->set_start_time && ret->set_end_time)
ret->duration = ret->end_time - ret->start_time;
else if(ret->set_start_time)
ret->duration = ret->track_info->duration - ret->start_time;
else if(ret->set_end_time)
ret->duration = ret->end_time;
else
ret->duration = ret->track_info->duration;
/* Start timer */
gavl_timer_start(ret->timer);
ret->state = TRANSCODER_STATE_RUNNING;
return 1;
fail:
return 0;
}
#define FREE_STR(str) if(str)free(str);
static void cleanup_stream(stream_t * s)
{
gavl_compression_info_free(&s->ci);
gavl_packet_free(&s->packet);
gavl_metadata_free(&s->m);
}
static void cleanup_audio_stream(audio_stream_t * s)
{
cleanup_stream(&s->com);
/* Free all resources */
if(s->out_frame)
gavl_audio_frame_destroy(s->out_frame);
if(s->pipe_frame)
gavl_audio_frame_destroy(s->pipe_frame);
if(s->cnv_out)
gavl_audio_converter_destroy(s->cnv_out);
if(s->fc)
bg_audio_filter_chain_destroy(s->fc);
if(s->volume_control)
gavl_volume_control_destroy(s->volume_control);
if(s->peak_detector)
gavl_peak_detector_destroy(s->peak_detector);
bg_gavl_audio_options_free(&s->options);
}
static void cleanup_video_stream(video_stream_t * s)
{
cleanup_stream(&s->com);
/* Free all resources */
if(s->frame)
{
// fprintf(stderr, "gavl_video_frame_destroy %p %p\n", s->frame, s->frame->planes[0]);
gavl_video_frame_destroy(s->frame);
}
if(s->fc)
bg_video_filter_chain_destroy(s->fc);
if(s->subtitle_streams)
free(s->subtitle_streams);
/* Delete stats file */
if(s->stats_file)
{
remove(s->stats_file);
free(s->stats_file);
}
bg_gavl_video_options_free(&s->options);
}
static void cleanup_subtitle_stream(subtitle_stream_t * s)
{
subtitle_text_stream_t * ts;
cleanup_stream(&s->com);
if(s->ovl1.frame)
gavl_video_frame_destroy(s->ovl1.frame);
if(s->ovl2.frame)
gavl_video_frame_destroy(s->ovl2.frame);
if(s->blend_context)
gavl_overlay_blend_context_destroy(s->blend_context);
if(s->com.type == STREAM_TYPE_SUBTITLE_TEXT)
{
ts = (subtitle_text_stream_t*)s;
if(ts->textrenderer) bg_text_renderer_destroy(ts->textrenderer);
}
}
static void reset_streams(bg_transcoder_t * t)
{
int i;
for(i = 0; i < t->num_audio_streams; i++)
{
t->audio_streams[i].samples_read = 0;
t->audio_streams[i].com.time = 0;
}
for(i = 0; i < t->num_video_streams; i++)
{
t->video_streams[i].frames_written = 0;
t->video_streams[i].com.time = 0;
}
}
static void close_input(bg_transcoder_t * t)
{
if(t->pp_only)
return;
if(t->in_plugin->stop)
t->in_plugin->stop(t->in_handle->priv);
t->in_plugin->close(t->in_handle->priv);
bg_plugin_unref(t->in_handle);
t->in_handle = NULL;
}
/* Switch to next pass */
static void next_pass(bg_transcoder_t * t)
{
char * tmp_string;
bg_encoder_destroy(t->enc, 1);
close_input(t);
t->pass++;
/* Reset stream times */
reset_streams(t);
gavl_timer_stop(t->timer);
gavl_timer_set(t->timer, 0);
t->percentage_done = 0.0;
t->time = 0;
t->last_seconds = 0.0;
open_input(t);
create_encoder(t);
/* Decide, which stream will be en/decoded*/
setup_pass(t);
start_input(t);
/* Some streams don't have this already */
set_input_formats(t);
/* Initialize encoding plugins */
init_encoder(t);
/* Set formats */
init_converters(t);
/* Init normalizing */
init_normalize(t);
/* Send message */
tmp_string = bg_sprintf(TR("Transcoding %s [Track %d, Pass %d/%d]"), t->location, t->track+1, t->pass, t->total_passes);
bg_transcoder_send_msg_start(t->message_queues, tmp_string);
bg_log_notranslate(BG_LOG_INFO, LOG_DOMAIN, "%s", tmp_string);
free(tmp_string);
gavl_timer_start(t->timer);
}
/*
* Do one iteration.
* If return value is FALSE, we are done
*/
int bg_transcoder_iteration(bg_transcoder_t * t)
{
int i;
gavl_time_t time;
stream_t * stream = NULL;
gavl_time_t real_time;
double real_seconds;
double remaining_seconds;
int done = 1;
if(t->pp_only)
{
t->state = TRANSCODER_STATE_FINISHED;
bg_transcoder_send_msg_finished(t->message_queues);
log_transcoding_time(t);
return 0;
}
time = GAVL_TIME_MAX;
/* Find the stream with the smallest time */
for(i = 0; i < t->num_audio_streams; i++)
{
/* Check for the most urgent audio/video stream */
if(t->audio_streams[i].com.status != STREAM_STATE_ON)
continue;
done = 0;
if(t->audio_streams[i].com.time < time)
{
time = t->audio_streams[i].com.time;
stream = &t->audio_streams[i].com;
}
}
for(i = 0; i < t->num_video_streams; i++)
{
if(t->video_streams[i].com.status != STREAM_STATE_ON)
continue;
done = 0;
if(t->video_streams[i].com.time < time)
{
time = t->video_streams[i].com.time;
stream = &t->video_streams[i].com;
}
}
/* Detecting EOF for subtitles doesn't work when we transcode until
a specified time */
if(t->end_time == GAVL_TIME_UNDEFINED)
{
for(i = 0; i < t->num_subtitle_text_streams; i++)
{
if(t->subtitle_text_streams[i].com.com.status != STREAM_STATE_ON)
continue;
done = 0;
}
for(i = 0; i < t->num_subtitle_overlay_streams; i++)
{
if(t->subtitle_overlay_streams[i].com.status != STREAM_STATE_ON)
continue;
done = 0;
}
}
if(done)
{
if(t->pass < t->total_passes)
{
log_transcoding_time(t);
next_pass(t);
return 1;
}
else
{
t->state = TRANSCODER_STATE_FINISHED;
bg_transcoder_send_msg_finished(t->message_queues);
log_transcoding_time(t);
return 0;
}
}
/* Do the actual transcoding */
/* Subtitle iteration must always be done */
if(!subtitle_iteration(t))
{
t->state = TRANSCODER_STATE_ERROR;
bg_transcoder_send_msg_error(t->message_queues);
return 0;
}
if(stream)
{
if(stream->type == STREAM_TYPE_AUDIO)
{
if(!audio_iteration((audio_stream_t*)stream, t))
{
t->state = TRANSCODER_STATE_ERROR;
bg_transcoder_send_msg_error(t->message_queues);
return 0;
}
}
else if(stream->type == STREAM_TYPE_VIDEO)
{
if(!video_iteration((video_stream_t*)stream, t))
{
t->state = TRANSCODER_STATE_ERROR;
bg_transcoder_send_msg_error(t->message_queues);
return 0;
}
}
if(stream->time > t->time)
t->time = stream->time;
}
/* Update status */
real_time = gavl_timer_get(t->timer);
real_seconds = gavl_time_to_seconds(real_time);
t->percentage_done =
gavl_time_to_seconds(t->time) /
gavl_time_to_seconds(t->duration);
if(t->percentage_done < 0.0)
t->percentage_done = 0.0;
if(t->percentage_done > 1.0)
t->percentage_done = 1.0;
if(t->percentage_done == 0.0)
remaining_seconds = 0.0;
else
{
remaining_seconds = real_seconds *
(1.0 / t->percentage_done - 1.0);
}
t->remaining_time =
gavl_seconds_to_time(remaining_seconds);
if(real_seconds - t->last_seconds > 1.0)
{
bg_transcoder_send_msg_progress(t->message_queues, t->percentage_done, t->remaining_time);
t->last_seconds = real_seconds;
}
return 1;
}
void bg_transcoder_destroy(bg_transcoder_t * t)
{
int i;
int do_delete = 0;
char tmp_string[128];
if((t->state == TRANSCODER_STATE_RUNNING) && t->delete_incomplete &&
!t->is_url)
do_delete = 1;
else if(t->state == TRANSCODER_STATE_INIT)
do_delete = 1;
else if(t->state == TRANSCODER_STATE_ERROR)
do_delete = 1;
/* Close all encoders so the files are finished */
if(t->enc)
bg_encoder_destroy(t->enc, do_delete);
/* Send created files to gmerlin */
if((t->state != TRANSCODER_STATE_RUNNING) && !do_delete)
{
send_file_messages(t);
}
free_output_files(t);
/* Cleanup streams */
for(i = 0; i < t->num_video_streams; i++)
{
if((t->video_streams[i].com.action != STREAM_ACTION_FORGET) && !do_delete)
{
sprintf(tmp_string, "%" PRId64, t->video_streams[i].frames_written);
bg_log(BG_LOG_INFO, LOG_DOMAIN,
"Video stream %d: Transcoded %s frames", i+1,
tmp_string);
}
cleanup_video_stream(&t->video_streams[i]);
}
for(i = 0; i < t->num_audio_streams; i++)
{
if((t->audio_streams[i].com.action != STREAM_ACTION_FORGET) && !do_delete)
{
sprintf(tmp_string, "%" PRId64, t->audio_streams[i].samples_read);
bg_log(BG_LOG_INFO, LOG_DOMAIN,
"Audio stream %d: Transcoded %s samples", i+1,
tmp_string);
}
cleanup_audio_stream(&t->audio_streams[i]);
}
for(i = 0; i < t->num_subtitle_text_streams; i++)
{
cleanup_subtitle_stream((subtitle_stream_t*)(&t->subtitle_text_streams[i]));
}
for(i = 0; i < t->num_subtitle_overlay_streams; i++)
{
cleanup_subtitle_stream(&t->subtitle_overlay_streams[i]);
}
if(t->audio_streams)
free(t->audio_streams);
if(t->video_streams)
free(t->video_streams);
if(t->subtitle_text_streams)
free(t->subtitle_text_streams);
if(t->subtitle_overlay_streams)
free(t->subtitle_overlay_streams);
/* Free metadata */
gavl_metadata_free(&t->metadata);
/* Close and destroy the input plugin */
close_input(t);
/* Free rest */
FREE_STR(t->name);
FREE_STR(t->location);
FREE_STR(t->plugin);
FREE_STR(t->output_directory);
FREE_STR(t->output_path);
FREE_STR(t->subdir);
FREE_STR(t->output_filename);
gavl_timer_destroy(t->timer);
bg_msg_queue_list_destroy(t->message_queues);
pthread_mutex_destroy(&t->stop_mutex);
free(t);
}
static void * thread_func(void * data)
{
int do_stop;
bg_transcoder_t * t = (bg_transcoder_t*)data;
while(bg_transcoder_iteration(t))
{
pthread_mutex_lock(&t->stop_mutex);
do_stop = t->do_stop;
pthread_mutex_unlock(&t->stop_mutex);
if(do_stop)
break;
}
return NULL;
}
void bg_transcoder_run(bg_transcoder_t * t)
{
pthread_create(&t->thread, NULL, thread_func, t);
}
void bg_transcoder_stop(bg_transcoder_t * t)
{
pthread_mutex_lock(&t->stop_mutex);
t->do_stop = 1;
pthread_mutex_unlock(&t->stop_mutex);
}
void bg_transcoder_finish(bg_transcoder_t * t)
{
pthread_join(t->thread, NULL);
}
gmerlin-1.2.0~dfsg/lib/thumbnail.c 0000644 0001750 0001750 00000016255 11764363410 017004 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include /* stat() */
#include /* stat() */
#include /* stat() */
#include
#include
#include
#include
#include
#include
#include
#define LOG_DOMAIN "thumbnails"
static int thumbnail_up_to_date(const char * thumbnail_file,
bg_plugin_registry_t * plugin_reg,
gavl_video_frame_t ** frame,
gavl_video_format_t * format,
int64_t mtime)
{
int i;
gavl_metadata_t metadata;
int64_t test_mtime;
int ret = 0;
const char * val;
memset(&metadata, 0, sizeof(metadata));
memset(format, 0, sizeof(*format));
*frame = bg_plugin_registry_load_image(plugin_reg,
thumbnail_file,
format,
&metadata);
i = 0;
val = gavl_metadata_get(&metadata, "Thumb::MTime");
if(val)
{
test_mtime = strtoll(val, NULL, 10);
if(mtime == test_mtime)
ret = 1;
}
gavl_metadata_free(&metadata);
return ret;
}
static void make_fail_thumbnail(const char * gml,
const char * thumb_filename,
bg_plugin_registry_t * plugin_reg,
int64_t mtime)
{
gavl_video_format_t format;
gavl_video_frame_t * frame;
gavl_metadata_t metadata;
char * tmp_string;
memset(&format, 0, sizeof(format));
gavl_metadata_init(&metadata);
format.image_width = 1;
format.image_height = 1;
format.frame_width = 1;
format.frame_height = 1;
format.pixel_width = 1;
format.pixel_height = 1;
format.pixelformat = GAVL_RGBA_32;
frame = gavl_video_frame_create(&format);
gavl_video_frame_clear(frame, &format);
tmp_string = bg_string_to_uri(gml, -1);
gavl_metadata_set_nocpy(&metadata, "Thumb::URI", tmp_string);
tmp_string = bg_sprintf("%"PRId64, mtime);
gavl_metadata_set_nocpy(&metadata, "Thumb::MTime", tmp_string);
bg_plugin_registry_save_image(plugin_reg,
thumb_filename,
frame,
&format, &metadata);
gavl_metadata_free(&metadata);
gavl_video_frame_destroy(frame);
}
int bg_get_thumbnail(const char * gml,
bg_plugin_registry_t * plugin_reg,
char ** thumbnail_filename_ret,
gavl_video_frame_t ** frame_ret,
gavl_video_format_t * format_ret)
{
bg_subprocess_t * sp;
char hash[33];
char * home_dir;
char * thumb_filename_normal = NULL;
char * thumb_filename_fail = NULL;
char * thumbs_dir_normal = NULL;
char * thumbs_dir_fail = NULL;
char * command;
int ret = 0;
gavl_video_frame_t * frame = NULL;
gavl_video_format_t format;
struct stat st;
if(stat(gml, &st))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot stat %s: %s",
gml, strerror(errno));
return 0;
}
/* Get and create directories */
home_dir = getenv("HOME");
if(!home_dir)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot get home directory");
return 0;
}
thumbs_dir_normal = bg_sprintf("%s/.thumbnails/normal", home_dir);
thumbs_dir_fail = bg_sprintf("%s/.thumbnails/fail/gmerlin", home_dir);
if(!bg_ensure_directory(thumbs_dir_normal) ||
!bg_ensure_directory(thumbs_dir_fail))
goto done;
bg_get_filename_hash(gml, hash);
thumb_filename_normal = bg_sprintf("%s/%s.png", thumbs_dir_normal, hash);
thumb_filename_fail = bg_sprintf("%s/%s.png", thumbs_dir_fail, hash);
if(access(thumb_filename_normal, R_OK)) /* Thumbnail file not present */
{
/* Check if there is a failed thumbnail */
if(!access(thumb_filename_fail, R_OK))
{
if(thumbnail_up_to_date(thumb_filename_fail, plugin_reg,
&frame, &format, st.st_mtime))
{
gavl_video_frame_destroy(frame);
frame = NULL;
goto done;
}
else /* Failed thumbnail is *not* up to date, remove it */
{
remove(thumb_filename_fail);
gavl_video_frame_destroy(frame);
frame = NULL;
}
}
/* else: Regenerate */
}
else /* Thumbnail file present */
{
/* Check if the thumbnail is recent */
if(thumbnail_up_to_date(thumb_filename_normal, plugin_reg,
&frame, &format, st.st_mtime))
{
if(thumbnail_filename_ret)
{
*thumbnail_filename_ret = thumb_filename_normal;
thumb_filename_normal = NULL;
}
if(frame_ret)
{
*frame_ret = frame;
frame = NULL;
}
if(format_ret)
gavl_video_format_copy(format_ret, &format);
ret = 1;
goto done;
}
else
{
remove(thumb_filename_normal);
gavl_video_frame_destroy(frame);
frame = NULL;
}
/* else: Regenerate */
}
/* Regenerate */
command = bg_sprintf("gmerlin-video-thumbnailer \"%s\" %s", gml, thumb_filename_normal);
sp = bg_subprocess_create(command, 0, 0, 0);
bg_subprocess_close(sp);
free(command);
if(!access(thumb_filename_normal, R_OK)) /* Thumbnail generation succeeded */
{
if(frame_ret && format_ret)
{
*frame_ret = bg_plugin_registry_load_image(plugin_reg,
thumb_filename_normal,
format_ret,
NULL);
}
if(thumbnail_filename_ret)
{
*thumbnail_filename_ret = thumb_filename_normal;
thumb_filename_normal = NULL;
}
ret = 1;
goto done;
}
else /* Thumbnail generation failed */
{
make_fail_thumbnail(gml, thumb_filename_fail,
plugin_reg,
st.st_mtime);
}
done:
free(thumbs_dir_normal);
free(thumbs_dir_fail);
if(thumb_filename_normal)
free(thumb_filename_normal);
if(thumb_filename_fail)
free(thumb_filename_fail);
if(frame)
gavl_video_frame_destroy(frame);
return ret;
}
gmerlin-1.2.0~dfsg/lib/socket.c 0000644 0001750 0001750 00000030554 11764363410 016307 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#ifdef HAVE_SYS_SELECT_H
#include
#else
#include
#include
#endif
#include
#include /* gethostbyname */
#include
//#include
#include
#include
#include
#include
#include
#include
#include
#define LOG_DOMAIN "tcpsocket"
#if !HAVE_DECL_MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
/* Opaque address structure so we can support IPv6 in the future */
struct bg_host_address_s
{
struct addrinfo * addr;
};
bg_host_address_t * bg_host_address_create()
{
bg_host_address_t * ret;
ret = calloc(1, sizeof(*ret));
return ret;
}
void bg_host_address_destroy(bg_host_address_t * a)
{
freeaddrinfo(a->addr);
free(a);
}
static int create_socket(int domain, int type, int protocol)
{
int ret;
#if HAVE_DECL_SO_NOSIGPIPE // OSX
int value = 1;
#endif
ret = socket(domain, type, protocol);
#if HAVE_DECL_SO_NOSIGPIPE // OSX
if(ret < 0)
return ret;
if(setsockopt(ret, SOL_SOCKET, SO_NOSIGPIPE, &value,
sizeof(int)) == -1)
return -1;
#endif
return ret;
}
/* */
static void address_set_port(struct addrinfo * info, int port)
{
while(info)
{
switch(info->ai_family)
{
case AF_INET:
{
struct sockaddr_in * addr;
addr = (struct sockaddr_in*)info->ai_addr;
addr->sin_port = htons(port);
}
break;
case AF_INET6:
{
struct sockaddr_in6 * addr;
addr = (struct sockaddr_in6*)info->ai_addr;
addr->sin6_port = htons(port);
}
break;
default:
break;
}
info = info->ai_next;
}
}
static struct addrinfo * hostbyname(const char * hostname, int port, int socktype)
{
int err;
struct in_addr ipv4_addr;
struct addrinfo hints;
struct addrinfo * ret;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = socktype; // SOCK_STREAM, SOCK_DGRAM
hints.ai_protocol = 0; // 0
hints.ai_flags = 0;
/* prevent DNS lookup for numeric IP addresses */
if(inet_aton(hostname, &ipv4_addr))
hints.ai_flags |= AI_NUMERICHOST;
if((err = getaddrinfo(hostname, NULL /* service */,
&hints, &ret)))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot resolve address of %s: %s",
hostname, gai_strerror(err));
return NULL;
}
#if 0
if(ret[0].ai_addr->sa_family == AF_INET)
fprintf(stderr, "Got IPV4 address\n");
else if(ret[0].ai_addr->sa_family == AF_INET6)
fprintf(stderr, "Got IPV6 address\n");
#endif
address_set_port(ret, port);
return ret;
}
int bg_host_address_set(bg_host_address_t * a, const char * hostname,
int port, int socktype)
{
a->addr = hostbyname(hostname, port, socktype);
// if(!hostbyname(a, hostname))
// return 0;
// a->port = port;
if(!a->addr)
return 0;
return 1;
}
/* Client connection (stream oriented) */
int bg_socket_connect_inet(bg_host_address_t * a, int milliseconds)
{
int ret = -1;
int err;
socklen_t err_len;
struct timeval timeout;
fd_set write_fds;
/* Create the socket */
if((ret = create_socket(a->addr->ai_family, SOCK_STREAM, 0)) < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot create socket");
return -1;
}
/* Set nonblocking mode */
if(fcntl(ret, F_SETFL, O_NONBLOCK) < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot set nonblocking mode");
return -1;
}
/* Connect the thing */
if(connect(ret, a->addr->ai_addr, a->addr->ai_addrlen)<0)
{
if(errno == EINPROGRESS)
{
timeout.tv_sec = milliseconds / 1000;
timeout.tv_usec = 1000 * (milliseconds % 1000);
FD_ZERO (&write_fds);
FD_SET (ret, &write_fds);
err = select(ret+1, NULL, &write_fds, NULL,&timeout);
if(!err)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Connection timed out");
return -1;
}
else if(err < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "select() failed on connect");
return -1;
}
}
else
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Connecting failed: %s", strerror(errno));
return -1;
}
}
/* Check for error */
err_len = sizeof(err);
getsockopt(ret, SOL_SOCKET, SO_ERROR, &err, &err_len);
if(err)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Connecting failed: %s",
strerror(err));
return -1;
}
/* Set back to blocking mode */
if(fcntl(ret, F_SETFL, 0) < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot set blocking mode");
return -1;
}
return ret;
}
int bg_socket_connect_unix(const char * name)
{
struct sockaddr_un addr;
int addr_len;
int ret;
ret = create_socket(PF_LOCAL, SOCK_STREAM, 0);
if(ret < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot create unix socket");
return -1;
}
addr.sun_family = AF_LOCAL;
strncpy (addr.sun_path, name, sizeof(addr.sun_path));
addr.sun_path[sizeof (addr.sun_path) - 1] = '\0';
addr_len = SUN_LEN(&addr);
if(connect(ret,(struct sockaddr*)(&addr),addr_len)<0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Connecting unix socket failed");
return -1;
}
return ret;
}
void bg_socket_disconnect(int sock)
{
close(sock);
}
/* Older systems assign an ipv4 address to localhost,
newer systems (e.g. Ubuntu Karmic) assign an ipv6 address to localhost.
To test this, we make a name lookup for "localhost" and test if it returns
an IPV4 or an IPV6 address */
static int have_ipv6()
{
struct addrinfo hints;
struct addrinfo * ret;
int err, has_ipv6;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; // SOCK_DGRAM
hints.ai_protocol = 0; // 0
hints.ai_flags = 0;
if((err = getaddrinfo("localhost", NULL /* service */,
&hints, &ret)))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot resolve address of localhost: %s",
gai_strerror(err));
return 0;
}
if(ret[0].ai_addr->sa_family == AF_INET6)
has_ipv6 = 1;
else
has_ipv6 = 0;
freeaddrinfo(ret);
return has_ipv6;
}
/* Server socket (stream oriented) */
int bg_listen_socket_create_inet(int port,
int queue_size,
int flags)
{
int ret, err, use_ipv6;
struct sockaddr_in name_ipv4;
struct sockaddr_in6 name_ipv6;
if(flags & BG_SOCKET_LOOPBACK)
use_ipv6 = have_ipv6();
else
use_ipv6 = 0;
memset(&name_ipv4, 0, sizeof(name_ipv4));
memset(&name_ipv6, 0, sizeof(name_ipv6));
if(use_ipv6)
ret = create_socket(PF_INET6, SOCK_STREAM, 0);
else
ret = create_socket(PF_INET, SOCK_STREAM, 0);
/* Create the socket. */
if (ret < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot create inet server socket");
return -1;
}
/* Give the socket a name. */
if(use_ipv6)
{
name_ipv6.sin6_family = AF_INET6;
name_ipv6.sin6_port = htons(port);
if(flags & BG_SOCKET_LOOPBACK)
name_ipv6.sin6_addr = in6addr_loopback;
else
name_ipv6.sin6_addr = in6addr_any;
err = bind(ret, (struct sockaddr *)&name_ipv6, sizeof(name_ipv6));
}
else
{
name_ipv4.sin_family = AF_INET;
name_ipv4.sin_port = htons(port);
if(flags & BG_SOCKET_LOOPBACK)
name_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
else
name_ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
err = bind(ret, (struct sockaddr *)&name_ipv4, sizeof(name_ipv4));
}
if(err < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot bind inet socket: %s", strerror(errno));
return -1;
}
if(fcntl(ret, F_SETFL, O_NONBLOCK) < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot set nonblocking mode");
return -1;
}
if(listen(ret, queue_size))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot put socket into listening mode");
return -1;
}
return ret;
}
int bg_listen_socket_create_unix(const char * name,
int queue_size)
{
int ret;
struct sockaddr_un addr;
int addr_len;
ret = create_socket(PF_LOCAL, SOCK_STREAM, 0);
if(ret < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot create unix server socket");
return -1;
}
addr.sun_family = AF_LOCAL;
strncpy (addr.sun_path, name, sizeof(addr.sun_path));
addr.sun_path[sizeof (addr.sun_path) - 1] = '\0';
addr_len = SUN_LEN(&addr);
if(bind(ret,(struct sockaddr*)(&addr),addr_len)<0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Could not bind socket");
return -1;
}
if(fcntl(ret, F_SETFL, O_NONBLOCK) < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot set nonblocking mode");
return -1;
}
if(listen(ret, queue_size))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Cannot put socket into listening mode");
return -1;
}
return ret;
}
/* Accept a new client connection, return -1 if there is none */
int bg_listen_socket_accept(int sock)
{
int ret;
ret = accept(sock, NULL, NULL);
if(ret < 0)
return -1;
return ret;
}
void bg_listen_socket_destroy(int sock)
{
close(sock);
}
int bg_socket_read_data(int fd, uint8_t * data, int len, int milliseconds)
{
int result;
fd_set rset;
struct timeval timeout;
if(milliseconds >= 0)
{
FD_ZERO (&rset);
FD_SET (fd, &rset);
timeout.tv_sec = milliseconds / 1000;
timeout.tv_usec = (milliseconds % 1000) * 1000;
if(select (fd+1, &rset, NULL, NULL, &timeout) <= 0)
{
// bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Got read timeout");
return 0;
}
}
result = recv(fd, data, len, MSG_WAITALL);
if(result <= 0)
{
return 0;
}
return result;
}
int bg_socket_write_data(int fd, const uint8_t * data, int len)
{
int result;
result = send(fd, data, len, MSG_NOSIGNAL);
if(result != len)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Sending data failed: %s",
strerror(errno));
return 0;
}
return result;
}
/*
* Read a single line from a filedescriptor
*
* ret will be reallocated if neccesary and ret_alloc will
* be updated then
*
* The string will be 0 terminated, a trailing \r or \n will
* be removed
*/
#define BYTES_TO_ALLOC 1024
int bg_socket_read_line(int fd, char ** ret,
int * ret_alloc, int milliseconds)
{
char * pos;
char c;
int bytes_read;
bytes_read = 0;
/* Allocate Memory for the case we have none */
if(!(*ret_alloc))
{
*ret_alloc = BYTES_TO_ALLOC;
*ret = realloc(*ret, *ret_alloc);
}
pos = *ret;
while(1)
{
if(!bg_socket_read_data(fd, (uint8_t*)(&c), 1, milliseconds))
{
if(!bytes_read)
return 0;
break;
}
/*
* Line break sequence
* is starting, remove the rest from the stream
*/
if(c == '\n')
{
break;
}
/* Reallocate buffer */
else if(c != '\r')
{
if(bytes_read+2 >= *ret_alloc)
{
*ret_alloc += BYTES_TO_ALLOC;
*ret = realloc(*ret, *ret_alloc);
pos = &((*ret)[bytes_read]);
}
/* Put the byte and advance pointer */
*pos = c;
pos++;
bytes_read++;
}
}
*pos = '\0';
return 1;
}
gmerlin-1.2.0~dfsg/lib/chapterlist.c 0000644 0001750 0001750 00000011254 11764363410 017335 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
bg_chapter_list_t * bg_chapter_list_create(int num_chapters)
{
bg_chapter_list_t * ret;
ret = calloc(1, sizeof(*ret));
if(num_chapters)
{
ret->chapters = calloc(num_chapters, sizeof(*(ret->chapters)));
ret->num_chapters = num_chapters;
}
return ret;
}
bg_chapter_list_t * bg_chapter_list_copy(const bg_chapter_list_t * list)
{
int i;
bg_chapter_list_t * ret;
if(!list || !list->num_chapters)
return NULL;
ret = bg_chapter_list_create(list->num_chapters);
for(i = 0; i < ret->num_chapters; i++)
{
ret->chapters[i].time = list->chapters[i].time;
ret->chapters[i].name = bg_strdup(ret->chapters[i].name,
list->chapters[i].name);
}
ret->timescale = list->timescale;
return ret;
}
void bg_chapter_list_destroy(bg_chapter_list_t * list)
{
int i;
for(i = 0; i < list->num_chapters; i++)
{
if(list->chapters[i].name)
free(list->chapters[i].name);
}
free(list->chapters);
free(list);
}
void bg_chapter_list_insert(bg_chapter_list_t * list, int index,
int64_t time, const char * name)
{
int chapters_to_add;
/* Add a chapter behind the last one */
if(index >= list->num_chapters)
{
chapters_to_add = index + 1 - list->num_chapters;
list->chapters =
realloc(list->chapters,
(chapters_to_add + list->num_chapters)*sizeof(*list->chapters));
memset(list->chapters + list->num_chapters,
0, sizeof(*list->chapters) * chapters_to_add);
list->chapters[index].name = bg_strdup(list->chapters[index].name, name);
list->chapters[index].time = time;
list->num_chapters = index + 1;
}
else
{
list->chapters = realloc(list->chapters,
(list->num_chapters + 1)*
sizeof(*list->chapters));
memmove(list->chapters + index + 1, list->chapters + index,
(list->num_chapters - index) * sizeof(*list->chapters));
list->chapters[index].name = bg_strdup(NULL, name);
list->chapters[index].time = time;
list->num_chapters++;
}
}
void bg_chapter_list_delete(bg_chapter_list_t * list, int index)
{
if((index < 0) || (index >= list->num_chapters))
return;
if(list->chapters[index].name)
free(list->chapters[index].name);
if(index < list->num_chapters-1)
{
memmove(list->chapters + index, list->chapters + index + 1,
sizeof(*list->chapters) * (list->num_chapters - index));
}
if(!index)
list->chapters[index].time = 0;
list->num_chapters--;
}
void bg_chapter_list_set_default_names(bg_chapter_list_t * list)
{
int i;
for(i = 0; i < list->num_chapters; i++)
{
if(!list->chapters[i].name)
list->chapters[i].name = bg_sprintf(TR("Chapter %d"), i+1);
}
}
int bg_chapter_list_get_current(bg_chapter_list_t * list,
gavl_time_t time)
{
int i;
int64_t time_scaled = gavl_time_scale(list->timescale, time);
for(i = 0; i < list->num_chapters-1; i++)
{
if(time_scaled < list->chapters[i+1].time)
return i;
}
return list->num_chapters-1;
}
int bg_chapter_list_changed(bg_chapter_list_t * list,
gavl_time_t time, int * current_chapter)
{
int ret = 0;
int64_t time_scaled = gavl_time_scale(list->timescale, time);
while(*current_chapter < list->num_chapters-1)
{
if(time_scaled >= list->chapters[(*current_chapter)+1].time)
{
(*current_chapter)++;
ret = 1;
}
else
break;
}
return ret;
}
gmerlin-1.2.0~dfsg/lib/player.c 0000644 0001750 0001750 00000023542 11764363410 016312 0 ustar alessio alessio /*****************************************************************
* Gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#define LOG_DOMAIN "player"
#include
/* Input callbacks */
static void duration_changed(void * data, gavl_time_t duration)
{
bg_player_t * p = data;
bg_player_set_duration(p, duration, p->can_seek);
}
static void name_changed(void * data, const char * name)
{
bg_player_t * p = data;
bg_player_set_track_name(p, name);
}
static void metadata_changed(void * data, const gavl_metadata_t * m)
{
bg_player_t * p = data;
bg_player_set_metadata(p, m);
}
static void buffer_notify(void * data, float percentage)
{
bg_player_t * p = data;
bg_player_set_state(p, BG_PLAYER_STATE_BUFFERING,
&percentage, NULL);
}
static void aspect_changed(void * data, int stream, int pixel_width,
int pixel_height)
{
bg_player_t * p = data;
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Aspect ratio changed");
bg_player_ov_update_aspect(&p->video_stream,
pixel_width, pixel_height);
}
bg_player_t * bg_player_create(bg_plugin_registry_t * plugin_reg)
{
bg_player_t * ret;
ret = calloc(1, sizeof(*ret));
/* Callbacks */
ret->input_callbacks.data = ret;
ret->input_callbacks.name_changed = name_changed;
ret->input_callbacks.duration_changed = duration_changed;
ret->input_callbacks.metadata_changed = metadata_changed;
ret->input_callbacks.buffer_notify = buffer_notify;
ret->input_callbacks.aspect_changed = aspect_changed;
/* Create message queues */
ret->command_queue = bg_msg_queue_create();
ret->message_queues = bg_msg_queue_list_create();
ret->visualizer = bg_visualizer_create(plugin_reg);
ret->thread_common = bg_player_thread_common_create();
/* Create contexts */
bg_player_audio_create(ret, plugin_reg);
bg_player_video_create(ret, plugin_reg);
bg_player_subtitle_create(ret);
bg_player_input_create(ret);
bg_player_ov_create(ret);
ret->threads[0] = ret->audio_stream.th;
ret->threads[1] = ret->video_stream.th;
pthread_mutex_init(&ret->state_mutex, NULL);
pthread_mutex_init(&ret->config_mutex, NULL);
/* Subtitles are off by default */
ret->current_subtitle_stream = -1;
// ret->current_subtitle_stream = 5;
ret->state = BG_PLAYER_STATE_INIT;
ret->wait_time = 10000;
return ret;
}
void bg_player_destroy(bg_player_t * player)
{
bg_player_input_destroy(player);
bg_player_ov_destroy(player);
bg_player_audio_destroy(player);
bg_player_video_destroy(player);
bg_player_subtitle_destroy(player);
bg_visualizer_destroy(player->visualizer);
bg_msg_queue_destroy(player->command_queue);
bg_msg_queue_list_destroy(player->message_queues);
pthread_mutex_destroy(&player->state_mutex);
pthread_mutex_destroy(&player->config_mutex);
bg_player_thread_common_destroy(player->thread_common);
free(player);
}
void bg_player_add_message_queue(bg_player_t * player,
bg_msg_queue_t * message_queue)
{
bg_msg_queue_list_add(player->message_queues, message_queue);
}
void bg_player_delete_message_queue(bg_player_t * player,
bg_msg_queue_t * message_queue)
{
bg_msg_queue_list_remove(player->message_queues, message_queue);
}
int bg_player_get_state(bg_player_t * player)
{
int ret;
pthread_mutex_lock(&player->state_mutex);
ret = player->state;
pthread_mutex_unlock(&player->state_mutex);
return ret;
}
struct state_struct
{
int state;
float percentage;
int want_new;
int can_pause;
};
static void msg_state(bg_msg_t * msg,
const void * data)
{
struct state_struct * s;
s = (struct state_struct *)data;
bg_msg_set_id(msg, BG_PLAYER_MSG_STATE_CHANGED);
bg_msg_set_arg_int(msg, 0, s->state);
if(s->state == BG_PLAYER_STATE_BUFFERING)
{
bg_msg_set_arg_float(msg, 1, s->percentage);
}
else if(s->state == BG_PLAYER_STATE_CHANGING)
{
bg_msg_set_arg_int(msg, 1, s->want_new);
}
else if(s->state == BG_PLAYER_STATE_PLAYING)
{
bg_msg_set_arg_int(msg, 1, s->can_pause);
}
}
void bg_player_set_state(bg_player_t * player, int state,
const void * arg1, const void * arg2)
{
struct state_struct s;
pthread_mutex_lock(&player->state_mutex);
player->state = state;
pthread_mutex_unlock(&player->state_mutex);
/* Broadcast this message */
// memset(&state, 0, sizeof(state));
s.state = state;
if(state == BG_PLAYER_STATE_BUFFERING)
s.percentage = *((const float*)arg1);
else if(state == BG_PLAYER_STATE_CHANGING)
s.want_new = *((const int*)arg1);
else if(state == BG_PLAYER_STATE_PLAYING)
s.can_pause = *((const int*)arg1);
bg_msg_queue_list_send(player->message_queues,
msg_state,
&s);
}
const bg_parameter_info_t *
bg_player_get_visualization_parameters(bg_player_t * player)
{
return bg_visualizer_get_parameters(player->visualizer);
}
void
bg_player_set_visualization_parameter(void*data,
const char * name,
const bg_parameter_value_t*val)
{
bg_player_t * p;
int do_init;
p = (bg_player_t*)data;
do_init = (bg_player_get_state(p) == BG_PLAYER_STATE_INIT);
bg_visualizer_set_parameter(p->visualizer, name, val);
if(!do_init)
{
if(bg_visualizer_need_restart(p->visualizer))
{
bg_player_interrupt(p);
bg_player_interrupt_resume(p);
}
}
}
void
bg_player_set_visualization_plugin_parameter(void*data,
const char * name,
const bg_parameter_value_t*val)
{
bg_player_t * p;
p = (bg_player_t*)data;
bg_visualizer_set_vis_parameter(p->visualizer, name, val);
}
void
bg_player_set_visualization(bg_player_t * p,
int enable)
{
int was_enabled;
int do_init;
do_init = (bg_player_get_state(p) == BG_PLAYER_STATE_INIT);
pthread_mutex_lock(&p->config_mutex);
was_enabled = p->visualizer_enabled;
p->visualizer_enabled = enable;
pthread_mutex_unlock(&p->config_mutex);
if((was_enabled != enable) && !do_init)
{
bg_player_interrupt(p);
bg_player_interrupt_resume(p);
}
}
void
bg_player_set_visualization_plugin(bg_player_t * p, const bg_plugin_info_t * plugin_info)
{
int do_init;
do_init = (bg_player_get_state(p) == BG_PLAYER_STATE_INIT);
bg_visualizer_set_vis_plugin(p->visualizer, plugin_info);
if(!do_init)
{
if(bg_visualizer_need_restart(p->visualizer))
{
bg_player_interrupt(p);
bg_player_interrupt_resume(p);
}
}
}
static const bg_parameter_info_t parameters[] =
{
{
.name = "message_interval",
.long_name = TRS("Control loop interval"),
.type = BG_PARAMETER_INT,
.val_default = { .val_i = 10 },
},
{
.name = "time_update",
.long_name = TRS("Time update interval"),
.type = BG_PARAMETER_STRINGLIST,
.multi_names = (char const *[]){ "seconds", "frames", NULL },
.multi_labels = (char const *[]){ TRS("Seconds"), TRS("frames"), NULL },
.val_default = { .val_str = "seconds" },
},
{
.name = "report_peak",
.long_name = TRS("Report peak values for audio"),
.type = BG_PARAMETER_CHECKBUTTON,
},
{
.name = "finish_mode",
.long_name = TRS("Finish mode"),
.type = BG_PARAMETER_STRINGLIST,
.multi_names = (char const *[]){ "change", "pause", NULL },
.multi_labels = (char const *[]){ TRS("Change"), TRS("Pause"), NULL },
.val_default = { .val_str = "change" },
},
{ /* End of parameters */ }
};
const bg_parameter_info_t * bg_player_get_parameters(bg_player_t * player)
{
return parameters;
}
void bg_player_set_parameter(void * player, const char * name,
const bg_parameter_value_t * val)
{
bg_player_t * p = player;
if(!name)
return;
else if(!strcmp(name, "message_interval"))
{
p->wait_time = val->val_i;
p->wait_time *= 1000;
}
else if(!strcmp(name, "time_update"))
{
if(!strcmp(val->val_str, "second"))
{
p->time_update_mode = TIME_UPDATE_SECOND;
}
else if(!strcmp(val->val_str, "frame"))
{
p->time_update_mode = TIME_UPDATE_FRAME;
}
}
else if(!strcmp(name, "finish_mode"))
{
if(!strcmp(val->val_str, "change"))
{
p->finish_mode = BG_PLAYER_FINISH_CHANGE;
}
else if(!strcmp(val->val_str, "pause"))
{
p->finish_mode = BG_PLAYER_FINISH_PAUSE;
}
}
else if(!strcmp(name, "report_peak"))
{
if(val->val_i)
p->flags |= PLAYER_DO_REPORT_PEAK;
else
p->flags &= ~PLAYER_DO_REPORT_PEAK;
}
}
gmerlin-1.2.0~dfsg/lib/Makefile.am 0000644 0001750 0001750 00000004327 11764363410 016706 0 ustar alessio alessio plugindir=$(pkglibdir)/plugins
INCLUDES = -I$(top_srcdir)/include
if HAVE_X11
x11_subdirs = x11
x11_libadd = x11/libx11.la
else
x11_subdirs =
x11_libadd =
endif
if HAVE_GTK
gtk_subdirs = gtk
else
gtk_subdirs =
endif
if HAVE_LV
lv_sources = bglv.c
lv_cflags = @LV_CFLAGS@
lv_libs = @LV_LIBS@
else
lv_sources =
lv_cflags =
lv_libs =
endif
if HAVE_POSIX_SEMAPHORES
sem_sources =
else
sem_sources = uthread_sem.c
endif
SUBDIRS = $(x11_subdirs) . $(gtk_subdirs)
AM_CFLAGS = \
@FONTCONFIG_CFLAGS@ \
@FREETYPE_CFLAGS@ \
-DLOCALE_DIR=\"$(localedir)\" \
-DPLUGIN_DIR=\"$(plugindir)\" \
-DDOC_DIR=\"$(docdir)\" \
-DDATA_DIR=\"$(pkgdatadir)\" \
$(lv_cflags)
libgmerlin_la_LDFLAGS = -version-info @LTVERSION_CURRENT@:@LTVERSION_REVISION@:@LTVERSION_AGE@ -export-symbols-regex ^bg_
libgmerlin_la_LIBADD = $(x11_libadd) @FONTCONFIG_LIBS@ @FREETYPE_LIBS@ @XML2_LIBS@ @ICONV_LIBS@ @LIBINTL@ $(lv_libs) -ldl -lpthread
bin_PROGRAMS = gmerlin_visualizer_slave
gmerlin_visualizer_slave_SOURCES = visualize_slave.c
gmerlin_visualizer_slave_LDADD = libgmerlin.la
lib_LTLIBRARIES = libgmerlin.la
libgmerlin_la_SOURCES = \
$(lv_sources) \
$(sem_sources) \
accelerator.c \
album.c \
album_xml.c \
bggavl.c \
bgladspa.c \
bgfrei0r.c \
bgxml.c \
cfg_item.c \
cfg_registry.c \
cfg_section.c \
cfg_xml.c \
chapterlist.c \
chapterlist_xml.c \
charset.c \
cmdline.c \
converters.c \
device.c \
edl.c \
edldec.c \
edl_xml.c \
encoder.c \
fileformat.c \
filters.c \
formats.c \
hexdump.c \
language_table.c \
lcdproc.c \
log.c \
md5.c \
mediafiledevice.c \
metadata.c \
metadata_xml.c \
msgqueue.c \
ocr.c \
osd.c \
ov.c \
ovl2text.c \
parameter.c \
parameter_xml.c \
player.c \
player_input.c \
player_oa.c \
player_ov.c \
player_audio.c \
player_loop.c \
player_subtitle.c \
player_thread.c \
player_time.c \
player_video.c \
playercmd.c \
pluginfuncs.c \
pluginregistry.c \
pluginreg_xml.c \
preset.c \
preset_xml.c \
recorder.c \
recorder_audio.c \
recorder_video.c \
remote.c \
searchpath.c \
serialize.c \
singlepic.c \
socket.c \
streaminfo.c \
stringutils.c \
subprocess.c \
textrenderer.c \
threadpool.c \
thumbnail.c \
transcoder.c \
transcoder_pp.c \
transcoder_track.c \
transcoder_track_xml.c \
translation.c \
tree.c \
tree_xml.c \
urilist.c \
utf8.c \
visualize.c
gmerlin-1.2.0~dfsg/lib/msgqueue.c 0000644 0001750 0001750 00000102701 11764363410 016644 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define TYPE_INT 0
#define TYPE_FLOAT 1
#define TYPE_POINTER 2
#define TYPE_POINTER_NOCOPY 3
#define TYPE_TIME 4
#define TYPE_COLOR_RGB 5
#define TYPE_COLOR_RGBA 6
#define TYPE_POSITION 7
struct bg_msg_s
{
uint32_t id;
struct
{
union
{
int val_i;
double val_f;
void * val_ptr;
gavl_time_t val_time;
float val_color[4];
double val_pos[2];
} value;
uint8_t type;
uint32_t size;
} args[BG_MSG_MAX_ARGS];
int num_args;
sem_t produced;
bg_msg_t * prev;
bg_msg_t * next;
};
void bg_msg_set_id(bg_msg_t * msg, int id)
{
msg->id = id;
msg->num_args = 0;
/* Zero everything */
memset(&msg->args, 0, sizeof(msg->args));
}
static int check_arg(int arg)
{
if(arg < 0)
return 0;
assert(arg < BG_MSG_MAX_ARGS);
return 1;
}
/* Set argument to a basic type */
void bg_msg_set_arg_int(bg_msg_t * msg, int arg, int value)
{
if(!check_arg(arg))
return;
msg->args[arg].value.val_i = value;
msg->args[arg].type = TYPE_INT;
if(arg+1 > msg->num_args)
msg->num_args = arg + 1;
}
void bg_msg_set_arg_time(bg_msg_t * msg, int arg, gavl_time_t value)
{
if(!check_arg(arg))
return;
msg->args[arg].value.val_time = value;
msg->args[arg].type = TYPE_TIME;
if(arg+1 > msg->num_args)
msg->num_args = arg + 1;
}
void * bg_msg_set_arg_ptr(bg_msg_t * msg, int arg, int len)
{
if(!check_arg(arg))
return NULL;
msg->args[arg].value.val_ptr = calloc(1, len);
msg->args[arg].size = len;
msg->args[arg].type = TYPE_POINTER;
if(arg+1 > msg->num_args)
msg->num_args = arg + 1;
return msg->args[arg].value.val_ptr;
}
void bg_msg_set_arg_ptr_nocopy(bg_msg_t * msg, int arg, void * value)
{
if(!check_arg(arg))
return;
msg->args[arg].value.val_ptr = value;
msg->args[arg].type = TYPE_POINTER_NOCOPY;
if(arg+1 > msg->num_args)
msg->num_args = arg + 1;
}
void bg_msg_set_arg_string(bg_msg_t * msg, int arg, const char * value)
{
int length;
void * dst;
if(!value)
return;
length = strlen(value)+1;
dst = bg_msg_set_arg_ptr(msg, arg, length);
memcpy(dst, value, length);
if(arg+1 > msg->num_args)
msg->num_args = arg + 1;
}
void bg_msg_set_arg_float(bg_msg_t * msg, int arg, double value)
{
if(!check_arg(arg))
return;
msg->args[arg].value.val_f = value;
msg->args[arg].type = TYPE_FLOAT;
if(arg+1 > msg->num_args)
msg->num_args = arg + 1;
}
void bg_msg_set_arg_color_rgb(bg_msg_t * msg, int arg, const float * value)
{
if(!check_arg(arg))
return;
msg->args[arg].value.val_color[0] = value[0];
msg->args[arg].value.val_color[1] = value[1];
msg->args[arg].value.val_color[2] = value[2];
msg->args[arg].type = TYPE_COLOR_RGB;
if(arg+1 > msg->num_args)
msg->num_args = arg + 1;
}
void bg_msg_set_arg_color_rgba(bg_msg_t * msg, int arg, const float * value)
{
if(!check_arg(arg))
return;
msg->args[arg].value.val_color[0] = value[0];
msg->args[arg].value.val_color[1] = value[1];
msg->args[arg].value.val_color[2] = value[2];
msg->args[arg].value.val_color[3] = value[3];
msg->args[arg].type = TYPE_COLOR_RGBA;
if(arg+1 > msg->num_args)
msg->num_args = arg + 1;
}
void bg_msg_set_arg_position(bg_msg_t * msg, int arg, const double * value)
{
if(!check_arg(arg))
return;
msg->args[arg].value.val_pos[0] = value[0];
msg->args[arg].value.val_pos[1] = value[1];
msg->args[arg].type = TYPE_POSITION;
if(arg+1 > msg->num_args)
msg->num_args = arg + 1;
}
/* Get basic types */
int bg_msg_get_id(bg_msg_t * msg)
{
return msg->id;
}
int bg_msg_get_arg_int(bg_msg_t * msg, int arg)
{
if(!check_arg(arg))
return 0;
return msg->args[arg].value.val_i;
}
gavl_time_t bg_msg_get_arg_time(bg_msg_t * msg, int arg)
{
if(!check_arg(arg))
return 0;
return msg->args[arg].value.val_time;
}
double bg_msg_get_arg_float(bg_msg_t * msg, int arg)
{
if(!check_arg(arg))
return 0.0;
return msg->args[arg].value.val_f;
}
void bg_msg_get_arg_color_rgb(bg_msg_t * msg, int arg, float * val)
{
if(!check_arg(arg))
return;
val[0] = msg->args[arg].value.val_color[0];
val[1] = msg->args[arg].value.val_color[1];
val[2] = msg->args[arg].value.val_color[2];
}
void bg_msg_get_arg_color_rgba(bg_msg_t * msg, int arg, float * val)
{
if(!check_arg(arg))
return;
val[0] = msg->args[arg].value.val_color[0];
val[1] = msg->args[arg].value.val_color[1];
val[2] = msg->args[arg].value.val_color[2];
val[3] = msg->args[arg].value.val_color[3];
}
void bg_msg_get_arg_position(bg_msg_t * msg, int arg, double * val)
{
if(!check_arg(arg))
return;
val[0] = msg->args[arg].value.val_pos[0];
val[1] = msg->args[arg].value.val_pos[1];
}
void * bg_msg_get_arg_ptr(bg_msg_t * msg, int arg, int * length)
{
void * ret;
if(!check_arg(arg))
return NULL;
ret = msg->args[arg].value.val_ptr;
msg->args[arg].value.val_ptr = NULL;
if(length)
*length = msg->args[arg].size;
return ret;
}
void * bg_msg_get_arg_ptr_nocopy(bg_msg_t * msg, int arg)
{
if(!check_arg(arg))
return NULL;
return msg->args[arg].value.val_ptr;
}
char * bg_msg_get_arg_string(bg_msg_t * msg, int arg)
{
char * ret;
if(!check_arg(arg))
return NULL;
ret = msg->args[arg].value.val_ptr;
msg->args[arg].value.val_ptr = NULL;
return ret;
}
/* Get/Set routines for structures */
static inline uint8_t * get_8(uint8_t * data, uint32_t * val)
{
*val = *data;
data++;
return data;
}
static inline uint8_t * get_16(uint8_t * data, uint32_t * val)
{
*val = ((data[0] << 8) | data[1]);
data+=2;
return data;
}
static inline uint8_t * get_32(uint8_t * data, uint32_t * val)
{
*val = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
data+=4;
return data;
}
static inline uint8_t * get_str(uint8_t * data, char ** val)
{
uint32_t len;
uint8_t * ret;
ret = get_32(data, &len);
if(len)
{
*val = malloc(len+1);
memcpy(*val, ret, len);
(*val)[len] = '\0';
}
return ret + len;
}
static inline uint8_t * set_8(uint8_t * data, uint8_t val)
{
*data = val;
data++;
return data;
}
static inline uint8_t * set_16(uint8_t * data, uint16_t val)
{
data[0] = (val & 0xff00) >> 8;
data[1] = (val & 0xff);
data+=2;
return data;
}
static inline uint8_t * set_32(uint8_t * data, uint32_t val)
{
data[0] = (val & 0xff000000) >> 24;
data[1] = (val & 0xff0000) >> 16;
data[2] = (val & 0xff00) >> 8;
data[3] = (val & 0xff);
data+=4;
return data;
}
static int str_len(const char * str)
{
int ret = 4;
if(str)
ret += strlen(str);
return ret;
}
static inline uint8_t * set_str(uint8_t * data, const char * val)
{
uint32_t len;
if(val)
len = strlen(val);
else
len = 0;
data = set_32(data, len);
if(len)
memcpy(data, val, len);
return data + len;
}
/*
int samples_per_frame;
int samplerate;
int num_channels;
gavl_sample_format_t sample_format;
gavl_interleave_mode_t interleave_mode;
gavl_channel_setup_t channel_setup;
int lfe;
*/
void bg_msg_set_arg_audio_format(bg_msg_t * msg, int arg,
const gavl_audio_format_t * format)
{
uint8_t * ptr;
int len;
len = bg_serialize_audio_format(format, NULL, 0);
ptr = bg_msg_set_arg_ptr(msg, arg, len);
bg_serialize_audio_format(format, ptr, len);
}
void bg_msg_get_arg_audio_format(bg_msg_t * msg, int arg,
gavl_audio_format_t * format, int * big_endian)
{
uint8_t * ptr;
int len, be;
ptr = bg_msg_get_arg_ptr(msg, arg, &len);
bg_deserialize_audio_format(format, ptr, len, &be);
if(big_endian)
*big_endian = be;
free(ptr);
}
/* Video format */
/*
int frame_width;
int frame_height;
int image_width;
int image_height;
int pixel_width;
int pixel_height;
gavl_pixelformat_t pixelformat;
int framerate_num;
int frame_duration;
int free_framerate;
*/
void bg_msg_set_arg_video_format(bg_msg_t * msg, int arg,
const gavl_video_format_t * format)
{
uint8_t * ptr;
int len;
len = bg_serialize_video_format(format, NULL, 0);
ptr = bg_msg_set_arg_ptr(msg, arg, len);
bg_serialize_video_format(format, ptr, len);
}
void bg_msg_get_arg_video_format(bg_msg_t * msg, int arg,
gavl_video_format_t * format, int * big_endian)
{
uint8_t * ptr;
int len, be;
ptr = bg_msg_get_arg_ptr(msg, arg, &len);
bg_deserialize_video_format(format, ptr, len, &be);
if(big_endian)
*big_endian = be;
free(ptr);
}
/*
char * artist;
char * title;
char * album;
int track;
char * date;
char * genre;
char * comment;
char * author;
char * copyright;
*/
void bg_msg_set_arg_metadata(bg_msg_t * msg, int arg,
const gavl_metadata_t * m)
{
int i;
uint8_t * ptr;
uint8_t * pos;
int len = 4;
for(i = 0; i < m->num_tags; i++)
{
len += str_len(m->tags[i].key);
len += str_len(m->tags[i].val);
}
ptr = bg_msg_set_arg_ptr(msg, arg, len);
pos = ptr;
pos = set_32(pos, m->num_tags);
for(i = 0; i < m->num_tags; i++)
{
pos = set_str(pos, m->tags[i].key);
pos = set_str(pos, m->tags[i].val);
}
}
void bg_msg_get_arg_metadata(bg_msg_t * msg, int arg,
gavl_metadata_t * m)
{
int i;
uint32_t num;
uint8_t * ptr;
uint8_t * pos;
char * key;
char * val;
ptr = bg_msg_get_arg_ptr(msg, arg, NULL);
pos = ptr;
pos = get_32(pos, &num);
for(i = 0; i < num; i++)
{
pos = get_str(pos, &key);
pos = get_str(pos, &val);
gavl_metadata_set_nocpy(m, key, val);
free(key);
}
free(ptr);
}
bg_msg_t * bg_msg_create()
{
bg_msg_t * ret;
ret = calloc(1, sizeof(*ret));
sem_init(&ret->produced, 0, 0);
return ret;
}
void bg_msg_free(bg_msg_t * m)
{
int i;
for(i = 0; i < m->num_args; i++)
{
if((m->args[i].type == TYPE_POINTER) &&
(m->args[i].value.val_ptr))
{
free(m->args[i].value.val_ptr);
m->args[i].value.val_ptr = NULL;
}
}
}
void bg_msg_destroy(bg_msg_t * m)
{
bg_msg_free(m);
sem_destroy(&m->produced);
free(m);
}
/* Audio frame:
.arg_0 = valid samples
.arg_1 = timestamp
.arg_2 = big endian
*/
int bg_msg_write_audio_frame(bg_msg_t * msg,
const gavl_audio_format_t * format,
const gavl_audio_frame_t * frame,
bg_msg_write_callback_t cb,
void * cb_data)
{
uint8_t * ptr;
int len;
len = bg_serialize_audio_frame_header(format, frame, NULL, 0);
ptr = bg_msg_set_arg_ptr(msg, 0, len);
bg_serialize_audio_frame_header(format, frame, ptr, len);
if(!bg_msg_write(msg, cb, cb_data))
return 0;
return bg_serialize_audio_frame(format, frame, cb, cb_data);
}
/** \brief Read an audio frame from a socket
* \param msg Message containing the frame header
* \param format Audio format
* \param frame An audio frame
* \param fd A socket
* \returns 1 on success, 0 on error
*
* Before you can use this function, msg must contain
* a valid audio frame header
*/
int bg_msg_read_audio_frame(gavl_dsp_context_t * ctx,
bg_msg_t * msg,
const gavl_audio_format_t * format,
gavl_audio_frame_t * frame,
bg_msg_read_callback_t cb,
void * cb_data, int big_endian)
{
uint8_t * ptr;
int len;
ptr = bg_msg_get_arg_ptr(msg, 0, &len);
bg_deserialize_audio_frame_header(format, frame, ptr, len);
free(ptr);
return bg_deserialize_audio_frame(ctx, format, frame,
cb, cb_data, big_endian);
}
/* .parameters =
.arg_0 = name
.arg_1 = type (int)
.arg_2 = value
*/
void bg_msg_set_parameter(bg_msg_t * msg,
const char * name,
bg_parameter_type_t type,
const bg_parameter_value_t * val)
{
if(!name)
return;
bg_msg_set_arg_string(msg, 0, name);
bg_msg_set_arg_int(msg, 1, type);
switch(type)
{
case BG_PARAMETER_SECTION: //!< Dummy type. It contains no data but acts as a separator in notebook style configuration windows
case BG_PARAMETER_BUTTON: /* Button (just tell we are called) */
break;
case BG_PARAMETER_CHECKBUTTON: //!< Bool
case BG_PARAMETER_INT: //!< Integer spinbutton
case BG_PARAMETER_SLIDER_INT: //!< Integer slider
bg_msg_set_arg_int(msg, 2, val->val_i);
break;
case BG_PARAMETER_FLOAT: // spinbutton
case BG_PARAMETER_SLIDER_FLOAT: //!< Float slider
bg_msg_set_arg_float(msg, 2, val->val_f);
break;
case BG_PARAMETER_STRING: //!< String (one line only)
case BG_PARAMETER_STRING_HIDDEN: //!< Encrypted string (displays as ***)
case BG_PARAMETER_STRINGLIST: //!< Popdown menu with string values
case BG_PARAMETER_FONT: //!< Font (contains fontconfig compatible fontname)
case BG_PARAMETER_DEVICE: //!< Device
case BG_PARAMETER_FILE: //!< File
case BG_PARAMETER_DIRECTORY: //!< Directory
case BG_PARAMETER_MULTI_MENU: //!< Menu with config- and infobutton
case BG_PARAMETER_MULTI_LIST: //!< List with config- and infobutton
case BG_PARAMETER_MULTI_CHAIN: //!< Several subitems (including suboptions) can be arranged in a chain
bg_msg_set_arg_string(msg, 2, val->val_str);
break;
case BG_PARAMETER_COLOR_RGB: //!< RGB Color
bg_msg_set_arg_color_rgb(msg, 2, val->val_color);
break;
case BG_PARAMETER_COLOR_RGBA: //!< RGBA Color
bg_msg_set_arg_color_rgba(msg, 2, val->val_color);
break;
case BG_PARAMETER_POSITION: //!< RGBA Color
bg_msg_set_arg_position(msg, 2, val->val_pos);
break;
case BG_PARAMETER_TIME: //!< Time
bg_msg_set_arg_time(msg, 2, val->val_time);
break;
}
}
void bg_msg_get_parameter(bg_msg_t * msg,
char ** name,
bg_parameter_type_t * type,
bg_parameter_value_t * val)
{
*name = bg_msg_get_arg_string(msg, 0);
if(!*name)
return;
*type = bg_msg_get_arg_int(msg, 1);
switch(*type)
{
case BG_PARAMETER_SECTION: //!< Dummy type. It contains no data but acts as a separator in notebook style configuration windows
case BG_PARAMETER_BUTTON:
break;
case BG_PARAMETER_CHECKBUTTON: //!< Bool
case BG_PARAMETER_INT: //!< Integer spinbutton
case BG_PARAMETER_SLIDER_INT: //!< Integer slider
val->val_i = bg_msg_get_arg_int(msg, 2);
break;
case BG_PARAMETER_FLOAT: // spinbutton
case BG_PARAMETER_SLIDER_FLOAT: //!< Float slider
val->val_f = bg_msg_get_arg_float(msg, 2);
break;
case BG_PARAMETER_STRING: //!< String (one line only)
case BG_PARAMETER_STRING_HIDDEN: //!< Encrypted string (displays as ***)
case BG_PARAMETER_STRINGLIST: //!< Popdown menu with string values
case BG_PARAMETER_FONT: //!< Font (contains fontconfig compatible fontname)
case BG_PARAMETER_DEVICE: //!< Device
case BG_PARAMETER_FILE: //!< File
case BG_PARAMETER_DIRECTORY: //!< Directory
case BG_PARAMETER_MULTI_MENU: //!< Menu with config- and infobutton
case BG_PARAMETER_MULTI_LIST: //!< List with config- and infobutton
case BG_PARAMETER_MULTI_CHAIN: //!< Several subitems (including suboptions) can be arranged in a chain
val->val_str = bg_msg_get_arg_string(msg, 2);
break;
case BG_PARAMETER_COLOR_RGB: //!< RGB Color
bg_msg_get_arg_color_rgb(msg, 2, val->val_color);
break;
case BG_PARAMETER_COLOR_RGBA: //!< RGBA Color
bg_msg_get_arg_color_rgba(msg, 2, val->val_color);
break;
case BG_PARAMETER_POSITION: //!< RGBA Color
bg_msg_get_arg_position(msg, 2, val->val_pos);
break;
case BG_PARAMETER_TIME: //!< Time
bg_msg_set_arg_time(msg, 2, val->val_time);
break;
}
}
struct bg_msg_queue_s
{
bg_msg_t * msg_input;
bg_msg_t * msg_output;
bg_msg_t * msg_last;
pthread_mutex_t chain_mutex;
pthread_mutex_t write_mutex;
int num_messages; /* Number of total messages */
};
bg_msg_queue_t * bg_msg_queue_create()
{
bg_msg_queue_t * ret;
ret = calloc(1, sizeof(*ret));
/* Allocate at least 2 messages */
ret->msg_output = bg_msg_create();
ret->msg_output->next = bg_msg_create();
/* Set in- and output messages */
ret->msg_input = ret->msg_output;
ret->msg_last = ret->msg_output->next;
/* Initialize chain mutex */
pthread_mutex_init(&ret->chain_mutex, NULL);
pthread_mutex_init(&ret->write_mutex, NULL);
return ret;
}
void bg_msg_queue_destroy(bg_msg_queue_t * m)
{
bg_msg_t * tmp_message;
while(m->msg_output)
{
tmp_message = m->msg_output->next;
bg_msg_destroy(m->msg_output);
m->msg_output = tmp_message;
}
free(m);
}
/* Lock message queue for reading, block until something arrives */
bg_msg_t * bg_msg_queue_lock_read(bg_msg_queue_t * m)
{
while(sem_wait(&m->msg_output->produced) == -1)
{
if(errno != EINTR)
return NULL;
}
return m->msg_output;
// sem_ret->msg_output
}
bg_msg_t * bg_msg_queue_try_lock_read(bg_msg_queue_t * m)
{
if(!sem_trywait(&m->msg_output->produced))
return m->msg_output;
else
return NULL;
}
int bg_msg_queue_peek(bg_msg_queue_t * m, uint32_t * id)
{
int sem_val;
sem_getvalue(&m->msg_output->produced, &sem_val);
if(sem_val)
{
if(id)
*id = m->msg_output->id;
return 1;
}
else
return 0;
}
void bg_msg_queue_unlock_read(bg_msg_queue_t * m)
{
bg_msg_t * old_out_message;
pthread_mutex_lock(&m->chain_mutex);
old_out_message = m->msg_output;
bg_msg_free(old_out_message);
m->msg_output = m->msg_output->next;
m->msg_last->next = old_out_message;
m->msg_last = m->msg_last->next;
m->msg_last->next = NULL;
pthread_mutex_unlock(&m->chain_mutex);
}
/*
* Lock queue for writing
*/
bg_msg_t * bg_msg_queue_lock_write(bg_msg_queue_t * m)
{
pthread_mutex_lock(&m->write_mutex);
return m->msg_input;
}
void bg_msg_queue_unlock_write(bg_msg_queue_t * m)
{
bg_msg_t * message = m->msg_input;
pthread_mutex_lock(&m->chain_mutex);
if(!m->msg_input->next)
{
m->msg_input->next = bg_msg_create();
m->msg_last = m->msg_input->next;
}
m->msg_input = m->msg_input->next;
sem_post(&message->produced);
pthread_mutex_unlock(&m->chain_mutex);
pthread_mutex_unlock(&m->write_mutex);
}
typedef struct list_entry_s
{
bg_msg_queue_t * q;
struct list_entry_s * next;
} list_entry_t;
struct bg_msg_queue_list_s
{
list_entry_t * entries;
pthread_mutex_t mutex;
};
bg_msg_queue_list_t * bg_msg_queue_list_create()
{
bg_msg_queue_list_t * ret = calloc(1, sizeof(*ret));
pthread_mutex_init(&ret->mutex, NULL);
return ret;
}
void bg_msg_queue_list_destroy(bg_msg_queue_list_t * l)
{
list_entry_t * tmp_entry;
while(l->entries)
{
tmp_entry = l->entries->next;
free(l->entries);
l->entries = tmp_entry;
}
free(l);
}
void
bg_msg_queue_list_send(bg_msg_queue_list_t * l,
void (*set_message)(bg_msg_t * message,
const void * data),
const void * data)
{
bg_msg_t * msg;
list_entry_t * entry;
pthread_mutex_lock(&l->mutex);
entry = l->entries;
while(entry)
{
msg = bg_msg_queue_lock_write(entry->q);
set_message(msg, data);
bg_msg_queue_unlock_write(entry->q);
entry = entry->next;
}
pthread_mutex_unlock(&l->mutex);
}
void bg_msg_queue_list_add(bg_msg_queue_list_t * list,
bg_msg_queue_t * queue)
{
list_entry_t * new_entry;
new_entry = calloc(1, sizeof(*new_entry));
pthread_mutex_lock(&list->mutex);
new_entry->next = list->entries;
new_entry->q = queue;
list->entries = new_entry;
pthread_mutex_unlock(&list->mutex);
}
void bg_msg_queue_list_remove(bg_msg_queue_list_t * list,
bg_msg_queue_t * queue)
{
list_entry_t * tmp_entry;
list_entry_t * entry_before;
pthread_mutex_lock(&list->mutex);
if(list->entries->q == queue)
{
tmp_entry = list->entries->next;
free(list->entries);
list->entries = tmp_entry;
}
else
{
entry_before = list->entries;
while(entry_before->next->q != queue)
entry_before = entry_before->next;
tmp_entry = entry_before->next;
entry_before->next = tmp_entry->next;
free(tmp_entry);
}
pthread_mutex_unlock(&list->mutex);
}
/*
* This on will be used for remote controls,
* return FALSE on error
*/
/* This is how a message is passed through pipes and sockets:
*
* content [ id |nargs|....
* bytes |0|1|2|3| 4 |5....
*
* An int argument is encoded as:
* content | type | value |
* bytes | 0 |1|2|3|4|
*
* Type is TYPE_INT, byte order is big endian (network byte order)
*
* Floating point arguments are coded with type TYPE_FLOAT and the
* value as an integer
*
* String arguments are coded as:
* content |length |string including final '\0'|
* bytes |0|1|2|3|4 .. 4 + len |
*
* Pointer messages cannot be transmited!
*/
static int read_uint32(uint32_t * ret, bg_msg_read_callback_t cb, void * cb_data)
{
uint8_t buf[4];
if(cb(cb_data, buf, 4) < 4)
return 0;
// bg_hexdump(buf, 4);
*ret = (buf[0]<<24) | (buf[1]<<16) |
(buf[2]<<8) | buf[3];
return 1;
}
static int read_uint64(uint64_t * ret, bg_msg_read_callback_t cb, void * cb_data)
{
uint8_t buf[8];
if(cb(cb_data, buf, 8) < 8)
return 0;
*ret =
((gavl_time_t)(buf[0])<<56) |
((gavl_time_t)(buf[1])<<48) |
((gavl_time_t)(buf[2])<<40) |
((gavl_time_t)(buf[3])<<32) |
((gavl_time_t)(buf[4])<<24) |
((gavl_time_t)(buf[5])<<16) |
((gavl_time_t)(buf[6])<<8) |
((gavl_time_t)(buf[7]));
return 1;
}
static float
float32_be_read (unsigned char *cptr)
{ int exponent, mantissa, negative ;
float fvalue ;
negative = cptr [0] & 0x80 ;
exponent = ((cptr [0] & 0x7F) << 1) | ((cptr [1] & 0x80) ? 1 : 0) ;
mantissa = ((cptr [1] & 0x7F) << 16) | (cptr [2] << 8) | (cptr [3]) ;
if (! (exponent || mantissa))
return 0.0 ;
mantissa |= 0x800000 ;
exponent = exponent ? exponent - 127 : 0 ;
fvalue = mantissa ? ((float) mantissa) / ((float) 0x800000) : 0.0 ;
if (negative)
fvalue *= -1 ;
if (exponent > 0)
fvalue *= (1 << exponent) ;
else if (exponent < 0)
fvalue /= (1 << abs (exponent)) ;
return fvalue ;
} /* float32_be_read */
static int read_float(float * ret, bg_msg_read_callback_t cb, void * cb_data)
{
uint8_t buf[4];
if(cb(cb_data, buf, 4) < 4)
return 0;
*ret = float32_be_read(buf);
return 1;
}
static double
double64_be_read (unsigned char *cptr)
{ int exponent, negative ;
double dvalue ;
negative = (cptr [0] & 0x80) ? 1 : 0 ;
exponent = ((cptr [0] & 0x7F) << 4) | ((cptr [1] >> 4) & 0xF) ;
/* Might not have a 64 bit long, so load the mantissa into a double. */
dvalue = (((cptr [1] & 0xF) << 24) | (cptr [2] << 16) | (cptr [3] << 8) | cptr [4]) ;
dvalue += ((cptr [5] << 16) | (cptr [6] << 8) | cptr [7]) / ((double) 0x1000000) ;
if (exponent == 0 && dvalue == 0.0)
return 0.0 ;
dvalue += 0x10000000 ;
exponent = exponent - 0x3FF ;
dvalue = dvalue / ((double) 0x10000000) ;
if (negative)
dvalue *= -1 ;
if (exponent > 0)
dvalue *= (1 << exponent) ;
else if (exponent < 0)
dvalue /= (1 << abs (exponent)) ;
return dvalue ;
} /* double64_be_read */
static int read_double(double * ret, bg_msg_read_callback_t cb, void * cb_data)
{
uint8_t buf[8];
if(cb(cb_data, buf, 8) < 8)
return 0;
*ret = double64_be_read(buf);
return 1;
}
static int read_time(gavl_time_t * ret, bg_msg_read_callback_t cb, void * cb_data)
{
return read_uint64((uint64_t*)ret, cb, cb_data);
}
static int write_uint32(uint32_t * val,
bg_msg_write_callback_t cb, void * cb_data)
{
uint8_t buf[4];
buf[0] = (*val & 0xff000000) >> 24;
buf[1] = (*val & 0x00ff0000) >> 16;
buf[2] = (*val & 0x0000ff00) >> 8;
buf[3] = (*val & 0x000000ff);
// bg_hexdump(buf, 4);
if(cb(cb_data, buf, 4) < 4)
return 0;
return 1;
}
static int write_uint64(uint64_t val,
bg_msg_write_callback_t cb, void * cb_data)
{
uint8_t buf[8];
buf[0] = (val & 0xff00000000000000LL) >> 56;
buf[1] = (val & 0x00ff000000000000LL) >> 48;
buf[2] = (val & 0x0000ff0000000000LL) >> 40;
buf[3] = (val & 0x000000ff00000000LL) >> 32;
buf[4] = (val & 0x00000000ff000000LL) >> 24;
buf[5] = (val & 0x0000000000ff0000LL) >> 16;
buf[6] = (val & 0x000000000000ff00LL) >> 8;
buf[7] = (val & 0x00000000000000ffLL);
if(cb(cb_data, buf, 8) < 8)
return 0;
return 1;
}
static void
float32_be_write (float in, unsigned char *out)
{ int exponent, mantissa, negative = 0 ;
memset (out, 0, sizeof (int)) ;
if (in == 0.0)
return ;
if (in < 0.0)
{ in *= -1.0 ;
negative = 1 ;
} ;
in = frexp (in, &exponent) ;
exponent += 126 ;
in *= (float) 0x1000000 ;
mantissa = (((int) in) & 0x7FFFFF) ;
if (negative)
out [0] |= 0x80 ;
if (exponent & 0x01)
out [1] |= 0x80 ;
out [3] = mantissa & 0xFF ;
out [2] = (mantissa >> 8) & 0xFF ;
out [1] |= (mantissa >> 16) & 0x7F ;
out [0] |= (exponent >> 1) & 0x7F ;
return ;
} /* float32_be_write */
static int write_float(float val, bg_msg_write_callback_t cb, void * cb_data)
{
uint8_t buf[4];
float32_be_write(val, buf);
if(cb(cb_data, buf, 4) < 4)
return 0;
return 1;
}
static void
double64_be_write (double in, unsigned char *out)
{ int exponent, mantissa ;
memset (out, 0, sizeof (double)) ;
if (in == 0.0)
return ;
if (in < 0.0)
{ in *= -1.0 ;
out [0] |= 0x80 ;
} ;
in = frexp (in, &exponent) ;
exponent += 1022 ;
out [0] |= (exponent >> 4) & 0x7F ;
out [1] |= (exponent << 4) & 0xF0 ;
in *= 0x20000000 ;
mantissa = lrint (floor (in)) ;
out [1] |= (mantissa >> 24) & 0xF ;
out [2] = (mantissa >> 16) & 0xFF ;
out [3] = (mantissa >> 8) & 0xFF ;
out [4] = mantissa & 0xFF ;
in = fmod (in, 1.0) ;
in *= 0x1000000 ;
mantissa = lrint (floor (in)) ;
out [5] = (mantissa >> 16) & 0xFF ;
out [6] = (mantissa >> 8) & 0xFF ;
out [7] = mantissa & 0xFF ;
return ;
} /* double64_be_write */
static int write_double(double val, bg_msg_write_callback_t cb, void * cb_data)
{
uint8_t buf[8];
double64_be_write(val, buf);
if(cb(cb_data, buf, 8) < 8)
return 0;
return 1;
}
static int write_time(gavl_time_t val,
bg_msg_write_callback_t cb, void * cb_data)
{
return write_uint64((uint64_t)val, cb, cb_data);
}
int bg_msg_read(bg_msg_t * ret, bg_msg_read_callback_t cb, void * cb_data)
{
int i;
void * ptr;
int32_t val_i;
gavl_time_t val_time;
uint8_t val_u8;
memset(ret, 0, sizeof(*ret));
/* Message ID */
if(!read_uint32((uint32_t*)(&val_i), cb, cb_data))
return 0;
ret->id = val_i;
/* Number of arguments */
if(!cb(cb_data, &val_u8, 1))
return 0;
ret->num_args = val_u8;
for(i = 0; i < ret->num_args; i++)
{
if(!cb(cb_data, &val_u8, 1))
return 0;
ret->args[i].type = val_u8;
switch(ret->args[i].type)
{
case TYPE_INT:
if(!read_uint32((uint32_t*)(&val_i), cb, cb_data))
return 0;
ret->args[i].value.val_i = val_i;
break;
case TYPE_FLOAT:
if(!read_double(&ret->args[i].value.val_f, cb, cb_data))
return 0;
break;
case TYPE_POINTER:
if(!read_uint32((uint32_t*)(&val_i), cb, cb_data)) /* Length */
return 0;
ptr = bg_msg_set_arg_ptr(ret, i, val_i);
if(cb(cb_data, ret->args[i].value.val_ptr, val_i) < val_i)
return 0;
break;
case TYPE_TIME:
if(!read_time(&val_time, cb, cb_data))
return 0;
ret->args[i].value.val_time = val_time;
break;
case TYPE_POINTER_NOCOPY:
break;
case TYPE_COLOR_RGB:
if(!read_float(&ret->args[i].value.val_color[0], cb, cb_data) ||
!read_float(&ret->args[i].value.val_color[1], cb, cb_data) ||
!read_float(&ret->args[i].value.val_color[2], cb, cb_data))
return 0;
break;
case TYPE_COLOR_RGBA:
if(!read_float(&ret->args[i].value.val_color[0], cb, cb_data) ||
!read_float(&ret->args[i].value.val_color[1], cb, cb_data) ||
!read_float(&ret->args[i].value.val_color[2], cb, cb_data) ||
!read_float(&ret->args[i].value.val_color[3], cb, cb_data))
return 0;
break;
}
}
return 1;
}
int bg_msg_write(bg_msg_t * msg, bg_msg_write_callback_t cb,
void * cb_data)
{
int i;
uint8_t val_u8;
/* Message id */
if(!write_uint32(&msg->id, cb, cb_data))
return 0;
/* Number of arguments */
val_u8 = msg->num_args;
if(!cb(cb_data, &val_u8, 1))
return 0;
/* Arguments */
for(i = 0; i < msg->num_args; i++)
{
cb(cb_data, &msg->args[i].type, 1);
switch(msg->args[i].type)
{
case TYPE_INT:
if(!write_uint32((uint32_t*)(&msg->args[i].value.val_i), cb, cb_data))
return 0;
break;
case TYPE_TIME:
if(!write_time(msg->args[i].value.val_time, cb, cb_data))
return 0;
break;
case TYPE_FLOAT:
if(!write_double(msg->args[i].value.val_f, cb, cb_data))
return 0;
break;
case TYPE_POINTER:
if(!write_uint32(&msg->args[i].size, cb, cb_data))
return 0;
if(cb(cb_data, msg->args[i].value.val_ptr, msg->args[i].size) <
msg->args[i].size)
return 0;
break;
case TYPE_POINTER_NOCOPY:
break;
case TYPE_COLOR_RGB:
if(!write_float(msg->args[i].value.val_color[0], cb, cb_data) ||
!write_float(msg->args[i].value.val_color[1], cb, cb_data) ||
!write_float(msg->args[i].value.val_color[2], cb, cb_data))
return 0;
break;
case TYPE_COLOR_RGBA:
if(!write_float(msg->args[i].value.val_color[0], cb, cb_data) ||
!write_float(msg->args[i].value.val_color[1], cb, cb_data) ||
!write_float(msg->args[i].value.val_color[2], cb, cb_data) ||
!write_float(msg->args[i].value.val_color[3], cb, cb_data))
return 0;
break;
}
}
return 1;
}
typedef struct
{
int timeout;
int fd;
} socket_data;
static int socket_read_cb(void * data, uint8_t * ptr, int len)
{
int result;
socket_data * cb_data = (socket_data *)data;
result = bg_socket_read_data(cb_data->fd,
ptr, len, cb_data->timeout);
/* After a successful read, timeout must be disabled */
if(result && cb_data->timeout >= 0)
cb_data->timeout = -1;
return result;
}
static int socket_write_cb(void * data, const uint8_t * ptr, int len)
{
socket_data * cb_data = (socket_data *)data;
return bg_socket_write_data(cb_data->fd, ptr, len);
}
int bg_msg_read_socket(bg_msg_t * ret, int fd, int milliseconds)
{
socket_data cb_data;
cb_data.fd = fd;
cb_data.timeout = milliseconds;
return bg_msg_read(ret, socket_read_cb, &cb_data);
}
int bg_msg_write_socket(bg_msg_t * msg, int fd)
{
socket_data cb_data;
cb_data.fd = fd;
cb_data.timeout = 0;
return bg_msg_write(msg, socket_write_cb, &cb_data);
}
gmerlin-1.2.0~dfsg/lib/streaminfo.c 0000644 0001750 0001750 00000007054 11764363410 017165 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#define MY_FREE(ptr) \
if(ptr) \
{ \
free(ptr); \
ptr = NULL; \
}
static void bg_audio_info_free(bg_audio_info_t * info)
{
gavl_metadata_free(&info->m);
}
static void bg_video_info_free(bg_video_info_t * info)
{
gavl_metadata_free(&info->m);
}
static void bg_subtitle_info_free(bg_subtitle_info_t * info)
{
gavl_metadata_free(&info->m);
}
void bg_track_info_free(bg_track_info_t * info)
{
int i;
if(info->audio_streams)
{
for(i = 0; i < info->num_audio_streams; i++)
bg_audio_info_free(&info->audio_streams[i]);
MY_FREE(info->audio_streams);
}
if(info->video_streams)
{
for(i = 0; i < info->num_video_streams; i++)
bg_video_info_free(&info->video_streams[i]);
MY_FREE(info->video_streams);
}
if(info->subtitle_streams)
{
for(i = 0; i < info->num_subtitle_streams; i++)
bg_subtitle_info_free(&info->subtitle_streams[i]);
MY_FREE(info->subtitle_streams);
}
gavl_metadata_free(&info->metadata);
if(info->chapter_list)
bg_chapter_list_destroy(info->chapter_list);
MY_FREE(info->name);
MY_FREE(info->url);
memset(info, 0, sizeof(*info));
}
void bg_set_track_name_default(bg_track_info_t * info,
const char * location)
{
const char * start_pos;
const char * end_pos;
if(bg_string_is_url(location))
{
info->name = bg_strdup(info->name, location);
return;
}
start_pos = strrchr(location, '/');
if(start_pos)
start_pos++;
else
start_pos = location;
end_pos = strrchr(start_pos, '.');
if(!end_pos)
end_pos = &start_pos[strlen(start_pos)];
info->name = bg_strndup(info->name, start_pos, end_pos);
}
char * bg_get_track_name_default(const char * location, int track, int num_tracks)
{
const char * start_pos;
const char * end_pos;
char * tmp_string, *ret;
if(bg_string_is_url(location))
{
tmp_string = bg_strdup(NULL, location);
}
else
{
start_pos = strrchr(location, '/');
if(start_pos)
start_pos++;
else
start_pos = location;
end_pos = strrchr(start_pos, '.');
if(!end_pos)
end_pos = &start_pos[strlen(start_pos)];
tmp_string = bg_system_to_utf8(start_pos, end_pos - start_pos);
}
if(num_tracks < 2)
return tmp_string;
else
{
ret = bg_sprintf("%s [%d]", tmp_string, track + 1);
free(tmp_string);
return ret;
}
}
gmerlin-1.2.0~dfsg/lib/pluginreg_xml.c 0000644 0001750 0001750 00000051534 11764363410 017674 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
static const struct
{
char * name;
bg_plugin_type_t type;
}
type_names[] =
{
{ "Input", BG_PLUGIN_INPUT },
{ "OutputAudio", BG_PLUGIN_OUTPUT_AUDIO },
{ "OutputVideo", BG_PLUGIN_OUTPUT_VIDEO },
{ "AudioRecorder", BG_PLUGIN_RECORDER_AUDIO },
{ "VideoRecorder", BG_PLUGIN_RECORDER_VIDEO },
{ "EncoderAudio", BG_PLUGIN_ENCODER_AUDIO },
{ "EncoderVideo", BG_PLUGIN_ENCODER_VIDEO },
{ "EncoderSubtitleText", BG_PLUGIN_ENCODER_SUBTITLE_TEXT },
{ "EncoderSubtitleOverlay", BG_PLUGIN_ENCODER_SUBTITLE_OVERLAY },
{ "Encoder", BG_PLUGIN_ENCODER },
{ "EncoderPP", BG_PLUGIN_ENCODER_PP },
{ "ImageReader", BG_PLUGIN_IMAGE_READER },
{ "ImageWriter", BG_PLUGIN_IMAGE_WRITER },
{ "AudioFilter", BG_PLUGIN_FILTER_AUDIO },
{ "VideoFilter", BG_PLUGIN_FILTER_VIDEO },
{ "Visualization", BG_PLUGIN_VISUALIZATION },
{ NULL, BG_PLUGIN_NONE }
};
static const struct
{
char * name;
int api;
}
api_names[] =
{
{ "gmerlin", BG_PLUGIN_API_GMERLIN },
{ "ladspa", BG_PLUGIN_API_LADSPA },
{ "lv", BG_PLUGIN_API_LV },
{ "frei0r", BG_PLUGIN_API_FREI0R },
{ NULL, BG_PLUGIN_NONE }
};
static const struct
{
char * name;
int flag;
}
flag_names[] =
{
{ "Removable", BG_PLUGIN_REMOVABLE }, /* Removable media (CD, DVD etc.) */
{ "Recorder", BG_PLUGIN_RECORDER }, /* Plugin can record */
{ "File", BG_PLUGIN_FILE }, /* Plugin reads/writes files */
{ "URL", BG_PLUGIN_URL }, /* Plugin reads URLs or streams */
{ "Playback", BG_PLUGIN_PLAYBACK }, /* Output plugins for playback */
{ "Stdin", BG_PLUGIN_STDIN }, /* Plugin reads from stdin */
{ "Tuner", BG_PLUGIN_TUNER }, /* Plugin has tuner */
{ "Filter1", BG_PLUGIN_FILTER_1 }, /* Filter with one input port */
{ "EmbedWindow", BG_PLUGIN_EMBED_WINDOW },
{ "VisualizeFrame", BG_PLUGIN_VISUALIZE_FRAME },
{ "VisualizeGL", BG_PLUGIN_VISUALIZE_GL },
{ "PP", BG_PLUGIN_PP },
{ "Callbacks", BG_PLUGIN_CALLBACKS },
{ "Broadcast", BG_PLUGIN_BROADCAST },
{ "Devparam", BG_PLUGIN_DEVPARAM },
{ "Unsupported", BG_PLUGIN_UNSUPPORTED },
{ NULL, 0 },
};
static const char * const plugin_key = "PLUGIN";
static const char * const plugin_registry_key = "PLUGIN_REGISTRY";
static const char * const name_key = "NAME";
static const char * const long_name_key = "LONG_NAME";
static const char * const description_key = "DESCRIPTION";
static const char * const mimetypes_key = "MIMETYPES";
static const char * const extensions_key = "EXTENSIONS";
static const char * const protocols_key = "PROTOCOLS";
static const char * const module_filename_key = "MODULE_FILENAME";
static const char * const module_time_key = "MODULE_TIME";
static const char * const type_key = "TYPE";
static const char * const flags_key = "FLAGS";
static const char * const priority_key = "PRIORITY";
static const char * const device_info_key = "DEVICE_INFO";
static const char * const device_key = "DEVICE";
static const char * const max_audio_streams_key = "MAX_AUDIO_STREAMS";
static const char * const max_video_streams_key = "MAX_VIDEO_STREAMS";
static const char * const max_subtitle_text_streams_key = "MAX_SUBTITLE_TEXT_STREAMS";
static const char * const max_subtitle_overlay_streams_key = "MAX_SUBTITLE_OVERLAY_STREAMS";
static const char * const parameters_key = "PARAMETERS";
static const char * const audio_parameters_key = "AUDIO_PARAMETERS";
static const char * const video_parameters_key = "VIDEO_PARAMETERS";
static const char * const subtitle_text_parameters_key = "SUBTITLE_TEXT_PARAMETERS";
static const char * const subtitle_overlay_parameters_key = "SUBTITLE_OVERLAY_PARAMETERS";
static const char * const gettext_domain_key = "GETTEXT_DOMAIN";
static const char * const gettext_directory_key = "GETTEXT_DIRECTORY";
static const char * const api_key = "API";
static const char * const index_key = "INDEX";
static bg_device_info_t *
load_device(bg_device_info_t * arr, xmlDocPtr doc, xmlNodePtr node)
{
char * tmp_string;
xmlNodePtr cur;
char * device = NULL;
char * name = NULL;
cur = node->children;
while(cur)
{
if(!cur->name)
{
cur = cur->next;
continue;
}
tmp_string = (char*)xmlNodeListGetString(doc, cur->children, 1);
if(!BG_XML_STRCMP(cur->name, name_key))
{
name = tmp_string;
tmp_string = NULL;
}
else if(!BG_XML_STRCMP(cur->name, device_key))
{
device = tmp_string;
tmp_string = NULL;
}
if(tmp_string)
free(tmp_string);
cur = cur->next;
}
if(device)
{
arr = bg_device_info_append(arr,
device,
name);
xmlFree(device);
}
if(name)
{
xmlFree(name);
}
return arr;
}
static bg_plugin_info_t * load_plugin(xmlDocPtr doc, xmlNodePtr node)
{
char * tmp_string;
xmlNodePtr cur;
int index;
char * start_ptr;
char * end_ptr;
bg_plugin_info_t * ret;
ret = calloc(1, sizeof(*ret));
cur = node->children;
while(cur)
{
if(!cur->name)
{
cur = cur->next;
continue;
}
if(!BG_XML_STRCMP(cur->name, parameters_key))
{
ret->parameters = bg_xml_2_parameters(doc, cur);
cur = cur->next;
continue;
}
else if(!BG_XML_STRCMP(cur->name, audio_parameters_key))
{
ret->audio_parameters = bg_xml_2_parameters(doc, cur);
cur = cur->next;
continue;
}
else if(!BG_XML_STRCMP(cur->name, video_parameters_key))
{
ret->video_parameters = bg_xml_2_parameters(doc, cur);
cur = cur->next;
continue;
}
else if(!BG_XML_STRCMP(cur->name, subtitle_text_parameters_key))
{
ret->subtitle_text_parameters = bg_xml_2_parameters(doc, cur);
cur = cur->next;
continue;
}
else if(!BG_XML_STRCMP(cur->name, subtitle_overlay_parameters_key))
{
ret->subtitle_overlay_parameters = bg_xml_2_parameters(doc, cur);
cur = cur->next;
continue;
}
tmp_string = (char*)xmlNodeListGetString(doc, cur->children, 1);
if(!BG_XML_STRCMP(cur->name, name_key))
{
ret->name = bg_strdup(ret->name, tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, long_name_key))
{
ret->long_name = bg_strdup(ret->long_name, tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, description_key))
{
ret->description = bg_strdup(ret->description, tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, mimetypes_key))
{
ret->mimetypes = bg_strdup(ret->mimetypes, tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, extensions_key))
{
ret->extensions = bg_strdup(ret->extensions, tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, protocols_key))
{
ret->protocols = bg_strdup(ret->protocols, tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, module_filename_key))
{
ret->module_filename = bg_strdup(ret->module_filename, tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, gettext_domain_key))
{
ret->gettext_domain = bg_strdup(ret->gettext_domain, tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, gettext_directory_key))
{
ret->gettext_directory = bg_strdup(ret->gettext_directory, tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, module_time_key))
{
sscanf(tmp_string, "%ld", &ret->module_time);
}
else if(!BG_XML_STRCMP(cur->name, priority_key))
{
sscanf(tmp_string, "%d", &ret->priority);
}
else if(!BG_XML_STRCMP(cur->name, index_key))
{
sscanf(tmp_string, "%d", &ret->index);
}
else if(!BG_XML_STRCMP(cur->name, type_key))
{
index = 0;
while(type_names[index].name)
{
if(!strcmp(tmp_string, type_names[index].name))
{
ret->type = type_names[index].type;
break;
}
index++;
}
}
else if(!BG_XML_STRCMP(cur->name, api_key))
{
index = 0;
while(api_names[index].name)
{
if(!strcmp(tmp_string, api_names[index].name))
{
ret->api = api_names[index].api;
break;
}
index++;
}
}
else if(!BG_XML_STRCMP(cur->name, flags_key))
{
start_ptr = tmp_string;
while(1)
{
if(!start_ptr) break;
end_ptr = strchr(start_ptr, '|');
if(!end_ptr)
end_ptr = &start_ptr[strlen(start_ptr)];
index = 0;
while(flag_names[index].name)
{
if(!strncmp(flag_names[index].name, start_ptr, end_ptr - start_ptr))
ret->flags |= flag_names[index].flag;
index++;
}
if(*end_ptr == '\0')
break;
start_ptr = end_ptr;
start_ptr++;
}
}
else if(!BG_XML_STRCMP(cur->name, device_info_key))
{
ret->devices = load_device(ret->devices, doc, cur);
}
else if(!BG_XML_STRCMP(cur->name, max_audio_streams_key))
{
ret->max_audio_streams = atoi(tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, max_video_streams_key))
{
ret->max_video_streams = atoi(tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, max_subtitle_text_streams_key))
{
ret->max_subtitle_text_streams = atoi(tmp_string);
}
else if(!BG_XML_STRCMP(cur->name, max_subtitle_overlay_streams_key))
{
ret->max_subtitle_overlay_streams = atoi(tmp_string);
}
xmlFree(tmp_string);
cur = cur->next;
}
return ret;
}
static const char * get_flag_name(uint32_t flag)
{
int index = 0;
while(flag_names[index].name)
{
if(flag_names[index].flag == flag)
break;
else
index++;
}
return flag_names[index].name;
}
static void save_devices(xmlNodePtr parent, const bg_device_info_t * info)
{
int i;
xmlNodePtr xml_device, xml_item;
i = 0;
while(info[i].device)
{
xmlAddChild(parent, BG_XML_NEW_TEXT("\n"));
xml_device = xmlNewTextChild(parent, NULL,
(xmlChar*)device_info_key, NULL);
xmlAddChild(xml_device, BG_XML_NEW_TEXT("\n"));
xml_item = xmlNewTextChild(xml_device, NULL, (xmlChar*)device_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info[i].device));
xmlAddChild(xml_device, BG_XML_NEW_TEXT("\n"));
if(info[i].name)
{
xml_item = xmlNewTextChild(xml_device, NULL, (xmlChar*)name_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info[i].name));
xmlAddChild(xml_device, BG_XML_NEW_TEXT("\n"));
}
i++;
}
}
static void save_plugin(xmlNodePtr parent, const bg_plugin_info_t * info)
{
char buffer[1024];
int index;
int i;
int num_flags;
const char * flag_name;
uint32_t flag;
xmlNodePtr xml_plugin;
xmlNodePtr xml_item;
// fprintf(stderr, "Save plugin: %s\n", info->name);
xmlAddChild(parent, BG_XML_NEW_TEXT("\n"));
xml_plugin = xmlNewTextChild(parent, NULL,
(xmlChar*)plugin_key, NULL);
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)name_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info->name));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)long_name_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info->long_name));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)description_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info->description));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)module_filename_key,
NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info->module_filename));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
if(info->extensions)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)extensions_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info->extensions));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->protocols)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)protocols_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info->protocols));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->mimetypes)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)mimetypes_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info->mimetypes));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->gettext_domain)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)gettext_domain_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info->gettext_domain));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->gettext_directory)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)gettext_directory_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(info->gettext_directory));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)module_time_key, NULL);
sprintf(buffer, "%ld", info->module_time);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)priority_key, NULL);
sprintf(buffer, "%d", info->priority);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
if(info->parameters)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)parameters_key, NULL);
bg_parameters_2_xml(info->parameters, xml_item);
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->audio_parameters)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)audio_parameters_key, NULL);
bg_parameters_2_xml(info->audio_parameters, xml_item);
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->video_parameters)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)video_parameters_key, NULL);
bg_parameters_2_xml(info->video_parameters, xml_item);
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->subtitle_text_parameters)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)subtitle_text_parameters_key, NULL);
bg_parameters_2_xml(info->subtitle_text_parameters, xml_item);
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->subtitle_overlay_parameters)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)subtitle_overlay_parameters_key, NULL);
bg_parameters_2_xml(info->subtitle_overlay_parameters, xml_item);
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->type & (BG_PLUGIN_ENCODER_AUDIO|
BG_PLUGIN_ENCODER_VIDEO|
BG_PLUGIN_ENCODER |
BG_PLUGIN_ENCODER_SUBTITLE_TEXT |
BG_PLUGIN_ENCODER_SUBTITLE_OVERLAY))
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)max_audio_streams_key, NULL);
sprintf(buffer, "%d", info->max_audio_streams);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)max_video_streams_key, NULL);
sprintf(buffer, "%d", info->max_video_streams);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)max_subtitle_text_streams_key, NULL);
sprintf(buffer, "%d", info->max_subtitle_text_streams);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)max_subtitle_overlay_streams_key, NULL);
sprintf(buffer, "%d", info->max_subtitle_overlay_streams);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
index = 0;
while(type_names[index].name)
{
if(info->type == type_names[index].type)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)type_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(type_names[index].name));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
break;
}
index++;
}
if(info->api)
{
index = 0;
while(api_names[index].name)
{
if(info->api == api_names[index].api)
{
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)api_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(api_names[index].name));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
break;
}
index++;
}
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)index_key, NULL);
sprintf(buffer, "%d", info->index);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
/* Write flags */
if(info->flags)
{
num_flags = 0;
for(i = 0; i < 32; i++)
{
flag = (1<flags & flag)
num_flags++;
}
buffer[0] = '\0';
index = 0;
for(i = 0; i < 32; i++)
{
flag = (1<flags & flag))
continue;
flag_name = get_flag_name(flag);
strcat(buffer, flag_name);
if(index < num_flags-1)
strcat(buffer, "|");
index++;
}
xml_item = xmlNewTextChild(xml_plugin, NULL, (xmlChar*)flags_key, NULL);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
xmlAddChild(xml_plugin, BG_XML_NEW_TEXT("\n"));
}
if(info->devices && info->devices->device)
save_devices(xml_plugin, info->devices);
}
bg_plugin_info_t * bg_plugin_registry_load(const char * filename)
{
bg_plugin_info_t * ret;
bg_plugin_info_t * end;
bg_plugin_info_t * new;
xmlDocPtr xml_doc;
xmlNodePtr node;
ret = NULL;
end = NULL;
xml_doc = bg_xml_parse_file(filename);
if(!xml_doc)
return NULL;
node = xml_doc->children;
if(BG_XML_STRCMP(node->name, plugin_registry_key))
{
xmlFreeDoc(xml_doc);
return NULL;
}
node = node->children;
while(node)
{
if(node->name && !BG_XML_STRCMP(node->name, plugin_key))
{
new = load_plugin(xml_doc, node);
if(!new->module_filename)
bg_plugin_info_destroy(new);
else if(!ret)
{
ret = new;
end = ret;
}
else
{
end->next = new;
end = end->next;
}
}
node = node->next;
}
xmlFreeDoc(xml_doc);
return ret;
}
void bg_plugin_registry_save(bg_plugin_info_t * info)
{
xmlDocPtr xml_doc;
xmlNodePtr xml_registry;
char * filename;
filename = bg_search_file_write("", "plugins.xml");
if(!filename)
{
return;
}
xml_doc = xmlNewDoc((xmlChar*)"1.0");
xml_registry = xmlNewDocRawNode(xml_doc, NULL, (xmlChar*)plugin_registry_key, NULL);
xmlDocSetRootElement(xml_doc, xml_registry);
while(info)
{
if(info->module_filename) /* We save only external plugins */
save_plugin(xml_registry, info);
info = info->next;
}
xmlAddChild(xml_registry, BG_XML_NEW_TEXT("\n"));
xmlSaveFile(filename, xml_doc);
xmlFreeDoc(xml_doc);
free(filename);
}
gmerlin-1.2.0~dfsg/lib/recorder_audio.c 0000644 0001750 0001750 00000023270 11764363410 020002 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#define LOG_DOMAIN "recorder.audio"
#include
void bg_recorder_create_audio(bg_recorder_t * rec)
{
bg_recorder_audio_stream_t * as = &rec->as;
as->output_cnv = gavl_audio_converter_create();
as->enc_cnv = gavl_audio_converter_create();
bg_gavl_audio_options_init(&as->opt);
as->fc = bg_audio_filter_chain_create(&as->opt, rec->plugin_reg);
as->th = bg_player_thread_create(rec->tc);
as->pd = gavl_peak_detector_create();
pthread_mutex_init(&as->eof_mutex, NULL);
}
void bg_recorder_audio_set_eof(bg_recorder_audio_stream_t * s, int eof)
{
pthread_mutex_lock(&s->eof_mutex);
s->eof = eof;
pthread_mutex_unlock(&s->eof_mutex);
}
int bg_recorder_audio_get_eof(bg_recorder_audio_stream_t * s)
{
int ret;
pthread_mutex_lock(&s->eof_mutex);
ret = s->eof;
pthread_mutex_unlock(&s->eof_mutex);
return ret;
}
void bg_recorder_destroy_audio(bg_recorder_t * rec)
{
bg_recorder_audio_stream_t * as = &rec->as;
gavl_audio_converter_destroy(as->output_cnv);
gavl_audio_converter_destroy(as->enc_cnv);
bg_audio_filter_chain_destroy(as->fc);
bg_player_thread_destroy(as->th);
gavl_peak_detector_destroy(as->pd);
pthread_mutex_destroy(&as->eof_mutex);
gavl_metadata_free(&as->m);
}
static const bg_parameter_info_t parameters[] =
{
{
.name = "do_audio",
.long_name = TRS("Record audio"),
.type = BG_PARAMETER_CHECKBUTTON,
.val_default = { .val_i = 1 },
},
{
.name = "plugin",
.long_name = TRS("Plugin"),
.type = BG_PARAMETER_MULTI_MENU,
.flags = BG_PARAMETER_PLUGIN,
},
{
.name = "language",
.long_name = TRS("Language"),
.type = BG_PARAMETER_STRINGLIST,
.val_default = { .val_str = "eng" },
.multi_names = bg_language_codes,
.multi_labels = bg_language_labels,
},
{ },
};
const bg_parameter_info_t *
bg_recorder_get_audio_parameters(bg_recorder_t * rec)
{
bg_recorder_audio_stream_t * as = &rec->as;
if(!as->parameters)
{
as->parameters = bg_parameter_info_copy_array(parameters);
bg_plugin_registry_set_parameter_info(rec->plugin_reg,
BG_PLUGIN_RECORDER_AUDIO,
BG_PLUGIN_RECORDER,
&as->parameters[1]);
}
return as->parameters;
}
void
bg_recorder_set_audio_parameter(void * data,
const char * name,
const bg_parameter_value_t * val)
{
bg_recorder_t * rec = data;
bg_recorder_audio_stream_t * as = &rec->as;
if(!name)
return;
// if(name)
// fprintf(stderr, "bg_recorder_set_audio_parameter %s\n", name);
if(!strcmp(name, "do_audio"))
{
if(!!(as->flags & STREAM_ACTIVE) != val->val_i)
bg_recorder_interrupt(rec);
if(val->val_i)
as->flags |= STREAM_ACTIVE;
else
as->flags &= ~STREAM_ACTIVE;
}
else if(!strcmp(name, "language"))
gavl_metadata_set(&as->m, GAVL_META_LANGUAGE, val->val_str);
else if(!strcmp(name, "plugin"))
{
const bg_plugin_info_t * info;
if(as->input_handle &&
!strcmp(as->input_handle->info->name, val->val_str))
return;
if(rec->flags & FLAG_RUNNING)
bg_recorder_interrupt(rec);
if(as->input_handle)
bg_plugin_unref(as->input_handle);
info = bg_plugin_find_by_name(rec->plugin_reg, val->val_str);
as->input_handle = bg_plugin_load(rec->plugin_reg, info);
as->input_plugin = (bg_recorder_plugin_t*)(as->input_handle->plugin);
if(as->input_plugin->set_callbacks)
as->input_plugin->set_callbacks(as->input_handle->priv, &rec->recorder_cb);
}
else if(as->input_handle && as->input_plugin->common.set_parameter)
{
as->input_plugin->common.set_parameter(as->input_handle->priv, name, val);
}
}
const bg_parameter_info_t *
bg_recorder_get_audio_filter_parameters(bg_recorder_t * rec)
{
bg_recorder_audio_stream_t * as = &rec->as;
return bg_audio_filter_chain_get_parameters(as->fc);
}
void
bg_recorder_set_audio_filter_parameter(void * data,
const char * name,
const bg_parameter_value_t * val)
{
int need_restart;
bg_recorder_t * rec = data;
bg_recorder_audio_stream_t * as = &rec->as;
if(!name)
{
if(!(rec->flags & FLAG_RUNNING))
bg_recorder_resume(rec);
return;
}
bg_recorder_interrupt(rec);
bg_audio_filter_chain_lock(as->fc);
bg_audio_filter_chain_set_parameter(as->fc, name, val);
if(bg_audio_filter_chain_need_restart(as->fc))
need_restart = 1;
else
need_restart = 0;
bg_audio_filter_chain_unlock(as->fc);
}
void * bg_recorder_audio_thread(void * data)
{
double peaks[2]; /* Doesn't work for > 2 channels!! */
gavl_time_t idle_time = GAVL_TIME_SCALE / 100; // 10 ms
bg_recorder_t * rec = data;
bg_recorder_audio_stream_t * as = &rec->as;
bg_player_thread_wait_for_start(as->th);
while(1)
{
if(!bg_player_thread_check(as->th))
break;
if(bg_recorder_audio_get_eof(as))
{
gavl_time_delay(&idle_time);
continue;
}
if(!as->in_func(as->in_data, as->pipe_frame, as->in_stream,
as->pipe_format.samples_per_frame))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Read failed (device unplugged?)");
bg_recorder_audio_set_eof(as, 1);
continue; // Need to go to bg_player_thread_check to stop the thread cleanly
}
/* Peak detection */
gavl_peak_detector_update(as->pd, as->pipe_frame);
gavl_peak_detector_get_peaks(as->pd, NULL, NULL, peaks);
if(as->pipe_format.num_channels == 1)
peaks[1] = peaks[0];
bg_recorder_msg_audiolevel(rec, peaks, as->pipe_frame->valid_samples);
gavl_peak_detector_reset(as->pd);
/* Encoding */
if(as->flags & STREAM_ENCODE_OPEN)
{
bg_recorder_update_time(rec,
gavl_time_unscale(as->pipe_format.samplerate,
as->pipe_frame->timestamp +
as->pipe_frame->valid_samples));
if(as->do_convert_enc)
{
gavl_audio_convert(as->enc_cnv, as->pipe_frame, as->enc_frame);
bg_encoder_write_audio_frame(rec->enc, as->enc_frame, as->enc_index);
}
else
bg_encoder_write_audio_frame(rec->enc, as->pipe_frame, as->enc_index);
}
}
return NULL;
}
int bg_recorder_audio_init(bg_recorder_t * rec)
{
bg_recorder_audio_stream_t * as = &rec->as;
/* Open input */
if(!as->input_plugin->open(as->input_handle->priv, &as->input_format, NULL))
{
return 0;
}
as->flags |= STREAM_INPUT_OPEN;
as->in_func = as->input_plugin->read_audio;
as->in_stream = 0;
as->in_data = as->input_handle->priv;
/* Set up filter chain */
bg_audio_filter_chain_connect_input(as->fc,
as->in_func,
as->in_data,
as->in_stream);
as->in_func = bg_audio_filter_chain_read;
as->in_data = as->fc;
as->in_stream = 0;
bg_audio_filter_chain_init(as->fc, &as->input_format, &as->pipe_format);
/* Set up peak detection */
gavl_peak_detector_set_format(as->pd, &as->pipe_format);
/* Create frame(s) */
as->pipe_frame = gavl_audio_frame_create(&as->pipe_format);
/* Set up output */
if(as->flags & STREAM_ENCODE)
{
as->enc_index = bg_encoder_add_audio_stream(rec->enc, &as->m,
&as->pipe_format, 0);
}
return 1;
}
void bg_recorder_audio_cleanup(bg_recorder_t * rec)
{
bg_recorder_audio_stream_t * as = &rec->as;
if(as->flags & STREAM_INPUT_OPEN)
as->input_plugin->close(as->input_handle->priv);
if(as->pipe_frame)
{
gavl_audio_frame_destroy(as->pipe_frame);
as->pipe_frame = NULL;
}
if(as->enc_frame)
{
gavl_audio_frame_destroy(as->enc_frame);
as->enc_frame = NULL;
}
as->flags &= ~(STREAM_INPUT_OPEN | STREAM_ENCODE_OPEN);
}
void bg_recorder_audio_finalize_encode(bg_recorder_t * rec)
{
bg_recorder_audio_stream_t * as = &rec->as;
bg_encoder_get_audio_format(rec->enc, as->enc_index, &as->enc_format);
as->do_convert_enc = gavl_audio_converter_init(as->enc_cnv, &as->pipe_format,
&as->enc_format);
if(as->do_convert_enc)
as->enc_frame = gavl_audio_frame_create(&as->enc_format);
as->flags |= STREAM_ENCODE_OPEN;
}
gmerlin-1.2.0~dfsg/lib/transcoder_track.c 0000644 0001750 0001750 00000177676 11764363410 020370 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LOG_DOMAIN "transcoder_track"
#include
static void create_sections(bg_transcoder_track_t * t,
bg_cfg_section_t * track_defaults_section,
bg_cfg_section_t * input_section,
bg_cfg_section_t * encoder_section,
bg_track_info_t * track_info)
{
int i, in_index;
const char * tag;
bg_cfg_section_t * general_section;
bg_cfg_section_t * filter_section;
bg_cfg_section_t * textrenderer_section;
t->input_section = bg_cfg_section_copy(input_section);
general_section =
bg_cfg_section_find_subsection(track_defaults_section, "general");
t->general_section = bg_cfg_section_copy(general_section);
/* The parameters which were initially hidden are not
present in the general section.
Therefore, we will create the missing items now */
i = 0;
while(t->general_parameters[i].name)
{
bg_cfg_section_set_parameter(t->general_section,
&t->general_parameters[i],
&t->general_parameters[i].val_default);
i++;
}
/* Stop here for redirectors */
if(t->url)
return;
t->metadata_section =
bg_cfg_section_create_from_parameters("Metadata", t->metadata_parameters);
if(t->num_audio_streams)
{
general_section =
bg_cfg_section_find_subsection(track_defaults_section, "audio");
filter_section =
bg_cfg_section_find_subsection(track_defaults_section, "audiofilters");
for(i = 0; i < t->num_audio_streams; i++)
{
t->audio_streams[i].general_section = bg_cfg_section_copy(general_section);
t->audio_streams[i].filter_section = bg_cfg_section_copy(filter_section);
tag = gavl_metadata_get(&track_info->audio_streams[i].m,
GAVL_META_LANGUAGE);
if(tag)
bg_cfg_section_set_parameter_string(t->audio_streams[i].general_section,
"in_language", tag);
}
}
if(t->num_video_streams)
{
general_section = bg_cfg_section_find_subsection(track_defaults_section,
"video");
filter_section =
bg_cfg_section_find_subsection(track_defaults_section, "videofilters");
for(i = 0; i < t->num_video_streams; i++)
{
t->video_streams[i].general_section = bg_cfg_section_copy(general_section);
t->video_streams[i].filter_section = bg_cfg_section_copy(filter_section);
}
}
if(t->num_subtitle_text_streams)
{
general_section = bg_cfg_section_find_subsection(track_defaults_section,
"subtitle_text");
textrenderer_section = bg_cfg_section_find_subsection(track_defaults_section,
"textrenderer");
for(i = 0; i < t->num_subtitle_text_streams; i++)
{
t->subtitle_text_streams[i].general_section = bg_cfg_section_copy(general_section);
t->subtitle_text_streams[i].textrenderer_section = bg_cfg_section_copy(textrenderer_section);
in_index = t->subtitle_text_streams[i].in_index;
tag = gavl_metadata_get(&track_info->subtitle_streams[i].m,
GAVL_META_LANGUAGE);
if(tag)
bg_cfg_section_set_parameter_string(t->subtitle_text_streams[i].general_section,
"in_language",
tag);
}
}
if(t->num_subtitle_overlay_streams)
{
general_section = bg_cfg_section_find_subsection(track_defaults_section,
"subtitle_overlay");
for(i = 0; i < t->num_subtitle_overlay_streams; i++)
{
t->subtitle_overlay_streams[i].general_section = bg_cfg_section_copy(general_section);
in_index = t->subtitle_overlay_streams[i].in_index;
tag = gavl_metadata_get(&track_info->subtitle_streams[in_index].m,
GAVL_META_LANGUAGE);
if(tag)
bg_cfg_section_set_parameter_string(t->subtitle_overlay_streams[i].general_section,
"in_language", tag);
}
}
}
static const bg_parameter_info_t parameters_general[] =
{
{
.name = "name",
.long_name = TRS("Name"),
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "location",
.long_name = TRS("Location"),
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "plugin",
.long_name = TRS("Plugin"),
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "prefer_edl",
.long_name = TRS("Prefer EDL"),
.type = BG_PARAMETER_CHECKBUTTON,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "track",
.long_name = TRS("Track"),
.type = BG_PARAMETER_INT,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "subdir",
.long_name = TRS("Subdirectory"),
.type = BG_PARAMETER_STRING,
.help_string = TRS("Subdirectory, where this track will be written to"),
},
{
.name = "duration",
.long_name = "Duration",
.type = BG_PARAMETER_TIME,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "flags",
.long_name = "Flags",
.type = BG_PARAMETER_INT,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "audio_encoder",
.long_name = "Audio encoder",
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "video_encoder",
.long_name = "Video encoder",
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "subtitle_text_encoder",
.long_name = "Subtitle text encoder",
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "subtitle_overlay_encoder",
.long_name = "Subtitle overlay encoder",
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "pp_only",
.long_name = TRS("Postprocess only"),
.type = BG_PARAMETER_CHECKBUTTON,
.help_string = TRS("Skip transcoding of this track and send the file directly to the postprocessor."),
},
{
.name = "set_start_time",
.long_name = TRS("Set start time"),
.type = BG_PARAMETER_CHECKBUTTON,
.flags = BG_PARAMETER_HIDE_DIALOG,
.val_default = { .val_i = 0 },
.help_string = TRS("Specify a start time below. This time will be slightly wrong if the input \
format doesn't support sample accurate seeking.")
},
{
.name = "start_time",
.long_name = TRS("Start time"),
.type = BG_PARAMETER_TIME,
.flags = BG_PARAMETER_HIDE_DIALOG,
.val_default = { .val_time = 0 }
},
{
.name = "set_end_time",
.long_name = TRS("Set end time"),
.type = BG_PARAMETER_CHECKBUTTON,
.flags = BG_PARAMETER_HIDE_DIALOG,
.val_default = { .val_i = 0 },
.help_string = TRS("Specify an end time below.")
},
{
.name = "end_time",
.long_name = TRS("End time"),
.type = BG_PARAMETER_TIME,
.flags = BG_PARAMETER_HIDE_DIALOG,
.val_default = { .val_time = 0 }
},
{ /* End of parameters */ }
};
/* Subtitle text parameters */
static const bg_parameter_info_t general_parameters_subtitle_text[] =
{
{
.name = "action",
.long_name = TRS("Action"),
.type = BG_PARAMETER_STRINGLIST,
.val_default = { .val_str = "forget" },
.multi_names = (char const *[]){ "forget",
"transcode",
"transcode_overlay",
"blend",
NULL },
.multi_labels = (char const *[]){ TRS("Forget"),
TRS("Transcode as text"),
TRS("Transcode as overlay"),
TRS("Blend onto video"),
NULL },
.help_string = TRS("Select action for this subtitle stream.")
},
{
.name = "in_language",
.long_name = TRS("Input Language"),
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "language",
.long_name = TRS("Language"),
.type = BG_PARAMETER_STRINGLIST,
.val_default = { .val_str = "eng" },
.multi_names = bg_language_codes,
.multi_labels = bg_language_labels,
},
{
.name = "force_language",
.long_name = TRS("Force language"),
.type = BG_PARAMETER_CHECKBUTTON,
.val_default = { .val_i = 1 },
.help_string = TRS("Force the given language even if the input has the language set differently.")
},
{
.name = "time_offset",
.long_name = TRS("Time offset"),
.flags = BG_PARAMETER_SYNC,
.type = BG_PARAMETER_FLOAT,
.val_min = { .val_f = -600.0 },
.val_max = { .val_f = 600.0 },
.num_digits = 3,
},
{
.name = "video_stream",
.long_name = TRS("Video stream"),
.type = BG_PARAMETER_INT,
.flags = BG_PARAMETER_HIDE_DIALOG,
.val_default = { .val_i = 1 },
.val_min = { .val_i = 1 },
.help_string = TRS("Attach subtitle stream to this video stream. For blending, this video stream will\
get the subtitles. For encoding, take frame dimensions and framerate from this video stream as they are\
sometimes needed by subtitle encoders.")
},
{ /* End of parameters */ }
};
/* Subtitle overlay parameters */
static const bg_parameter_info_t general_parameters_subtitle_overlay[] =
{
{
.name = "action",
.long_name = TRS("Action"),
.type = BG_PARAMETER_STRINGLIST,
.multi_names = (char const *[]){ "forget",
"transcode",
"blend",
NULL },
.multi_labels = (char const *[]){ TRS("Forget"),
TRS("Transcode"),
TRS("Blend onto video"),
NULL },
.val_default = { .val_str = "forget" },
},
{
.name = "in_language",
.long_name = TRS("Input Language"),
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "language",
.long_name = TRS("Language"),
.type = BG_PARAMETER_STRINGLIST,
.val_default = { .val_str = "eng" },
.multi_names = bg_language_codes,
.multi_labels = bg_language_labels,
},
{
.name = "force_language",
.long_name = TRS("Force language"),
.type = BG_PARAMETER_CHECKBUTTON,
.val_default = { .val_i = 1 },
.help_string = TRS("Force the given language even if the input has the language set differently.")
},
{
.name = "blend_stream",
.long_name = TRS("Video stream to blend onto"),
.type = BG_PARAMETER_INT,
.flags = BG_PARAMETER_HIDE_DIALOG,
.val_default = { .val_i = 1 },
.val_min = { .val_i = 1 },
},
{ /* End of parameters */ }
};
/* Create subtitle parameters */
static void create_subtitle_parameters(bg_transcoder_track_t * track)
{
int i;
bg_parameter_info_t * info;
/* Create subtitle parameters. These depend on the number of video streams */
for(i = 0; i < track->num_subtitle_text_streams; i++)
{
/* Forget, Dump, Blend #1, Blend #2 ... */
track->subtitle_text_streams[i].general_parameters =
bg_parameter_info_copy_array(general_parameters_subtitle_text);
info = track->subtitle_text_streams[i].general_parameters;
if(track->num_video_streams > 1)
{
info[1].val_max.val_i = track->num_video_streams;
info[1].flags &= ~BG_PARAMETER_HIDE_DIALOG;
}
}
for(i = 0; i < track->num_subtitle_overlay_streams; i++)
{
/* Forget, Blend #1, Blend #2 ... */
track->subtitle_overlay_streams[i].general_parameters =
bg_parameter_info_copy_array(general_parameters_subtitle_overlay);
info = track->subtitle_overlay_streams[i].general_parameters;
if(track->num_video_streams > 1)
{
info[1].val_max.val_i = track->num_video_streams;
info[1].flags &= ~BG_PARAMETER_HIDE_DIALOG;
}
}
}
static void create_filter_parameters(bg_transcoder_track_t * track,
bg_plugin_registry_t * plugin_reg)
{
int i;
bg_audio_filter_chain_t * afc;
bg_video_filter_chain_t * vfc;
bg_gavl_audio_options_t ao;
bg_gavl_video_options_t vo;
memset(&ao, 0, sizeof(ao));
memset(&vo, 0, sizeof(vo));
bg_gavl_audio_options_init(&ao);
bg_gavl_video_options_init(&vo);
if(track->num_audio_streams > 0)
{
afc = bg_audio_filter_chain_create(&ao, plugin_reg);
for(i = 0; i < track->num_audio_streams; i++)
{
track->audio_streams[i].filter_parameters =
bg_parameter_info_copy_array(bg_audio_filter_chain_get_parameters(afc));
}
bg_audio_filter_chain_destroy(afc);
}
if(track->num_video_streams > 0)
{
vfc = bg_video_filter_chain_create(&vo, plugin_reg);
for(i = 0; i < track->num_video_streams; i++)
{
track->video_streams[i].filter_parameters =
bg_parameter_info_copy_array(bg_video_filter_chain_get_parameters(vfc));
}
bg_video_filter_chain_destroy(vfc);
}
bg_gavl_audio_options_free(&ao);
bg_gavl_video_options_free(&vo);
}
/* Create parameters if the config sections are already there */
void bg_transcoder_track_create_parameters(bg_transcoder_track_t * track,
bg_plugin_registry_t * plugin_reg)
{
gavl_time_t duration = GAVL_TIME_UNDEFINED;
int i;
int flags = 0;
if(!track->general_parameters)
{
track->general_parameters = bg_parameter_info_copy_array(parameters_general);
bg_cfg_section_get_parameter_time(track->general_section,
"duration", &duration);
bg_cfg_section_get_parameter_int(track->general_section,
"flags", &flags);
if(duration != GAVL_TIME_UNDEFINED)
{
i = 0;
while(track->general_parameters[i].name)
{
if(!strcmp(track->general_parameters[i].name, "start_time") ||
!strcmp(track->general_parameters[i].name, "end_time"))
track->general_parameters[i].val_max.val_time = duration;
i++;
}
if(flags & BG_TRACK_SEEKABLE)
{
i = 0;
while(track->general_parameters[i].name)
{
if(!strcmp(track->general_parameters[i].name, "start_time") ||
!strcmp(track->general_parameters[i].name, "set_start_time"))
track->general_parameters[i].flags &= ~BG_PARAMETER_HIDE_DIALOG;
i++;
}
}
}
i = 0;
while(track->general_parameters[i].name)
{
if(!strcmp(track->general_parameters[i].name, "name") ||
!strcmp(track->general_parameters[i].name, "set_end_time") ||
!strcmp(track->general_parameters[i].name, "end_time"))
track->general_parameters[i].flags &= ~BG_PARAMETER_HIDE_DIALOG;
i++;
}
}
if(!track->metadata_parameters)
track->metadata_parameters = bg_metadata_get_parameters(NULL);
create_subtitle_parameters(track);
create_filter_parameters(track, plugin_reg);
}
static char * create_stream_label(const gavl_metadata_t * m)
{
const char * info;
const char * language;
info = gavl_metadata_get(m, GAVL_META_LABEL);
language = gavl_metadata_get(m, GAVL_META_LANGUAGE);
if(language && info)
return bg_sprintf("%s [%s]", info, bg_get_language_name(language));
else if(language)
return bg_strdup(NULL, bg_get_language_name(language));
else if(info)
return bg_strdup(NULL, info);
else
return NULL;
}
static void set_track(bg_transcoder_track_t * track,
bg_track_info_t * track_info,
bg_plugin_handle_t * input_plugin,
const bg_plugin_info_t * input_info,
const char * location,
int track_index,
int total_tracks,
bg_plugin_registry_t * plugin_reg)
{
int i;
int subtitle_text_index, subtitle_overlay_index;
const bg_input_plugin_t * input;
input = (bg_input_plugin_t *)input_plugin->plugin;
/* General parameters */
track->general_parameters =
bg_parameter_info_copy_array(parameters_general);
i = 0;
while(track->general_parameters[i].name)
{
if(!strcmp(track->general_parameters[i].name, "name"))
{
if(track_info->name)
track->general_parameters[i].val_default.val_str =
bg_strdup(NULL, track_info->name);
else
track->general_parameters[i].val_default.val_str =
bg_get_track_name_default(location, track_index, total_tracks);
track->general_parameters[i].flags &= ~BG_PARAMETER_HIDE_DIALOG;
}
else if(!strcmp(track->general_parameters[i].name, "duration"))
track->general_parameters[i].val_default.val_time = track_info->duration;
else if(!strcmp(track->general_parameters[i].name, "subdir"))
{
if(input->get_disc_name)
track->general_parameters[i].val_default.val_str =
bg_strdup(track->general_parameters[i].val_default.val_str,
input->get_disc_name(input_plugin->priv));
}
else if(!strcmp(track->general_parameters[i].name, "flags"))
track->general_parameters[i].val_default.val_i = track_info->flags;
else if(!strcmp(track->general_parameters[i].name, "location"))
track->general_parameters[i].val_default.val_str = bg_strdup(NULL, location);
else if(!strcmp(track->general_parameters[i].name, "plugin"))
track->general_parameters[i].val_default.val_str =
bg_strdup(NULL, input_info->name);
else if(!strcmp(track->general_parameters[i].name, "prefer_edl"))
{
if(input_plugin->edl)
track->general_parameters[i].val_default.val_i = 1;
else
track->general_parameters[i].val_default.val_i = 0;
}
else if(!strcmp(track->general_parameters[i].name, "track"))
track->general_parameters[i].val_default.val_i = track_index;
else if(!strcmp(track->general_parameters[i].name, "set_start_time"))
{
if(track_info->flags & BG_TRACK_SEEKABLE)
track->general_parameters[i].flags &= ~BG_PARAMETER_HIDE_DIALOG;
}
else if(!strcmp(track->general_parameters[i].name, "start_time"))
{
if(track_info->flags & BG_TRACK_SEEKABLE)
{
track->general_parameters[i].flags &= ~BG_PARAMETER_HIDE_DIALOG;
track->general_parameters[i].val_max.val_time = track_info->duration;
}
}
else if(!strcmp(track->general_parameters[i].name, "end_time"))
{
if(track_info->duration != GAVL_TIME_UNDEFINED)
{
track->general_parameters[i].val_max.val_time = track_info->duration;
track->general_parameters[i].val_default.val_time = track_info->duration;
}
track->general_parameters[i].flags &= ~BG_PARAMETER_HIDE_DIALOG;
}
else if(!strcmp(track->general_parameters[i].name, "set_end_time"))
{
track->general_parameters[i].flags &= ~BG_PARAMETER_HIDE_DIALOG;
}
i++;
}
/* Stop here for redirectors */
if(track->url)
return;
/* Metadata */
track->metadata_parameters =
bg_metadata_get_parameters(&track_info->metadata);
/* Chapter list */
if(track_info->chapter_list)
track->chapter_list = bg_chapter_list_copy(track_info->chapter_list);
/* Audio streams */
if(track_info->num_audio_streams)
{
track->num_audio_streams = track_info->num_audio_streams;
track->audio_streams = calloc(track_info->num_audio_streams,
sizeof(*(track->audio_streams)));
for(i = 0; i < track_info->num_audio_streams; i++)
{
track->audio_streams[i].label =
create_stream_label(&track_info->audio_streams[i].m);
}
}
/* Video streams */
if(track_info->num_video_streams)
{
track->num_video_streams = track_info->num_video_streams;
track->video_streams = calloc(track_info->num_video_streams,
sizeof(*(track->video_streams)));
for(i = 0; i < track_info->num_video_streams; i++)
{
track->video_streams[i].label =
create_stream_label(&track_info->video_streams[i].m);
}
}
/* Subtitle streams */
if(track_info->num_subtitle_streams)
{
for(i = 0; i < track_info->num_subtitle_streams; i++)
{
if(track_info->subtitle_streams[i].is_text)
track->num_subtitle_text_streams++;
else
track->num_subtitle_overlay_streams++;
}
if(track->num_subtitle_text_streams)
track->subtitle_text_streams =
calloc(track->num_subtitle_text_streams,
sizeof(*(track->subtitle_text_streams)));
if(track->num_subtitle_overlay_streams)
track->subtitle_overlay_streams =
calloc(track->num_subtitle_overlay_streams,
sizeof(*(track->subtitle_overlay_streams)));
subtitle_text_index = 0;
subtitle_overlay_index = 0;
for(i = 0; i < track_info->num_subtitle_streams; i++)
{
if(track_info->subtitle_streams[i].is_text)
{
track->subtitle_text_streams[subtitle_text_index].label =
create_stream_label(&track_info->subtitle_streams[i].m);
track->subtitle_text_streams[subtitle_text_index].in_index = i;
subtitle_text_index++;
}
else
{
track->subtitle_overlay_streams[subtitle_overlay_index].label =
create_stream_label(&track_info->subtitle_streams[i].m);
track->subtitle_overlay_streams[subtitle_overlay_index].in_index = i;
subtitle_overlay_index++;
}
}
}
create_subtitle_parameters(track);
create_filter_parameters(track, plugin_reg);
}
static void enable_streams(bg_input_plugin_t * plugin, void * priv,
int track,
int num_audio_streams, int num_video_streams,
int num_subtitle_streams)
{
int i;
if(plugin->set_track)
plugin->set_track(priv, track);
if(plugin->set_audio_stream)
{
for(i = 0; i < num_audio_streams; i++)
{
plugin->set_audio_stream(priv, i, BG_STREAM_ACTION_DECODE);
}
}
if(plugin->set_video_stream)
{
for(i = 0; i < num_video_streams; i++)
{
plugin->set_video_stream(priv, i, BG_STREAM_ACTION_DECODE);
}
}
if(plugin->set_subtitle_stream)
{
for(i = 0; i < num_subtitle_streams; i++)
{
plugin->set_subtitle_stream(priv, i, BG_STREAM_ACTION_DECODE);
}
}
if(plugin->start)
plugin->start(priv);
}
static void disable_streams(bg_input_plugin_t * plugin, void * priv)
{
if(plugin->stop)
plugin->stop(priv);
}
bg_transcoder_track_t *
bg_transcoder_track_create(const char * url,
const bg_plugin_info_t * input_info,
int prefer_edl,
int track, bg_plugin_registry_t * plugin_reg,
bg_cfg_section_t * track_defaults_section,
bg_cfg_section_t * encoder_section,
char * name)
{
int i;
bg_transcoder_track_t * ret = NULL;
bg_transcoder_track_t * new_track = NULL;
bg_transcoder_track_t * end_track = NULL;
bg_input_plugin_t * input;
bg_track_info_t * track_info;
bg_plugin_handle_t * plugin_handle = NULL;
int num_tracks;
int streams_enabled = 0;
bg_cfg_section_t * input_section;
/* Load the plugin */
if(!input_info)
{
if(!bg_input_plugin_load(plugin_reg, url,
input_info, &plugin_handle, NULL, 0))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Loading %s failed", url);
return NULL;
}
input_info = bg_plugin_find_by_name(plugin_reg, plugin_handle->info->name);
}
if(!plugin_handle || prefer_edl)
{
if(!bg_input_plugin_load(plugin_reg, url,
input_info, &plugin_handle, NULL, prefer_edl))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Loading %s failed", url);
return NULL;
}
}
if(plugin_handle->edl)
bg_cfg_section_set_parameter_int(track_defaults_section, "prefer_edl", 1);
else
bg_cfg_section_set_parameter_int(track_defaults_section, "prefer_edl", 0);
input = (bg_input_plugin_t*)plugin_handle->plugin;
input_section = bg_plugin_registry_get_section(plugin_reg, input_info->name);
/* Decide what to load */
num_tracks = input->get_num_tracks ?
input->get_num_tracks(plugin_handle->priv) : 1;
if(track >= 0)
{
/* Load single track */
track_info = input->get_track_info(plugin_handle->priv, track);
if(name)
track_info->name = bg_strdup(track_info->name, name);
new_track = calloc(1, sizeof(*new_track));
ret = new_track;
if(track_info->url)
{
new_track->url = bg_strdup(new_track->url, track_info->url);
}
else
{
enable_streams(input, plugin_handle->priv,
track,
track_info->num_audio_streams,
track_info->num_video_streams,
track_info->num_subtitle_streams);
streams_enabled = 1;
}
set_track(new_track, track_info, plugin_handle, input_info, url, track, num_tracks,
plugin_reg);
create_sections(new_track, track_defaults_section, input_section,
encoder_section, track_info);
bg_transcoder_track_set_encoders(new_track, plugin_reg, encoder_section);
if(streams_enabled)
disable_streams(input, plugin_handle->priv);
}
else
{
/* Load all tracks */
for(i = 0; i < num_tracks; i++)
{
track_info = input->get_track_info(plugin_handle->priv, i);
if(name)
track_info->name = bg_strdup(track_info->name, name);
new_track = calloc(1, sizeof(*new_track));
if(ret)
{
end_track->next = new_track;
end_track = end_track->next;
}
else
{
ret = new_track;
end_track = new_track;
}
if(track_info->url)
{
new_track->url = bg_strdup(new_track->url, track_info->url);
}
else
{
enable_streams(input, plugin_handle->priv,
i,
track_info->num_audio_streams,
track_info->num_video_streams,
track_info->num_subtitle_streams);
streams_enabled = 1;
}
set_track(new_track, track_info, plugin_handle, input_info, url, i, num_tracks,
plugin_reg);
create_sections(new_track, track_defaults_section, input_section,
encoder_section, track_info);
bg_transcoder_track_set_encoders(new_track, plugin_reg, encoder_section);
if(streams_enabled)
{
disable_streams(input, plugin_handle->priv);
streams_enabled = 0;
}
}
}
bg_plugin_unref(plugin_handle);
return ret;
}
static bg_transcoder_track_t * remove_redirectors(bg_transcoder_track_t * entries,
bg_plugin_registry_t * plugin_reg,
bg_cfg_section_t * track_defaults_section,
bg_cfg_section_t * encoder_section)
{
bg_transcoder_track_t * before, * e;
bg_transcoder_track_t * new_entry, * end_entry;
int done = 0;
const char * plugin_name = NULL;
const bg_plugin_info_t * info;
done = 1;
e = entries;
while(e)
{
if(e->url)
{
bg_cfg_section_get_parameter_string(e->general_section, "plugin", &plugin_name);
if(plugin_name)
info = bg_plugin_find_by_name(plugin_reg, plugin_name);
else
info = NULL;
/* Load "real" url */
new_entry = bg_transcoder_track_create(e->url,
info, 0,
-1, plugin_reg,
track_defaults_section,
encoder_section,
NULL);
if(new_entry)
{
/* Insert new entries into list */
if(e != entries)
{
before = entries;
while(before->next != e)
before = before->next;
before->next = new_entry;
}
else
{
entries = new_entry;
}
end_entry = new_entry;
while(end_entry->next)
end_entry = end_entry->next;
end_entry->next = e->next;
bg_transcoder_track_destroy(e);
e = new_entry;
}
else
{
/* Remove e from list */
if(e != entries)
{
before = entries;
while(before->next != e)
before = before->next;
before->next = e->next;
}
else
{
entries = e->next;
before = NULL;
}
bg_transcoder_track_destroy(e);
e = (before) ? before->next : entries;
}
}
else
{
/* Leave e as it is */
e = e->next;
}
}
return entries;
}
bg_transcoder_track_t *
bg_transcoder_track_create_from_urilist(const char * list,
int len,
bg_plugin_registry_t * plugin_reg,
bg_cfg_section_t * track_defaults_section,
bg_cfg_section_t * encoder_section)
{
int i;
char ** uri_list;
bg_transcoder_track_t * ret_last = NULL;
bg_transcoder_track_t * ret = NULL;
uri_list = bg_urilist_decode(list, len);
if(!uri_list)
return NULL;
i = 0;
while(uri_list[i])
{
if(!ret)
{
ret = bg_transcoder_track_create(uri_list[i],
NULL, 0,
-1,
plugin_reg,
track_defaults_section,
encoder_section, NULL);
if(ret)
{
ret_last = ret;
while(ret_last->next)
ret_last = ret_last->next;
}
}
else
{
ret_last->next = bg_transcoder_track_create(uri_list[i],
NULL, 0,
-1,
plugin_reg,
track_defaults_section,
encoder_section,
NULL);
if(ret)
{
while(ret_last->next)
ret_last = ret_last->next;
}
}
i++;
}
bg_urilist_free(uri_list);
ret = remove_redirectors(ret,
plugin_reg,
track_defaults_section, encoder_section);
return ret;
}
bg_transcoder_track_t *
bg_transcoder_track_create_from_albumentries(const char * xml_string,
bg_plugin_registry_t * plugin_reg,
bg_cfg_section_t * track_defaults_section,
bg_cfg_section_t * encoder_section)
{
bg_album_entry_t * new_entries, *entry;
bg_transcoder_track_t * ret_last =NULL;
bg_transcoder_track_t * ret =NULL;
const bg_plugin_info_t * plugin_info;
int prefer_edl;
new_entries = bg_album_entries_new_from_xml(xml_string);
entry = new_entries;
while(entry)
{
if(entry->plugin)
plugin_info = bg_plugin_find_by_name(plugin_reg, entry->plugin);
else
plugin_info = NULL;
if(entry->flags & BG_ALBUM_ENTRY_EDL)
prefer_edl = 1;
else
prefer_edl = 0;
if(!ret)
{
ret = bg_transcoder_track_create(entry->location,
plugin_info,
prefer_edl,
entry->index,
plugin_reg,
track_defaults_section, encoder_section,
entry->name);
ret_last = ret;
}
else
{
ret_last->next = bg_transcoder_track_create(entry->location,
plugin_info,
prefer_edl,
entry->index,
plugin_reg,
track_defaults_section,
encoder_section,
entry->name);
ret_last = ret_last->next;
}
entry = entry->next;
}
bg_album_entries_destroy(new_entries);
ret = remove_redirectors(ret,
plugin_reg,
track_defaults_section, encoder_section);
return ret;
}
static void free_encoders(bg_transcoder_track_t * track)
{
int i;
/* Free all encoder related data */
if(track->audio_encoder_section)
{
bg_cfg_section_destroy(track->audio_encoder_section);
track->audio_encoder_section = NULL;
}
if(track->video_encoder_section)
{
bg_cfg_section_destroy(track->video_encoder_section);
track->video_encoder_section = NULL;
}
if(track->subtitle_text_encoder_section)
{
bg_cfg_section_destroy(track->subtitle_text_encoder_section);
track->subtitle_text_encoder_section = NULL;
}
if(track->subtitle_overlay_encoder_section)
{
bg_cfg_section_destroy(track->subtitle_overlay_encoder_section);
track->subtitle_overlay_encoder_section = NULL;
}
for(i = 0; i < track->num_audio_streams; i++)
{
if(track->audio_streams[i].encoder_section)
{
bg_cfg_section_destroy(track->audio_streams[i].encoder_section);
track->audio_streams[i].encoder_section = NULL;
}
}
for(i = 0; i < track->num_video_streams; i++)
{
if(track->video_streams[i].encoder_section)
{
bg_cfg_section_destroy(track->video_streams[i].encoder_section);
track->video_streams[i].encoder_section = NULL;
}
}
for(i = 0; i < track->num_subtitle_text_streams; i++)
{
if(track->subtitle_text_streams[i].encoder_section_text)
{
bg_cfg_section_destroy(track->subtitle_text_streams[i].encoder_section_text);
track->subtitle_text_streams[i].encoder_section_text = NULL;
}
if(track->subtitle_text_streams[i].encoder_section_overlay)
{
bg_cfg_section_destroy(track->subtitle_text_streams[i].encoder_section_overlay);
track->subtitle_text_streams[i].encoder_section_overlay = NULL;
}
}
for(i = 0; i < track->num_subtitle_overlay_streams; i++)
{
if(track->subtitle_overlay_streams[i].encoder_section)
{
bg_cfg_section_destroy(track->subtitle_overlay_streams[i].encoder_section);
track->subtitle_overlay_streams[i].encoder_section = NULL;
}
}
}
void bg_transcoder_track_destroy(bg_transcoder_track_t * t)
{
int i;
free_encoders(t);
/* Shredder everything */
for(i = 0; i < t->num_audio_streams; i++)
{
if(t->audio_streams[i].general_section)
bg_cfg_section_destroy(t->audio_streams[i].general_section);
if(t->audio_streams[i].encoder_section)
bg_cfg_section_destroy(t->audio_streams[i].encoder_section);
if(t->audio_streams[i].filter_section)
bg_cfg_section_destroy(t->audio_streams[i].filter_section);
if(t->audio_streams[i].label) free(t->audio_streams[i].label);
bg_parameter_info_destroy_array(t->audio_streams[i].filter_parameters);
}
for(i = 0; i < t->num_video_streams; i++)
{
if(t->video_streams[i].general_section)
bg_cfg_section_destroy(t->video_streams[i].general_section);
if(t->video_streams[i].encoder_section)
bg_cfg_section_destroy(t->video_streams[i].encoder_section);
if(t->video_streams[i].filter_section)
bg_cfg_section_destroy(t->video_streams[i].filter_section);
if(t->video_streams[i].label) free(t->video_streams[i].label);
bg_parameter_info_destroy_array(t->video_streams[i].filter_parameters);
}
for(i = 0; i < t->num_subtitle_text_streams; i++)
{
if(t->subtitle_text_streams[i].general_section)
bg_cfg_section_destroy(t->subtitle_text_streams[i].general_section);
if(t->subtitle_text_streams[i].encoder_section_text)
bg_cfg_section_destroy(t->subtitle_text_streams[i].encoder_section_text);
if(t->subtitle_text_streams[i].encoder_section_overlay)
bg_cfg_section_destroy(t->subtitle_text_streams[i].encoder_section_overlay);
if(t->subtitle_text_streams[i].textrenderer_section)
bg_cfg_section_destroy(t->subtitle_text_streams[i].textrenderer_section);
if(t->subtitle_text_streams[i].general_parameters)
bg_parameter_info_destroy_array(t->subtitle_text_streams[i].general_parameters);
if(t->subtitle_text_streams[i].label) free(t->subtitle_text_streams[i].label);
}
for(i = 0; i < t->num_subtitle_overlay_streams; i++)
{
if(t->subtitle_overlay_streams[i].general_section)
bg_cfg_section_destroy(t->subtitle_overlay_streams[i].general_section);
if(t->subtitle_overlay_streams[i].encoder_section)
bg_cfg_section_destroy(t->subtitle_overlay_streams[i].encoder_section);
if(t->subtitle_overlay_streams[i].general_parameters)
bg_parameter_info_destroy_array(t->subtitle_overlay_streams[i].general_parameters);
if(t->subtitle_overlay_streams[i].label) free(t->subtitle_overlay_streams[i].label);
}
if(t->audio_streams)
free(t->audio_streams);
if(t->video_streams)
free(t->video_streams);
if(t->general_section)
bg_cfg_section_destroy(t->general_section);
if(t->input_section)
bg_cfg_section_destroy(t->input_section);
if(t->metadata_section)
bg_cfg_section_destroy(t->metadata_section);
if(t->audio_encoder_section)
bg_cfg_section_destroy(t->audio_encoder_section);
if(t->video_encoder_section)
bg_cfg_section_destroy(t->video_encoder_section);
if(t->subtitle_text_encoder_section)
bg_cfg_section_destroy(t->subtitle_text_encoder_section);
if(t->subtitle_overlay_encoder_section)
bg_cfg_section_destroy(t->subtitle_overlay_encoder_section);
if(t->general_parameters)
bg_parameter_info_destroy_array(t->general_parameters);
if(t->metadata_parameters)
bg_parameter_info_destroy_array(t->metadata_parameters);
if(t->chapter_list)
bg_chapter_list_destroy(t->chapter_list);
if(t->url)
free(t->url);
free(t);
}
static const bg_parameter_info_t general_parameters_video[] =
{
{
.name = "general",
.long_name = TRS("General"),
.type = BG_PARAMETER_SECTION
},
{
.name = "action",
.long_name = TRS("Action"),
.type = BG_PARAMETER_STRINGLIST,
.multi_names = (char const *[]){ "transcode", "copy", "forget", NULL },
.multi_labels = (char const *[]){ TRS("Transcode"),
TRS("Copy (if possible)"),
TRS("Forget"), NULL },
.val_default = { .val_str = "transcode" },
.help_string = TRS("Choose the desired action for the stream. If copying is not possible, the stream will be transcoded"),
},
{
.name = "twopass",
.long_name = TRS("Enable 2-pass encoding"),
.type = BG_PARAMETER_CHECKBUTTON,
.help_string = TRS("Encode this stream in 2 passes, i.e. analyze it first and do the final\
transcoding in the second pass. This enables higher quality within the given bitrate constraints but roughly doubles the video encoding time."),
},
BG_GAVL_PARAM_CONVERSION_QUALITY,
BG_GAVL_PARAM_FRAMERATE,
BG_GAVL_PARAM_ALPHA,
BG_GAVL_PARAM_RESAMPLE_CHROMA,
BG_GAVL_PARAM_THREADS,
{ /* End of parameters */ }
};
static const bg_parameter_info_t general_parameters_audio[] =
{
{
.name = "action",
.long_name = TRS("Action"),
.type = BG_PARAMETER_STRINGLIST,
.multi_names = (char const *[]){ "transcode", "copy", "forget", NULL },
.multi_labels = (char const *[]){ TRS("Transcode"),
TRS("Copy (if possible)"),
TRS("Forget"), NULL },
.val_default = { .val_str = "transcode" },
.help_string = TRS("Choose the desired action for the stream. If copying is not possible, the stream will be transcoded"),
},
{
.name = "in_language",
.long_name = TRS("Input Language"),
.type = BG_PARAMETER_STRING,
.flags = BG_PARAMETER_HIDE_DIALOG,
},
{
.name = "language",
.long_name = TRS("Language"),
.type = BG_PARAMETER_STRINGLIST,
.val_default = { .val_str = "eng" },
.multi_names = bg_language_codes,
.multi_labels = bg_language_labels,
},
{
.name = "force_language",
.long_name = TRS("Force language"),
.type = BG_PARAMETER_CHECKBUTTON,
.val_default = { .val_i = 1 },
.help_string = TRS("Force the given language even if the input has the language set differently.")
},
{
.name = "normalize",
.long_name = TRS("Normalize audio"),
.type = BG_PARAMETER_CHECKBUTTON,
.help_string = TRS("This will enable 2 pass transcoding. In the first pass, the peak volume\
is detected. In the second pass, the stream is transcoded with normalized volume.")
},
BG_GAVL_PARAM_FORCE_SAMPLEFORMAT,
BG_GAVL_PARAM_CONVERSION_QUALITY,
BG_GAVL_PARAM_AUDIO_DITHER_MODE,
BG_GAVL_PARAM_SAMPLERATE,
BG_GAVL_PARAM_RESAMPLE_MODE,
BG_GAVL_PARAM_CHANNEL_SETUP,
{ /* End of parameters */ }
};
/* Audio stream parameters */
const bg_parameter_info_t *
bg_transcoder_track_audio_get_general_parameters()
{
return general_parameters_audio;
}
/* Video stream parameters */
const bg_parameter_info_t *
bg_transcoder_track_video_get_general_parameters()
{
return general_parameters_video;
}
const bg_parameter_info_t *
bg_transcoder_track_subtitle_text_get_general_parameters()
{
return general_parameters_subtitle_text;
}
const bg_parameter_info_t *
bg_transcoder_track_subtitle_overlay_get_general_parameters()
{
return general_parameters_subtitle_overlay;
}
const bg_parameter_info_t *
bg_transcoder_track_get_general_parameters(bg_transcoder_track_t * t)
{
return parameters_general;
}
char * bg_transcoder_track_get_name(bg_transcoder_track_t * t)
{
bg_parameter_value_t val;
bg_parameter_info_t info;
memset(&val, 0, sizeof(val));
memset(&info, 0, sizeof(info));
info.name = "name";
bg_cfg_section_get_parameter(t->general_section, &info, &val);
return val.val_str;
}
const char * bg_transcoder_track_get_audio_encoder(bg_transcoder_track_t * t)
{
const char * ret;
bg_cfg_section_get_parameter_string(t->general_section, "audio_encoder", &ret);
return ret;
}
const char * bg_transcoder_track_get_video_encoder(bg_transcoder_track_t * t)
{
const char * ret;
bg_cfg_section_get_parameter_string(t->general_section, "video_encoder", &ret);
return ret;
}
const char * bg_transcoder_track_get_subtitle_text_encoder(bg_transcoder_track_t * t)
{
const char * ret;
bg_cfg_section_get_parameter_string(t->general_section, "subtitle_text_encoder", &ret);
return ret;
}
const char * bg_transcoder_track_get_subtitle_overlay_encoder(bg_transcoder_track_t * t)
{
const char * ret;
bg_cfg_section_get_parameter_string(t->general_section, "subtitle_overlay_encoder", &ret);
return ret;
}
void bg_transcoder_track_get_duration(bg_transcoder_track_t * t, gavl_time_t * ret,
gavl_time_t * ret_total)
{
gavl_time_t start_time = 0, end_time = 0, duration_total = 0;
int set_start_time = 0, set_end_time = 0;
bg_cfg_section_get_parameter_int(t->general_section, "set_start_time", &set_start_time);
bg_cfg_section_get_parameter_int(t->general_section, "set_end_time", &set_end_time);
bg_cfg_section_get_parameter_time(t->general_section, "duration", &duration_total);
bg_cfg_section_get_parameter_time(t->general_section, "start_time", &start_time);
bg_cfg_section_get_parameter_time(t->general_section, "end_time", &end_time);
*ret_total = duration_total;
if(duration_total == GAVL_TIME_UNDEFINED)
{
if(set_end_time)
*ret = end_time;
else
*ret = duration_total;
}
else
{
if(set_start_time)
{
if(set_end_time) /* Start and end */
{
*ret = end_time - start_time;
if(*ret < 0)
*ret = 0;
}
else /* Start only */
{
*ret = duration_total - start_time;
if(*ret < 0)
*ret = 0;
}
}
else
{
if(set_end_time) /* End only */
{
*ret = end_time;
}
else
{
*ret = duration_total;
}
}
}
return;
}
#if 0
void
bg_transcoder_track_set_encoders(bg_transcoder_track_t * track,
bg_plugin_registry_t * plugin_reg,
const bg_encoder_info_t * info)
{
free_encoders(track);
/* Update the plugin entries in the general section */
bg_cfg_section_set_parameter_string(track->general_section,
"audio_encoder",
(info->audio_info) ?
info->audio_info->name : info->video_info->name);
bg_cfg_section_set_parameter_string(track->general_section,
"video_encoder", info->video_info->name);
bg_cfg_section_set_parameter_string(track->general_section,
"subtitle_text_encoder",
(info->subtitle_text_info) ?
info->subtitle_text_info->name : info->video_info->name);
bg_cfg_section_set_parameter_string(track->general_section,
"subtitle_overlay_encoder",
(info->subtitle_overlay_info) ?
info->subtitle_overlay_info->name : info->video_info->name);
bg_transcoder_track_create_encoder_sections(track, info);
}
#endif
void
bg_transcoder_track_global_to_reg(bg_transcoder_track_global_t * g,
bg_plugin_registry_t * plugin_reg)
{
bg_cfg_section_t * plugin_section;
if(g->pp_plugin)
{
bg_plugin_registry_set_default(plugin_reg, BG_PLUGIN_ENCODER_PP, BG_PLUGIN_PP,
g->pp_plugin);
bg_plugin_registry_set_encode_pp(plugin_reg, 1);
plugin_section =
bg_plugin_registry_get_section(plugin_reg, g->pp_plugin);
bg_cfg_section_transfer(g->pp_section, plugin_section);
}
else
{
bg_plugin_registry_set_encode_pp(plugin_reg, 0);
}
}
void
bg_transcoder_track_global_from_reg(bg_transcoder_track_global_t * g,
bg_plugin_registry_t * plugin_reg)
{
bg_cfg_section_t * plugin_section;
const bg_plugin_info_t * plugin_info;
bg_transcoder_track_global_free(g);
if(bg_plugin_registry_get_encode_pp(plugin_reg))
{
plugin_info = bg_plugin_registry_get_default(plugin_reg, BG_PLUGIN_ENCODER_PP, BG_PLUGIN_PP);
g->pp_plugin = bg_strdup(NULL, plugin_info->name);
plugin_section = bg_plugin_registry_get_section(plugin_reg, plugin_info->name);
g->pp_section = bg_cfg_section_copy(plugin_section);
}
}
void
bg_transcoder_track_global_free(bg_transcoder_track_global_t * g)
{
if(g->pp_plugin)
{
free(g->pp_plugin);
g->pp_plugin = NULL;
}
if(g->pp_section)
{
bg_cfg_section_destroy(g->pp_section);
g->pp_section = NULL;
}
}
//
/* Functions, which operate on lists of transcoder tracks */
bg_transcoder_track_t *
bg_transcoder_tracks_delete_selected(bg_transcoder_track_t * t)
{
bg_transcoder_track_t * track, *tmp_track;
bg_transcoder_track_t * new_tracks = NULL;
bg_transcoder_track_t * end_track = NULL;
track = t;
while(track)
{
if(track->selected)
{
/* Copy non selected tracks */
tmp_track = track->next;
bg_transcoder_track_destroy(track);
track = tmp_track;
}
else
{
/* Insert into new list */
if(!new_tracks)
{
new_tracks = track;
end_track = track;
}
else
{
end_track->next = track;
end_track = end_track->next;
}
track = track->next;
end_track->next = NULL;
}
}
return new_tracks;
}
bg_transcoder_track_t *
bg_transcoder_tracks_append(bg_transcoder_track_t * t, bg_transcoder_track_t * tail)
{
bg_transcoder_track_t * end;
if(!t)
return tail;
end = t;
while(end->next)
end = end->next;
end->next = tail;
return t;
}
bg_transcoder_track_t *
bg_transcoder_tracks_prepend(bg_transcoder_track_t * t, bg_transcoder_track_t * head)
{
return bg_transcoder_tracks_append(head, t);
}
bg_transcoder_track_t *
bg_transcoder_tracks_extract_selected(bg_transcoder_track_t ** t)
{
bg_transcoder_track_t * track;
bg_transcoder_track_t * ret = NULL;
bg_transcoder_track_t * ret_end = NULL;
bg_transcoder_track_t * new_tracks = NULL;
bg_transcoder_track_t * new_tracks_end = NULL;
track = *t;
while(track)
{
if(track->selected)
{
if(!ret_end)
{
ret = track;
ret_end = ret;
}
else
{
ret_end->next = track;
ret_end = ret_end->next;
}
}
else
{
if(!new_tracks_end)
{
new_tracks = track;
new_tracks_end = new_tracks;
}
else
{
new_tracks_end->next = track;
new_tracks_end = new_tracks_end->next;
}
}
track = track->next;
}
/* Zero terminate */
if(ret_end)
ret_end->next = NULL;
if(new_tracks_end)
new_tracks_end->next = NULL;
*t = new_tracks;
return ret;
}
bg_transcoder_track_t *
bg_transcoder_tracks_move_selected_up(bg_transcoder_track_t * t)
{
bg_transcoder_track_t * selected;
selected = bg_transcoder_tracks_extract_selected(&t);
if(selected)
t = bg_transcoder_tracks_prepend(t, selected);
return t;
}
bg_transcoder_track_t *
bg_transcoder_tracks_move_selected_down(bg_transcoder_track_t * t)
{
bg_transcoder_track_t * selected;
selected = bg_transcoder_tracks_extract_selected(&t);
if(selected)
t = bg_transcoder_tracks_append(t, selected);
return t;
}
void bg_transcoder_track_get_encoders(bg_transcoder_track_t * t,
bg_plugin_registry_t * plugin_reg,
bg_cfg_section_t * encoder_section)
{
bg_cfg_section_t * dst;
const char * video_name;
const char * name;
/* Video */
video_name = bg_transcoder_track_get_video_encoder(t);
bg_cfg_section_set_parameter_string(encoder_section, "video_encoder", video_name);
dst = bg_cfg_section_find_subsection(encoder_section, "video_encoder");
dst = bg_cfg_section_find_subsection(dst, video_name);
if(t->video_encoder_section)
bg_cfg_section_transfer(t->video_encoder_section, dst);
if(t->video_streams && t->video_streams->encoder_section)
{
dst = bg_cfg_section_find_subsection(dst, "$video");
bg_cfg_section_transfer(t->video_streams->encoder_section, dst);
}
/* Audio */
name = bg_transcoder_track_get_audio_encoder(t);
if(name && strcmp(name, video_name))
{
bg_cfg_section_set_parameter_string(encoder_section, "audio_encoder", name);
bg_cfg_section_set_parameter_int(encoder_section, "encode_audio_to_video", 0);
dst = bg_cfg_section_find_subsection(encoder_section, "audio_encoder");
dst = bg_cfg_section_find_subsection(dst, name);
if(t->audio_encoder_section)
bg_cfg_section_transfer(t->audio_encoder_section, dst);
if(t->audio_streams && t->audio_streams->encoder_section)
{
dst = bg_cfg_section_find_subsection(dst, "$audio");
bg_cfg_section_transfer(t->audio_streams->encoder_section, dst);
}
}
else
{
bg_cfg_section_set_parameter_string(encoder_section, "audio_encoder", NULL);
bg_cfg_section_set_parameter_int(encoder_section, "encode_audio_to_video", 1);
}
/* Text subtitles */
name = bg_transcoder_track_get_subtitle_text_encoder(t);
if(name && strcmp(name, video_name))
{
bg_cfg_section_set_parameter_int(encoder_section, "encode_subtitle_text_to_video", 0);
bg_cfg_section_set_parameter_string(encoder_section, "subtitle_text_encoder", name);
dst = bg_cfg_section_find_subsection(encoder_section, "subtitle_text_encoder");
dst = bg_cfg_section_find_subsection(dst, name);
if(t->subtitle_text_encoder_section)
bg_cfg_section_transfer(t->subtitle_text_encoder_section, dst);
if(t->subtitle_text_streams && t->subtitle_text_streams->encoder_section_text)
{
dst = bg_cfg_section_find_subsection(dst, "$subtitle_text");
bg_cfg_section_transfer(t->subtitle_text_streams->encoder_section_text, dst);
}
}
else
{
bg_cfg_section_set_parameter_int(encoder_section, "encode_subtitle_text_to_video", 1);
bg_cfg_section_set_parameter_string(encoder_section, "subtitle_text_encoder", 0);
}
/* Overlay subtitles */
name = bg_transcoder_track_get_subtitle_overlay_encoder(t);
if(name && strcmp(name, video_name))
{
bg_cfg_section_set_parameter_int(encoder_section, "encode_subtitle_overlay_to_video", 0);
bg_cfg_section_set_parameter_string(encoder_section, "subtitle_overlay_encoder", name);
dst = bg_cfg_section_find_subsection(encoder_section, "subtitle_overlay_encoder");
dst = bg_cfg_section_find_subsection(dst, name);
if(t->subtitle_overlay_encoder_section)
bg_cfg_section_transfer(t->subtitle_overlay_encoder_section, dst);
dst = bg_cfg_section_find_subsection(dst, "$subtitle_overlay");
if(t->subtitle_overlay_streams && t->subtitle_overlay_streams->encoder_section)
bg_cfg_section_transfer(t->subtitle_overlay_streams->encoder_section, dst);
else if(t->subtitle_text_streams && t->subtitle_text_streams->encoder_section_overlay)
bg_cfg_section_transfer(t->subtitle_text_streams->encoder_section_overlay, dst);
}
else
{
bg_cfg_section_set_parameter_int(encoder_section, "encode_subtitle_overlay_to_video", 1);
bg_cfg_section_set_parameter_string(encoder_section, "subtitle_overlay_encoder", NULL);
}
}
static void delete_subsection(bg_cfg_section_t * s, const char * name)
{
if(bg_cfg_section_has_subsection(s, name))
bg_cfg_section_delete_subsection(s,
bg_cfg_section_find_subsection(s, name));
}
static void clean_section(bg_cfg_section_t * s)
{
delete_subsection(s, "$audio");
delete_subsection(s, "$video");
delete_subsection(s, "$subtitle_text");
delete_subsection(s, "$subtitle_overlay");
}
#define DELETE_SECTION(s) if(s) { bg_cfg_section_destroy(s); s = NULL; }
void bg_transcoder_track_set_encoders(bg_transcoder_track_t * t,
bg_plugin_registry_t * plugin_reg,
bg_cfg_section_t * encoder_section)
{
int i;
bg_cfg_section_t * s;
bg_cfg_section_t * s1;
const char * name;
static const uint32_t stream_flags = BG_STREAM_AUDIO |
BG_STREAM_VIDEO |
BG_STREAM_SUBTITLE_TEXT |
BG_STREAM_SUBTITLE_OVERLAY;
/* Delete config sections */
DELETE_SECTION(t->audio_encoder_section);
DELETE_SECTION(t->video_encoder_section);
DELETE_SECTION(t->subtitle_text_encoder_section);
DELETE_SECTION(t->subtitle_overlay_encoder_section);
for(i = 0; i < t->num_audio_streams; i++)
DELETE_SECTION(t->audio_streams[i].encoder_section);
for(i = 0; i < t->num_video_streams; i++)
DELETE_SECTION(t->video_streams[i].encoder_section);
for(i = 0; i < t->num_subtitle_text_streams; i++)
{
DELETE_SECTION(t->subtitle_text_streams[i].encoder_section_text);
DELETE_SECTION(t->subtitle_text_streams[i].encoder_section_overlay);
}
for(i = 0; i < t->num_subtitle_overlay_streams; i++)
DELETE_SECTION(t->subtitle_overlay_streams[i].encoder_section);
/* Audio encoder */
name = bg_encoder_section_get_plugin(plugin_reg,
encoder_section,
BG_STREAM_AUDIO,
stream_flags);
bg_cfg_section_set_parameter_string(t->general_section, "audio_encoder", name);
bg_encoder_section_get_plugin_config(plugin_reg,
encoder_section,
BG_STREAM_AUDIO,
stream_flags,
&s,
NULL);
if(s)
{
t->audio_encoder_section = bg_cfg_section_copy(s);
clean_section(t->audio_encoder_section);
}
// else
// fprintf(stderr, "Got no audio encoder section\n");
bg_encoder_section_get_stream_config(plugin_reg,
encoder_section,
BG_STREAM_AUDIO,
stream_flags,
&s,
NULL);
if(s)
{
for(i = 0; i < t->num_audio_streams; i++)
t->audio_streams[i].encoder_section = bg_cfg_section_copy(s);
}
/* Video encoder */
name = bg_encoder_section_get_plugin(plugin_reg,
encoder_section,
BG_STREAM_VIDEO,
stream_flags);
bg_cfg_section_set_parameter_string(t->general_section, "video_encoder", name);
bg_encoder_section_get_plugin_config(plugin_reg,
encoder_section,
BG_STREAM_VIDEO,
stream_flags,
&s,
NULL);
if(s)
{
t->video_encoder_section = bg_cfg_section_copy(s);
clean_section(t->video_encoder_section);
// fprintf(stderr, "Got video encoder section\n");
}
// else
// fprintf(stderr, "Got no video encoder section\n");
bg_encoder_section_get_stream_config(plugin_reg,
encoder_section,
BG_STREAM_VIDEO,
stream_flags,
&s,
NULL);
if(s)
{
for(i = 0; i < t->num_video_streams; i++)
t->video_streams[i].encoder_section = bg_cfg_section_copy(s);
}
/* Subtitle text encoder */
name = bg_encoder_section_get_plugin(plugin_reg,
encoder_section,
BG_STREAM_SUBTITLE_TEXT,
stream_flags);
bg_cfg_section_set_parameter_string(t->general_section, "subtitle_text_encoder", name);
bg_encoder_section_get_plugin_config(plugin_reg,
encoder_section,
BG_STREAM_SUBTITLE_TEXT,
stream_flags,
&s,
NULL);
if(s)
{
t->subtitle_text_encoder_section = bg_cfg_section_copy(s);
clean_section(t->subtitle_text_encoder_section);
}
bg_encoder_section_get_stream_config(plugin_reg,
encoder_section,
BG_STREAM_SUBTITLE_TEXT,
stream_flags,
&s,
NULL);
bg_encoder_section_get_stream_config(plugin_reg,
encoder_section,
BG_STREAM_SUBTITLE_OVERLAY,
stream_flags,
&s1,
NULL);
if(s)
{
for(i = 0; i < t->num_subtitle_text_streams; i++)
t->subtitle_text_streams[i].encoder_section_text = bg_cfg_section_copy(s);
}
if(s1)
{
for(i = 0; i < t->num_subtitle_text_streams; i++)
t->subtitle_text_streams[i].encoder_section_overlay = bg_cfg_section_copy(s);
}
/* Subtitle overlay encoder */
name = bg_encoder_section_get_plugin(plugin_reg,
encoder_section,
BG_STREAM_SUBTITLE_OVERLAY,
stream_flags);
bg_cfg_section_set_parameter_string(t->general_section, "subtitle_overlay_encoder", name);
bg_encoder_section_get_plugin_config(plugin_reg,
encoder_section,
BG_STREAM_SUBTITLE_OVERLAY,
stream_flags,
&s,
NULL);
if(s)
{
t->subtitle_overlay_encoder_section = bg_cfg_section_copy(s);
clean_section(t->subtitle_overlay_encoder_section);
}
bg_encoder_section_get_stream_config(plugin_reg,
encoder_section,
BG_STREAM_SUBTITLE_OVERLAY,
stream_flags,
&s,
NULL);
if(s)
{
for(i = 0; i < t->num_subtitle_overlay_streams; i++)
t->subtitle_overlay_streams[i].encoder_section = bg_cfg_section_copy(s);
}
}
gmerlin-1.2.0~dfsg/lib/album.c 0000644 0001750 0001750 00000202124 11764363410 016111 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
/* For stat/opendir */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LOG_DOMAIN "album"
#ifdef HAVE_INOTIFY
#include
#endif
/*
* This must be called, whenever the tracks in an album
* change.
*/
static void delete_shuffle_list(bg_album_t * album)
{
bg_shuffle_list_destroy(album->com->shuffle_list);
album->com->shuffle_list = NULL;
}
static char * new_filename(bg_album_t * album)
{
/*
* Album filenames are constructed like "aXXXXXXXX.xml",
* where XXXXXXXX is a hexadecimal unique identifier
*/
char * template = NULL;
char * path = NULL;
char * ret = NULL;
char * pos;
template = bg_sprintf("%s/a%%08x.xml", album->com->directory);
path = bg_create_unique_filename(template);
if(!path)
goto fail;
pos = strrchr(path, '/');
pos++;
ret = bg_strdup(NULL, pos);
free(path);
fail:
if(template)
free(template);
return ret;
}
void bg_album_set_default_location(bg_album_t * album)
{
if(!album->xml_file)
{
album->xml_file = new_filename(album);
}
}
static void entry_from_track_info(bg_album_common_t * com,
bg_album_entry_t * entry,
bg_track_info_t * track_info,
int update_name)
{
int i;
int name_set = 0;
entry->num_audio_streams = track_info->num_audio_streams;
entry->num_video_streams = 0;
entry->num_still_streams = 0;
for(i = 0; i < track_info->num_video_streams; i++)
{
if(track_info->video_streams[i].format.framerate_mode ==
GAVL_FRAMERATE_STILL)
entry->num_still_streams++;
else
entry->num_video_streams++;
}
entry->num_subtitle_streams = track_info->num_subtitle_streams;
if(!entry->name || update_name)
{
if(entry->name)
{
free(entry->name);
entry->name = NULL;
}
if(entry->name_w)
{
free(entry->name_w);
entry->name_w = NULL;
entry->len_w = 0;
}
/* Track info has a name */
if(com && com->use_metadata && com->metadata_format)
{
entry->name = bg_create_track_name(&track_info->metadata,
com->metadata_format);
if(entry->name)
name_set = 1;
}
if(!name_set)
{
if(track_info->name)
{
entry->name = bg_strdup(entry->name, track_info->name);
}
/* Take filename minus extension */
else
{
entry->name =
bg_get_track_name_default(entry->location,
entry->index, entry->total_tracks);
}
}
}
entry->duration = track_info->duration;
entry->flags &= ~BG_ALBUM_ENTRY_ERROR;
if(track_info->url)
{
entry->location = bg_strdup(entry->location, track_info->url);
entry->index = 0;
entry->total_tracks = 1;
entry->flags = BG_ALBUM_ENTRY_REDIRECTOR;
}
}
void bg_album_update_entry(bg_album_t * album,
bg_album_entry_t * entry,
bg_track_info_t * track_info,
int callback, int update_name)
{
entry_from_track_info(album->com, entry, track_info, update_name);
if(callback)
bg_album_entry_changed(album, entry);
}
bg_album_entry_t *
bg_album_entry_create_from_track_info(bg_track_info_t * track_info,
const char * url)
{
bg_album_entry_t * ret;
ret = bg_album_entry_create();
ret->location = bg_strdup(ret->location, url);
entry_from_track_info(NULL, ret, track_info, 1);
return ret;
}
bg_album_t * bg_album_create(bg_album_common_t * com, bg_album_type_t type,
bg_album_t * parent)
{
bg_album_t * ret = calloc(1, sizeof(*ret));
ret->com = com;
ret->parent = parent;
ret->type = type;
#ifdef HAVE_INOTIFY
ret->inotify_wd = -1;
#endif
return ret;
}
char * bg_album_get_name(bg_album_t * a)
{
return a->name;
}
int bg_album_get_num_entries(bg_album_t * a)
{
int ret = 0;
bg_album_entry_t * entry;
entry = a->entries;
while(entry)
{
ret++;
entry = entry->next;
}
return ret;
}
bg_album_entry_t * bg_album_get_entry(bg_album_t * a, int i)
{
bg_album_entry_t * ret;
ret = a->entries;
while(i--)
{
if(!ret)
return NULL;
ret = ret->next;
}
return ret;
}
/* Add items */
static void insertion_done(bg_album_t * album, int start, int num)
{
switch(album->type)
{
case BG_ALBUM_TYPE_INCOMING:
case BG_ALBUM_TYPE_FAVOURITES:
break;
case BG_ALBUM_TYPE_REGULAR:
case BG_ALBUM_TYPE_TUNER:
if(!album->xml_file)
album->xml_file = new_filename(album);
break;
case BG_ALBUM_TYPE_REMOVABLE:
case BG_ALBUM_TYPE_PLUGIN:
break;
}
delete_shuffle_list(album);
if(album->insert_callback)
album->insert_callback(album, start, num, album->insert_callback_data);
}
void bg_album_insert_entries_after(bg_album_t * album,
bg_album_entry_t * new_entries,
bg_album_entry_t * before)
{
bg_album_entry_t * last_new_entry;
int start, num;
if(!new_entries)
return;
last_new_entry = new_entries;
num = 1;
while(last_new_entry->next)
{
last_new_entry = last_new_entry->next;
num++;
}
if(!before)
{
last_new_entry->next = album->entries;
album->entries = new_entries;
start = 0;
}
else
{
start = bg_album_get_index(album, before) + 1;
last_new_entry->next = before->next;
before->next = new_entries;
}
insertion_done(album, start, num);
}
void bg_album_insert_entries_before(bg_album_t * album,
bg_album_entry_t * new_entries,
bg_album_entry_t * after)
{
bg_album_entry_t * before;
bg_album_entry_t * last_new_entry;
int start, num;
if(!new_entries)
return;
last_new_entry = new_entries;
num = 1;
while(last_new_entry->next)
{
last_new_entry = last_new_entry->next;
num++;
}
/* Fill empty album */
if(!album->entries)
{
album->entries = new_entries;
start = 0;
}
/* Append as first item */
else if(after == album->entries)
{
last_new_entry->next = album->entries;
album->entries = new_entries;
start = 0;
}
else
{
before = album->entries;
start = 1;
while(before->next != after)
{
before = before->next;
start++;
}
before->next = new_entries;
last_new_entry->next = after;
}
insertion_done(album, start, num);
}
void bg_album_insert_urls_before(bg_album_t * a,
char ** locations,
const char * plugin,
int prefer_edl,
bg_album_entry_t * after)
{
int i = 0;
bg_album_entry_t * new_entries;
while(locations[i])
{
new_entries = bg_album_load_url(a, locations[i], plugin, prefer_edl);
bg_album_insert_entries_before(a, new_entries, after);
// bg_album_changed(a);
i++;
}
}
void bg_album_insert_file_before(bg_album_t * a,
char * file,
const char * plugin,
int prefer_edl,
bg_album_entry_t * after,
time_t mtime)
{
bg_album_entry_t * new_entries;
bg_album_entry_t * e;
new_entries = bg_album_load_url(a, file, plugin, prefer_edl);
e = new_entries;
while(e)
{
e->mtime = mtime;
e->flags |= BG_ALBUM_ENTRY_SYNC;
e = e->next;
}
bg_album_insert_entries_before(a, new_entries, after);
// bg_album_changed(a);
}
void bg_album_insert_urls_after(bg_album_t * a,
char ** locations,
const char * plugin,
int prefer_edl,
bg_album_entry_t * before)
{
int i = 0;
bg_album_entry_t * new_entries;
while(locations[i])
{
new_entries = bg_album_load_url(a, locations[i], plugin, prefer_edl);
bg_album_insert_entries_after(a, new_entries, before);
before = new_entries;
if(before)
{
while(before->next)
before = before->next;
}
// bg_album_changed(a);
i++;
}
}
/* Inserts a string of the type text/uri-list into the album */
void bg_album_insert_urilist_after(bg_album_t * a, const char * str,
int len, bg_album_entry_t * before)
{
char ** uri_list;
uri_list = bg_urilist_decode(str, len);
if(!uri_list)
return;
bg_album_insert_urls_after(a, uri_list, NULL, 0, before);
bg_urilist_free(uri_list);
}
void bg_album_insert_urilist_before(bg_album_t * a, const char * str,
int len, bg_album_entry_t * after)
{
char ** uri_list;
uri_list = bg_urilist_decode(str, len);
if(!uri_list)
return;
bg_album_insert_urls_before(a, uri_list, NULL, 0, after);
bg_urilist_free(uri_list);
}
static char * get_playlist_location(const char * orig,
int strip_leading,
const char * prefix)
{
int i;
char * pos;
if(!strncmp(orig, "file://", 7))
orig += 7;
if((*orig == '/') && strip_leading)
{
for(i = 0; i < strip_leading; i++)
{
pos = strchr(orig+1, '/');
if(pos)
orig = pos;
else
return NULL;
}
}
if(prefix)
return bg_sprintf("%s%s", prefix, orig);
else
return bg_strdup(NULL, orig);
}
int bg_album_entries_save_extm3u(bg_album_entry_t * e, const char * name,
int strip_leading, const char * prefix)
{
FILE * out;
char * tmp_string;
if(!e)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN,
"Not exporting empty album");
return 0;
}
out = fopen(name, "w");
if(!out)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN,
"Could not open %s: %s", name, strerror(errno));
return 0;
}
fprintf(out, "#EXTM3U\r\n");
while(e)
{
tmp_string = get_playlist_location(e->location,
strip_leading, prefix);
if(!tmp_string)
{
e = e->next;
continue;
}
fprintf(out, "#EXTINF:%d,%s\r\n",
(int)(e->duration / GAVL_TIME_SCALE),
e->name);
fprintf(out, "%s\r\n", tmp_string);
free(tmp_string);
e = e->next;
}
fclose(out);
return 1;
}
int bg_album_entries_save_pls(bg_album_entry_t * e, const char * name,
int strip_leading, const char * prefix)
{
FILE * out;
int count = 1;
char * tmp_string;
if(!e)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN,
"Not exporting empty album");
return 0;
}
out = fopen(name, "w");
if(!out)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN,
"Could not open %s: %s", name, strerror(errno));
return 0;
}
fprintf(out, "[Playlist]\r\n");
while(e)
{
tmp_string = get_playlist_location(e->location,
strip_leading, prefix);
if(!tmp_string)
{
e = e->next;
continue;
}
fprintf(out, "File%d=%s\r\n", count, tmp_string);
fprintf(out, "Title%d=%s\r\n",count, e->name);
fprintf(out, "Length%d=%d\r\n",count, (int)(e->duration / GAVL_TIME_SCALE));
free(tmp_string);
e = e->next;
count++;
}
// Footer
fprintf(out, "NumberOfEntries=%d\r\n", count-1);
fprintf(out, "Version=2\r\n");
fclose(out);
return 1;
}
/* Open / close */
static int open_device(bg_album_t * a)
{
bg_track_info_t * track_info;
int i, j;
int num_tracks;
bg_input_plugin_t * plugin;
bg_album_entry_t * new_entry;
a->handle = bg_plugin_load(a->com->plugin_reg, a->plugin_info);
bg_plugin_lock(a->handle);
plugin = (bg_input_plugin_t*)a->handle->plugin;
/* Open the plugin */
if(!plugin->open(a->handle->priv, a->device))
{
bg_plugin_unlock(a->handle);
return 0;
}
if(plugin->get_disc_name)
{
a->disc_name = bg_strdup(a->disc_name,
plugin->get_disc_name(a->handle->priv));
if(!a->disc_name || (*a->disc_name == '\0'))
a->disc_name = bg_strdup(a->disc_name, TR("Unnamed disc"));
}
if(plugin->eject_disc)
a->flags |= BG_ALBUM_CAN_EJECT;
/* Get number of tracks */
num_tracks = plugin->get_num_tracks(a->handle->priv);
for(i = 0; i < num_tracks; i++)
{
track_info = plugin->get_track_info(a->handle->priv, i);
new_entry = calloc(1, sizeof(*new_entry));
new_entry->index = i;
new_entry->total_tracks = num_tracks;
new_entry->name = bg_strdup(NULL, track_info->name);
new_entry->plugin = bg_strdup(NULL, a->handle->info->name);
new_entry->location = bg_strdup(new_entry->location,
a->device);
new_entry->num_audio_streams = track_info->num_audio_streams;
new_entry->num_still_streams = 0;
new_entry->num_video_streams = 0;
for(j = 0; j < track_info->num_video_streams; j++)
{
if(track_info->video_streams[j].format.framerate_mode ==
GAVL_FRAMERATE_STILL)
new_entry->num_still_streams++;
else
new_entry->num_video_streams++;
}
new_entry->num_subtitle_streams =
track_info->num_subtitle_streams;
new_entry->duration = track_info->duration;
bg_album_insert_entries_before(a, new_entry, NULL);
}
bg_plugin_unlock(a->handle);
return 1;
}
bg_album_type_t bg_album_get_type(bg_album_t * a)
{
return a->type;
}
static bg_album_entry_t *
find_next_with_location(bg_album_t * a, char * filename, bg_album_entry_t * before)
{
if(!before)
before = a->entries;
else
before = before->next;
while(before)
{
if(!strcmp(before->location, filename))
break;
before = before->next;
}
return before;
}
static void sync_dir_add(bg_album_t * a, char * filename, time_t mtime)
{
bg_album_insert_file_before(a,
filename,
NULL,
0,
NULL,
mtime);
}
static void sync_dir_modify(bg_album_t * a, char * filename, time_t mtime)
{
bg_album_delete_with_file(a, filename);
sync_dir_add(a, filename, mtime);
}
int bg_album_inotify(bg_album_t * a, uint8_t * event1)
{
#ifdef HAVE_INOTIFY
char * filename;
bg_album_t * child;
struct stat stat_buf;
if(a->inotify_wd >= 0)
{
struct inotify_event *event =
( struct inotify_event * ) event1;
if(event->wd == a->inotify_wd)
{
switch(event->mask)
{
case IN_DELETE:
case IN_MOVED_FROM:
filename = bg_sprintf("%s/%s", a->watch_dir,
event->name);
bg_log(BG_LOG_INFO, LOG_DOMAIN,
"%s disappeared, updating album",
filename);
bg_album_delete_with_file(a, filename);
free(filename);
break;
case IN_CLOSE_WRITE:
case IN_MOVED_TO:
filename = bg_sprintf("%s/%s", a->watch_dir,
event->name);
bg_log(BG_LOG_INFO, LOG_DOMAIN,
"%s appeared, updating album",
filename);
if(stat(filename, &stat_buf))
{
free(filename);
return 1;
}
sync_dir_add(a, filename, stat_buf.st_mtime);
free(filename);
break;
}
return 1;
}
}
/* Not our event, try children */
child = a->children;
while(child)
{
if(bg_album_inotify(child, event1))
return 1;
child = child->next;
}
return 0;
#else
return 0;
#endif
}
static void sync_with_dir(bg_album_t * a)
{
// char * tmp_string;
DIR * dir;
char filename[FILENAME_MAX];
struct dirent * dent_ptr;
struct stat stat_buf;
bg_album_entry_t * e;
struct
{
struct dirent d;
char b[NAME_MAX]; /* Make sure there is enough memory */
} dent;
dir = opendir(a->watch_dir);
if(!dir)
return;
while(!readdir_r(dir, &dent.d, &dent_ptr))
{
if(!dent_ptr)
break;
if(dent.d.d_name[0] == '.') /* Don't import hidden files */
continue;
sprintf(filename, "%s/%s", a->watch_dir, dent.d.d_name);
if(stat(filename, &stat_buf))
continue;
/* Add directory as subalbum */
if(S_ISDIR(stat_buf.st_mode))
continue;
else if(S_ISREG(stat_buf.st_mode))
{
e = find_next_with_location(a, filename, NULL);
if(e)
{
while(e)
{
if(e->mtime != stat_buf.st_mtime)
{
sync_dir_modify(a, filename, stat_buf.st_mtime);
break;
}
else
e->flags |= BG_ALBUM_ENTRY_SYNC;
e = find_next_with_location(a, filename, e);
}
}
else
sync_dir_add(a, filename, stat_buf.st_mtime);
}
}
closedir(dir);
bg_album_delete_unsync(a);
}
int bg_album_open(bg_album_t * a)
{
char * tmp_filename;
FILE * testfile;
bg_input_plugin_t * plugin;
if(a->open_count)
{
bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Album %s already open", a->name);
a->open_count++;
return 1;
}
bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Opening album %s", a->name);
a->cfg_section = bg_cfg_section_create(NULL);
switch(a->type)
{
case BG_ALBUM_TYPE_REGULAR:
case BG_ALBUM_TYPE_FAVOURITES:
case BG_ALBUM_TYPE_INCOMING:
if(a->xml_file)
{
tmp_filename = bg_sprintf("%s/%s", a->com->directory,
a->xml_file);
/* If the file cannot be opened, it was deleted earlier
we exit quietly here */
if(!(testfile = fopen(tmp_filename,"r")))
{
free(tmp_filename);
break;
}
fclose(testfile);
bg_album_load(a, tmp_filename);
free(tmp_filename);
}
break;
case BG_ALBUM_TYPE_TUNER:
if(a->xml_file)
{
tmp_filename = bg_sprintf("%s/%s", a->com->directory,
a->xml_file);
/* If the file cannot be opened, it was deleted earlier
we exit quietly here */
if(!(testfile = fopen(tmp_filename,"r")))
{
free(tmp_filename);
if(!open_device(a))
return 0;
break;
}
fclose(testfile);
bg_album_load(a, tmp_filename);
free(tmp_filename);
a->handle = bg_plugin_load(a->com->plugin_reg, a->plugin_info);
bg_plugin_lock(a->handle);
plugin = (bg_input_plugin_t*)a->handle->plugin;
/* Open the plugin */
if(!plugin->open(a->handle->priv, a->device))
{
bg_plugin_unlock(a->handle);
return 0;
}
bg_plugin_unlock(a->handle);
}
else
if(!open_device(a))
return 0;
break;
case BG_ALBUM_TYPE_REMOVABLE:
/* Get infos from the plugin */
if(!open_device(a))
return 0;
break;
case BG_ALBUM_TYPE_PLUGIN:
return 0; /* Cannot be opened */
break;
}
a->open_count++;
if((a->type == BG_ALBUM_TYPE_REGULAR) &&
a->watch_dir)
{
sync_with_dir(a);
#ifdef HAVE_INOTIFY
a->inotify_wd = inotify_add_watch(a->com->inotify_fd,
a->watch_dir,
IN_CLOSE_WRITE | IN_DELETE |
IN_MOVED_TO | IN_MOVED_FROM);
#endif
}
return 1;
}
void bg_album_entry_destroy(bg_album_entry_t * entry)
{
if(entry->name)
free(entry->name);
if(entry->location)
free(entry->location);
if(entry->plugin)
free(entry->plugin);
free(entry);
}
void bg_album_entries_destroy(bg_album_entry_t * entries)
{
bg_album_entry_t * tmp_entry;
while(entries)
{
tmp_entry = entries->next;
bg_album_entry_destroy(entries);
entries = tmp_entry;
}
}
int bg_album_entries_count(bg_album_entry_t * e)
{
int ret = 0;
while(e)
{
ret++;
e = e->next;
}
return ret;
}
bg_album_entry_t * bg_album_entry_create()
{
bg_album_entry_t * ret;
ret = calloc(1, sizeof(*ret));
return ret;
}
void bg_album_close(bg_album_t *a )
{
// char * tmp_filename;
a->open_count--;
if(a->open_count)
{
bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Not closing album %s (open_count > 0)", a->name);
return;
}
bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Closing album %s", a->name);
/* Tell the tree, if we are the current album */
if((a == a->com->current_album) && a->com->set_current_callback)
{
a->com->set_current_callback(a->com->set_current_callback_data,
NULL, NULL);
}
switch(a->type)
{
case BG_ALBUM_TYPE_REMOVABLE:
case BG_ALBUM_TYPE_TUNER:
a->flags &= ~BG_ALBUM_CAN_EJECT;
bg_plugin_unref(a->handle);
a->handle = NULL;
if(a->disc_name)
{
free(a->disc_name);
a->disc_name = NULL;
}
if(a->type == BG_ALBUM_TYPE_TUNER)
bg_album_save(a, NULL);
break;
case BG_ALBUM_TYPE_REGULAR:
case BG_ALBUM_TYPE_INCOMING:
case BG_ALBUM_TYPE_FAVOURITES:
bg_album_save(a, NULL);
break;
case BG_ALBUM_TYPE_PLUGIN:
break;
}
/* Delete entries */
bg_album_entries_destroy(a->entries);
a->entries = NULL;
/* Delete shuffle list */
delete_shuffle_list(a);
/* Destroy config data */
if(a->cfg_section)
{
bg_cfg_section_destroy(a->cfg_section);
a->cfg_section = NULL;
}
#ifdef HAVE_INOTIFY
if(a->inotify_wd >= 0)
{
inotify_rm_watch(a->com->inotify_fd, a->inotify_wd);
a->inotify_wd = -1;
}
#endif
// if(a->watch_dir)
}
void bg_album_set_expanded(bg_album_t * a, int expanded)
{
if(expanded)
a->flags |= BG_ALBUM_EXPANDED;
else
{
bg_album_t * child;
a->flags &= ~BG_ALBUM_EXPANDED;
/* Unexpand the children too */
child = a->children;
while(child)
{
bg_album_set_expanded(child, 0);
child = child->next;
}
}
}
int bg_album_get_expanded(bg_album_t * a)
{
if(a->flags & BG_ALBUM_EXPANDED)
return 1;
return 0;
}
int bg_album_is_open(bg_album_t * a)
{
return (a->open_count) ? 1 : 0;
}
void bg_album_destroy(bg_album_t * a)
{
// char * tmp_filename;
bg_album_t * tmp_album;
/* Things to do if an album was open */
if(a->open_count)
{
bg_album_save(a, NULL);
}
if(a->name)
free(a->name);
if(a->xml_file)
free(a->xml_file);
if(a->device)
free(a->device);
if(a->disc_name)
free(a->disc_name);
if(a->cfg_section)
bg_cfg_section_destroy(a->cfg_section);
/* Free entries */
bg_album_entries_destroy(a->entries);
/* Free Children */
while(a->children)
{
tmp_album = a->children->next;
bg_album_destroy(a->children);
a->children = tmp_album;
}
/* free rest */
free(a);
}
void bg_album_delete_selected(bg_album_t * album)
{
int num_selected = 0;
bg_album_entry_t * cur;
bg_album_entry_t * cur_next;
bg_album_entry_t * new_entries_end = NULL;
bg_album_entry_t * new_entries;
int index, i;
int * indices = NULL;
if(!album->entries)
return;
cur = album->entries;
num_selected = bg_album_num_selected(album);
if(!num_selected)
return;
if(album->delete_callback)
{
indices = malloc((num_selected +1)*sizeof(*indices));
}
cur = album->entries;
new_entries = NULL;
index = 0;
i = 0;
while(cur)
{
cur_next = cur->next;
if(cur->flags & BG_ALBUM_ENTRY_SELECTED)
{
if(cur == album->com->current_entry)
{
album->com->current_entry = NULL;
album->com->current_album = NULL;
}
bg_album_entry_destroy(cur);
if(indices)
indices[i] = index;
i++;
}
else
{
if(!new_entries)
{
new_entries = cur;
new_entries_end = cur;
}
else
{
new_entries_end->next = cur;
new_entries_end = new_entries_end->next;
}
}
cur = cur_next;
index++;
}
if(new_entries)
new_entries_end->next = NULL;
album->entries = new_entries;
delete_shuffle_list(album);
if(indices)
{
indices[i] = -1;
album->delete_callback(album, indices, album->delete_callback_data);
free(indices);
}
// bg_album_changed(album);
}
void bg_album_delete_unsync(bg_album_t * album)
{
int num_selected = 0;
bg_album_entry_t * cur;
bg_album_entry_t * cur_next;
bg_album_entry_t * new_entries_end = NULL;
bg_album_entry_t * new_entries;
int index, i;
int * indices = NULL;
if(!album->entries)
return;
cur = album->entries;
num_selected = bg_album_num_unsync(album);
if(!num_selected)
return;
if(album->delete_callback)
{
indices = malloc((num_selected +1)*sizeof(*indices));
}
cur = album->entries;
new_entries = NULL;
index = 0;
i = 0;
while(cur)
{
cur_next = cur->next;
if(!(cur->flags & BG_ALBUM_ENTRY_SYNC))
{
if(cur == album->com->current_entry)
{
album->com->current_entry = NULL;
album->com->current_album = NULL;
}
bg_album_entry_destroy(cur);
if(indices)
indices[i] = index;
i++;
}
else
{
if(!new_entries)
{
new_entries = cur;
new_entries_end = cur;
}
else
{
new_entries_end->next = cur;
new_entries_end = new_entries_end->next;
}
}
cur = cur_next;
index++;
}
if(new_entries)
new_entries_end->next = NULL;
album->entries = new_entries;
delete_shuffle_list(album);
if(indices)
{
indices[i] = -1;
album->delete_callback(album, indices, album->delete_callback_data);
free(indices);
}
// bg_album_changed(album);
}
void bg_album_delete_with_file(bg_album_t * album, const char * filename)
{
bg_album_entry_t * cur;
bg_album_entry_t * cur_next;
bg_album_entry_t * new_entries_end = NULL;
bg_album_entry_t * new_entries;
int index, i;
int * indices = NULL;
if(!album->entries)
return;
cur = album->entries;
cur = album->entries;
new_entries = NULL;
index = 0;
i = 0;
while(cur)
{
cur_next = cur->next;
if(!strcmp(cur->location, filename))
{
if(cur == album->com->current_entry)
{
album->com->current_entry = NULL;
album->com->current_album = NULL;
}
bg_album_entry_destroy(cur);
if(album->delete_callback)
{
indices = realloc(indices, (i+1) * sizeof(indices));
indices[i] = index;
}
i++;
}
else
{
if(!new_entries)
{
new_entries = cur;
new_entries_end = cur;
}
else
{
new_entries_end->next = cur;
new_entries_end = new_entries_end->next;
}
}
cur = cur_next;
index++;
}
if(new_entries)
new_entries_end->next = NULL;
album->entries = new_entries;
delete_shuffle_list(album);
if(indices)
{
indices = realloc(indices, (i+1) * sizeof(indices));
indices[i] = -1;
album->delete_callback(album, indices, album->delete_callback_data);
free(indices);
}
}
void bg_album_select_error_tracks(bg_album_t * album)
{
bg_album_entry_t * cur;
cur = album->entries;
while(cur)
{
if(cur->flags & BG_ALBUM_ENTRY_ERROR)
cur->flags |= BG_ALBUM_ENTRY_SELECTED;
else
cur->flags &= ~BG_ALBUM_ENTRY_SELECTED;
cur = cur->next;
}
bg_album_changed(album);
}
static bg_album_entry_t * copy_selected(bg_album_t * album)
{
bg_album_entry_t * ret = NULL;
bg_album_entry_t * ret_end = NULL;
bg_album_entry_t * tmp_entry;
tmp_entry = album->entries;
while(tmp_entry)
{
if(tmp_entry->flags & BG_ALBUM_ENTRY_SELECTED)
{
if(ret)
{
ret_end->next = bg_album_entry_copy(album, tmp_entry);
ret_end = ret_end->next;
}
else
{
ret = bg_album_entry_copy(album, tmp_entry);
ret_end = ret;
}
}
tmp_entry = tmp_entry->next;
}
return ret;
}
static bg_album_entry_t * extract_selected(bg_album_t * album)
{
bg_album_entry_t * selected_end = NULL;
bg_album_entry_t * other_end = NULL;
bg_album_entry_t * tmp_entry;
bg_album_entry_t * other = NULL;
bg_album_entry_t * selected = NULL;
while(album->entries)
{
tmp_entry = album->entries->next;
if(album->entries->flags & BG_ALBUM_ENTRY_SELECTED)
{
if(!selected)
{
selected = album->entries;
selected_end = selected;
}
else
{
selected_end->next = album->entries;
selected_end = selected_end->next;
}
selected_end->next = NULL;
}
else
{
if(!other)
{
other = album->entries;
other_end = other;
}
else
{
other_end->next = album->entries;
other_end = other_end->next;
}
other_end->next = NULL;
}
album->entries = tmp_entry;
}
album->entries = other;
return selected;
}
void bg_album_move_selected_up(bg_album_t * album)
{
bg_album_entry_t * selected;
selected = extract_selected(album);
bg_album_insert_entries_after(album,
selected,
NULL);
bg_album_changed(album);
}
void bg_album_move_selected_down(bg_album_t * album)
{
bg_album_entry_t * selected;
selected = extract_selected(album);
bg_album_insert_entries_before(album,
selected,
NULL);
bg_album_changed(album);
}
typedef struct
{
bg_album_entry_t * entry;
char * sort_string;
} sort_entries_struct;
void bg_album_sort_entries(bg_album_t * album)
{
int num_entries;
int i, j;
char * tmp_string;
int sort_string_len;
sort_entries_struct * s_tmp;
int keep_going;
sort_entries_struct ** s;
bg_album_entry_t * tmp_entry;
/* 1. Count the entries */
num_entries = 0;
tmp_entry = album->entries;
while(tmp_entry)
{
tmp_entry = tmp_entry->next;
num_entries++;
}
if(!num_entries)
return;
/* Set up the album array */
s = malloc(num_entries * sizeof(*s));
tmp_entry = album->entries;
for(i = 0; i < num_entries; i++)
{
s[i] = calloc(1, sizeof(*(s[i])));
s[i]->entry = tmp_entry;
/* Set up the sort string */
tmp_string = bg_utf8_to_system(tmp_entry->name,
strlen(tmp_entry->name));
sort_string_len = strxfrm(NULL, tmp_string, 0);
s[i]->sort_string = malloc(sort_string_len+1);
strxfrm(s[i]->sort_string, tmp_string, sort_string_len+1);
free(tmp_string);
/* Advance */
tmp_entry = tmp_entry->next;
}
/* Now, do a braindead bubblesort algorithm */
for(i = 0; i < num_entries - 1; i++)
{
keep_going = 0;
for(j = num_entries-1; j > i; j--)
{
if(strcmp(s[j]->sort_string, s[j-1]->sort_string) < 0)
{
s_tmp = s[j];
s[j] = s[j-1];
s[j-1] = s_tmp;
keep_going = 1;
}
}
if(!keep_going)
break;
}
/* Rechain entries */
album->entries = s[0]->entry;
for(i = 0; i < num_entries-1; i++)
{
s[i]->entry->next = s[i+1]->entry;
}
s[num_entries-1]->entry->next = NULL;
/* Free everything */
for(i = 0; i < num_entries; i++)
{
free(s[i]->sort_string);
free(s[i]);
}
free(s);
bg_album_changed(album);
}
typedef struct
{
bg_album_t * child;
char * sort_string;
} sort_children_struct;
void bg_album_sort_children(bg_album_t * album)
{
int num_children;
int i, j;
char * tmp_string;
int sort_string_len;
sort_children_struct * s_tmp;
int keep_going;
sort_children_struct ** s;
bg_album_t * tmp_child;
/* 1. Count the children */
num_children = 0;
tmp_child = album->children;
while(tmp_child)
{
tmp_child = tmp_child->next;
num_children++;
}
if(!num_children)
return;
/* Set up the album array */
s = malloc(num_children * sizeof(*s));
tmp_child = album->children;
for(i = 0; i < num_children; i++)
{
s[i] = calloc(1, sizeof(*(s[i])));
s[i]->child = tmp_child;
/* Set up the sort string */
tmp_string = bg_utf8_to_system(tmp_child->name,
strlen(tmp_child->name));
sort_string_len = strxfrm(NULL, tmp_string, 0);
s[i]->sort_string = malloc(sort_string_len+1);
strxfrm(s[i]->sort_string, tmp_string, sort_string_len+1);
free(tmp_string);
/* Advance */
tmp_child = tmp_child->next;
}
/* Now, do a braindead bubblesort algorithm */
for(i = 0; i < num_children - 1; i++)
{
keep_going = 0;
for(j = num_children-1; j > i; j--)
{
if(strcmp(s[j]->sort_string, s[j-1]->sort_string) < 0)
{
s_tmp = s[j];
s[j] = s[j-1];
s[j-1] = s_tmp;
keep_going = 1;
}
}
if(!keep_going)
break;
}
/* Rechain children */
album->children = s[0]->child;
for(i = 0; i < num_children-1; i++)
{
s[i]->child->next = s[i+1]->child;
}
s[num_children-1]->child->next = NULL;
/* Free everything */
for(i = 0; i < num_children; i++)
{
free(s[i]->sort_string);
free(s[i]);
}
free(s);
}
/* END */
void bg_album_rename_track(bg_album_t * album,
const bg_album_entry_t * entry_c,
const char * name)
{
bg_album_entry_t * entry;
entry = album->entries;
while(entry)
{
if(entry == entry_c)
break;
entry = entry->next;
}
entry->name = bg_strdup(entry->name, name);
if(entry->name_w)
{
free(entry->name_w);
entry->name_w = NULL;
entry->len_w = 0;
}
bg_album_entry_changed(album, entry);
}
void bg_album_rename(bg_album_t * a, const char * name)
{
a->name = bg_strdup(a->name, name);
if(((a->type == BG_ALBUM_TYPE_REMOVABLE) ||
(a->type == BG_ALBUM_TYPE_TUNER)) &&
a->plugin_info)
{
bg_plugin_registry_set_device_name(a->com->plugin_reg,
a->plugin_info->name, a->device,
name);
}
if(a->name_change_callback)
a->name_change_callback(a, name, a->name_change_callback_data);
}
bg_album_entry_t * bg_album_get_current_entry(bg_album_t * a)
{
return a->com->current_entry;
}
int bg_album_next(bg_album_t * a, int wrap)
{
if(a->com->current_entry)
{
if(!a->com->current_entry->next)
{
if(wrap)
{
if(a->com->set_current_callback)
a->com->set_current_callback(a->com->set_current_callback_data,
a, a->entries);
return 1;
}
else
return 0;
}
else
{
if(a->com->set_current_callback)
a->com->set_current_callback(a->com->set_current_callback_data,
a, a->com->current_entry->next);
return 1;
}
}
else
return 0;
}
gavl_time_t bg_album_get_duration(bg_album_t * a)
{
gavl_time_t ret = 0;
bg_album_entry_t * e;
e = a->entries;
while(e)
{
if(e->duration == GAVL_TIME_UNDEFINED)
return GAVL_TIME_UNDEFINED;
else
ret += e->duration;
e = e->next;
}
return ret;
}
int bg_album_get_index(bg_album_t* a, const bg_album_entry_t * entry)
{
int index = 0;
const bg_album_entry_t * e;
e = a->entries;
while(1)
{
if(e == entry)
return index;
index++;
e = e->next;
if(!e)
break;
}
return -1;
}
int bg_album_previous(bg_album_t * a, int wrap)
{
bg_album_entry_t * tmp_entry;
if(!a->com->current_entry)
return 0;
if(a->com->current_entry == a->entries)
{
if(!wrap)
return 0;
tmp_entry = a->entries;
while(tmp_entry->next)
tmp_entry = tmp_entry->next;
if(a->com->set_current_callback)
a->com->set_current_callback(a->com->set_current_callback_data,
a, tmp_entry);
return 1;
}
else
{
tmp_entry = a->entries;
while(tmp_entry->next != a->com->current_entry)
tmp_entry = tmp_entry->next;
if(a->com->set_current_callback)
a->com->set_current_callback(a->com->set_current_callback_data,
a, tmp_entry);
return 1;
}
}
void bg_album_set_change_callback(bg_album_t * a,
void (*change_callback)(bg_album_t * a, void * data),
void * change_callback_data)
{
a->change_callback = change_callback;
a->change_callback_data = change_callback_data;
}
void bg_album_set_entry_change_callback(bg_album_t * a,
void (*change_callback)(bg_album_t * a,
const bg_album_entry_t * e,
void * data),
void * change_callback_data)
{
a->entry_change_callback = change_callback;
a->entry_change_callback_data = change_callback_data;
}
void bg_album_set_current_change_callback(bg_album_t * a,
void (*change_callback)(bg_album_t * a,
const bg_album_entry_t * e,
void * data),
void * change_callback_data)
{
a->current_change_callback = change_callback;
a->current_change_callback_data = change_callback_data;
}
void bg_album_set_delete_callback(bg_album_t * a,
void (*delete_callback)(bg_album_t * current_album,
int * indices, void * data),
void * delete_callback_data)
{
a->delete_callback = delete_callback;
a->delete_callback_data = delete_callback_data;
}
void bg_album_set_insert_callback(bg_album_t * a,
void (*insert_callback)(bg_album_t * current_album,
int start, int num, void * data),
void * insert_callback_data)
{
a->insert_callback = insert_callback;
a->insert_callback_data = insert_callback_data;
}
void bg_album_set_name_change_callback(bg_album_t * a,
void (*name_change_callback)(bg_album_t * a,
const char * name,
void * data),
void * name_change_callback_data)
{
a->name_change_callback = name_change_callback;
a->name_change_callback_data = name_change_callback_data;
}
void bg_album_changed(bg_album_t * a)
{
if(a->change_callback)
a->change_callback(a, a->change_callback_data);
}
void bg_album_current_changed(bg_album_t * a)
{
if(a->current_change_callback)
a->current_change_callback(a->com->current_album, a->com->current_entry,
a->current_change_callback_data);
}
void bg_album_entry_changed(bg_album_t * a, const bg_album_entry_t * e)
{
if(a->entry_change_callback)
a->entry_change_callback(a->com->current_album, e,
a->entry_change_callback_data);
}
void bg_album_set_current(bg_album_t * a, const bg_album_entry_t * e)
{
bg_album_entry_t * tmp_entry;
tmp_entry = a->entries;
while(tmp_entry != e)
tmp_entry = tmp_entry->next;
if(a->com->set_current_callback)
a->com->set_current_callback(a->com->set_current_callback_data,
a, tmp_entry);
// bg_album_current_changed(a);
}
void bg_album_play(bg_album_t * a)
{
if(a->com->play_callback)
a->com->play_callback(a->com->play_callback_data);
}
bg_cfg_section_t * bg_album_get_cfg_section(bg_album_t * album)
{
return album->cfg_section;
}
int bg_album_is_current(bg_album_t * a)
{
return (a == a->com->current_album) ? 1 : 0;
}
int bg_album_entry_is_current(bg_album_t * a,
bg_album_entry_t * e)
{
return ((a == a->com->current_album) &&
(e == a->com->current_entry)) ? 1 : 0;
}
bg_plugin_registry_t * bg_album_get_plugin_registry(bg_album_t * a)
{
return a->com->plugin_reg;
}
void bg_album_get_times(bg_album_t * a,
gavl_time_t * duration_before,
gavl_time_t * duration_current,
gavl_time_t * duration_after)
{
bg_album_entry_t * e;
if(a != a->com->current_album)
{
*duration_before = GAVL_TIME_UNDEFINED;
*duration_current = GAVL_TIME_UNDEFINED;
*duration_after = GAVL_TIME_UNDEFINED;
return;
}
e = a->entries;
*duration_before = 0;
while(e != a->com->current_entry)
{
if(e->duration == GAVL_TIME_UNDEFINED)
{
*duration_before = GAVL_TIME_UNDEFINED;
break;
}
*duration_before += e->duration;
e = e->next;
}
*duration_current = a->com->current_entry->duration;
*duration_after = 0;
e = a->com->current_entry->next;
while(e)
{
if(e->duration == GAVL_TIME_UNDEFINED)
{
*duration_after = GAVL_TIME_UNDEFINED;
break;
}
*duration_after += e->duration;
e = e->next;
}
}
void bg_album_set_error(bg_album_t * a, int err)
{
if(err)
a->flags |= BG_ALBUM_ERROR;
else
a->flags &= ~BG_ALBUM_ERROR;
}
int bg_album_get_error(bg_album_t * a)
{
return !!(a->flags & BG_ALBUM_ERROR);
}
void bg_album_append_child(bg_album_t * parent, bg_album_t * child)
{
bg_album_t * album_before;
if(parent->children)
{
album_before = parent->children;
while(album_before->next)
album_before = album_before->next;
album_before->next = child;
}
else
parent->children = child;
}
static void add_device(bg_album_t * album,
const char * device,
const char * name)
{
bg_album_t * device_album;
bg_album_type_t type = BG_ALBUM_TYPE_REGULAR;
if(album->plugin_info->flags & BG_PLUGIN_REMOVABLE)
type = BG_ALBUM_TYPE_REMOVABLE;
else if(album->plugin_info->flags & BG_PLUGIN_TUNER)
type = BG_ALBUM_TYPE_TUNER;
device_album = bg_album_create(album->com, type, album);
device_album->device = bg_strdup(device_album->device, device);
if(name)
{
device_album->name = bg_strdup(device_album->name,
name);
}
else
{
device_album->name = bg_strdup(device_album->name,
device);
}
device_album->plugin_info = album->plugin_info;
bg_album_append_child(album, device_album);
}
void bg_album_add_device(bg_album_t * album,
const char * device,
const char * name)
{
add_device(album, device, name);
bg_plugin_registry_add_device(album->com->plugin_reg,
album->plugin_info->name,
device, name);
}
static bg_album_t *
remove_from_list(bg_album_t * list, bg_album_t * album, int * index)
{
bg_album_t * sibling_before;
*index = 0;
if(album == list)
return album->next;
else
{
sibling_before = list;
(*index)++;
while(sibling_before->next != album)
{
sibling_before = sibling_before->next;
(*index)++;
}
sibling_before->next = album->next;
return list;
}
}
void bg_album_remove_from_parent(bg_album_t * album)
{
int index;
if(!album->parent)
return;
album->parent->children = remove_from_list(album->parent->children, album, &index);
if(album->type == BG_ALBUM_TYPE_REMOVABLE)
{
bg_plugin_registry_remove_device(album->com->plugin_reg,
album->plugin_info->name,
album->plugin_info->devices[index].device,
album->plugin_info->devices[index].name);
}
}
void bg_album_set_devices(bg_album_t * a)
{
bg_album_t * tmp_album;
int j;
/* Delete previous children */
while(a->children)
{
tmp_album = a->children->next;
bg_album_destroy(a->children);
a->children = tmp_album;
}
if(a->plugin_info->devices && a->plugin_info->devices->device)
{
j = 0;
while(a->plugin_info->devices[j].device)
{
add_device(a, a->plugin_info->devices[j].device,
a->plugin_info->devices[j].name);
j++;
} /* Device loop */
}
}
void bg_album_find_devices(bg_album_t * a)
{
bg_plugin_registry_find_devices(a->com->plugin_reg, a->plugin_info->name);
bg_album_set_devices(a);
}
static bg_album_entry_t * remove_redirectors(bg_album_t * album,
bg_album_entry_t * entries)
{
bg_album_entry_t * before;
bg_album_entry_t * e;
bg_album_entry_t * new_entry, * end_entry;
int done = 0;
const bg_plugin_info_t * info;
const char * name;
done = 1;
e = entries;
while(e)
{
if(e->flags & BG_ALBUM_ENTRY_REDIRECTOR)
{
/* Load "real" url */
if(e->plugin)
{
info = bg_plugin_find_by_name(album->com->plugin_reg,
e->plugin);
name = info->name;
}
else
name = NULL;
new_entry = bg_album_load_url(album,
e->location,
name, 0);
if(new_entry)
{
/* Insert new entries into list */
if(e != entries)
{
before = entries;
while(before->next != e)
before = before->next;
before->next = new_entry;
}
else
{
entries = new_entry;
}
end_entry = new_entry;
while(end_entry->next)
{
/* Set plugin so we don't have to set it next time */
end_entry->plugin = bg_strdup(end_entry->plugin, album->com->load_handle->info->name);
end_entry = end_entry->next;
}
/* Set plugin so we don't have to set it next time */
end_entry->plugin = bg_strdup(end_entry->plugin, album->com->load_handle->info->name);
end_entry->next = e->next;
bg_album_entry_destroy(e);
e = new_entry;
}
else
{
/* Remove e from list */
if(e != entries)
{
before = entries;
while(before->next != e)
before = before->next;
before->next = e->next;
}
else
{
entries = e->next;
before = NULL;
}
bg_album_entry_destroy(e);
e = (before) ? before->next : entries;
}
}
else
{
/* Leave e as it is */
e = e->next;
}
}
return entries;
}
static int is_blacklisted(bg_album_common_t * com,
const char * url)
{
const char * pos;
if(!com->blacklist) // No blacklist
return 0;
if(strncmp(url, "file:", 5) && (*url != '/')) // Remote file
return 0;
pos = strrchr(url, '.');
if(pos)
{
pos++;
if(bg_string_match(pos, com->blacklist))
{
bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Not loading %s (blacklisted extension)", url);
return 1;
}
}
pos = strrchr(url, '/');
if(pos)
{
pos++;
if(bg_string_match(pos, com->blacklist_files))
{
bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Not loading %s (blacklisted filename)", url);
return 1;
}
}
return 0;
}
bg_album_entry_t * bg_album_load_url(bg_album_t * album,
char * url,
const char * plugin_name,
int prefer_edl)
{
int i, num_entries;
bg_album_entry_t * new_entry;
bg_album_entry_t * end_entry = NULL;
bg_album_entry_t * ret = NULL;
// char * system_location;
bg_input_plugin_t * plugin;
bg_track_info_t * track_info;
const bg_plugin_info_t * info;
// const char * file_plugin_name;
if(is_blacklisted(album->com, url))
{
return NULL;
}
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Loading %s", url);
/* Load the appropriate plugin */
if(plugin_name)
{
info = bg_plugin_find_by_name(album->com->plugin_reg,
plugin_name);
}
else
info = NULL;
bg_album_common_prepare_callbacks(album->com, NULL);
if(!bg_input_plugin_load(album->com->plugin_reg,
url, info,
&album->com->load_handle,
&album->com->input_callbacks, prefer_edl))
{
bg_log(BG_LOG_WARNING, LOG_DOMAIN, "Loading %s failed", url);
return NULL;
}
plugin = (bg_input_plugin_t*)(album->com->load_handle->plugin);
/* Open the track */
if(!plugin->get_num_tracks)
num_entries = 1;
else
num_entries = plugin->get_num_tracks(album->com->load_handle->priv);
for(i = 0; i < num_entries; i++)
{
track_info = plugin->get_track_info(album->com->load_handle->priv, i);
new_entry = bg_album_entry_create();
// new_entry->location = bg_system_to_utf8(url, strlen(url));
new_entry->location = bg_strdup(new_entry->location, url);
new_entry->index = i;
new_entry->total_tracks = num_entries;
if(album->com->load_handle->edl)
new_entry->flags |= BG_ALBUM_ENTRY_EDL;
bg_log(BG_LOG_INFO, LOG_DOMAIN, "Loaded %s (track %d of %d)", url,
new_entry->index+1, new_entry->total_tracks);
bg_album_common_set_auth_info(album->com, new_entry);
bg_album_update_entry(album, new_entry, track_info, 0, 1);
new_entry->plugin = bg_strdup(new_entry->plugin, plugin_name);
if(ret)
{
end_entry->next = new_entry;
end_entry = end_entry->next;
}
else
{
ret = new_entry;
end_entry = ret;
}
}
plugin->close(album->com->load_handle->priv);
ret = remove_redirectors(album, ret);
return ret;
}
int bg_album_get_unique_id(bg_album_t * album)
{
album->com->highest_id++;
return album->com->highest_id;
}
static int refresh_entry(bg_album_t * album,
bg_album_entry_t * entry, bg_edl_t * edl)
{
const bg_plugin_info_t * info;
// char * system_location;
bg_input_plugin_t * plugin;
bg_track_info_t * track_info;
int i;
/* Check, which plugin to use */
if(entry->plugin)
{
info = bg_plugin_find_by_name(album->com->plugin_reg, entry->plugin);
}
else
info = NULL;
// system_location = bg_utf8_to_system(entry->location,
// strlen(entry->location));
bg_album_common_prepare_callbacks(album->com, entry);
if(!bg_input_plugin_load(album->com->plugin_reg,
entry->location,
info,
&album->com->load_handle, &album->com->input_callbacks,
!!(entry->flags & BG_ALBUM_ENTRY_EDL)))
{
entry->flags |= BG_ALBUM_ENTRY_ERROR;
bg_album_entry_changed(album, entry);
return 0;
}
plugin = (bg_input_plugin_t*)(album->com->load_handle->plugin);
track_info = plugin->get_track_info(album->com->load_handle->priv,
entry->index);
bg_album_common_set_auth_info(album->com, entry);
if(edl) /* Need timescales */
{
if(plugin->set_track)
plugin->set_track(album->com->load_handle->priv, entry->index);
if(plugin->set_audio_stream)
{
for(i = 0; i < track_info->num_audio_streams; i++)
plugin->set_audio_stream(album->com->load_handle->priv,
i, BG_STREAM_ACTION_DECODE);
}
if(plugin->set_video_stream)
{
for(i = 0; i < track_info->num_video_streams; i++)
plugin->set_video_stream(album->com->load_handle->priv,
i, BG_STREAM_ACTION_DECODE);
}
if(plugin->set_subtitle_stream)
{
for(i = 0; i < track_info->num_subtitle_streams; i++)
plugin->set_subtitle_stream(album->com->load_handle->priv,
i, BG_STREAM_ACTION_DECODE);
}
if(plugin->start)
plugin->start(album->com->load_handle->priv);
bg_edl_append_track_info(edl, track_info, entry->location, entry->index,
entry->total_tracks, entry->name);
}
bg_album_update_entry(album, entry, track_info, 1, 1);
plugin->close(album->com->load_handle->priv);
bg_album_entry_changed(album, entry);
return 1;
}
void bg_album_refresh_selected(bg_album_t * album)
{
bg_album_entry_t * cur;
cur = album->entries;
while(cur)
{
if(cur->flags & BG_ALBUM_ENTRY_SELECTED)
refresh_entry(album, cur, NULL);
cur = cur->next;
}
}
bg_edl_t * bg_album_selected_to_edl(bg_album_t * album)
{
bg_edl_t * ret;
bg_album_entry_t * cur;
ret = bg_edl_create();
cur = album->entries;
while(cur)
{
if(cur->flags & BG_ALBUM_ENTRY_SELECTED)
refresh_entry(album, cur, ret);
cur = cur->next;
}
return ret;
}
void bg_album_copy_selected_to_favourites(bg_album_t * a)
{
int was_open;
bg_album_entry_t * sel;
sel = copy_selected(a);
if(!bg_album_is_open(a->com->favourites))
{
bg_album_open(a->com->favourites);
was_open = 0;
}
was_open = 1;
bg_album_insert_entries_before(a->com->favourites, sel, NULL);
if(!was_open)
bg_album_close(a->com->favourites);
}
void bg_album_move_selected_to_favourites(bg_album_t * a)
{
int was_open;
bg_album_entry_t * sel;
sel = extract_selected(a);
if(!bg_album_is_open(a->com->favourites))
{
bg_album_open(a->com->favourites);
was_open = 0;
}
was_open = 1;
bg_album_insert_entries_before(a->com->favourites, sel, NULL);
if(!was_open)
bg_album_close(a->com->favourites);
}
const char * bg_album_get_disc_name(bg_album_t * a)
{
return a->disc_name;
}
char * bg_album_get_label(bg_album_t * a)
{
return a->disc_name ? a->disc_name : a->name;
}
int bg_album_can_eject(bg_album_t * a)
{
/* Leave this disabled until ejecting really works */
return !!(a->flags & BG_ALBUM_CAN_EJECT);
// return 0;
}
void bg_album_eject(bg_album_t * a)
{
bg_input_plugin_t * plugin;
bg_plugin_handle_t * handle;
handle = bg_plugin_load(a->com->plugin_reg, a->plugin_info);
plugin = (bg_input_plugin_t*)handle->plugin;
if(!plugin->eject_disc(a->device))
bg_log(BG_LOG_ERROR, LOG_DOMAIN, "Ejecting disc failed");
bg_plugin_unref(handle);
}
char * bg_album_selected_to_string(bg_album_t * a)
{
char time_string[GAVL_TIME_STRING_LEN];
bg_album_entry_t * entry;
char * ret = NULL;
char * tmp_string;
int index = 1;
entry = a->entries;
while(entry)
{
if(entry->flags & BG_ALBUM_ENTRY_SELECTED)
{
if(ret)
ret = bg_strcat(ret, "\n");
gavl_time_prettyprint(entry->duration, time_string);
tmp_string = bg_sprintf("%d.\t%s\t%s", index, entry->name, time_string);
ret = bg_strcat(ret, tmp_string);
free(tmp_string);
}
entry = entry->next;
index++;
}
return ret;
}
void bg_album_select_entry(bg_album_t * a, int entry)
{
bg_album_entry_t * e;
e = bg_album_get_entry(a, entry);
e->flags |= BG_ALBUM_ENTRY_SELECTED;
}
void bg_album_unselect_entry(bg_album_t * a, int entry)
{
bg_album_entry_t * e;
e = bg_album_get_entry(a, entry);
e->flags &= ~BG_ALBUM_ENTRY_SELECTED;
}
void bg_album_unselect_all(bg_album_t * a)
{
bg_album_entry_t * e;
e = a->entries;
while(e)
{
e->flags &= ~BG_ALBUM_ENTRY_SELECTED;
e = e->next;
}
}
int bg_album_num_selected(bg_album_t * a)
{
bg_album_entry_t * e;
int ret = 0;
e = a->entries;
while(e)
{
if(e->flags & BG_ALBUM_ENTRY_SELECTED)
ret++;
e = e->next;
}
return ret;
}
int bg_album_num_unsync(bg_album_t * a)
{
bg_album_entry_t * e;
int ret = 0;
e = a->entries;
while(e)
{
if(!(e->flags & BG_ALBUM_ENTRY_SYNC))
ret++;
e = e->next;
}
return ret;
}
int bg_album_entry_is_selected(bg_album_t * a, int entry)
{
bg_album_entry_t * e;
e = bg_album_get_entry(a, entry);
return !!(e->flags & BG_ALBUM_ENTRY_SELECTED);
}
void bg_album_toggle_select_entry(bg_album_t * a, int entry)
{
bg_album_entry_t * e;
e = bg_album_get_entry(a, entry);
if(e->flags & BG_ALBUM_ENTRY_SELECTED)
e->flags &= ~BG_ALBUM_ENTRY_SELECTED;
else
e->flags |= BG_ALBUM_ENTRY_SELECTED;
}
void bg_album_select_entries(bg_album_t * a, int start, int end)
{
int i;
int swp;
bg_album_entry_t * e;
if(end < start)
{
swp = end;
end = start;
start = swp;
}
e = bg_album_get_entry(a, start);
for(i = start; i <= end; i++)
{
if(!e)
{
bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Invalid selection range given");
return;
}
e->flags |= BG_ALBUM_ENTRY_SELECTED;
e = e->next;
}
}
/*********************************
* Seek support
*********************************/
struct bg_album_seek_data_s
{
char * str;
int ignore;
int exact;
int changed;
struct
{
wchar_t * str;
int len;
int matched;
} * substrings;
int num_substrings;
int substrings_alloc;
int (*match_func)(const wchar_t *s1, const wchar_t *s2, size_t n);
bg_charset_converter_t * cnv;
};
bg_album_seek_data_t * bg_album_seek_data_create()
{
bg_album_seek_data_t * ret;
ret = calloc(1, sizeof(*ret));
ret->cnv = bg_charset_converter_create("UTF-8", "WCHAR_T");
return ret;
}
void bg_album_seek_data_set_string(bg_album_seek_data_t * d, const char * str)
{
if(d->str && !strcmp(d->str, str))
return;
d->str = bg_strdup(d->str, str);
d->changed = 1;
}
void bg_album_seek_data_ignore_case(bg_album_seek_data_t * d, int ignore)
{
if(d->ignore == ignore)
return;
d->ignore = ignore;
d->changed = 1;
}
void bg_album_seek_data_exact_string(bg_album_seek_data_t * d, int exact)
{
if(d->exact == exact)
return;
d->exact = exact;
d->changed = 1;
}
static int match_string_ignore(const wchar_t *s1, const wchar_t *s2, size_t n)
{
int i;
for(i = 0; i < n; i++)
{
if(!(*s1) || !(*s2) || (towlower(*s1) != towlower(*s2)))
return 0;
s1++;
s2++;
}
return 1;
}
static int match_string(const wchar_t *s1, const wchar_t *s2, size_t n)
{
int i;
for(i = 0; i < n; i++)
{
if(!(*s1) || !(*s2) || (*s1 != *s2))
return 0;
s1++;
s2++;
}
return 1;
}
static void update_seek_data(bg_album_seek_data_t * d)
{
int i;
char ** substrings;
char ** substrings_d = NULL;
char * substrings_s[2];
if(d->exact)
{
d->num_substrings = 1;
substrings_s[0] = d->str;
substrings_s[1] = NULL;
substrings = substrings_s;
}
else
{
substrings_d = bg_strbreak(d->str, ' ');
d->num_substrings = 0;
while(substrings_d[d->num_substrings])
d->num_substrings++;
substrings = substrings_d;
}
if(d->num_substrings > d->substrings_alloc)
{
d->substrings = realloc(d->substrings,
d->num_substrings * sizeof(*d->substrings));
memset(d->substrings + d->substrings_alloc,
0, sizeof(*d->substrings) * (d->num_substrings - d->substrings_alloc));
d->substrings_alloc = d->num_substrings;
}
for(i = 0; i < d->num_substrings; i++)
{
if(d->substrings[i].str)
free(d->substrings[i].str);
d->substrings[i].str =
(wchar_t*)bg_convert_string(d->cnv,
substrings[i], -1,
&d->substrings[i].len);
d->substrings[i].len /= sizeof(wchar_t);
}
if(d->ignore)
d->match_func = match_string_ignore;
else
d->match_func = match_string;
if(substrings_d)
bg_strbreak_free(substrings_d);
d->changed = 0;
}
static int entry_matches(bg_album_entry_t * entry, bg_album_seek_data_t * d)
{
int i, j, keep_going;
wchar_t * ptr;
if(d->changed)
update_seek_data(d);
if(!entry->name_w)
{
entry->name_w = (wchar_t*)bg_convert_string(d->cnv,
entry->name, -1,
&entry->len_w);
entry->len_w /= sizeof(wchar_t);
}
ptr = entry->name_w;
for(j = 0; j < d->num_substrings; j++)
d->substrings[j].matched = 0;
keep_going = 1;
for(i = 0; i < entry->len_w-1; i++)
{
keep_going = 0;
for(j = 0; j < d->num_substrings; j++)
{
if(!d->substrings[j].matched)
d->substrings[j].matched = d->match_func(d->substrings[j].str,
ptr, d->substrings[j].len);
if(!d->substrings[j].matched)
keep_going = 1;
}
if(!keep_going)
break;
ptr++;
}
if(keep_going)
return 0;
return 1;
}
bg_album_entry_t * bg_album_seek_entry_after(bg_album_t * a,
bg_album_entry_t * e,
bg_album_seek_data_t * d)
{
if(!e)
e = a->entries;
else
e = e->next;
while(e)
{
if(entry_matches(e, d))
return e;
e = e->next;
}
return e;
}
bg_album_entry_t * bg_album_seek_entry_before(bg_album_t * a,
bg_album_entry_t * e,
bg_album_seek_data_t * d)
{
bg_album_entry_t * cur;
bg_album_entry_t * match = NULL;
if(!e)
{
e = a->entries;
while(e->next)
e = e->next;
if(entry_matches(e, d))
return e;
}
cur = a->entries;
while(1)
{
if(cur == e)
return match;
if(entry_matches(cur, d))
{
if(cur->next == e)
return cur;
match = cur;
}
cur = cur->next;
if(!cur)
break;
}
return NULL;
}
void bg_album_seek_data_destroy(bg_album_seek_data_t * d)
{
int i;
bg_charset_converter_destroy(d->cnv);
if(d->str)
free(d->str);
for(i = 0; i < d->substrings_alloc; i++)
{
if(d->substrings[i].str)
free(d->substrings[i].str);
}
if(d->substrings)
free(d->substrings);
free(d);
}
int bg_album_seek_data_changed(bg_album_seek_data_t * d)
{
return d->changed;
}
bg_album_entry_t * bg_album_entry_copy(bg_album_t * a, bg_album_entry_t * e)
{
bg_album_entry_t * ret;
/* Also sets unique ID */
ret = bg_album_entry_create();
ret->name = bg_strdup(ret->name, e->name);
ret->location = bg_strdup(ret->location, e->location);
ret->plugin = bg_strdup(ret->plugin, e->plugin);
ret->duration = e->duration;
ret->num_audio_streams = e->num_audio_streams;
ret->num_still_streams = e->num_still_streams;
ret->num_video_streams = e->num_video_streams;
ret->num_subtitle_streams = e->num_subtitle_streams;
/*
* Track index for multi track files/plugins
*/
ret->index = e->index;
ret->total_tracks = e->total_tracks;
/* Authentication data */
ret->username = bg_strdup(ret->username, e->username);
ret->password = bg_strdup(ret->password, e->password);
ret->flags = e->flags;
/* Clear selected bit */
ret->flags &= ~(BG_ALBUM_ENTRY_SELECTED);
/* The wchar stuff will be rebuilt on demand */
return ret;
}
void bg_album_set_watch_dir(bg_album_t * a,
const char * dir)
{
char * pos;
a->watch_dir = bg_strdup(a->watch_dir, dir);
pos = a->watch_dir + strlen(a->watch_dir) - 1;
if(*pos == '\n')
*pos = '\0';
}
gmerlin-1.2.0~dfsg/lib/player_thread.c 0000644 0001750 0001750 00000015301 11764363410 017633 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#define LOG_DOMAIN "player.thread"
#include
#include
/* Binary semaphores: Like POSIX semaphores but the
value can only be 1 or 0 */
typedef struct
{
int count;
int nwaiting;
pthread_mutex_t lock;
pthread_cond_t cond;
} bin_sem_t;
static void bin_sem_init(bin_sem_t * s)
{
pthread_mutex_init(&s->lock, NULL);
pthread_cond_init(&s->cond, NULL);
}
static void bin_sem_destroy(bin_sem_t * s)
{
pthread_mutex_destroy(&s->lock);
pthread_cond_destroy(&s->cond);
}
static void bin_sem_wait(bin_sem_t * s)
{
pthread_mutex_lock(&s->lock);
if(!s->count)
{
s->nwaiting++;
pthread_cond_wait(&s->cond, &s->lock);
s->nwaiting--;
}
s->count = 0;
pthread_mutex_unlock(&s->lock);
}
static void bin_sem_post(bin_sem_t * s)
{
pthread_mutex_lock(&s->lock);
s->count = 1;
if(s->nwaiting)
pthread_cond_broadcast(&s->cond);
pthread_mutex_unlock(&s->lock);
}
static void bin_sem_reset(bin_sem_t * s)
{
pthread_mutex_lock(&s->lock);
s->count = 0;
pthread_mutex_unlock(&s->lock);
}
struct bg_player_thread_common_s
{
pthread_cond_t start_cond;
pthread_mutex_t start_mutex;
};
struct bg_player_thread_s
{
bg_player_thread_common_t * com;
pthread_t thread;
bin_sem_t sem;
void * (*func)(void*);
void * arg;
int do_stop;
int do_pause;
pthread_mutex_t mutex;
};
bg_player_thread_common_t * bg_player_thread_common_create()
{
bg_player_thread_common_t * com = calloc(1, sizeof(*com));
pthread_cond_init(&com->start_cond, NULL);
pthread_mutex_init(&com->start_mutex, NULL);
return com;
}
void bg_player_thread_common_destroy(bg_player_thread_common_t * com)
{
pthread_cond_destroy(&com->start_cond);
pthread_mutex_destroy(&com->start_mutex);
free(com);
}
bg_player_thread_t *
bg_player_thread_create(bg_player_thread_common_t * com)
{
bg_player_thread_t * th = calloc(1, sizeof(*th));
th->com = com;
bin_sem_init(&th->sem);
pthread_mutex_init(&th->mutex, NULL);
return th;
}
void bg_player_thread_destroy(bg_player_thread_t * th)
{
bin_sem_destroy(&th->sem);
pthread_mutex_destroy(&th->mutex);
free(th);
}
void bg_player_thread_set_func(bg_player_thread_t * th,
void * (*func)(void*), void * arg)
{
th->func = func;
th->arg = arg;
th->do_pause = 0;
th->do_stop = 0;
}
void bg_player_threads_init(bg_player_thread_t ** th, int num)
{
int i;
for(i = 0; i < num; i++)
{
if(th[i]->func)
{
// fprintf(stderr, "Starting thread...\n");
pthread_create(&th[i]->thread, NULL, th[i]->func, th[i]->arg);
// fprintf(stderr, "Starting thread done\n");
}
}
/* Wait until all threads are started */
for(i = 0; i < num; i++)
{
if(th[i]->func)
{
// fprintf(stderr, "Sem wait...");
bin_sem_wait(&th[i]->sem);
// fprintf(stderr, "done ret: %d, val: %d\n", ret, val);
}
}
}
void bg_player_threads_start(bg_player_thread_t ** th, int num)
{
int i;
/* Lock the global mutex. This will succeed after all
threads wait for the start condition */
pthread_mutex_lock(&th[0]->com->start_mutex);
pthread_cond_broadcast(&th[0]->com->start_cond);
pthread_mutex_unlock(&th[0]->com->start_mutex);
/* Wait until all threads woke up */
for(i = 0; i < num; i++)
{
if(th[i]->func)
{
// fprintf(stderr, "Sem wait...");
bin_sem_wait(&th[i]->sem);
// fprintf(stderr, "done ret: %d, val: %d\n", ret, val);
}
}
}
void bg_player_threads_pause(bg_player_thread_t ** th, int num)
{
int i;
/* Set pause flag */
for(i = 0; i < num; i++)
{
if(th[i]->func)
{
pthread_mutex_lock(&th[i]->mutex);
th[i]->do_pause = 1;
pthread_mutex_unlock(&th[i]->mutex);
}
}
/* Wait until all threads are paused */
for(i = 0; i < num; i++)
{
if(th[i]->func)
bin_sem_wait(&th[i]->sem);
}
}
void bg_player_threads_join(bg_player_thread_t ** th, int num)
{
int i;
/* Set stop flag */
for(i = 0; i < num; i++)
{
if(th[i]->func)
{
pthread_mutex_lock(&th[i]->mutex);
th[i]->do_stop = 1;
pthread_mutex_unlock(&th[i]->mutex);
}
}
/* Start the threads if they where paused.
If not paused, this call does no harm */
bg_player_threads_start(th, num);
for(i = 0; i < num; i++)
{
if(th[i]->func)
{
// fprintf(stderr, "Joining thread...\n");
pthread_join(th[i]->thread, NULL);
// fprintf(stderr, "Joining thread done, sem\n");
bin_sem_reset(&th[i]->sem);
// bin_sem_init(&th[i]->sem, 0, 0);
}
}
}
/* called from within the thread */
int bg_player_thread_wait_for_start(bg_player_thread_t * th)
{
int ret = 1;
pthread_mutex_lock(&th->com->start_mutex);
// fprintf(stderr, "Sem post...\n");
bin_sem_post(&th->sem);
// fprintf(stderr, "Sem post done\n");
pthread_cond_wait(&th->com->start_cond, &th->com->start_mutex);
pthread_mutex_unlock(&th->com->start_mutex);
pthread_mutex_lock(&th->mutex);
th->do_pause = 0;
if(th->do_stop)
ret = 0;
pthread_mutex_unlock(&th->mutex);
bin_sem_post(&th->sem);
return ret;
}
int bg_player_thread_check(bg_player_thread_t * th)
{
int do_pause;
pthread_mutex_lock(&th->mutex);
if(th->do_stop)
{
pthread_mutex_unlock(&th->mutex);
bin_sem_post(&th->sem);
return 0;
}
do_pause = th->do_pause;
pthread_mutex_unlock(&th->mutex);
if(do_pause)
{
pthread_mutex_lock(&th->mutex);
th->do_pause = 0;
pthread_mutex_unlock(&th->mutex);
return bg_player_thread_wait_for_start(th);
}
return 1;
}
gmerlin-1.2.0~dfsg/lib/formats.c 0000644 0001750 0001750 00000015062 11764363410 016467 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
static char * get_dB(float val)
{
if(val == 0.0)
return bg_strdup(NULL, TR("Zero"));
else
return bg_sprintf("%02f dB", val);
}
char * bg_audio_format_to_string(gavl_audio_format_t * f, int use_tabs)
{
const char * format;
char * channel_order = NULL;
char * center_level;
char * rear_level;
char * ret;
int i;
center_level = get_dB(f->center_level);
rear_level = get_dB(f->rear_level);
/* Create channel order string */
for(i = 0; i < f->num_channels; i++)
{
channel_order = bg_strcat(channel_order,
TRD(gavl_channel_id_to_string(f->channel_locations[i]), NULL));
if(i < f->num_channels - 1)
channel_order = bg_strcat(channel_order, ", ");
}
if(!use_tabs)
format = TR("Channels: %d\nChannel order: %s\nSamplerate: %d\nSamples per frame: %d\nInterleave Mode: %s\nSample format: %s");
else
format = TR("Channels:\t %d\nChannel order\t %s\nSamplerate:\t %d\nSamples per frame:\t %d\nInterleave Mode:\t %s\nSample format:\t %s");
ret =
bg_sprintf(format,
f->num_channels,
channel_order,
f->samplerate, f->samples_per_frame,
TRD(gavl_interleave_mode_to_string(f->interleave_mode), NULL),
TRD(gavl_sample_format_to_string(f->sample_format), NULL));
free(channel_order);
free(center_level);
free(rear_level);
return ret;
}
char * bg_video_format_to_string(gavl_video_format_t * format, int use_tabs)
{
char * str, *ret;
const char * s;
if(!use_tabs)
s = TR("Frame size: %d x %d\nImage size: %d x %d\nPixel size: %d x %d\nPixel format: %s\n");
else
s = TR("Frame size:\t %d x %d\nImage size:\t %d x %d\nPixel size:\t %d x %d\nPixel format:\t %s\n");
ret =
bg_sprintf(s,
format->frame_width, format->frame_height,
format->image_width, format->image_height,
format->pixel_width, format->pixel_height,
TRD(gavl_pixelformat_to_string(format->pixelformat), NULL));
if(format->framerate_mode == GAVL_FRAMERATE_STILL)
{
ret = bg_strcat(ret, TR("Still image\n"));
}
else if(!format->frame_duration &&
(format->framerate_mode == GAVL_FRAMERATE_VARIABLE))
{
if(!use_tabs)
s = TR("Framerate: Variable (timescale: %d)\n");
else
s = TR("Framerate:\tVariable (timescale: %d)\n");
str = bg_sprintf(s, format->timescale);
ret = bg_strcat(ret, str);
free(str);
}
else
{
if(!use_tabs)
s = TR("Framerate: %f fps [%d / %d]%s\n");
else
s = TR("Framerate:\t%f fps [%d / %d]%s\n");
str =
bg_sprintf(s,
(float)(format->timescale)/((float)format->frame_duration),
format->timescale, format->frame_duration,
((format->framerate_mode == GAVL_FRAMERATE_CONSTANT) ? TR(" (constant)") :
TR(" (variable)")));
ret = bg_strcat(ret, str);
free(str);
}
if(!use_tabs)
s = TR("Interlace mode: %s");
else
s = TR("Interlace mode:\t%s");
str =
bg_sprintf(s, TRD(gavl_interlace_mode_to_string(format->interlace_mode),
NULL));
ret = bg_strcat(ret, str);
free(str);
if(format->pixelformat == GAVL_YUV_420_P)
{
if(!use_tabs)
s = TR("\nChroma placement: %s");
else
s = TR("\nChroma placement:\t%s");
str = bg_sprintf(s, TRD(gavl_chroma_placement_to_string(format->chroma_placement), NULL));
ret = bg_strcat(ret, str);
free(str);
}
if(format->timecode_format.int_framerate)
{
if(!use_tabs)
s = TR("\nTimecode rate: %d");
else
s = TR("\nTimecode rate:\t%d");
str = bg_sprintf(s, format->timecode_format.int_framerate);
ret = bg_strcat(ret, str);
free(str);
if(format->timecode_format.flags)
{
if(!use_tabs)
s = TR("\nTimecode flags: ");
else
s = TR("\nTimecode flags:\t");
ret = bg_strcat(ret, s);
if(format->timecode_format.flags &
GAVL_TIMECODE_DROP_FRAME)
ret = bg_strcat(ret, TR("Drop "));
}
}
return ret;
}
/*
* When gavl will be gettextized (probably never), the
* following stuff must be removed
*/
// gavl_channel_id_t
TRU("Unknown channel")
TRU("Front C")
TRU("Front L")
TRU("Front R")
TRU("Front CL")
TRU("Front CR")
TRU("Rear C")
TRU("Rear L")
TRU("Rear R")
TRU("Side L")
TRU("Side R")
TRU("LFE")
TRU("AUX")
// gavl_sample_format_t
TRU("Not specified")
TRU("Unsigned 8 bit")
TRU("Signed 8 bit")
TRU("Unsigned 16 bit")
TRU("Signed 16 bit")
TRU("Signed 32 bit")
TRU("Floating point")
TRU("Double precision")
// gavl_interleave_mode_t
TRU("Not interleaved")
TRU("Interleaved channel pairs")
TRU("All channels interleaved")
// gavl_interlace_mode_t
TRU("None (Progressive)")
TRU("Top field first")
TRU("Bottom field first")
TRU("Mixed")
// gavl_chroma_placement_t
TRU("MPEG-1/JPEG")
TRU("MPEG-2")
TRU("DV PAL")
// gavl_pixelformat_t
TRU("15 bpp RGB")
TRU("15 bpp BGR")
TRU("16 bpp RGB")
TRU("16 bpp BGR")
TRU("24 bpp RGB")
TRU("24 bpp BGR")
TRU("32 bpp RGB")
TRU("32 bpp BGR")
TRU("32 bpp RGBA")
TRU("48 bpp RGB")
TRU("64 bpp RGBA")
TRU("Float RGB")
TRU("Float RGBA")
TRU("YUV 422 (YUY2)")
TRU("YUV 422 (UYVY)")
TRU("YUVA 4444")
TRU("YUV 420 Planar")
TRU("YUV 410 Planar")
TRU("YUV 411 Planar")
TRU("YUV 422 Planar")
TRU("YUV 422 Planar (16 bit)")
TRU("YUV 444 Planar")
TRU("YUV 444 Planar (16 bit)")
TRU("YUVJ 420 Planar")
TRU("YUVJ 422 Planar")
TRU("YUVJ 444 Planar")
TRU("Undefined")
gmerlin-1.2.0~dfsg/lib/fileformat.c 0000644 0001750 0001750 00000022746 11764363410 017153 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
static int read_stdio(void * priv, uint8_t * data, int len)
{
FILE * f = priv;
return fread(data, 1, len, f);
}
static int write_stdio(void * priv, const uint8_t * data, int len)
{
FILE * f = priv;
return fwrite(data, 1, len, f);
}
static int64_t ftell_stdio(void * priv)
{
FILE * f = priv;
return ftell(f);
}
static int64_t seek_stdio(void * priv, int64_t pos, int whence)
{
FILE * f = priv;
fseek(f, pos, whence);
return ftell(f);
}
static void close_stdio(void * priv)
{
FILE * f = priv;
if(f)
fclose(f);
}
int bg_f_io_open_stdio_read(bg_f_io_t * io, const char * filename)
{
FILE * f;
f = fopen(filename, "r");
if(!f)
return 0;
io->data = f;
io->read_callback = read_stdio;
io->seek_callback = seek_stdio;
io->ftell_callback = ftell_stdio;
io->close_callback = close_stdio;
return 1;
}
int bg_f_io_open_stdio_write(bg_f_io_t * io, const char * filename)
{
FILE * f;
f = fopen(filename, "w");
if(!f)
return 0;
io->data = f;
io->write_callback = write_stdio;
io->seek_callback = seek_stdio;
io->ftell_callback = ftell_stdio;
io->close_callback = close_stdio;
return 1;
}
void bg_f_io_close(bg_f_io_t * io)
{
if(io->close_callback)
io->close_callback(io->data);
if(io->buffer)
free(io->buffer);
}
static void bg_f_io_buffer_alloc(bg_f_io_t * io, int len)
{
if(len <= io->buffer_alloc)
return;
io->buffer_alloc = len + 512;
io->buffer = realloc(io->buffer, io->buffer_alloc);
}
static inline int read_32(bg_f_io_t * io, uint32_t * val)
{
uint8_t data[4];
if(io->read_callback(io->data, data, 4) < 4)
return 0;
*val = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
return 1;
}
static inline int read_64(bg_f_io_t * io, uint64_t * val)
{
uint8_t data[8];
if(io->read_callback(io->data, data, 8) < 8)
return 0;
*val = data[0]; *val <<= 8;
*val |= data[1]; *val <<= 8;
*val |= data[2]; *val <<= 8;
*val |= data[3]; *val <<= 8;
*val |= data[4]; *val <<= 8;
*val |= data[5]; *val <<= 8;
*val |= data[6]; *val <<= 8;
*val |= data[7];
return 1;
}
static inline int write_32(bg_f_io_t * io, uint32_t val)
{
uint8_t data[4];
data[0] = (val & 0xff000000) >> 24;
data[1] = (val & 0xff0000) >> 16;
data[2] = (val & 0xff00) >> 8;
data[3] = (val & 0xff);
return io->write_callback(io->data, data, 4);
}
static inline int write_64(bg_f_io_t * io, uint64_t val)
{
uint8_t data[8];
data[0] = (val & 0xff00000000000000LL) >> 56;
data[1] = (val & 0x00ff000000000000LL) >> 48;
data[2] = (val & 0x0000ff0000000000LL) >> 40;
data[3] = (val & 0x000000ff00000000LL) >> 32;
data[4] = (val & 0x00000000ff000000LL) >> 24;
data[5] = (val & 0x0000000000ff0000LL) >> 16;
data[6] = (val & 0x000000000000ff00LL) >> 8;
data[7] = (val & 0x00000000000000ffLL);
return io->write_callback(io->data, data, 8);
}
int bg_f_chunk_read(bg_f_io_t * io, bg_f_chunk_t * ch)
{
ch->start = io->ftell_callback(io->data);
if(!read_32(io, &ch->type) ||
!read_64(io, &ch->size))
return 0;
return 1;
}
int bg_f_chunk_write_header(bg_f_io_t * io, bg_f_chunk_t * ch, bg_f_chunk_type_t type)
{
ch->type = type;
ch->start = io->ftell_callback(io->data);
if(!write_32(io, ch->type) ||
!write_64(io, ch->size))
return 0;
return 1;
}
int bg_f_chunk_write_footer(bg_f_io_t * io, bg_f_chunk_t * ch)
{
int64_t end_pos = io->ftell_callback(io->data);
io->seek_callback(io->data, ch->start + 4, SEEK_SET);
ch->size = end_pos - ch->start - 12;
if(!write_64(io, ch->size))
return 0;
io->seek_callback(io->data, end_pos, SEEK_SET);
return 1;
}
int bg_f_signature_read(bg_f_io_t * io, bg_f_chunk_t * ch, bg_f_signature_t * sig)
{
if(!read_32(io, &sig->type))
return 0;
return 1;
}
int bg_f_signature_write(bg_f_io_t * io, bg_f_signature_t * sig)
{
bg_f_chunk_t ch;
if(!bg_f_chunk_write_header(io, &ch, CHUNK_TYPE_SIGNATURE))
return 0;
if(!write_32(io, sig->type))
return 0;
if(!bg_f_chunk_write_footer(io, &ch))
return 0;
return 1;
}
int bg_f_audio_format_read(bg_f_io_t * io, bg_f_chunk_t * ch, gavl_audio_format_t * f,
int * big_endian)
{
bg_f_io_buffer_alloc(io, ch->size);
if(io->read_callback(io->data, io->buffer, ch->size) < ch->size)
return 0;
bg_deserialize_audio_format(f, io->buffer, ch->size, big_endian);
return 1;
}
int bg_f_audio_format_write(bg_f_io_t * io, const gavl_audio_format_t * f)
{
bg_f_chunk_t ch;
int len;
if(!bg_f_chunk_write_header(io, &ch, CHUNK_TYPE_AUDIO_FORMAT))
return 0;
len = bg_serialize_audio_format(f, NULL, 0);
bg_f_io_buffer_alloc(io, len);
len = bg_serialize_audio_format(f, io->buffer, len);
if(io->write_callback(io->data, io->buffer, len) < len)
return 0;
if(!bg_f_chunk_write_footer(io, &ch))
return 0;
return 1;
}
int bg_f_video_format_read(bg_f_io_t * io, bg_f_chunk_t * ch, gavl_video_format_t * f,
int * big_endian)
{
bg_f_io_buffer_alloc(io, ch->size);
if(io->read_callback(io->data, io->buffer, ch->size) < ch->size)
return 0;
bg_deserialize_video_format(f, io->buffer, ch->size, big_endian);
return 1;
}
int bg_f_video_format_write(bg_f_io_t * io, const gavl_video_format_t * f)
{
bg_f_chunk_t ch;
int len;
if(!bg_f_chunk_write_header(io, &ch, CHUNK_TYPE_VIDEO_FORMAT))
return 0;
len = bg_serialize_video_format(f, NULL, 0);
bg_f_io_buffer_alloc(io, len);
len = bg_serialize_video_format(f, io->buffer, len);
if(io->write_callback(io->data, io->buffer, len) < len)
return 0;
if(!bg_f_chunk_write_footer(io, &ch))
return 0;
return 1;
}
int bg_f_audio_frame_read(bg_f_io_t * io, bg_f_chunk_t * ch,
const gavl_audio_format_t * format,
gavl_audio_frame_t * f, int big_endian,
gavl_dsp_context_t * ctx)
{
int len;
len = bg_serialize_audio_frame_header(format, f, NULL, 0);
bg_f_io_buffer_alloc(io, len);
if(io->read_callback(io->data, io->buffer, len) < len)
return 0;
bg_deserialize_audio_frame_header(format, f, io->buffer, len);
return bg_deserialize_audio_frame(ctx,
format, f, io->read_callback,
io->data, big_endian);
}
int bg_f_audio_frame_write(bg_f_io_t * io,
const gavl_audio_format_t * format,
const gavl_audio_frame_t * f)
{
int len;
bg_f_chunk_t ch;
if(!bg_f_chunk_write_header(io, &ch, CHUNK_TYPE_AUDIO_FRAME))
return 0;
len = bg_serialize_audio_frame_header(format, f, NULL, 0);
bg_f_io_buffer_alloc(io, len);
bg_serialize_audio_frame_header(format, f, io->buffer, len);
if(io->write_callback(io->data, io->buffer, len) < len)
return 0;
if(!bg_serialize_audio_frame(format,
f, io->write_callback,
io->data))
return 0;
if(!bg_f_chunk_write_footer(io, &ch))
return 0;
return 1;
}
int bg_f_video_frame_read(bg_f_io_t * io, bg_f_chunk_t * ch,
const gavl_video_format_t * format,
gavl_video_frame_t * f, int big_endian,
gavl_dsp_context_t * ctx)
{
int len;
len = bg_serialize_video_frame_header(format, f, NULL, 0);
bg_f_io_buffer_alloc(io, len);
if(io->read_callback(io->data, io->buffer, len) < len)
return 0;
bg_deserialize_video_frame_header(format, f, io->buffer, len);
return bg_deserialize_video_frame(ctx,
format, f, io->read_callback,
io->data, big_endian);
}
int bg_f_video_frame_write(bg_f_io_t * io,
const gavl_video_format_t * format,
const gavl_video_frame_t * f)
{
int len;
bg_f_chunk_t ch;
if(!bg_f_chunk_write_header(io, &ch, CHUNK_TYPE_VIDEO_FRAME))
return 0;
len = bg_serialize_video_frame_header(format, f, NULL, 0);
bg_f_io_buffer_alloc(io, len);
bg_serialize_video_frame_header(format, f, io->buffer, len);
if(io->write_callback(io->data, io->buffer, len) < len)
return 0;
if(!bg_serialize_video_frame(format,
f, io->write_callback,
io->data))
return 0;
if(!bg_f_chunk_write_footer(io, &ch))
return 0;
return 1;
}
gmerlin-1.2.0~dfsg/lib/remote.c 0000644 0001750 0001750 00000026411 11764363410 016307 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#ifdef HAVE_SYS_SELECT_H
#include
#else
#include
#include
#endif
#include
#include
#include
#include
#include
#include
/* For INADDR_ Macros */
#include
#define LOG_DOMAIN_SERVER "remote.server"
#define LOG_DOMAIN_CLIENT "remote.client"
/*
* Server
*/
typedef struct server_connection_s
{
int fd;
int do_put_msg;
struct server_connection_s * next;
} server_connection_t;
struct bg_remote_server_s
{
int fd;
char * protocol_id;
/* Configuration stuff */
int allow_remote;
int listen_port;
int max_connections;
server_connection_t * connections;
int do_reopen;
bg_msg_t * msg;
int last_read_fd;
};
bg_remote_server_t * bg_remote_server_create(int listen_port,
char * protocol_id)
{
bg_remote_server_t * ret;
ret = calloc(1, sizeof(*ret));
ret->listen_port = listen_port;
ret->protocol_id = bg_strdup(ret->protocol_id, protocol_id);
ret->fd = -1;
ret->msg = bg_msg_create();
return ret;
}
int bg_remote_server_init(bg_remote_server_t * s)
{
int flags = 0;
if(!s->allow_remote)
flags |= BG_SOCKET_LOOPBACK;
s->fd = bg_listen_socket_create_inet(s->listen_port,
s->max_connections,
flags);
if(s->fd < 0)
{
bg_log(BG_LOG_WARNING, LOG_DOMAIN_SERVER,
"Setting up socket failed, this instance won't be reachable via remote");
return 0;
}
bg_log(BG_LOG_INFO, LOG_DOMAIN_SERVER,
"Remote socket listening at port %d", s->listen_port);
return 1;
}
static server_connection_t * add_connection(bg_remote_server_t * s,
int fd)
{
int len;
char ** strings = NULL;
char * welcome_msg = NULL;
char * buffer = NULL;
int buffer_alloc = 0;
server_connection_t * ret = NULL;
if(!bg_socket_read_line(fd, &buffer,
&buffer_alloc, 1))
{
bg_log(BG_LOG_INFO, LOG_DOMAIN_SERVER, "Reading hello line failed");
goto fail;
}
strings = bg_strbreak(buffer, ' ');
if(!strings[0] || strcmp(strings[0], s->protocol_id) ||
!strings[1] || strcmp(strings[1], VERSION) ||
!strings[2])
{
bg_log(BG_LOG_INFO, LOG_DOMAIN_SERVER, "Protocol mismatch");
goto fail;
}
/* Write the answer message */
welcome_msg = bg_sprintf("%s %s\r\n", s->protocol_id, VERSION);
len = strlen(welcome_msg);
if(bg_socket_write_data(fd, (uint8_t*)welcome_msg, len) < len)
goto fail;
ret = calloc(1, sizeof(*ret));
ret->fd = fd;
ret->do_put_msg = atoi(strings[2]);
fail:
if(buffer)
free(buffer);
if(welcome_msg)
free(welcome_msg);
if(strings)
bg_strbreak_free(strings);
if(!ret)
close(fd);
return ret;
}
static server_connection_t *
remove_connection(server_connection_t * connections,
server_connection_t * conn)
{
server_connection_t * before;
if(conn == connections)
{
connections = connections->next;
}
else
{
before = connections;
while(before->next != conn)
before = before->next;
before->next = conn->next;
}
close(conn->fd);
free(conn);
bg_log(BG_LOG_DEBUG, LOG_DOMAIN_SERVER, "Client connection closed");
return connections;
}
static void bg_remote_server_cleanup(bg_remote_server_t * s)
{
close(s->fd);
s->fd = -1;
while(s->connections) s->connections =
remove_connection(s->connections, s->connections);
}
static void check_connections(bg_remote_server_t * s)
{
int new_fd;
server_connection_t * conn;
/* Check for new connections */
new_fd = bg_listen_socket_accept(s->fd);
if(new_fd >= 0)
{
bg_log(BG_LOG_DEBUG, LOG_DOMAIN_SERVER, "New client connection");
conn = add_connection(s, new_fd);
if(conn)
{
conn->next = s->connections;
s->connections = conn;
}
}
}
static int select_socket(int fd, int milliseconds)
{
fd_set rset;
struct timeval timeout;
FD_ZERO (&rset);
FD_SET (fd, &rset);
timeout.tv_sec = milliseconds / 1000;
timeout.tv_usec = (milliseconds % 1000) * 1000;
if(select (fd+1, &rset, NULL, NULL, &timeout) > 0)
return 1;
return 0;
}
bg_msg_t * bg_remote_server_get_msg(bg_remote_server_t * s)
{
server_connection_t * conn, * tmp_conn;
check_connections(s);
if(!s->connections)
return NULL;
conn = s->connections;
while(conn)
{
if(select_socket(conn->fd, 0))
{
bg_msg_free(s->msg);
if(bg_msg_read_socket(s->msg, conn->fd, -1))
{
s->last_read_fd = conn->fd;
return s->msg;
}
else /* Select said reading won't block but reading failed
-> Client probably disconnected */
{
tmp_conn = conn->next;
s->connections = remove_connection(s->connections, conn);
conn = tmp_conn;
}
}
else
conn = conn->next;
}
return NULL;
}
bg_msg_t * bg_remote_server_get_msg_write(bg_remote_server_t * s)
{
bg_msg_free(s->msg);
return s->msg;
}
int bg_remote_server_done_msg_write(bg_remote_server_t * s)
{
/* Write message */
return bg_msg_write_socket(s->msg, s->last_read_fd);
}
void bg_remote_server_wait_close(bg_remote_server_t * s)
{
gavl_time_t delay_time = GAVL_TIME_SCALE/200;
while(1)
{
bg_remote_server_get_msg(s);
if(!s->connections)
break;
gavl_time_delay(&delay_time);
}
}
void bg_remote_server_put_msg(bg_remote_server_t * s, bg_msg_t * m)
{
}
void bg_remote_server_destroy(bg_remote_server_t * s)
{
while(s->connections)
s->connections = remove_connection(s->connections, s->connections);
if(s->protocol_id)
free(s->protocol_id);
if(s->fd >= 0)
close(s->fd);
if(s->msg)
bg_msg_destroy(s->msg);
free(s);
}
static const bg_parameter_info_t server_parameters[] =
{
{
.name = "allow_remote",
.long_name = TRS("Allow connections from other machines"),
.type = BG_PARAMETER_CHECKBUTTON,
.val_default = { .val_i = 0 },
},
{
.name = "max_connections",
.long_name = TRS("Maximum number of connections"),
.type = BG_PARAMETER_INT,
.val_min = { .val_i = 0 },
.val_max = { .val_i = 100 },
.val_default = { .val_i = 5 },
},
{ /* End of parameters */ }
};
const bg_parameter_info_t * bg_remote_server_get_parameters(bg_remote_server_t * s)
{
return server_parameters;
}
void
bg_remote_server_set_parameter(void * data,
const char * name,
const bg_parameter_value_t * v)
{
bg_remote_server_t * s;
s = (bg_remote_server_t *)data;
if(!name)
{
if((s->fd < 0) && s->max_connections)
s->do_reopen = 1;
if(!s->max_connections)
{
s->do_reopen = 0;
if(s->fd >= 0)
bg_remote_server_cleanup(s); /* Close everything */
}
else if(s->do_reopen)
{
if(s->fd >= 0)
bg_remote_server_cleanup(s); /* Close everything */
bg_remote_server_init(s);
}
return;
}
else if(!strcmp(name, "allow_remote"))
{
if(s->allow_remote != v->val_i)
s->do_reopen = 1;
s->allow_remote = v->val_i;
}
else if(!strcmp(name, "max_connections"))
s->max_connections = v->val_i;
}
/* Client */
struct bg_remote_client_s
{
int fd;
char * protocol_id;
int read_messages;
bg_msg_t * msg;
int milliseconds; /* Read and connect timeout */
};
bg_remote_client_t * bg_remote_client_create(const char * protocol_id,
int read_messages)
{
bg_remote_client_t * ret;
ret = calloc(1, sizeof(*ret));
ret->protocol_id = bg_strdup(ret->protocol_id, protocol_id);
ret->read_messages = read_messages;
ret->msg = bg_msg_create();
return ret;
}
void bg_remote_client_close(bg_remote_client_t * c)
{
close(c->fd);
c->fd = -1;
}
int bg_remote_client_init(bg_remote_client_t * c,
const char * host, int port,
int milliseconds)
{
int ret = 0;
char ** strings = NULL;
int buffer_alloc = 0;
char * buffer = NULL;
int len;
char * answer_message;
bg_host_address_t * addr = bg_host_address_create();
c->milliseconds = milliseconds;
if(!bg_host_address_set(addr, host, port, SOCK_STREAM))
goto fail;
c->fd = bg_socket_connect_inet(addr, c->milliseconds);
if(c->fd < 0)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN_CLIENT, "Connecting failed");
goto fail;
}
/* Send hello line */
answer_message = bg_sprintf("%s %s %s\r\n", c->protocol_id,
VERSION, (c->read_messages ? "1" : "0"));
len = strlen(answer_message);
if(bg_socket_write_data(c->fd, (uint8_t*)answer_message, len) < len)
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN_CLIENT,
"Sending initialization string failed");
goto fail;
}
/* Read welcome message */
if(!bg_socket_read_line(c->fd, &buffer,
&buffer_alloc, c->milliseconds))
{
bg_log(BG_LOG_ERROR,
LOG_DOMAIN_CLIENT, "Reading welcome line failed");
goto fail;
}
strings = bg_strbreak(buffer, ' ');
if((!strings[0] || strcmp(strings[0], c->protocol_id)) ||
(!strings[1] || strcmp(strings[1], VERSION)))
{
bg_log(BG_LOG_ERROR, LOG_DOMAIN_CLIENT, "Protocol mismatch");
goto fail;
}
ret = 1;
fail:
if(strings)
bg_strbreak_free(strings);
bg_host_address_destroy(addr);
return ret;
}
void bg_remote_client_destroy(bg_remote_client_t * c)
{
bg_msg_destroy(c->msg);
if(c->protocol_id)
free(c->protocol_id);
if(c->fd >= 0)
close(c->fd);
free(c);
}
bg_msg_t * bg_remote_client_get_msg_write(bg_remote_client_t * c)
{
bg_msg_free(c->msg);
return c->msg;
}
int bg_remote_client_done_msg_write(bg_remote_client_t * c)
{
/* Write message */
return bg_msg_write_socket(c->msg, c->fd);
}
bg_msg_t * bg_remote_client_get_msg_read(bg_remote_client_t * c)
{
if(select_socket(c->fd, 1000))
{
bg_msg_free(c->msg);
if(bg_msg_read_socket(c->msg, c->fd, -1))
return c->msg;
}
return NULL;
}
gmerlin-1.2.0~dfsg/lib/cfg_xml.c 0000644 0001750 0001750 00000024051 11764363410 016431 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
// #include
#include
#include
#include
#include
static void load_item(xmlDocPtr xml_doc, xmlNodePtr xml_item,
bg_cfg_section_t * cfg_section)
{
char * tmp_type;
char * tmp_string;
char * start;
bg_parameter_info_t info;
bg_cfg_item_t * item;
memset(&info, 0, sizeof(info));
tmp_type = BG_XML_GET_PROP(xml_item, "type");
info.name = BG_XML_GET_PROP(xml_item, "name");
if(!tmp_type || !info.name)
{
if(tmp_type)
xmlFree(tmp_type);
if(info.name)
xmlFree(info.name);
return;
}
/* Check for the type */
if(!strcmp(tmp_type, "int"))
{
info.type = BG_PARAMETER_INT;
}
else if(!strcmp(tmp_type, "float"))
{
info.type = BG_PARAMETER_FLOAT;
}
else if(!strcmp(tmp_type, "string"))
{
info.type = BG_PARAMETER_STRING;
}
else if(!strcmp(tmp_type, "string_hidden"))
{
info.type = BG_PARAMETER_STRING_HIDDEN;
}
else if(!strcmp(tmp_type, "color"))
{
info.type = BG_PARAMETER_COLOR_RGBA;
}
else if(!strcmp(tmp_type, "time"))
{
info.type = BG_PARAMETER_TIME;
}
else if(!strcmp(tmp_type, "pos"))
{
info.type = BG_PARAMETER_POSITION;
}
else
{
return;
}
/* Find the item */
item = bg_cfg_section_find_item(cfg_section, &info);
tmp_string = (char*)xmlNodeListGetString(xml_doc, xml_item->children, 1);
switch(item->type)
{
case BG_CFG_INT:
sscanf(tmp_string, "%d", &item->value.val_i);
break;
case BG_CFG_TIME:
sscanf(tmp_string, "%" PRId64, &item->value.val_time);
break;
case BG_CFG_FLOAT:
sscanf(tmp_string, "%lf", &item->value.val_f);
break;
case BG_CFG_STRING:
item->value.val_str = bg_strdup(item->value.val_str,
tmp_string);
break;
case BG_CFG_STRING_HIDDEN:
if(item->value.val_str)
{
free(item->value.val_str);
item->value.val_str = NULL;
}
if(tmp_string && (*tmp_string != '\0'))
item->value.val_str = bg_descramble_string(tmp_string);
break;
case BG_CFG_COLOR:
start = tmp_string;
sscanf(tmp_string, "%f %f %f %f",
&item->value.val_color[0],
&item->value.val_color[1],
&item->value.val_color[2],
&item->value.val_color[3]);
break;
case BG_CFG_POSITION:
start = tmp_string;
sscanf(tmp_string, "%lf %lf",
&item->value.val_pos[0],
&item->value.val_pos[1]);
break;
}
if(tmp_string)
xmlFree(tmp_string);
if(info.name)
xmlFree(info.name);
if(tmp_type)
xmlFree(tmp_type);
}
void bg_cfg_xml_2_section(xmlDocPtr xml_doc,
xmlNodePtr xml_section,
bg_cfg_section_t * cfg_section)
{
xmlNodePtr cur;
char * tmp_string;
bg_cfg_section_t * cfg_child_section;
cur = xml_section->children;
tmp_string = BG_XML_GET_PROP(xml_section, "gettext_domain");
if(tmp_string)
{
cfg_section->gettext_domain =
bg_strdup(cfg_section->gettext_domain, tmp_string);
xmlFree(tmp_string);
}
tmp_string = BG_XML_GET_PROP(xml_section, "gettext_directory");
if(tmp_string)
{
cfg_section->gettext_directory =
bg_strdup(cfg_section->gettext_directory, tmp_string);
xmlFree(tmp_string);
}
while(cur)
{
if(!cur->name)
{
cur = cur->next;
continue;
}
/* Load items */
else if(!BG_XML_STRCMP(cur->name, "ITEM"))
{
load_item(xml_doc, cur, cfg_section);
}
/* Load child */
else if(!BG_XML_STRCMP(cur->name, "SECTION"))
{
tmp_string = BG_XML_GET_PROP(cur, "name");
if(tmp_string)
{
cfg_child_section = bg_cfg_section_find_subsection(cfg_section, tmp_string);
bg_cfg_xml_2_section(xml_doc, cur, cfg_child_section);
xmlFree(tmp_string);
}
}
cur = cur->next;
}
}
void bg_cfg_registry_load(bg_cfg_registry_t * r, const char * filename)
{
xmlDocPtr xml_doc;
xmlNodePtr node;
bg_cfg_section_t * cfg_section;
char * tmp_string;
if(!filename)
return;
xml_doc = bg_xml_parse_file(filename);
if(!xml_doc)
return;
node = xml_doc->children;
if(BG_XML_STRCMP(node->name, "REGISTRY"))
{
xmlFreeDoc(xml_doc);
return;
}
node = node->children;
while(node)
{
if(node->name && !BG_XML_STRCMP(node->name, "SECTION"))
{
tmp_string = BG_XML_GET_PROP(node, "name");
if(tmp_string)
{
cfg_section = bg_cfg_registry_find_section(r, tmp_string);
bg_cfg_xml_2_section(xml_doc, node, cfg_section);
xmlFree(tmp_string);
}
}
node = node->next;
}
xmlFreeDoc(xml_doc);
}
void bg_cfg_section_2_xml(const bg_cfg_section_t * section, xmlNodePtr xml_section)
{
char * tmp_string;
bg_cfg_item_t * item;
bg_cfg_section_t * tmp_section;
char buffer[1024];
xmlNodePtr xml_item, xml_child;
/* Save items */
item = section->items;
if(section->gettext_domain)
BG_XML_SET_PROP(xml_section, "gettext_domain", section->gettext_domain);
if(section->gettext_directory)
BG_XML_SET_PROP(xml_section, "gettext_directory", section->gettext_directory);
xmlAddChild(xml_section, BG_XML_NEW_TEXT("\n"));
while(item)
{
xml_item = xmlNewTextChild(xml_section, NULL, (xmlChar*)"ITEM", NULL);
BG_XML_SET_PROP(xml_item, "name", item->name);
switch(item->type)
{
case BG_CFG_INT:
BG_XML_SET_PROP(xml_item, "type", "int");
sprintf(buffer, "%d", item->value.val_i);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
break;
case BG_CFG_TIME:
BG_XML_SET_PROP(xml_item, "type", "time");
sprintf(buffer, "%" PRId64, item->value.val_time);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
break;
case BG_CFG_FLOAT:
BG_XML_SET_PROP(xml_item, "type", "float");
sprintf(buffer, "%.15e", item->value.val_f);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
break;
case BG_CFG_STRING:
BG_XML_SET_PROP(xml_item, "type", "string");
/* Yes, that's stupid */
if(item->value.val_str)
xmlAddChild(xml_item, BG_XML_NEW_TEXT(item->value.val_str));
break;
case BG_CFG_STRING_HIDDEN:
BG_XML_SET_PROP(xml_item, "type", "string_hidden");
/* Yes, that's stupid */
if(item->value.val_str)
{
tmp_string = bg_scramble_string(item->value.val_str);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(tmp_string));
free(tmp_string);
}
break;
case BG_CFG_COLOR:
BG_XML_SET_PROP(xml_item, "type", "color");
sprintf(buffer, "%f %f %f %f",
item->value.val_color[0],
item->value.val_color[1],
item->value.val_color[2],
item->value.val_color[3]);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
break;
case BG_CFG_POSITION:
BG_XML_SET_PROP(xml_item, "type", "pos");
sprintf(buffer, "%f %f",
item->value.val_pos[0],
item->value.val_pos[1]);
xmlAddChild(xml_item, BG_XML_NEW_TEXT(buffer));
break;
}
xmlAddChild(xml_section, BG_XML_NEW_TEXT("\n"));
item = item->next;
}
/* Save child sections */
tmp_section = section->children;
while(tmp_section)
{
xml_child = xmlNewTextChild(xml_section, NULL, (xmlChar*)"SECTION", NULL);
BG_XML_SET_PROP(xml_child, "name", tmp_section->name);
bg_cfg_section_2_xml(tmp_section, xml_child);
xmlAddChild(xml_section, BG_XML_NEW_TEXT("\n"));
tmp_section = tmp_section->next;
}
}
void bg_cfg_registry_save(bg_cfg_registry_t * r, const char * filename)
{
xmlDocPtr xml_doc;
xmlNodePtr xml_registry, xml_section;
bg_cfg_section_t * tmp_section;
if(!filename)
return;
xml_doc = xmlNewDoc((xmlChar*)"1.0");
xml_registry = xmlNewDocRawNode(xml_doc, NULL, (xmlChar*)"REGISTRY", NULL);
xmlDocSetRootElement(xml_doc, xml_registry);
xmlAddChild(xml_registry, BG_XML_NEW_TEXT("\n"));
tmp_section = r->sections;
while(tmp_section)
{
xml_section = xmlNewTextChild(xml_registry, NULL, (xmlChar*)"SECTION", NULL);
BG_XML_SET_PROP(xml_section, "name", tmp_section->name);
bg_cfg_section_2_xml(tmp_section, xml_section);
tmp_section = tmp_section->next;
xmlAddChild(xml_registry, BG_XML_NEW_TEXT("\n"));
}
xmlSaveFile(filename, xml_doc);
xmlFreeDoc(xml_doc);
}
void bg_cfg_section_dump(bg_cfg_section_t * section, const char * filename)
{
xmlDocPtr xml_doc;
xmlNodePtr xml_section;
xml_doc = xmlNewDoc((xmlChar*)"1.0");
xml_section = xmlNewDocRawNode(xml_doc, NULL, (xmlChar*)"SECTION", NULL);
xmlDocSetRootElement(xml_doc, xml_section);
BG_XML_SET_PROP(xml_section, "name", section->name);
bg_cfg_section_2_xml(section, xml_section);
xmlSaveFile(filename, xml_doc);
xmlFreeDoc(xml_doc);
}
gmerlin-1.2.0~dfsg/lib/gtk/ 0000755 0001750 0001750 00000000000 11764363442 015436 5 ustar alessio alessio gmerlin-1.2.0~dfsg/lib/gtk/textview.c 0000644 0001750 0001750 00000014165 11764363406 017470 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
/* Font stuff */
static GtkTextTag * text_tag = NULL;
static GtkTextTagTable * tag_table = NULL;
struct bg_gtk_textview_s
{
GtkWidget * textview;
GtkTextBuffer * buffer;
};
static void set_bg(GtkWidget * widget, gpointer data)
{
GtkRcStyle *rc_style;
rc_style = gtk_rc_style_new ();
rc_style->color_flags[GTK_STATE_NORMAL] = GTK_RC_BASE;
#if 1
rc_style->base[GTK_STATE_NORMAL].red = widget->style->bg[GTK_STATE_NORMAL].red;
rc_style->base[GTK_STATE_NORMAL].green = widget->style->bg[GTK_STATE_NORMAL].green;
rc_style->base[GTK_STATE_NORMAL].blue = widget->style->bg[GTK_STATE_NORMAL].blue;
rc_style->base[GTK_STATE_NORMAL].pixel = widget->style->bg[GTK_STATE_NORMAL].pixel;
#endif
gtk_widget_modify_style(widget, rc_style);
#if GTK_MINOR_VERSION >= 12
g_object_unref (rc_style);
#else
gtk_rc_style_unref (rc_style);
#endif
}
bg_gtk_textview_t * bg_gtk_textview_create()
{
bg_gtk_textview_t * t;
t = calloc(1, sizeof(*t));
if(!tag_table)
{
tag_table = gtk_text_tag_table_new();
text_tag = gtk_text_tag_new("Font");
g_object_set(text_tag, "editable", 0, NULL);
gtk_text_tag_table_add(tag_table,
text_tag);
}
t->buffer = gtk_text_buffer_new(tag_table);
t->textview = gtk_text_view_new_with_buffer(t->buffer);
g_signal_connect(G_OBJECT(t->textview), "realize",
G_CALLBACK(set_bg), NULL);
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(t->textview), FALSE);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(t->textview), GTK_WRAP_NONE);
gtk_text_view_set_editable(GTK_TEXT_VIEW(t->textview), 0);
gtk_widget_show(t->textview);
return t;
}
void bg_gtk_textview_destroy(bg_gtk_textview_t * t)
{
g_object_unref(t->buffer);
free(t);
}
void bg_gtk_textview_update(bg_gtk_textview_t * t,
const char * text)
{
const char * next_tab;
const char * pos;
const char * end_pos;
int line;
int tab_pos;
int line_width;
int i;
PangoTabArray * tab_array;
GtkTextIter start_iter;
GtkTextIter end_iter;
GdkRectangle start_rect;
GdkRectangle end_rect;
pos = text;
next_tab = strchr(pos, '\t');
/* No tabs here, just copy the text */
if(!next_tab)
{
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(t->textview), GTK_WRAP_WORD);
gtk_text_buffer_set_text(t->buffer, text, -1);
}
else /* This is the complicated version */
{
line = 0;
tab_pos = 0;
while(1)
{
next_tab = strchr(pos, '\t');
end_pos = strchr(pos, '\n');
if(!end_pos)
end_pos = pos + strlen(pos);
if(next_tab > end_pos)
next_tab = NULL;
/* Insert everything before the tab and calculate width */
gtk_text_buffer_get_end_iter(t->buffer,
&end_iter);
if(!next_tab)
{
gtk_text_buffer_insert(t->buffer, &end_iter, pos, (int)(end_pos - pos));
}
else
{
gtk_text_buffer_insert(t->buffer, &end_iter, pos, (int)(next_tab - pos));
}
gtk_text_buffer_get_bounds(t->buffer, &start_iter, &end_iter);
for(i = 0; i < line; i++)
{
gtk_text_view_forward_display_line(GTK_TEXT_VIEW(t->textview),
&start_iter);
}
gtk_text_view_get_iter_location(GTK_TEXT_VIEW(t->textview),
&start_iter, &start_rect);
gtk_text_view_get_iter_location(GTK_TEXT_VIEW(t->textview),
&end_iter, &end_rect);
line_width = end_rect.x + end_rect.width;
if(tab_pos < line_width)
tab_pos = line_width;
/* Insert everything after the tab */
if(next_tab)
{
gtk_text_buffer_get_end_iter(t->buffer, &end_iter);
gtk_text_buffer_insert(t->buffer, &end_iter, next_tab, (int)(end_pos - next_tab));
}
pos = end_pos;
line++;
if(*pos == '\0')
break;
else
{
while(*pos == '\n')
{
gtk_text_buffer_get_end_iter(t->buffer, &end_iter);
gtk_text_buffer_insert(t->buffer, &end_iter, pos, 1);
pos++;
}
}
}
/* Set the tab positions */
tab_array =
pango_tab_array_new_with_positions(1, /* gint size, */
1, /* gboolean positions_in_pixels, */
PANGO_TAB_LEFT, /* PangoTabAlign first_alignment, */
tab_pos+10 /* gint first_position, */ );
gtk_text_view_set_tabs(GTK_TEXT_VIEW(t->textview), tab_array);
pango_tab_array_free(tab_array);
}
gtk_text_buffer_get_bounds(t->buffer,
&start_iter,
&end_iter);
gtk_text_buffer_apply_tag(t->buffer,
text_tag,
&start_iter,
&end_iter);
};
GtkWidget * bg_gtk_textview_get_widget(bg_gtk_textview_t * t)
{
return t->textview;
}
gmerlin-1.2.0~dfsg/lib/gtk/Makefile.am 0000644 0001750 0001750 00000002315 11764363406 017473 0 ustar alessio alessio plugindir=$(pkglibdir)/plugins
INCLUDES = -I$(top_srcdir)/include
AM_CFLAGS = \
@GTK_CFLAGS@ \
@FREETYPE_CFLAGS@ \
@FONTCONFIG_CFLAGS@ \
-DLOCALE_DIR=\"$(localedir)\" \
-DPLUGIN_DIR=\"$(plugindir)\" \
-DDOC_DIR=\"$(docdir)\" \
-DDATA_DIR=\"$(pkgdatadir)\"
libgmerlin_gtk_la_LDFLAGS = -version-info @LTVERSION_CURRENT@:@LTVERSION_REVISION@:@LTVERSION_AGE@
lib_LTLIBRARIES = libgmerlin_gtk.la
noinst_HEADERS = gtk_dialog.h
libgmerlin_gtk_la_SOURCES = \
aboutwindow.c \
albumentry.c \
albumwidget.c \
albumwindow.c \
auth.c \
button.c \
cfg_button.c \
cfg_checkbutton.c \
cfg_color.c \
cfg_device.c \
cfg_dialog.c \
cfg_file.c \
cfg_font.c \
cfg_multi_list.c \
cfg_multi_menu.c \
cfg_position.c \
cfg_slider.c \
cfg_spinbutton.c \
cfg_string.c \
cfg_stringlist.c \
cfg_time.c \
chapterdialog.c \
display.c \
driveselect.c \
fileselect.c \
fileentry.c \
gtkutils.c \
infowindow.c \
logwindow.c \
message.c \
multiinfo.c \
plugininfo.c \
plugin_multi.c \
plugin_single.c \
pluginmenu.c \
presetmenu.c \
question.c \
slider.c \
scrolltext.c \
textview.c \
textwindow.c \
treewidget.c \
treewindow.c \
urllink.c \
urlselect.c \
vumeter.c
libgmerlin_gtk_la_LIBADD = @GTK_LIBS@ $(top_builddir)/lib/libgmerlin.la @XML2_LIBS@ @LIBM@
gmerlin-1.2.0~dfsg/lib/gtk/treewindow.c 0000644 0001750 0001750 00000014037 11764363406 017776 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
extern void
gtk_decorated_window_move_resize_window(GtkWindow*,int, int, int, int);
struct bg_gtk_tree_window_s
{
bg_gtk_tree_widget_t * widget;
GtkWidget * window;
void (*close_callback)(bg_gtk_tree_window_t*,void*);
void * close_callback_data;
/* Configuration stuff */
bg_cfg_section_t * cfg_section;
int x, y, width, height;
};
/* Configuration stuff */
static const bg_parameter_info_t parameters[] =
{
{
.name = "x",
.long_name = "X",
.type = BG_PARAMETER_INT,
.val_default = { .val_i = 100 }
},
{
.name = "y",
.long_name = "Y",
.type = BG_PARAMETER_INT,
.val_default = { .val_i = 100 }
},
{
.name = "width",
.long_name = "Width",
.type = BG_PARAMETER_INT,
.val_default = { .val_i = 200 }
},
{
.name = "height",
.long_name = "Height",
.type = BG_PARAMETER_INT,
.val_default = { .val_i = 300 }
},
{ /* End of parameters */ }
};
static void set_parameter(void * data, const char * name,
const bg_parameter_value_t * val)
{
bg_gtk_tree_window_t * win;
win = (bg_gtk_tree_window_t*)data;
if(!name)
return;
else if(!strcmp(name, "x"))
{
win->x = val->val_i;
}
else if(!strcmp(name, "y"))
{
win->y = val->val_i;
}
else if(!strcmp(name, "width"))
{
win->width = val->val_i;
}
else if(!strcmp(name, "height"))
{
win->height = val->val_i;
}
}
static int get_parameter(void * data, const char * name,
bg_parameter_value_t * val)
{
bg_gtk_tree_window_t * win;
win = (bg_gtk_tree_window_t*)data;
if(!name)
return 1;
else if(!strcmp(name, "x"))
{
val->val_i = win->x;
return 1;
}
else if(!strcmp(name, "y"))
{
val->val_i = win->y;
return 1;
}
else if(!strcmp(name, "width"))
{
val->val_i = win->width;
return 1;
}
else if(!strcmp(name, "height"))
{
val->val_i = win->height;
return 1;
}
return 0;
}
static gboolean delete_callback(GtkWidget * w, GdkEventAny * event,
gpointer data)
{
bg_gtk_tree_window_t * win;
win = (bg_gtk_tree_window_t*)data;
if(win->close_callback)
win->close_callback(win, win->close_callback_data);
bg_gtk_tree_window_hide(win);
return TRUE;
}
bg_gtk_tree_window_t *
bg_gtk_tree_window_create(bg_media_tree_t * tree,
void (*close_callback)(bg_gtk_tree_window_t*,void*),
void * close_callback_data, GtkAccelGroup * accel_group)
{
bg_gtk_tree_window_t * ret = calloc(1, sizeof(*ret));
ret->cfg_section =
bg_cfg_section_find_subsection(bg_media_tree_get_cfg_section(tree), "gtk_treewindow");
ret->window = bg_gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_add_accel_group(GTK_WINDOW(ret->window), accel_group);
ret->widget = bg_gtk_tree_widget_create(tree, accel_group, ret->window);
ret->close_callback = close_callback;
ret->close_callback_data = close_callback_data;
g_signal_connect(G_OBJECT(ret->window), "delete-event",
G_CALLBACK(delete_callback),
ret);
gtk_container_add(GTK_CONTAINER(ret->window),
bg_gtk_tree_widget_get_widget(ret->widget));
gtk_window_set_title(GTK_WINDOW(ret->window), TR("Gmerlin Media Tree"));
return ret;
}
void bg_gtk_tree_window_destroy(bg_gtk_tree_window_t * w)
{
bg_gtk_tree_widget_destroy(w->widget);
gtk_widget_hide(w->window);
gtk_widget_destroy(w->window);
free(w);
}
void bg_gtk_tree_window_show(bg_gtk_tree_window_t* w)
{
gtk_widget_show(w->window);
// gtk_window_present(GTK_WINDOW(w->window));
bg_cfg_section_apply(w->cfg_section,
parameters,
set_parameter,
w);
if((w->width > 0) && (w->height > 0))
{
gtk_decorated_window_move_resize_window(GTK_WINDOW(w->window),
w->x, w->y, w->width, w->height);
}
}
void bg_gtk_tree_window_hide(bg_gtk_tree_window_t* w)
{
if(w->window->window)
{
gdk_window_get_geometry(w->window->window,
NULL, NULL, &w->width, &w->height,
NULL);
gdk_window_get_root_origin(w->window->window, &w->x, &w->y);
bg_cfg_section_get(w->cfg_section,
parameters,
get_parameter,
w);
}
gtk_widget_hide(w->window);
}
void bg_gtk_tree_window_open_incoming(bg_gtk_tree_window_t * win)
{
bg_gtk_tree_widget_open_incoming(win->widget);
}
void bg_gtk_tree_window_goto_current(bg_gtk_tree_window_t * win)
{
bg_gtk_tree_widget_goto_current(win->widget);
}
void bg_gtk_tree_window_update(bg_gtk_tree_window_t * w,
int open_albums)
{
bg_gtk_tree_widget_update(w->widget, open_albums);
}
gmerlin-1.2.0~dfsg/lib/gtk/presetmenu.c 0000644 0001750 0001750 00000023744 11764363406 020003 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct
{
GtkWidget * menu;
GtkWidget ** preset_items;
int num_preset_items;
int preset_items_alloc;
} preset_menu_t;
struct bg_gtk_preset_menu_s
{
GtkWidget * menubar;
GtkWidget * menu;
GtkWidget * save_item;
GtkWidget * load_item;
GtkWidget * delete_item;
GtkWidget * save_to_item;
char * path;
void (*load_cb)(void*);
void (*save_cb)(void*);
void * cb_data;
char * preset_name;
preset_menu_t load_menu;
preset_menu_t save_to_menu;
preset_menu_t delete_menu;
bg_cfg_section_t * section;
bg_preset_t * presets;
};
static void menu_callback(GtkWidget * w, gpointer data);
static void set_name_parameter(void * data, const char * name,
const bg_parameter_value_t * val)
{
bg_gtk_preset_menu_t * menu = data;
if(!name)
return;
if(!strcmp(name, "name"))
menu->preset_name = bg_strdup(menu->preset_name, val->val_str);
}
static void do_update_menu(preset_menu_t * menu, bg_preset_t * presets, void * callback_data)
{
bg_preset_t * tmp;
int i;
// Count presets
menu->num_preset_items = 0;
tmp = presets;
while(tmp)
{
menu->num_preset_items++;
tmp = tmp->next;
}
if(menu->num_preset_items > menu->preset_items_alloc)
{
// Create preset items
int old_alloc = menu->preset_items_alloc;
menu->preset_items_alloc = menu->num_preset_items + 4;
menu->preset_items = realloc(menu->preset_items,
menu->preset_items_alloc *
sizeof(*menu->preset_items));
for(i = old_alloc; i < menu->preset_items_alloc; i++)
{
menu->preset_items[i] = gtk_menu_item_new_with_label("");
g_signal_connect(G_OBJECT(menu->preset_items[i]),
"activate", G_CALLBACK(menu_callback),
(gpointer)callback_data);
gtk_menu_shell_append(GTK_MENU_SHELL(menu->menu),
menu->preset_items[i]);
}
}
tmp = presets;
for(i = 0; i < menu->num_preset_items; i++)
{
gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(menu->preset_items[i]))), tmp->name);
gtk_widget_show(menu->preset_items[i]);
tmp = tmp->next;
}
for(i = menu->num_preset_items; i < menu->preset_items_alloc; i++)
{
gtk_widget_hide(menu->preset_items[i]);
}
}
static void update_menu(bg_gtk_preset_menu_t * menu)
{
do_update_menu(&menu->load_menu, menu->presets, menu);
do_update_menu(&menu->save_to_menu, menu->presets, menu);
do_update_menu(&menu->delete_menu, menu->presets, menu);
}
static bg_preset_t * has_preset_widget(preset_menu_t * menu, GtkWidget * w, bg_preset_t * presets)
{
int i, index;
bg_preset_t * ret;
for(i = 0; i < menu->num_preset_items; i++)
{
if(w == menu->preset_items[i])
break;
}
if(i >= menu->num_preset_items)
return NULL;
index = i;
ret = presets;
for(i = 0; i < index; i++)
ret = ret->next;
return ret;
}
static void menu_callback(GtkWidget * w, gpointer data)
{
bg_gtk_preset_menu_t * menu = data;
bg_preset_t * p;
if(w == menu->save_item)
{
bg_parameter_info_t pi[2];
bg_dialog_t * dlg;
if(menu->save_cb)
menu->save_cb(menu->cb_data);
memset(pi, 0, sizeof(pi));
pi[0].name = "name";
pi[0].long_name = TR("Name");
pi[0].type = BG_PARAMETER_STRING;
pi[0].val_default.val_str = TR("New preset");
dlg = bg_dialog_create(NULL,
set_name_parameter,
NULL,
menu,
pi,
TR("Save preset"));
if(!bg_dialog_show(dlg, menu->menubar))
return;
if(!menu->preset_name)
return;
if(bg_preset_find_by_name(menu->presets, menu->preset_name))
{
char * question = bg_sprintf("Overwrite preset %s?", menu->preset_name);
if(!bg_gtk_question(question, menu->menubar))
{
free(question);
return;
}
free(question);
}
menu->presets = bg_preset_add(menu->presets,
menu->path,
menu->preset_name,
menu->section);
update_menu(menu);
}
else if ((p = has_preset_widget(&menu->load_menu, w, menu->presets)))
{
bg_cfg_section_t * preset_section;
preset_section = bg_preset_load(p);
bg_cfg_section_transfer(preset_section, menu->section);
if(menu->load_cb)
menu->load_cb(menu->cb_data);
bg_cfg_section_destroy(preset_section);
}
else if ((p = has_preset_widget(&menu->delete_menu, w, menu->presets)))
{
char * question = bg_sprintf("Delete preset %s?", p->name);
if(!bg_gtk_question(question, menu->menubar))
{
free(question);
return;
}
free(question);
menu->presets = bg_preset_delete(menu->presets, p);
update_menu(menu);
}
else if ((p = has_preset_widget(&menu->save_to_menu, w, menu->presets)))
{
char * question = bg_sprintf("Overwrite preset %s?", p->name);
if(!bg_gtk_question(question, menu->menubar))
{
free(question);
return;
}
free(question);
if(menu->save_cb)
menu->save_cb(menu->cb_data);
menu->presets = bg_preset_add(menu->presets,
menu->path,
p->name,
menu->section);
}
}
static GtkWidget *
create_item(bg_gtk_preset_menu_t * w, GtkWidget * parent,
const char * label, const char * pixmap)
{
GtkWidget * ret, *image;
char * path;
if(pixmap)
{
path = bg_search_file_read("icons", pixmap);
if(path)
{
image = gtk_image_new_from_file(path);
free(path);
}
else
image = gtk_image_new();
gtk_widget_show(image);
ret = gtk_image_menu_item_new_with_label(label);
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(ret), image);
}
else
{
ret = gtk_menu_item_new_with_label(label);
}
g_signal_connect(G_OBJECT(ret), "activate", G_CALLBACK(menu_callback),
(gpointer)w);
gtk_widget_show(ret);
gtk_menu_shell_append(GTK_MENU_SHELL(parent), ret);
return ret;
}
static void init_preset_menu(preset_menu_t * menu)
{
menu->menu = gtk_menu_new();
gtk_widget_show(menu->menu);
}
bg_gtk_preset_menu_t *
bg_gtk_preset_menu_create(const char * preset_path,
bg_cfg_section_t * s,
void (*load_cb)(void *),
void (*save_cb)(void *),
void * cb_data)
{
GtkWidget * item;
bg_gtk_preset_menu_t * ret = calloc(1, sizeof(*ret));
ret->load_cb = load_cb;
ret->save_cb = save_cb;
ret->cb_data = cb_data;
ret->path = bg_strdup(ret->path, preset_path);
ret->presets = bg_presets_load(ret->path);
ret->section = s;
ret->menu = gtk_menu_new();
init_preset_menu(&ret->load_menu);
init_preset_menu(&ret->save_to_menu);
init_preset_menu(&ret->delete_menu);
ret->save_item = create_item(ret, ret->menu, TR("Save new..."),
"save_16.png");
ret->load_item = create_item(ret, ret->menu, TR("Load..."),
"folder_open_16.png");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(ret->load_item),
ret->load_menu.menu);
ret->save_to_item = create_item(ret, ret->menu, TR("Save to..."),
"save_16.png");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(ret->save_to_item),
ret->save_to_menu.menu);
ret->delete_item = create_item(ret, ret->menu, TR("Delete..."),
"trash_16.png");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(ret->delete_item),
ret->delete_menu.menu);
item = gtk_separator_menu_item_new();
gtk_widget_show(item);
gtk_menu_shell_append(GTK_MENU_SHELL(ret->menu), item);
ret->menubar = gtk_menu_bar_new();
item = gtk_menu_item_new_with_label(TR("Preset"));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), ret->menu);
gtk_widget_show(item);
gtk_menu_shell_append(GTK_MENU_SHELL(ret->menubar), item);
gtk_widget_show(ret->menubar);
update_menu(ret);
return ret;
}
static void free_preset_menu(preset_menu_t * m)
{
if(m->preset_items)
free(m->preset_items);
}
void bg_gtk_preset_menu_destroy(bg_gtk_preset_menu_t * m)
{
free_preset_menu(&m->load_menu);
free_preset_menu(&m->save_to_menu);
free_preset_menu(&m->delete_menu);
if(m->preset_name)
free(m->preset_name);
if(m->path)
free(m->path);
free(m);
}
GtkWidget * bg_gtk_preset_menu_get_widget(bg_gtk_preset_menu_t * m)
{
return m->menubar;
}
gmerlin-1.2.0~dfsg/lib/gtk/driveselect.c 0000644 0001750 0001750 00000017676 11764363406 020134 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct bg_gtk_drivesel_s
{
GtkWidget * window;
GtkWidget * add_button;
GtkWidget * close_button;
GtkWidget * drive_menu;
const bg_plugin_info_t * plugin_info;
bg_gtk_plugin_menu_t * plugin_menu;
void (*add_files)(char ** files, const char * plugin,
int prefer_edl,
void * data);
void (*close_notify)(bg_gtk_drivesel_t * f, void * data);
void * callback_data;
int is_modal;
int num_drives;
bg_plugin_registry_t * plugin_reg;
};
static void plugin_change_callback(bg_gtk_plugin_menu_t * m, void * data)
{
int i;
bg_gtk_drivesel_t * ds;
bg_device_info_t * devices;
ds = (bg_gtk_drivesel_t*)data;
for(i = 0; i < ds->num_drives; i++)
bg_gtk_combo_box_remove_text(ds->drive_menu, 0);
ds->plugin_info = bg_plugin_find_by_name(ds->plugin_reg,
bg_gtk_plugin_menu_get_plugin(ds->plugin_menu));
devices = ds->plugin_info->devices;
ds->num_drives = 0;
while(devices[ds->num_drives].device)
{
if(devices[ds->num_drives].name)
bg_gtk_combo_box_append_text(ds->drive_menu,
devices[ds->num_drives].name);
else
bg_gtk_combo_box_append_text(ds->drive_menu,
devices[ds->num_drives].device);
ds->num_drives++;
}
/* Select first entry */
gtk_combo_box_set_active(GTK_COMBO_BOX(ds->drive_menu), 0);
}
static void button_callback(GtkWidget * w, gpointer data)
{
bg_gtk_drivesel_t * f;
const char * plugin = NULL;
char * drives[2];
f = (bg_gtk_drivesel_t *)data;
if(w == f->add_button)
{
// plugin = menu_get_current(&f->plugins);
drives[0] = f->plugin_info->devices[gtk_combo_box_get_active(GTK_COMBO_BOX(f->drive_menu))].device;
drives[1] = NULL;
plugin = f->plugin_info->name;
f->add_files(drives, plugin, 0, f->callback_data);
}
else if((w == f->window) || (w == f->close_button))
{
if(f->close_notify)
f->close_notify(f, f->callback_data);
gtk_widget_hide(f->window);
if(f->is_modal)
gtk_main_quit();
bg_gtk_drivesel_destroy(f);
}
}
static gboolean delete_callback(GtkWidget * w, GdkEventAny * event,
gpointer data)
{
button_callback(w, data);
return TRUE;
}
static gboolean destroy_callback(GtkWidget * w, GdkEvent * event,
gpointer data)
{
button_callback(w, data);
return TRUE;
}
bg_gtk_drivesel_t *
bg_gtk_drivesel_create(const char * title,
void (*add_files)(char ** files, const char * plugin,
int prefer_edl, void * data),
void (*close_notify)(bg_gtk_drivesel_t *,
void * data),
void * user_data,
GtkWidget * parent_window,
bg_plugin_registry_t * plugin_reg,
int type_mask, int flag_mask)
{
bg_gtk_drivesel_t * ret;
GtkWidget * box;
GtkWidget * table;
GtkWidget * mainbox;
GtkWidget * label;
ret = calloc(1, sizeof(*ret));
/* Create window */
ret->window = bg_gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(ret->window), title);
gtk_window_set_position(GTK_WINDOW(ret->window), GTK_WIN_POS_CENTER_ON_PARENT);
gtk_container_set_border_width(GTK_CONTAINER(ret->window), 5);
if(parent_window)
{
gtk_window_set_transient_for(GTK_WINDOW(ret->window),
GTK_WINDOW(parent_window));
gtk_window_set_destroy_with_parent(GTK_WINDOW(ret->window), TRUE);
g_signal_connect(G_OBJECT(ret->window), "destroy-event",
G_CALLBACK(destroy_callback), ret);
}
/* Create device menu */
ret->drive_menu = bg_gtk_combo_box_new_text();
gtk_widget_show(ret->drive_menu);
/* Create plugin menu */
ret->plugin_reg = plugin_reg;
ret->plugin_menu = bg_gtk_plugin_menu_create(0, plugin_reg,
type_mask, flag_mask);
bg_gtk_plugin_menu_set_change_callback(ret->plugin_menu, plugin_change_callback,
ret);
/* Create Buttons */
ret->add_button = gtk_button_new_from_stock(GTK_STOCK_ADD);
ret->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
bg_gtk_widget_set_can_default(ret->close_button, TRUE);
bg_gtk_widget_set_can_default(ret->add_button, TRUE);
/* Set callbacks */
g_signal_connect(G_OBJECT(ret->window), "delete-event",
G_CALLBACK(delete_callback), ret);
g_signal_connect(G_OBJECT(ret->add_button),
"clicked", G_CALLBACK(button_callback), ret);
g_signal_connect(G_OBJECT(ret->close_button),
"clicked", G_CALLBACK(button_callback), ret);
/* Show Buttons */
gtk_widget_show(ret->add_button);
gtk_widget_show(ret->close_button);
/* Pack everything */
mainbox = gtk_vbox_new(0, 5);
table = gtk_table_new(2, 2, 0);
gtk_table_set_col_spacings(GTK_TABLE(table), 5);
gtk_table_set_row_spacings(GTK_TABLE(table), 5);
bg_gtk_plugin_menu_attach(ret->plugin_menu, table,
0, 0);
label = gtk_label_new(TR("Drive:"));
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
gtk_table_attach_defaults(GTK_TABLE(table), ret->drive_menu, 1, 2, 1, 2);
gtk_widget_show(table);
bg_gtk_box_pack_start_defaults(GTK_BOX(mainbox), table);
box = gtk_hbutton_box_new();
gtk_container_add(GTK_CONTAINER(box), ret->close_button);
gtk_container_add(GTK_CONTAINER(box), ret->add_button);
gtk_widget_show(box);
bg_gtk_box_pack_start_defaults(GTK_BOX(mainbox), box);
gtk_widget_show(mainbox);
gtk_container_add(GTK_CONTAINER(ret->window), mainbox);
/* Set pointers */
ret->add_files = add_files;
ret->close_notify = close_notify;
ret->callback_data = user_data;
plugin_change_callback(ret->plugin_menu, ret);
return ret;
}
/* Destroy driveselector */
void bg_gtk_drivesel_destroy(bg_gtk_drivesel_t * drivesel)
{
free(drivesel);
}
/* Show the window */
void bg_gtk_drivesel_run(bg_gtk_drivesel_t * drivesel, int modal,
GtkWidget * parent)
{
if(modal)
{
parent = bg_gtk_get_toplevel(parent);
if(parent)
gtk_window_set_transient_for(GTK_WINDOW(drivesel->window),
GTK_WINDOW(parent));
}
gtk_window_set_modal(GTK_WINDOW(drivesel->window), modal);
gtk_widget_show(drivesel->window);
gtk_widget_grab_focus(drivesel->close_button);
gtk_widget_grab_default(drivesel->close_button);
drivesel->is_modal = modal;
if(modal)
gtk_main();
}
gmerlin-1.2.0~dfsg/lib/gtk/treewidget.c 0000644 0001750 0001750 00000165172 11764363406 017761 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#include
static void set_tabbed_mode(bg_gtk_tree_widget_t * w);
static void set_windowed_mode(bg_gtk_tree_widget_t * w);
static GdkPixbuf * root_pixbuf = NULL;
static GdkPixbuf * folder_closed_pixbuf = NULL;
static GdkPixbuf * folder_open_pixbuf = NULL;
static GdkPixbuf * favourites_closed_pixbuf = NULL;
static GdkPixbuf * favourites_open_pixbuf = NULL;
static GdkPixbuf * incoming_closed_pixbuf = NULL;
static GdkPixbuf * incoming_open_pixbuf = NULL;
static GdkPixbuf * removable_closed_pixbuf = NULL;
static GdkPixbuf * removable_open_pixbuf = NULL;
static GdkPixbuf * error_pixbuf = NULL;
static GdkPixbuf * hardware_pixbuf = NULL;
static GdkPixbuf * tuner_pixbuf = NULL;
static int num_tree_widgets = 0;
/* Atoms */
static int atoms_created = 0;
GdkAtom bg_gtk_atom_entries = (GdkAtom)0;
GdkAtom bg_gtk_atom_entries_r = (GdkAtom)0;
GdkAtom bg_gtk_atom_album = (GdkAtom)0;
void bg_gtk_tree_create_atoms()
{
if(atoms_created)
return;
atoms_created = 1;
bg_gtk_atom_entries_r = gdk_atom_intern(bg_gtk_atom_entries_name_r, FALSE);
bg_gtk_atom_entries = gdk_atom_intern(bg_gtk_atom_entries_name, FALSE);
bg_gtk_atom_album = gdk_atom_intern(bg_gtk_atom_album_name, FALSE);
}
/* 0 means undefined */
#define DND_GMERLIN_TRACKS 1
#define DND_GMERLIN_TRACKS_R 2
#define DND_GMERLIN_ALBUM 3
#define DND_TEXT_URI_LIST 4
#define DND_TEXT_PLAIN 5
static const GtkTargetEntry dnd_src_entries[] =
{
{bg_gtk_atom_album_name, GTK_TARGET_SAME_WIDGET, DND_GMERLIN_ALBUM },
};
static const GtkTargetEntry dnd_dst_entries[] =
{
{bg_gtk_atom_entries_name, GTK_TARGET_SAME_APP, DND_GMERLIN_TRACKS },
{bg_gtk_atom_album_name, GTK_TARGET_SAME_WIDGET, DND_GMERLIN_ALBUM },
{"text/uri-list", 0, DND_TEXT_URI_LIST },
{"text/plain", 0, DND_TEXT_PLAIN }
};
/* Open the current album if it isn't already open */
static void open_album(bg_gtk_tree_widget_t * widget,
bg_album_t * album);
static void load_pixmaps()
{
char * filename;
if(num_tree_widgets)
{
num_tree_widgets++;
return;
}
num_tree_widgets++;
filename = bg_search_file_read("icons", "folder_closed_16.png");
if(filename)
{
folder_closed_pixbuf =
gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "folder_open_16.png");
if(filename)
{
folder_open_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "incoming_closed_16.png");
if(filename)
{
incoming_closed_pixbuf =
gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "incoming_open_16.png");
if(filename)
{
incoming_open_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "favourites_closed_16.png");
if(filename)
{
favourites_closed_pixbuf =
gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "favourites_open_16.png");
if(filename)
{
favourites_open_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "drive_16.png");
if(filename)
{
removable_closed_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "drive_running_16.png");
if(filename)
{
removable_open_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "drive_error_16.png");
if(filename)
{
error_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "hardware_16.png");
if(filename)
{
hardware_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "tree_root_16.png");
if(filename)
{
root_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
filename = bg_search_file_read("icons", "tuner_16.png");
if(filename)
{
tuner_pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
free(filename);
}
}
#if 0
static void unload_pixmaps()
{
num_tree_widgets--;
if(num_tree_widgets)
return;
g_object_unref(root_pixbuf);
g_object_unref(folder_closed_pixbuf);
g_object_unref(folder_open_pixbuf);
g_object_unref(removable_closed_pixbuf);
g_object_unref(removable_open_pixbuf);
g_object_unref(error_pixbuf);
g_object_unref(hardware_pixbuf);
g_object_unref(tuner_pixbuf);
root_pixbuf = NULL;
folder_closed_pixbuf = NULL;
folder_open_pixbuf = NULL;
removable_closed_pixbuf = NULL;
removable_open_pixbuf = NULL;
error_pixbuf = NULL;
hardware_pixbuf = NULL;
tuner_pixbuf = NULL;
}
#endif
enum
{
COLUMN_NAME,
COLUMN_PIXMAP,
COLUMN_WEIGHT,
COLUMN_COLOR,
NUM_COLUMNS
};
typedef struct
{
GtkWidget * menu;
GtkWidget * expand_item;
GtkWidget * collapse_item;
GtkWidget * tabbed_mode_item;
GtkWidget * windowed_mode_item;
GtkWidget * goto_current_item;
} tree_menu_t;
typedef struct
{
GtkWidget * new_item;
GtkWidget * new_from_directory_item;
GtkWidget * rename_item;
GtkWidget * open_item;
GtkWidget * close_item;
GtkWidget * remove_item;
GtkWidget * menu;
} album_menu_t;
typedef struct
{
GtkWidget * find_devices_item;
GtkWidget * add_device_item;
GtkWidget * menu;
} plugin_menu_t;
typedef struct
{
GtkWidget * tree_item;
tree_menu_t tree_menu;
GtkWidget * album_item;
album_menu_t album_menu;
GtkWidget * plugin_item;
plugin_menu_t plugin_menu;
GtkWidget * menu;
} root_menu_t;
struct bg_gtk_tree_widget_s
{
bg_cfg_section_t * cfg_section;
GtkWidget * widget;
GtkWidget * treeview;
bg_media_tree_t * tree;
bg_album_t * selected_album;
root_menu_t menu;
GList * album_windows;
gulong select_handler_id;
guint drop_time;
/* Buttons */
GtkWidget * new_button;
GtkWidget * remove_button;
GtkWidget * rename_button;
GtkWidget * goto_current_button;
/* Notebook */
GtkWidget * notebook;
int tabbed_mode;
GtkAccelGroup * accel_group;
GtkAccelGroup * album_accel_group;
GtkWidget * toplevel_window;
gulong timeout_tag;
};
/* Configuration */
static const bg_parameter_info_t parameters[] =
{
{
.name = "tabbed_mode",
.long_name = "Tabbed mode",
.type = BG_PARAMETER_CHECKBUTTON,
.flags = BG_PARAMETER_HIDE_DIALOG,
.val_default = { .val_i = 1 },
},
{ /* End of parameters */ }
};
static void set_parameter(void * data, const char * name,
const bg_parameter_value_t * val)
{
bg_gtk_tree_widget_t * wid;
wid = (bg_gtk_tree_widget_t*)data;
if(!name)
return;
else if(!strcmp(name, "tabbed_mode"))
{
if(val->val_i)
set_tabbed_mode(wid);
else
set_windowed_mode(wid);
}
}
static int get_parameter(void * data, const char * name,
bg_parameter_value_t * val)
{
bg_gtk_tree_widget_t * wid;
wid = (bg_gtk_tree_widget_t*)data;
if(!name)
return 1;
else if(!strcmp(name, "tabbed_mode"))
{
val->val_i = wid->tabbed_mode;
return 1;
}
return 0;
}
/* Utility funcions */
/* Return 1 if album and album_window match */
static gint is_window_of(gconstpointer a, gconstpointer b)
{
bg_gtk_album_window_t * album_win;
bg_album_t * album;
album_win = (bg_gtk_album_window_t*)a;
album = (bg_album_t *)b;
return (album == bg_gtk_album_window_get_album(album_win)) ? 0 : 1;
}
/*
* Return the corresponding album window for a widget,
* or NULL if there is none
*/
static bg_gtk_album_window_t *
album_is_open(bg_gtk_tree_widget_t * widget,
bg_album_t * album)
{
GList * tmp_list;
tmp_list = g_list_find_custom(widget->album_windows,
(gconstpointer*)(album),
is_window_of);
if(tmp_list)
return tmp_list->data;
return NULL;
}
/* Update the menu */
static void rename_item(GtkWidget * w, const char * label)
{
gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(w))), label);
}
static void update_menu(bg_gtk_tree_widget_t * w)
{
bg_album_type_t type;
/* En- or disable menu items */
if(w->tabbed_mode)
{
gtk_widget_hide(w->menu.tree_menu.tabbed_mode_item);
gtk_widget_show(w->menu.tree_menu.windowed_mode_item);
}
else
{
gtk_widget_show(w->menu.tree_menu.tabbed_mode_item);
gtk_widget_hide(w->menu.tree_menu.windowed_mode_item);
}
if(!w->selected_album)
{
rename_item(w->menu.album_item, "Album...");
gtk_widget_show(w->menu.album_item);
gtk_widget_hide(w->menu.album_menu.remove_item);
gtk_widget_set_sensitive(w->remove_button, 0);
gtk_widget_set_sensitive(w->rename_button, 0);
gtk_widget_set_sensitive(w->new_button, 1);
gtk_widget_hide(w->menu.album_menu.rename_item);
gtk_widget_hide(w->menu.album_menu.open_item);
gtk_widget_hide(w->menu.album_menu.close_item);
gtk_widget_show(w->menu.album_menu.new_item);
gtk_widget_show(w->menu.album_menu.new_from_directory_item);
gtk_widget_hide(w->menu.plugin_item);
return;
}
else
{
type = bg_album_get_type(w->selected_album);
switch(type)
{
case BG_ALBUM_TYPE_PLUGIN:
gtk_widget_hide(w->menu.album_item);
gtk_widget_show(w->menu.plugin_item);
gtk_widget_set_sensitive(w->remove_button, 0);
gtk_widget_set_sensitive(w->rename_button, 0);
gtk_widget_set_sensitive(w->new_button, 0);
break;
case BG_ALBUM_TYPE_REMOVABLE:
case BG_ALBUM_TYPE_TUNER:
rename_item(w->menu.album_item, "Device...");
gtk_widget_show(w->menu.album_item);
gtk_widget_hide(w->menu.plugin_item);
gtk_widget_show(w->menu.album_menu.remove_item);
gtk_widget_set_sensitive(w->remove_button, 1);
gtk_widget_set_sensitive(w->rename_button, 1);
gtk_widget_set_sensitive(w->new_button, 0);
gtk_widget_show(w->menu.album_menu.rename_item);
gtk_widget_hide(w->menu.album_menu.new_item);
gtk_widget_hide(w->menu.album_menu.new_from_directory_item);
if(album_is_open(w, w->selected_album))
{
gtk_widget_hide(w->menu.album_menu.open_item);
gtk_widget_show(w->menu.album_menu.close_item);
}
else
{
gtk_widget_show(w->menu.album_menu.open_item);
gtk_widget_hide(w->menu.album_menu.close_item);
}
break;
case BG_ALBUM_TYPE_REGULAR:
gtk_widget_hide(w->menu.plugin_item);
rename_item(w->menu.album_item, "Album...");
gtk_widget_show(w->menu.album_item);
gtk_widget_show(w->menu.album_menu.remove_item);
gtk_widget_set_sensitive(w->remove_button, 1);
gtk_widget_set_sensitive(w->rename_button, 1);
gtk_widget_set_sensitive(w->new_button, 1);
gtk_widget_show(w->menu.album_menu.new_item);
gtk_widget_show(w->menu.album_menu.new_from_directory_item);
gtk_widget_show(w->menu.album_menu.rename_item);
if(album_is_open(w, w->selected_album))
{
gtk_widget_hide(w->menu.album_menu.open_item);
gtk_widget_show(w->menu.album_menu.close_item);
}
else
{
gtk_widget_show(w->menu.album_menu.open_item);
gtk_widget_hide(w->menu.album_menu.close_item);
}
break;
case BG_ALBUM_TYPE_INCOMING:
case BG_ALBUM_TYPE_FAVOURITES:
gtk_widget_hide(w->menu.plugin_item);
rename_item(w->menu.album_item, "Album...");
gtk_widget_show(w->menu.album_item);
gtk_widget_hide(w->menu.album_menu.remove_item);
gtk_widget_set_sensitive(w->remove_button, 0);
gtk_widget_set_sensitive(w->rename_button, 0);
gtk_widget_set_sensitive(w->new_button, 0);
gtk_widget_hide(w->menu.album_menu.new_item);
gtk_widget_hide(w->menu.album_menu.new_from_directory_item);
gtk_widget_hide(w->menu.album_menu.rename_item);
if(album_is_open(w, w->selected_album))
{
gtk_widget_hide(w->menu.album_menu.open_item);
gtk_widget_show(w->menu.album_menu.close_item);
}
else
{
gtk_widget_show(w->menu.album_menu.open_item);
gtk_widget_hide(w->menu.album_menu.close_item);
}
break;
break;
}
}
}
/* Utility functions: Convert between GtkTreeIter and bg_album_t */
static void album_2_iter(bg_gtk_tree_widget_t * widget,
bg_album_t * album,
GtkTreeIter * iter)
{
int * indices;
int i;
GtkTreeModel *model;
GtkTreePath *path;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget->treeview));
indices = bg_media_tree_get_path(widget->tree, album);
path = gtk_tree_path_new_first();
i = 0;
while(indices[i] != -1)
{
gtk_tree_path_append_index(path, indices[i]);
i++;
}
free(indices);
gtk_tree_model_get_iter(model, iter, path);
gtk_tree_path_free(path);
}
static bg_album_t * path_2_album(bg_gtk_tree_widget_t * w,
GtkTreePath * path)
{
int i;
bg_album_t * ret;
gint * indices;
gint depth;
depth = gtk_tree_path_get_depth(path);
if(depth < 2)
return NULL;
indices = gtk_tree_path_get_indices(path);
ret = bg_media_tree_get_album(w->tree, indices[1]);
for(i = 2; i < depth; i++)
{
ret = bg_album_get_child(ret, indices[i]);
}
return ret;
}
static bg_album_t * iter_2_album(bg_gtk_tree_widget_t * w,
GtkTreeIter * iter)
{
GtkTreePath* path;
GtkTreeModel * model;
bg_album_t * ret;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(w->treeview));
path = gtk_tree_model_get_path(model, iter);
ret = path_2_album(w, path);
gtk_tree_path_free(path);
return ret;
}
/* Set one album */
static void set_album(bg_gtk_tree_widget_t * widget,
bg_album_t * album,
GtkTreeIter * iter,
int set_children,
int open_window)
{
bg_album_type_t type;
int num_children = 0;
bg_album_t * child;
int i;
GtkTreeIter child_iter;
GtkTreeModel * model;
bg_album_t * current_album;
bg_gtk_album_window_t * album_window;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget->treeview));
/* Open window if necessary */
// if(bg_album_is_open(album))
// open_album(widget, album);
/* Set values */
/* Get the appropriate pixmap */
current_album = bg_media_tree_get_current_album(widget->tree);
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_NAME,
bg_album_get_label(album), -1);
type = bg_album_get_type(album);
switch(type)
{
/* Regular album */
case BG_ALBUM_TYPE_REGULAR:
if(bg_album_is_open(album))
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
folder_open_pixbuf, -1);
else
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
folder_closed_pixbuf, -1);
break;
/* Drive for removable media */
case BG_ALBUM_TYPE_REMOVABLE:
if(bg_album_is_open(album))
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
removable_open_pixbuf, -1);
else if(bg_album_get_error(album))
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
error_pixbuf, -1);
else
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
removable_closed_pixbuf, -1);
break;
/* Tuner */
case BG_ALBUM_TYPE_TUNER:
if(bg_album_get_error(album))
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
error_pixbuf, -1);
else
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
tuner_pixbuf, -1);
break;
/* Hardware plugin (Subalbums are devices) */
case BG_ALBUM_TYPE_PLUGIN:
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
hardware_pixbuf, -1);
break;
/* Incoming album: Stuff from the commandline and the remote will go there */
case BG_ALBUM_TYPE_INCOMING:
if(bg_album_is_open(album))
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
incoming_open_pixbuf, -1);
else
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
incoming_closed_pixbuf, -1);
break;
case BG_ALBUM_TYPE_FAVOURITES:
if(bg_album_is_open(album))
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
favourites_open_pixbuf, -1);
else
gtk_tree_store_set(GTK_TREE_STORE(model), iter, COLUMN_PIXMAP,
favourites_closed_pixbuf, -1);
break;
}
if(album == current_album)
gtk_tree_store_set(GTK_TREE_STORE(model),
iter,
COLUMN_WEIGHT,
PANGO_WEIGHT_BOLD, -1);
else
gtk_tree_store_set(GTK_TREE_STORE(model),
iter,
COLUMN_WEIGHT,
PANGO_WEIGHT_NORMAL, -1);
if(bg_album_get_error(album))
gtk_tree_store_set(GTK_TREE_STORE(model),
iter,
COLUMN_COLOR,
"#FF0000", -1);
else
gtk_tree_store_set(GTK_TREE_STORE(model),
iter,
COLUMN_COLOR,
"#000000", -1);
if(open_window && bg_album_is_open(album) && !album_is_open(widget, album))
{
album_window = bg_gtk_album_window_create(album,
widget, widget->accel_group);
widget->album_windows = g_list_append(widget->album_windows,
album_window);
if(widget->tabbed_mode)
bg_gtk_album_window_attach(album_window, widget->notebook);
else
bg_gtk_album_window_detach(album_window);
}
else
album_window = album_is_open(widget, album);
if(album_window)
bg_gtk_album_window_set_current(album_window, (album == current_album) ? 1 : 0);
/* Append all subalbums of one album */
num_children = bg_album_get_num_children(album);
if(set_children)
{
for(i = 0; i < num_children; i++)
{
gtk_tree_store_append(GTK_TREE_STORE(model),
&child_iter,
iter);
child = bg_album_get_child(album, i);
set_album(widget, child, &child_iter, set_children, open_window);
}
}
}
static void expand_album(bg_gtk_tree_widget_t * w, bg_album_t * album)
{
GtkTreeIter iter;
GtkTreePath * path;
GtkTreeModel * model;
GtkTreeSelection * selection;
int i;
bg_album_t * child_album;
int num_children;
int expanded;
int selected;
expanded = bg_album_get_expanded(album);
selected = (w->selected_album == album) ? 1 : 0;
if(!selected && !expanded)
return;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(w->treeview));
album_2_iter(w, album, &iter);
path = gtk_tree_model_get_path(model, &iter);
if(selected)
{
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(w->treeview));
gtk_tree_selection_select_path(selection, path);
}
if(expanded)
gtk_tree_view_expand_row(GTK_TREE_VIEW(w->treeview), path, 0);
gtk_tree_path_free(path);
num_children = bg_album_get_num_children(album);
for(i = 0; i < num_children; i++)
{
child_album = bg_album_get_child(album, i);
expand_album(w, child_album);
}
}
/* Update the entire tree */
void bg_gtk_tree_widget_update(bg_gtk_tree_widget_t * w,
int open_albums)
{
GtkTreeModel * model;
GtkTreeIter iter;
GtkTreeIter root_iter;
GtkTreePath * path;
int i;
int num_children;
bg_album_t * album;
GtkTreeSelection * selection;
/* We must block the selection callback */
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(w->treeview));
g_signal_handler_block(G_OBJECT(selection), w->select_handler_id);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(w->treeview));
gtk_tree_store_clear(GTK_TREE_STORE(model));
gtk_tree_store_append(GTK_TREE_STORE(model),
&root_iter,
NULL);
gtk_tree_store_set(GTK_TREE_STORE(model), &root_iter, COLUMN_NAME,
"Mediatree", -1);
gtk_tree_store_set(GTK_TREE_STORE(model), &root_iter, COLUMN_PIXMAP,
root_pixbuf, -1);
num_children = bg_media_tree_get_num_albums(w->tree);
for(i = 0; i < num_children; i++)
{
gtk_tree_store_append(GTK_TREE_STORE(model),
&iter,
&root_iter);
album = bg_media_tree_get_album(w->tree, i);
set_album(w, album, &iter, 1, open_albums);
}
path = gtk_tree_model_get_path(model, &root_iter);
gtk_tree_view_expand_row(GTK_TREE_VIEW(w->treeview), path, FALSE);
gtk_tree_path_free(path);
for(i = 0; i < num_children; i++)
{
album = bg_media_tree_get_album(w->tree, i);
expand_album(w, album);
}
g_signal_handler_unblock(G_OBJECT(selection), w->select_handler_id);
}
/*
* Remove an album window from the list
* This is called by the destructor of album windows
*/
void
bg_gtk_tree_widget_close_album(bg_gtk_tree_widget_t * widget,
bg_gtk_album_window_t * win)
{
bg_album_t * album;
GtkTreeIter iter;
widget->album_windows = g_list_remove(widget->album_windows, win);
album = bg_gtk_album_window_get_album(win);
bg_album_close(album);
album_2_iter(widget, album, &iter);
set_album(widget, album, &iter, 0, 0);
update_menu(widget);
}
/* Open the current album if it isn't already open */
static void open_album(bg_gtk_tree_widget_t * widget,
bg_album_t * album)
{
bg_album_type_t type;
int result;
GtkTreeIter iter;
if(!album)
return;
type = bg_album_get_type(album);
if(type == BG_ALBUM_TYPE_PLUGIN)
return;
/* Check, if the album is already open */
if(!album_is_open(widget, album))
{
if(!bg_album_is_open(album))
{
result = bg_album_open(album);
bg_album_set_error(album, !result);
}
album_2_iter(widget, album, &iter);
set_album(widget, album, &iter, 0, 1);
}
update_menu(widget);
}
void bg_gtk_tree_widget_open_incoming(bg_gtk_tree_widget_t * w)
{
open_album(w, bg_media_tree_get_incoming(w->tree));
}
static void set_parameter_rename_album(void * data, const char * name,
const bg_parameter_value_t * val)
{
GtkTreeIter iter;
bg_gtk_tree_widget_t * w = (bg_gtk_tree_widget_t*)data;
if(!name)
return;
if(!strcmp(name, "album_name"))
{
if(w->selected_album)
{
bg_album_rename(w->selected_album, val->val_str);
album_2_iter(w, w->selected_album, &iter);
set_album(w, w->selected_album, &iter, 0, 0);
}
}
}
static void rename_selected_album(bg_gtk_tree_widget_t * w)
{
bg_dialog_t * dialog;
GtkTreeIter iter;
bg_parameter_info_t info[2];
memset(info, 0, sizeof(info));
info[0].name = "album_name";
info[0].long_name = TRS("Album name");
info[0].type = BG_PARAMETER_STRING;
info[0].val_default.val_str = bg_album_get_name(w->selected_album);
dialog = bg_dialog_create(NULL,
set_parameter_rename_album,
NULL,
w,
info, TR("Rename album"));
bg_dialog_show(dialog, w->treeview);
bg_dialog_destroy(dialog);
album_2_iter(w, w->selected_album, &iter);
set_album(w, w->selected_album, &iter, 0, 0);
}
static void add_dir_callback(char * dir, int recursive,
int subdirs_as_subalbums,
int watch,
const char * plugin,
int prefer_edl,
void * data)
{
bg_gtk_tree_widget_t * w = (bg_gtk_tree_widget_t*)data;
gtk_widget_set_sensitive(w->treeview, 0);
bg_media_tree_add_directory(w->tree, w->selected_album,
dir,
recursive, subdirs_as_subalbums,
watch, plugin, prefer_edl);
gtk_widget_set_sensitive(w->treeview, 1);
}
static void add_dir_close_notify(bg_gtk_filesel_t * s, void * data)
{
}
static void add_directory(bg_gtk_tree_widget_t * w)
{
bg_gtk_filesel_t * dirsel;
dirsel =
bg_gtk_dirsel_create("Add directory",
add_dir_callback,
add_dir_close_notify,
w,
NULL /* parent_window */,
bg_media_tree_get_plugin_registry(w->tree),
BG_PLUGIN_INPUT,
BG_PLUGIN_FILE);
bg_gtk_filesel_set_directory(dirsel,
bg_media_tree_get_add_directory_path(w->tree));
bg_gtk_filesel_run(dirsel, 1);
}
static void create_new_album(bg_gtk_tree_widget_t * w)
{
bg_album_t * new_album =
bg_media_tree_append_album(w->tree,
w->selected_album);
if(w->selected_album)
bg_album_set_expanded(w->selected_album, 1);
w->selected_album = new_album;
bg_gtk_tree_widget_update(w, 0);
rename_selected_album(w);
if(!bg_album_get_name(w->selected_album))
{
bg_media_tree_remove_album(w->tree, w->selected_album);
w->selected_album = NULL;
bg_gtk_tree_widget_update(w, 0);
}
else
{
update_menu(w);
}
}
static void remove_album(bg_gtk_tree_widget_t * w, bg_album_t * a)
{
int i, num_children;
bg_gtk_album_window_t * album_window;
bg_album_t * child;
if(!a)
return;
/* Delete children as long as the parent still exists */
num_children = bg_album_get_num_children(a);
for(i = 0; i < num_children; i++)
{
child = bg_album_get_child(a, 0);
remove_album(w, child);
}
/* Delete parent */
album_window = album_is_open(w, a);
if(album_window)
{
bg_gtk_album_window_destroy(album_window, 1);
}
if(a == w->selected_album)
w->selected_album = NULL;
bg_media_tree_remove_album(w->tree, a);
update_menu(w);
}
typedef struct
{
char * device;
char * name;
bg_album_t * album;
} add_device_struct;
static void set_parameter_add_device(void * data, const char * name,
const bg_parameter_value_t * val)
{
add_device_struct * s = (add_device_struct*)data;
if(!name)
{
if(s->device)
bg_album_add_device(s->album, s->device, s->name);
return;
}
else if(!strcmp(name, "device"))
{
s->device = bg_strdup(s->device, val->val_str);
}
else if(!strcmp(name, "name"))
{
s->name = bg_strdup(s->name, val->val_str);
}
}
static void add_device(bg_gtk_tree_widget_t * w)
{
bg_dialog_t * dialog;
GtkTreeIter iter;
add_device_struct s;
bg_parameter_info_t info[3];
memset(info, 0, sizeof(info));
memset(&s, 0, sizeof(s));
s.album = w->selected_album;
info[0].name = "device";
info[0].long_name = TRS("Device");
info[0].type = BG_PARAMETER_FILE;
info[1].name = "name";
info[1].long_name = TRS("Name");
info[1].type = BG_PARAMETER_STRING;
dialog = bg_dialog_create(NULL,
set_parameter_add_device,
NULL,
&s,
info, TR("Add device"));
bg_dialog_show(dialog, w->treeview);
bg_dialog_destroy(dialog);
album_2_iter(w, w->selected_album, &iter);
set_album(w, w->selected_album, &iter, 0, 0);
bg_gtk_tree_widget_update(w, 0);
if(s.name)
free(s.name);
if(s.device)
free(s.device);
}
static void find_devices(bg_gtk_tree_widget_t * w)
{
int num_children, i;
bg_album_t * child;
bg_gtk_album_window_t * album_window;
/* 1st step: Close all album windows */
num_children = bg_album_get_num_children(w->selected_album);
for(i = 0; i < num_children; i++)
{
child = bg_album_get_child(w->selected_album, i);
album_window = album_is_open(w, child);
if(album_window)
bg_gtk_album_window_destroy(album_window, 1);
}
/* 2nd step: Tell the album to scan for devices */
bg_album_find_devices(w->selected_album);
/* 3rd step: Make changes visible */
bg_gtk_tree_widget_update(w, 0);
}
static void attach_func(gpointer data,
gpointer user_data)
{
bg_gtk_album_window_t * win;
bg_gtk_tree_widget_t * widget;
win = (bg_gtk_album_window_t *)data;
widget = (bg_gtk_tree_widget_t *)user_data;
bg_gtk_album_window_attach(win, widget->notebook);
}
static void set_tabbed_mode(bg_gtk_tree_widget_t * w)
{
g_list_foreach(w->album_windows, attach_func, w);
gtk_widget_show(w->notebook);
w->tabbed_mode = 1;
update_menu(w);
}
static void detach_func(gpointer data,
gpointer user_data)
{
bg_gtk_album_window_t * win;
bg_gtk_tree_widget_t * widget;
win = (bg_gtk_album_window_t *)data;
widget = (bg_gtk_tree_widget_t *)user_data;
bg_gtk_album_window_detach(win);
}
static void set_windowed_mode(bg_gtk_tree_widget_t * w)
{
g_list_foreach(w->album_windows, detach_func, w);
gtk_widget_hide(w->notebook);
w->tabbed_mode = 0;
update_menu(w);
if(w->album_accel_group)
{
gtk_window_remove_accel_group(GTK_WINDOW(w->toplevel_window), w->album_accel_group);
w->album_accel_group = NULL;
}
}
void bg_gtk_tree_widget_goto_current(bg_gtk_tree_widget_t * w)
{
bg_album_t * current_album;
bg_gtk_album_window_t * current_win;
current_album = bg_media_tree_get_current_album(w->tree);
if(w->toplevel_window)
{
gtk_window_present(GTK_WINDOW(w->toplevel_window));
// gtk_widget_grab_focus(
}
if(!current_album)
return;
current_win = album_is_open(w, current_album);
if(!current_win)
return;
bg_gtk_album_window_goto_current(current_win);
}
static void menu_callback(GtkWidget * w, gpointer data)
{
bg_gtk_tree_widget_t * widget = (bg_gtk_tree_widget_t*)data;
bg_gtk_album_window_t * album_window;
/* Album menu */
if(widget->selected_album)
{
if(w == widget->menu.album_menu.open_item)
{
open_album(widget, widget->selected_album);
}
else if((w == widget->menu.album_menu.rename_item) ||
(w == widget->rename_button))
{
rename_selected_album(widget);
}
else if((w == widget->menu.album_menu.remove_item) ||
(w == widget->remove_button))
{
remove_album(widget, widget->selected_album);
bg_gtk_tree_widget_update(widget, 0);
}
else if(w == widget->menu.album_menu.close_item)
{
/* Check, if album has already a window */
album_window = album_is_open(widget, widget->selected_album);
if(album_window)
{
/* Open Album and create window */
bg_gtk_album_window_destroy(album_window, 1);
}
}
else if(w == widget->menu.plugin_menu.find_devices_item)
{
find_devices(widget);
}
else if(w == widget->menu.plugin_menu.add_device_item)
{
add_device(widget);
}
}
if(w == widget->menu.album_menu.new_from_directory_item)
{
add_directory(widget);
}
else if((w == widget->menu.album_menu.new_item) ||
(w == widget->new_button))
{
create_new_album(widget);
}
else if(w == widget->menu.tree_menu.expand_item)
{
gtk_tree_view_expand_all(GTK_TREE_VIEW(widget->treeview));
}
else if(w == widget->menu.tree_menu.collapse_item)
{
gtk_tree_view_collapse_all(GTK_TREE_VIEW(widget->treeview));
}
else if(w == widget->menu.tree_menu.tabbed_mode_item)
{
set_tabbed_mode(widget);
}
else if(w == widget->menu.tree_menu.windowed_mode_item)
{
set_windowed_mode(widget);
}
else if((w == widget->menu.tree_menu.goto_current_item) ||
(w == widget->goto_current_button))
{
bg_gtk_tree_widget_goto_current(widget);
}
}
/* Menu stuff */
static GtkWidget * create_item(bg_gtk_tree_widget_t * w,
const char * label)
{
GtkWidget * ret;
ret = gtk_menu_item_new_with_label(label);
g_signal_connect(G_OBJECT(ret), "activate", G_CALLBACK(menu_callback),
(gpointer)w);
gtk_widget_show(ret);
return ret;
}
static void init_menu(bg_gtk_tree_widget_t * w)
{
/* Tree Menu */
w->menu.tree_menu.menu = gtk_menu_new();
w->menu.tree_menu.goto_current_item = create_item(w, TR("Goto current track"));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.tree_menu.menu),
w->menu.tree_menu.goto_current_item);
gtk_widget_add_accelerator(w->menu.tree_menu.goto_current_item, "activate", w->accel_group,
GDK_g, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
w->menu.tree_menu.expand_item = create_item(w, TR("Expand all"));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.tree_menu.menu),
w->menu.tree_menu.expand_item);
w->menu.tree_menu.collapse_item = create_item(w, TR("Collapse all"));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.tree_menu.menu),
w->menu.tree_menu.collapse_item);
w->menu.tree_menu.tabbed_mode_item = create_item(w, TR("Tabbed mode"));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.tree_menu.menu),
w->menu.tree_menu.tabbed_mode_item);
w->menu.tree_menu.windowed_mode_item = create_item(w, TR("Windowed mode"));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.tree_menu.menu),
w->menu.tree_menu.windowed_mode_item);
gtk_widget_show(w->menu.tree_menu.menu);
/* Album menu */
w->menu.album_menu.menu = gtk_menu_new();
w->menu.album_menu.open_item = create_item(w, TR("Open"));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.album_menu.menu),
w->menu.album_menu.open_item);
w->menu.album_menu.close_item = create_item(w, TR("Close"));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.album_menu.menu),
w->menu.album_menu.close_item);
w->menu.album_menu.new_item = create_item(w, TR("New..."));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.album_menu.menu),
w->menu.album_menu.new_item);
w->menu.album_menu.new_from_directory_item = create_item(w, TR("New from directory..."));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.album_menu.menu),
w->menu.album_menu.new_from_directory_item);
w->menu.album_menu.rename_item = create_item(w, TR("Rename..."));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.album_menu.menu),
w->menu.album_menu.rename_item);
w->menu.album_menu.remove_item = create_item(w, TR("Remove"));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.album_menu.menu),
w->menu.album_menu.remove_item);
gtk_widget_show(w->menu.album_menu.menu);
/* Plugin menu */
w->menu.plugin_menu.menu = gtk_menu_new();
w->menu.plugin_menu.find_devices_item = create_item(w, TR("Scan for devices"));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.plugin_menu.menu),
w->menu.plugin_menu.find_devices_item);
w->menu.plugin_menu.add_device_item = create_item(w, TR("Add device..."));
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.plugin_menu.menu),
w->menu.plugin_menu.add_device_item);
/* Root menu */
w->menu.menu = gtk_menu_new();
w->menu.album_item = create_item(w, TR("Album..."));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(w->menu.album_item),
w->menu.album_menu.menu);
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.menu),
w->menu.album_item);
w->menu.plugin_item = create_item(w, TR("Plugin..."));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(w->menu.plugin_item),
w->menu.plugin_menu.menu);
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.menu),
w->menu.plugin_item);
w->menu.tree_item = create_item(w, TR("Tree..."));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(w->menu.tree_item),
w->menu.tree_menu.menu);
gtk_menu_shell_append(GTK_MENU_SHELL(w->menu.menu),
w->menu.tree_item);
}
/* Buttons */
static GtkWidget * create_pixmap_button(bg_gtk_tree_widget_t * w,
const char * filename,
const char * tooltip)
{
GtkWidget * button;
GtkWidget * image;
char * path;
path = bg_search_file_read("icons", filename);
if(path)
{
image = gtk_image_new_from_file(path);
free(path);
}
else
image = gtk_image_new();
gtk_widget_show(image);
button = gtk_button_new();
gtk_container_add(GTK_CONTAINER(button), image);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(menu_callback), w);
gtk_widget_show(button);
bg_gtk_tooltips_set_tip(button, tooltip, PACKAGE);
return button;
}
/* */
GtkWidget * bg_gtk_tree_widget_get_widget(bg_gtk_tree_widget_t * w)
{
return w->widget;
}
/* Callbacks */
static gboolean button_press_callback(GtkWidget * w, GdkEventButton * evt,
gpointer data)
{
GtkTreeSelection * selection;
GtkTreeModel * model;
GtkTreeIter clicked_iter;
GtkTreePath * path;
bg_gtk_tree_widget_t * tw = (bg_gtk_tree_widget_t *)data;
if((evt->button == 3) && (evt->type == GDK_BUTTON_PRESS))
{
if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tw->treeview),
evt->x, evt->y, &path,
NULL,
NULL,
NULL))
{
path = NULL;
/* Didn't click any entry, return here */
// return TRUE;
}
if(path)
{
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tw->treeview));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(tw->treeview));
gtk_tree_model_get_iter(model, &clicked_iter, path);
gtk_tree_selection_select_iter(selection, &clicked_iter);
}
gtk_menu_popup(GTK_MENU(tw->menu.menu),
NULL,
NULL,
NULL,
NULL,
3, evt->time);
if(path)
gtk_tree_path_free(path);
return TRUE;
}
else if((evt->button == 1) && (evt->type == GDK_2BUTTON_PRESS))
{
open_album(tw, tw->selected_album);
return TRUE;
}
return FALSE;
}
static void select_row_callback(GtkTreeSelection * sel,
gpointer data)
{
bg_album_type_t type;
GtkTreeIter iter;
GtkTreeModel * model;
bg_gtk_album_window_t * album_window;
bg_gtk_tree_widget_t * w = (bg_gtk_tree_widget_t *)data;
if(!gtk_tree_selection_get_selected(sel,
&model,
&iter))
{
w->selected_album = NULL;
}
else
{
w->selected_album = iter_2_album(w, &iter);
}
update_menu(w);
if(w->selected_album)
{
album_window = album_is_open(w, w->selected_album);
if(album_window)
bg_gtk_album_window_raise(album_window);
type = bg_album_get_type(w->selected_album);
switch(type)
{
case BG_ALBUM_TYPE_PLUGIN:
case BG_ALBUM_TYPE_REMOVABLE:
case BG_ALBUM_TYPE_TUNER:
gtk_tree_view_unset_rows_drag_source(GTK_TREE_VIEW(w->treeview));
break;
case BG_ALBUM_TYPE_REGULAR:
case BG_ALBUM_TYPE_INCOMING:
case BG_ALBUM_TYPE_FAVOURITES:
gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(w->treeview),
GDK_BUTTON1_MASK,
dnd_src_entries,
sizeof(dnd_src_entries)/sizeof(dnd_src_entries[0]),
GDK_ACTION_COPY);
break;
}
}
else
gtk_tree_view_unset_rows_drag_source(GTK_TREE_VIEW(w->treeview));
}
static void row_expanded_callback(GtkTreeView *treeview,
GtkTreeIter *arg1,
GtkTreePath *arg2,
gpointer user_data)
{
bg_gtk_tree_widget_t * w;
bg_album_t * album;
w = (bg_gtk_tree_widget_t*)user_data;
album = iter_2_album(w, arg1);
if(!album)
return;
bg_album_set_expanded(album, 1);
}
static void row_collapsed_callback(GtkTreeView *treeview,
GtkTreeIter *arg1,
GtkTreePath *arg2,
gpointer user_data)
{
bg_gtk_tree_widget_t * w;
bg_album_t * album;
w = (bg_gtk_tree_widget_t*)user_data;
album = iter_2_album(w, arg1);
if(!album)
return;
bg_album_set_expanded(album, 0);
}
static void drag_get_callback(GtkWidget *widget,
GdkDragContext *drag_context,
GtkSelectionData *data,
guint info,
guint time,
gpointer user_data)
{
GdkAtom type_atom;
bg_gtk_tree_widget_t * w;
w = (bg_gtk_tree_widget_t *)user_data;
type_atom = gdk_atom_intern("INTEGER", FALSE);
if(!type_atom)
return;
gtk_selection_data_set(data, type_atom, 8, (void*)(w->selected_album),
sizeof(w->selected_album));
}
static void drag_received_callback(GtkWidget *widget,
GdkDragContext *drag_context,
gint x,
gint y,
GtkSelectionData *data,
guint info,
guint time,
gpointer d)
{
bg_album_t * dest_album;
gchar * atom_name;
GtkTreePath * path;
GtkTreeViewDropPosition pos;
int was_open;
int do_delete = 0;
bg_album_type_t type;
bg_gtk_tree_widget_t * w = (bg_gtk_tree_widget_t *)d;
if(!gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(w->treeview),
x, y, &path,
&pos))
return;
if(!path)
return;
dest_album = path_2_album(w, path);
gtk_tree_path_free(path);
if(!dest_album)
return;
atom_name = gdk_atom_name(data->target);
if(!strcmp(atom_name, bg_gtk_atom_album_name))
{
switch(pos)
{
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
bg_media_tree_move_album(w->tree,
w->selected_album,
dest_album);
break;
case GTK_TREE_VIEW_DROP_AFTER:
bg_media_tree_move_album_after(w->tree,
w->selected_album,
dest_album);
break;
case GTK_TREE_VIEW_DROP_BEFORE:
bg_media_tree_move_album_before(w->tree,
w->selected_album,
dest_album);
break;
}
}
else
{
type = bg_album_get_type(dest_album);
switch(type)
{
case BG_ALBUM_TYPE_PLUGIN:
case BG_ALBUM_TYPE_REMOVABLE:
case BG_ALBUM_TYPE_TUNER:
return;
case BG_ALBUM_TYPE_REGULAR:
case BG_ALBUM_TYPE_INCOMING:
case BG_ALBUM_TYPE_FAVOURITES:
break;
}
/* Open album if necessary */
if(!bg_album_is_open(dest_album))
{
bg_album_open(dest_album);
was_open = 0;
}
else
was_open = 1;
if(!strcmp(atom_name, "text/uri-list") ||
!strcmp(atom_name, "text/plain"))
{
bg_album_insert_urilist_before(dest_album, (char*)(data->data), data->length,
NULL);
}
else if(!strcmp(atom_name, bg_gtk_atom_entries_name))
{
bg_album_insert_xml_before(dest_album, (char*)(data->data),
NULL);
if(drag_context->action == GDK_ACTION_MOVE)
do_delete = 1;
}
if(!was_open)
bg_album_close(dest_album);
}
g_free(atom_name);
gtk_drag_finish(drag_context,
TRUE, /* Success */
do_delete, /* Delete */
w->drop_time);
bg_gtk_tree_widget_update(w, 0);
}
static gboolean drag_drop_callback(GtkWidget *widget,
GdkDragContext *drag_context,
gint x,
gint y,
guint time,
gpointer d)
{
bg_gtk_tree_widget_t * w = (bg_gtk_tree_widget_t *)d;
w->drop_time = time;
#if 0
gtk_drag_finish(drag_context,
FALSE, /* Success */
FALSE, /* Delete */
w->drop_time);
return TRUE;
#else
return TRUE;
#endif
}
static gboolean drag_motion_callback(GtkWidget *widget,
GdkDragContext *drag_context,
gint x,
gint y,
guint time,
gpointer user_data)
{
GtkTreePath * path;
bg_album_t * drop_album;
GtkTreeViewDropPosition pos;
int result = 0;
bg_album_type_t type;
bg_gtk_tree_widget_t * w = (bg_gtk_tree_widget_t *)user_data;
if(!gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(w->treeview),
x, y, &path,
&pos))
{
return TRUE;
}
else
{
drop_album = path_2_album(w, path);
if(drop_album)
{
type = bg_album_get_type(drop_album);
switch(type)
{
case BG_ALBUM_TYPE_REGULAR:
case BG_ALBUM_TYPE_INCOMING:
case BG_ALBUM_TYPE_FAVOURITES:
if(widget != gtk_drag_get_source_widget(drag_context))
{
gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(w->treeview),
path,
GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
}
else
{
/* Check if we can drop here */
switch(pos)
{
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
result =
bg_media_tree_check_move_album(w->tree,
w->selected_album,
drop_album);
break;
case GTK_TREE_VIEW_DROP_AFTER:
result =
bg_media_tree_check_move_album_after(w->tree,
w->selected_album,
drop_album);
break;
case GTK_TREE_VIEW_DROP_BEFORE:
result =
bg_media_tree_check_move_album_before(w->tree,
w->selected_album,
drop_album);
break;
}
if(result)
{
gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(w->treeview),
path, pos);
}
}
break;
case BG_ALBUM_TYPE_PLUGIN:
case BG_ALBUM_TYPE_REMOVABLE:
case BG_ALBUM_TYPE_TUNER:
break;
}
}
gtk_tree_path_free(path);
}
return TRUE;
}
static void tree_changed_callback(bg_media_tree_t * t, void * data)
{
bg_gtk_tree_widget_t * w = (bg_gtk_tree_widget_t *)data;
bg_gtk_tree_widget_update(w, 0);
/* This is also called during loading of huge amounts of
urls, so we update the GUI a bit */
while(gdk_events_pending() || gtk_events_pending())
gtk_main_iteration();
}
#if GTK_CHECK_VERSION(2, 21, 6)
static void notebook_change_page(GtkWidget * widget, gpointer page, int num, gpointer data)
#else
static void notebook_change_page(GtkWidget * widget, GtkNotebookPage *page, int num, gpointer data)
#endif
{
bg_gtk_tree_widget_t * wid;
bg_gtk_album_window_t * win;
wid = (bg_gtk_tree_widget_t*)data;
win = g_list_nth_data(wid->album_windows, num);
// bg_gtk_tree_widget_t * wid;
// wid = (bg_gtk_tree_widget_t *)data;
if(wid->album_accel_group)
{
gtk_window_remove_accel_group(GTK_WINDOW (wid->toplevel_window), wid->album_accel_group);
wid->album_accel_group = NULL;
}
if(win)
{
wid->album_accel_group = bg_gtk_album_window_get_accel_group(win);
gtk_window_add_accel_group(GTK_WINDOW (wid->toplevel_window), wid->album_accel_group);
}
}
static gboolean timeout_func(void * data)
{
bg_gtk_tree_widget_t * w = data;
bg_media_tree_check_sync(w->tree);
return TRUE;
}
/* Constructor */
bg_gtk_tree_widget_t *
bg_gtk_tree_widget_create(bg_media_tree_t * tree,
GtkAccelGroup * accel_group,
GtkWidget * toplevel_window)
{
GtkWidget * scrolledwindow;
GtkWidget * buttonbox;
GtkWidget * mainbox;
bg_gtk_tree_widget_t * ret;
GtkTreeStore *store;
GtkCellRenderer *text_renderer;
GtkCellRenderer *pixmap_renderer;
GtkTreeViewColumn *column;
GtkTreeSelection *selection;
load_pixmaps();
bg_gtk_tree_create_atoms();
ret = calloc(1, sizeof(*ret));
ret->tree = tree;
ret->toplevel_window = toplevel_window;
ret->accel_group = accel_group;
bg_media_tree_set_change_callback(ret->tree, tree_changed_callback, ret);
store = gtk_tree_store_new (NUM_COLUMNS,
G_TYPE_STRING,
GDK_TYPE_PIXBUF,
G_TYPE_INT,
G_TYPE_STRING);
ret->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
/* Set as drag destination */
gtk_drag_dest_set(ret->treeview,
GTK_DEST_DEFAULT_ALL,
dnd_dst_entries,
sizeof(dnd_dst_entries)/sizeof(dnd_dst_entries[0]),
GDK_ACTION_COPY | GDK_ACTION_MOVE);
/*
gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(ret->treeview),
dnd_dst_entries,
sizeof(dnd_dst_entries)/sizeof(dnd_dst_entries[0]),
GDK_ACTION_COPY);
*/
// iface = GTK_TREE_DRAG_DEST_GET_IFACE (GTK_TREE_DRAG_DEST(store));
// iface->row_drop_possible = row_drop_possible;
/* Set callbacks */
gtk_widget_set_events(ret->treeview, GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(ret->treeview), "button-press-event",
G_CALLBACK(button_press_callback), (gpointer)ret);
g_signal_connect(G_OBJECT(ret->treeview), "row-collapsed",
G_CALLBACK(row_collapsed_callback), (gpointer)ret);
g_signal_connect(G_OBJECT(ret->treeview), "row-expanded",
G_CALLBACK(row_expanded_callback), (gpointer)ret);
g_signal_connect(G_OBJECT(ret->treeview), "drag-data-received",
G_CALLBACK(drag_received_callback),
(gpointer)ret);
g_signal_connect(G_OBJECT(ret->treeview), "drag-drop",
G_CALLBACK(drag_drop_callback),
(gpointer)ret);
g_signal_connect(G_OBJECT(ret->treeview), "drag-motion",
G_CALLBACK(drag_motion_callback),
(gpointer)ret);
g_signal_connect(G_OBJECT(ret->treeview), "drag-data-get",
G_CALLBACK(drag_get_callback),
(gpointer)ret);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(ret->treeview), 0);
gtk_widget_set_size_request(ret->treeview, 200, 300);
/* Add the columns */
text_renderer = gtk_cell_renderer_text_new();
pixmap_renderer = gtk_cell_renderer_pixbuf_new();
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title(column, "Albums");
gtk_tree_view_column_pack_start(column, pixmap_renderer, FALSE);
gtk_tree_view_column_pack_end(column, text_renderer, TRUE);
gtk_tree_view_column_add_attribute(column,
text_renderer,
"text", COLUMN_NAME);
gtk_tree_view_column_add_attribute(column,
text_renderer,
"weight", COLUMN_WEIGHT);
gtk_tree_view_column_add_attribute(column,
text_renderer,
"foreground", COLUMN_COLOR);
gtk_tree_view_column_add_attribute(column,
pixmap_renderer,
"pixbuf-expander-closed", COLUMN_PIXMAP);
gtk_tree_view_column_add_attribute(column,
pixmap_renderer,
"pixbuf-expander-open", COLUMN_PIXMAP);
gtk_tree_view_column_add_attribute(column,
pixmap_renderer,
"pixbuf", COLUMN_PIXMAP);
// gtk_tree_view_column_set_sort_column_id (column, COLUMN_DEVICE);
gtk_tree_view_append_column (GTK_TREE_VIEW(ret->treeview), column);
/* Set Selection mode */
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ret->treeview));
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
ret->select_handler_id =
g_signal_connect(G_OBJECT(selection), "changed",
G_CALLBACK(select_row_callback), (gpointer)ret);
gtk_widget_show(ret->treeview);
scrolledwindow =
gtk_scrolled_window_new(gtk_tree_view_get_hadjustment(GTK_TREE_VIEW(ret->treeview)),
gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(ret->treeview)));
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
gtk_container_add(GTK_CONTAINER(scrolledwindow), ret->treeview);
gtk_widget_show(scrolledwindow);
/* Create buttons */
ret->remove_button = create_pixmap_button(ret, "trash_16.png",
TRS("Delete album"));
ret->rename_button = create_pixmap_button(ret, "rename_16.png",
TRS("Rename album"));
ret->goto_current_button =
create_pixmap_button(ret, "goto_current_16.png",
TRS("Goto current track"));
ret->new_button =
create_pixmap_button(ret, "folder_new_16.png",
TRS("New album"));
buttonbox = gtk_hbox_new(0, 0);
gtk_box_pack_start(GTK_BOX(buttonbox), ret->new_button,
FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(buttonbox), ret->remove_button,
FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(buttonbox), ret->rename_button,
FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(buttonbox), ret->goto_current_button,
FALSE, FALSE, 0);
gtk_widget_show(buttonbox);
mainbox = gtk_vbox_new(0, 0);
bg_gtk_box_pack_start_defaults(GTK_BOX(mainbox), scrolledwindow);
gtk_box_pack_start(GTK_BOX(mainbox), buttonbox, FALSE, FALSE, 0);
gtk_widget_show(mainbox);
/* Create notebook */
ret->notebook = gtk_notebook_new();
if(ret->toplevel_window)
{
g_signal_connect(G_OBJECT(ret->notebook), "switch-page", G_CALLBACK(notebook_change_page), (gpointer*)ret);
}
gtk_notebook_set_scrollable(GTK_NOTEBOOK(ret->notebook), TRUE);
gtk_notebook_popup_enable(GTK_NOTEBOOK(ret->notebook));
// gtk_widget_show(ret->notebook);
/* Create paned */
ret->widget = gtk_hpaned_new();
gtk_paned_add1(GTK_PANED(ret->widget), mainbox);
gtk_paned_add2(GTK_PANED(ret->widget), ret->notebook);
gtk_widget_show(ret->widget);
init_menu(ret);
ret->cfg_section =
bg_cfg_section_find_subsection(bg_media_tree_get_cfg_section(tree), "gtk_treewidget");
bg_cfg_section_apply(ret->cfg_section, parameters, set_parameter, ret);
bg_gtk_tree_widget_update(ret, 1);
#ifdef HAVE_INOTIFY
ret->timeout_tag = g_timeout_add(500, timeout_func, (gpointer)ret);
#endif
return ret;
}
void bg_gtk_tree_widget_destroy(bg_gtk_tree_widget_t * w)
{
bg_gtk_album_window_t * win;
bg_cfg_section_get(w->cfg_section, parameters, get_parameter, w);
g_signal_handlers_block_by_func(G_OBJECT(w->notebook), notebook_change_page, w);
while(w->album_windows)
{
win = (bg_gtk_album_window_t*)w->album_windows->data;
w->album_windows = g_list_remove(w->album_windows, (gpointer)win);
bg_gtk_album_window_destroy(win, 0);
}
if(w->timeout_tag > 0)
g_source_remove(w->timeout_tag);
free(w);
}
bg_media_tree_t * bg_gtk_tree_widget_get_tree(bg_gtk_tree_widget_t * w)
{
return w->tree;
}
gmerlin-1.2.0~dfsg/lib/gtk/cfg_dialog.c 0000644 0001750 0001750 00000121215 11764363406 017662 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include "gtk_dialog.h"
#include
#include
#include
#define LOG_DOMAIN "cfg_dialog"
#include
enum
{
COLUMN_NAME,
NUM_COLUMNS
};
typedef struct dialog_section_s
{
bg_set_parameter_func_t set_param;
bg_get_parameter_func_t get_param;
void * callback_data;
bg_gtk_widget_t * widgets;
int num_widgets;
const bg_parameter_info_t * infos;
bg_cfg_section_t * cfg_section;
/* Dialog sections can be nested */
struct dialog_section_s ** children;
int num_children;
struct dialog_section_s * parent;
/* Index in global notebook */
int notebook_index;
bg_gtk_preset_menu_t * preset_menu;
bg_cfg_section_t * preset_section;
int private_cfg_section;
} dialog_section_t;
struct bg_dialog_s
{
GtkWidget * ok_button;
GtkWidget * apply_button;
GtkWidget * close_button;
GtkWidget * window;
GtkWidget * mainbox;
GtkWidget * action_box;
dialog_section_t root_section;
int visible;
GtkWidget * notebook;
GtkWidget * treeview;
GtkWidget * scrolledwindow;
guint select_handler_id;
int result;
bg_plugin_registry_t * plugin_reg;
bg_gtk_preset_menu_t * preset_menu;
bg_cfg_section_t * preset_section;
};
static int parent_index(dialog_section_t * s)
{
int i;
if(!s->parent)
return -1;
for(i = 0; i < s->parent->num_children; i++)
{
if(s->parent->children[i] == s)
return i;
}
return -1;
}
static int * section_to_path(bg_dialog_t * d, dialog_section_t * s)
{
int * ret;
int index;
dialog_section_t * tmp;
int depth;
/* Get depth */
tmp = s;
depth = 0;
while(tmp->parent)
{
tmp = tmp->parent;
depth++;
}
/* Allocate return value */
ret = malloc((depth+1)*sizeof(*ret));
index = depth;
ret[index--] = -1;
tmp = s;
while(index >= 0)
{
ret[index--] = parent_index(tmp);
tmp = tmp->parent;
}
return ret;
}
static void
section_to_iter(bg_dialog_t * d, dialog_section_t * s, GtkTreeIter * iter)
{
int * indices;
int i;
GtkTreeModel *model;
GtkTreePath *path;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->treeview));
indices = section_to_path(d, s);
path = gtk_tree_path_new();
i = 0;
while(indices[i] != -1)
{
gtk_tree_path_append_index(path, indices[i]);
i++;
}
free(indices);
gtk_tree_model_get_iter(model, iter, path);
gtk_tree_path_free(path);
}
static dialog_section_t * iter_2_section(bg_dialog_t * d,
GtkTreeIter * iter)
{
dialog_section_t * ret;
int i, depth;
GtkTreeModel *model;
GtkTreePath *path;
gint * indices;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->treeview));
path = gtk_tree_model_get_path(model, iter);
indices = gtk_tree_path_get_indices(path);
ret = d->root_section.children[indices[0]];
depth = gtk_tree_path_get_depth(path);
for(i = 1; i < depth; i++)
{
ret = ret->children[indices[i]];
}
gtk_tree_path_free(path);
return ret;
}
static void reset_section(dialog_section_t * s)
{
int i;
bg_parameter_value_t val;
char * pos;
bg_cfg_section_t * cfg_subsection;
int set_param = 0;
for(i = 0; i < s->num_widgets; i++)
{
if(!s->widgets[i].funcs->get_value)
continue;
bg_parameter_value_copy(&s->widgets[i].value, &s->widgets[i].last_value,
s->widgets[i].info);
s->widgets[i].funcs->get_value(&s->widgets[i]);
if(s->set_param && (s->widgets[i].info->flags & BG_PARAMETER_SYNC))
{
set_param = 1;
if((s->widgets[i].info->type == BG_PARAMETER_DEVICE) &&
(s->widgets[i].value.val_str) &&
strchr(s->widgets[i].value.val_str, ':'))
{
val.val_str = bg_strdup(NULL, s->widgets[i].value.val_str);
pos = strchr(val.val_str, ':');
if(pos)
*pos = '\0';
s->set_param(s->callback_data, s->widgets[i].info->name,
&val);
free(val.val_str);
}
else
s->set_param(s->callback_data, s->widgets[i].info->name,
&s->widgets[i].value);
}
if(s->cfg_section)
{
if(s->widgets[i].info->flags & BG_PARAMETER_SYNC)
bg_cfg_section_set_parameter(s->cfg_section,
s->widgets[i].info,
&s->widgets[i].value);
/* If we have multi parameters, we'll also reset the subsection, even if
BG_PARAMETER_SYNC isn't set */
if(s->widgets[i].cfg_subsection_save)
{
cfg_subsection = bg_cfg_section_find_subsection(s->cfg_section, s->widgets[i].info->name);
bg_cfg_section_transfer(s->widgets[i].cfg_subsection_save, cfg_subsection);
}
if(s->widgets[i].funcs->apply_sub_params)
s->widgets[i].funcs->apply_sub_params(&s->widgets[i]);
}
}
if(set_param)
s->set_param(s->callback_data, NULL, NULL);
for(i = 0; i < s->num_children; i++)
reset_section(s->children[i]);
}
static void restore_section(dialog_section_t * s,
bg_cfg_section_t * cfg_section)
{
int i;
bg_parameter_value_t val;
char * pos;
int set_param = 0;
if(!cfg_section)
return;
for(i = 0; i < s->num_widgets; i++)
{
if(!s->widgets[i].funcs->get_value)
continue;
bg_cfg_section_get_parameter(cfg_section,
s->widgets[i].info,
&s->widgets[i].value);
s->widgets[i].funcs->get_value(&s->widgets[i]);
if(s->set_param && (s->widgets[i].info->flags & BG_PARAMETER_SYNC))
{
set_param = 1;
if((s->widgets[i].info->type == BG_PARAMETER_DEVICE) &&
(s->widgets[i].value.val_str) &&
strchr(s->widgets[i].value.val_str, ':'))
{
val.val_str = bg_strdup(NULL, s->widgets[i].value.val_str);
pos = strchr(val.val_str, ':');
if(pos)
*pos = '\0';
s->set_param(s->callback_data, s->widgets[i].info->name,
&val);
free(val.val_str);
}
else
s->set_param(s->callback_data, s->widgets[i].info->name,
&s->widgets[i].value);
}
/* Copy subsections */
if(s->widgets[i].info->multi_parameters)
{
bg_cfg_section_t * src1, * dst1;
dst1 = bg_cfg_section_find_subsection(s->cfg_section, s->widgets[i].info->name);
src1 = bg_cfg_section_find_subsection(cfg_section, s->widgets[i].info->name);
if(s->widgets[i].info->type == BG_PARAMETER_MULTI_CHAIN)
bg_cfg_section_delete_subsections(dst1);
bg_cfg_section_transfer_children(src1, dst1);
}
}
if(set_param)
s->set_param(s->callback_data, NULL, NULL);
}
static void apply_section(dialog_section_t * s)
{
bg_parameter_value_t val;
char * pos;
int i;
bg_cfg_section_t * cfg_subsection;
for(i = 0; i < s->num_widgets; i++)
{
if(!s->widgets[i].funcs->set_value)
continue;
s->widgets[i].funcs->set_value(&s->widgets[i]);
bg_parameter_value_copy(&s->widgets[i].last_value, &s->widgets[i].value,
s->widgets[i].info);
if(s->cfg_section)
{
bg_cfg_section_set_parameter(s->cfg_section,
s->widgets[i].info,
&s->widgets[i].value);
}
if(s->widgets[i].cfg_subsection_save)
{
bg_cfg_section_destroy(s->widgets[i].cfg_subsection_save);
cfg_subsection =
bg_cfg_section_find_subsection(s->cfg_section, s->widgets[i].info->name);
s->widgets[i].cfg_subsection_save = bg_cfg_section_copy(cfg_subsection);
}
if(s->set_param)
{
if((s->widgets[i].info->type == BG_PARAMETER_DEVICE) &&
(s->widgets[i].value.val_str) &&
strchr(s->widgets[i].value.val_str, ':'))
{
val.val_str = malloc(strlen(s->widgets[i].value.val_str)+1);
strcpy(val.val_str, s->widgets[i].value.val_str);
pos = strchr(val.val_str, ':');
*pos = '\0';
s->set_param(s->callback_data, s->widgets[i].info->name,
&val);
free(val.val_str);
}
else
s->set_param(s->callback_data, s->widgets[i].info->name,
&s->widgets[i].value);
}
}
if(s->set_param)
s->set_param(s->callback_data, NULL, NULL);
for(i = 0; i < s->num_children; i++)
apply_section(s->children[i]);
}
static void apply_values(bg_dialog_t * d)
{
apply_section(&d->root_section);
}
static void reset_values(bg_dialog_t * d)
{
reset_section(&d->root_section);
}
static void button_callback(GtkWidget * w, gpointer * data)
{
bg_dialog_t * d = (bg_dialog_t *)data;
if((w == d->close_button) ||
(w == d->window))
{
reset_values(d);
d->visible = 0;
gtk_widget_hide(d->window);
gtk_main_quit();
}
else if(w == d->apply_button)
{
apply_values(d);
d->result = 1;
}
else if(w == d->ok_button)
{
d->visible = 0;
gtk_widget_hide(d->window);
gtk_main_quit();
apply_values(d);
d->result = 1;
}
}
static gboolean delete_callback(GtkWidget * w, GdkEventAny * event,
gpointer data)
{
button_callback(w, data);
return TRUE;
}
static void select_row_callback(GtkTreeSelection * sel,
gpointer data)
{
int index = 0;
dialog_section_t * selected_section;
GtkTreeModel * model;
GtkTreeIter iter;
bg_dialog_t * d = (bg_dialog_t *)data;
if(!gtk_tree_selection_get_selected(sel,
&model,
&iter))
index = 0;
else
{
selected_section = iter_2_section(d, &iter);
index = selected_section->notebook_index;
}
gtk_notebook_set_current_page(GTK_NOTEBOOK(d->notebook), index);
}
static bg_dialog_t * create_dialog(const char * title)
{
bg_dialog_t * ret;
GtkWidget * buttonbox;
GtkWidget * label, *tab_label;
GtkWidget * hbox;
GtkTreeStore *store;
GtkCellRenderer *text_renderer;
GtkTreeViewColumn *column;
GtkTreeSelection *selection;
ret = calloc(1, sizeof(*ret));
ret->window = bg_gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(ret->window), GTK_WIN_POS_CENTER_ON_PARENT);
gtk_window_set_title(GTK_WINDOW(ret->window), title);
ret->apply_button = gtk_button_new_from_stock(GTK_STOCK_APPLY);
ret->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
ret->ok_button = gtk_button_new_from_stock(GTK_STOCK_OK);
bg_gtk_widget_set_can_default(ret->apply_button, TRUE);
bg_gtk_widget_set_can_default(ret->close_button, TRUE);
bg_gtk_widget_set_can_default(ret->ok_button, TRUE);
gtk_window_set_modal(GTK_WINDOW(ret->window), TRUE);
g_signal_connect(G_OBJECT(ret->ok_button), "clicked",
G_CALLBACK(button_callback), (gpointer)ret);
g_signal_connect(G_OBJECT(ret->close_button), "clicked",
G_CALLBACK(button_callback), (gpointer)ret);
g_signal_connect(G_OBJECT(ret->apply_button), "clicked",
G_CALLBACK(button_callback), (gpointer)ret);
g_signal_connect(G_OBJECT(ret->window), "delete_event",
G_CALLBACK(delete_callback), (gpointer)ret);
bg_gtk_widget_set_can_default(ret->close_button, TRUE);
bg_gtk_widget_set_can_default(ret->apply_button, TRUE);
bg_gtk_widget_set_can_default(ret->ok_button, TRUE);
gtk_widget_show(ret->apply_button);
gtk_widget_show(ret->close_button);
gtk_widget_show(ret->ok_button);
/* Create notebook */
ret->notebook = gtk_notebook_new();
label = gtk_label_new(TR("No options here, choose subcategory"));
tab_label = gtk_label_new("");
gtk_widget_show(label);
gtk_widget_show(tab_label);
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(ret->notebook), 0);
gtk_notebook_append_page(GTK_NOTEBOOK(ret->notebook), label, tab_label);
gtk_widget_show(ret->notebook);
/* Create treeview */
store = gtk_tree_store_new (NUM_COLUMNS,
G_TYPE_STRING);
ret->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(ret->treeview), 0);
// gtk_widget_set_size_request(ret->treeview, 200, 300);
text_renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new ();
gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
gtk_tree_view_column_add_attribute(column,
text_renderer,
"text", COLUMN_NAME);
gtk_tree_view_append_column (GTK_TREE_VIEW(ret->treeview), column);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(ret->treeview));
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
ret->select_handler_id =
g_signal_connect(G_OBJECT(selection), "changed",
G_CALLBACK(select_row_callback), (gpointer)ret);
gtk_widget_show(ret->treeview);
ret->scrolledwindow =
gtk_scrolled_window_new(gtk_tree_view_get_hadjustment(GTK_TREE_VIEW(ret->treeview)),
gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(ret->treeview)));
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ret->scrolledwindow),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(ret->scrolledwindow), ret->treeview);
gtk_widget_show(ret->scrolledwindow);
/* Create the rest */
hbox = gtk_hpaned_new();
gtk_paned_add1(GTK_PANED(hbox), ret->scrolledwindow);
gtk_paned_add2(GTK_PANED(hbox), ret->notebook);
gtk_widget_show(hbox);
ret->action_box = gtk_hbox_new(FALSE, 0);
ret->mainbox = gtk_vbox_new(0, 5);
gtk_box_pack_start(GTK_BOX(ret->mainbox), hbox, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(ret->mainbox), ret->action_box, FALSE, FALSE, 0);
buttonbox = gtk_hbutton_box_new();
gtk_box_set_spacing(GTK_BOX(buttonbox), 10);
gtk_container_set_border_width(GTK_CONTAINER(buttonbox), 10);
gtk_container_add(GTK_CONTAINER(buttonbox), ret->ok_button);
gtk_container_add(GTK_CONTAINER(buttonbox), ret->apply_button);
gtk_container_add(GTK_CONTAINER(buttonbox), ret->close_button);
gtk_widget_show(buttonbox);
gtk_box_pack_end(GTK_BOX(ret->mainbox), buttonbox, FALSE, FALSE, 0);
gtk_widget_show(ret->mainbox);
gtk_container_add(GTK_CONTAINER(ret->window), ret->mainbox);
gtk_widget_realize(ret->window);
return ret;
}
static void restore_button_callback(GtkWidget * w, gpointer data)
{
dialog_section_t * section = data;
bg_cfg_section_t * tmp_section;
// fprintf(stderr, "Restore factory defaults %p\n", section);
tmp_section = bg_cfg_section_copy(section->cfg_section);
bg_cfg_section_restore_defaults(tmp_section,
section->widgets[0].info);
restore_section(section, tmp_section);
bg_cfg_section_destroy(tmp_section);
}
static GtkWidget * create_restore_button(dialog_section_t * section)
{
GtkWidget * button;
GtkWidget * image;
char * path;
path = bg_search_file_read("icons", "refresh_16.png");
if(path)
{
image = gtk_image_new_from_file(path);
free(path);
}
else
image = gtk_image_new();
gtk_widget_show(image);
button = gtk_button_new();
gtk_container_add(GTK_CONTAINER(button), image);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(restore_button_callback), section);
gtk_widget_show(button);
bg_gtk_tooltips_set_tip(button, "Restore factory defaults", PACKAGE);
return button;
}
static void preset_load_callback(void * data)
{
dialog_section_t * s = data;
restore_section(s, s->preset_section);
}
static void preset_save_callback(void * data)
{
int i;
dialog_section_t * s = data;
// fprintf(stderr, "preset_save_callback %p\n", data);
for(i = 0; i < s->num_widgets; i++)
{
if(!s->widgets[i].funcs->set_value)
continue;
s->widgets[i].funcs->set_value(&s->widgets[i]);
bg_cfg_section_set_parameter(s->preset_section,
s->widgets[i].info,
&s->widgets[i].value);
if(s->widgets[i].info->multi_parameters)
{
bg_cfg_section_t * src1;
bg_cfg_section_t * dst1;
src1 = bg_cfg_section_find_subsection(s->cfg_section, s->widgets[i].info->name);
dst1 = bg_cfg_section_find_subsection(s->preset_section, s->widgets[i].info->name);
if(s->widgets[i].info->type == BG_PARAMETER_MULTI_CHAIN)
bg_cfg_section_delete_subsections(dst1);
bg_cfg_section_transfer_children(src1, dst1);
}
}
}
static GtkWidget * create_section(dialog_section_t * section,
const bg_parameter_info_t * info,
bg_cfg_section_t * s,
bg_set_parameter_func_t set_param,
bg_get_parameter_func_t get_param,
void * data,
const char * translation_domain,
bg_plugin_registry_t * plugin_reg,
bg_cfg_section_t * preset_section)
{
int i, count, j;
int row, column, num_columns;
bg_cfg_section_t * cfg_subsection;
GtkWidget * table;
GtkWidget * label;
GtkWidget * action_box;
GtkWidget * vbox;
GtkWidget * restore_button;
/* If info == NULL, we create a label, which
tell the user, that there is nothing to do */
if(!info)
{
table = gtk_table_new(1, 1, 0);
label = gtk_label_new(TR("No options available"));
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL|GTK_EXPAND,
GTK_FILL|GTK_EXPAND, 0, 0);
gtk_widget_show(table);
return table;
}
// fprintf(stderr, "create section %p %p %p\n", section, s, preset_section);
section->num_widgets = 0;
i = 0;
if(info[0].type == BG_PARAMETER_SECTION)
i++;
while(info[i].name &&
(info[i].type != BG_PARAMETER_SECTION))
{
if(!(info[i].flags & BG_PARAMETER_HIDE_DIALOG))
section->num_widgets++;
i++;
}
section->infos = info;
if((info[0].type == BG_PARAMETER_SECTION) &&
(info[0].flags & BG_PARAMETER_OWN_SECTION))
section->cfg_section =
bg_cfg_section_find_subsection(s, info[0].name);
else
section->cfg_section = s;
section->callback_data = data;
section->set_param = set_param;
section->get_param = get_param;
/* Now, create and place all widgets */
table = gtk_table_new(1, 1, 0);
gtk_table_set_col_spacings(GTK_TABLE(table), 5);
gtk_table_set_row_spacings(GTK_TABLE(table), 5);
gtk_container_set_border_width(GTK_CONTAINER(table), 5);
row = 0;
column = 0;
num_columns = 1;
section->widgets = calloc(section->num_widgets, sizeof(bg_gtk_widget_t));
i = 0;
count = 0;
while(count < section->num_widgets)
{
if(info[i].gettext_domain)
translation_domain = info[i].gettext_domain;
if(info[i].gettext_directory)
bg_bindtextdomain(translation_domain, info[i].gettext_directory);
/* Check what to skip */
if((info[i].flags & BG_PARAMETER_HIDE_DIALOG) ||
((info[i].type == BG_PARAMETER_BUTTON) && !set_param) ||
(info[i].type == BG_PARAMETER_SECTION))
{
i++;
continue;
}
if((info[i].flags & BG_PARAMETER_PLUGIN))
{
section->widgets[count].plugin_reg = plugin_reg;
if(!section->cfg_section)
{
section->cfg_section = bg_cfg_section_create(NULL);
section->private_cfg_section = 1;
}
}
if((info[i].flags & BG_PARAMETER_SYNC) ||
(info[i].type == BG_PARAMETER_BUTTON))
{
section->widgets[count].change_callback = set_param;
section->widgets[count].change_callback_data = data;
}
section->widgets[count].info = &info[i];
section->widgets[count].cfg_section = section->cfg_section;
if(info[i].multi_parameters && section->cfg_section)
{
cfg_subsection = bg_cfg_section_find_subsection(section->cfg_section, info[i].name);
/* Add references to the plugin registry */
if(section->widgets[count].plugin_reg)
{
j = 0;
while(info[i].multi_names[j])
{
bg_cfg_section_add_ref(cfg_subsection,
bg_plugin_registry_get_section(section->widgets[count].plugin_reg,
info[i].multi_names[j]));
j++;
}
}
section->widgets[count].cfg_subsection_save = bg_cfg_section_copy(cfg_subsection);
}
switch(info[i].type)
{
case BG_PARAMETER_BUTTON:
bg_gtk_create_button(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_CHECKBUTTON:
bg_gtk_create_checkbutton(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_INT:
bg_gtk_create_int(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_FLOAT:
bg_gtk_create_float(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_SLIDER_INT:
bg_gtk_create_slider_int(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_TIME:
bg_gtk_create_time(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_SLIDER_FLOAT:
bg_gtk_create_slider_float(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_STRING:
case BG_PARAMETER_STRING_HIDDEN:
bg_gtk_create_string(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_STRINGLIST:
bg_gtk_create_stringlist(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_COLOR_RGB:
bg_gtk_create_color_rgb(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_COLOR_RGBA:
bg_gtk_create_color_rgba(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_FILE:
bg_gtk_create_file(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_DIRECTORY:
bg_gtk_create_directory(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_FONT:
bg_gtk_create_font(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_DEVICE:
bg_gtk_create_device(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_MULTI_MENU:
bg_gtk_create_multi_menu(§ion->widgets[count],
set_param, get_param, data,
translation_domain);
break;
case BG_PARAMETER_MULTI_LIST:
bg_gtk_create_multi_list(§ion->widgets[count],
set_param, get_param, data,
translation_domain);
break;
case BG_PARAMETER_MULTI_CHAIN:
bg_gtk_create_multi_chain(§ion->widgets[count],
set_param, get_param, data,
translation_domain);
break;
case BG_PARAMETER_POSITION:
bg_gtk_create_position(§ion->widgets[count],
translation_domain);
break;
case BG_PARAMETER_SECTION:
break;
}
section->widgets[count].funcs->attach(section->widgets[count].priv, table,
&row, &num_columns);
/* Get the value from the get_param function */
if(get_param &&
get_param(data, info[i].name, §ion->widgets[count].value))
;
/* .. or from the config data... */
else if(section->cfg_section)
bg_cfg_section_get_parameter(section->cfg_section, &info[i],
§ion->widgets[count].value);
/* ... or from the parameter default */
else
bg_parameter_value_copy(§ion->widgets[count].value,
&info[i].val_default,
&info[i]);
bg_parameter_value_copy(§ion->widgets[count].last_value,
§ion->widgets[count].value,
&info[i]);
if(section->widgets[count].info->flags & BG_PARAMETER_SYNC)
bg_gtk_change_callback_block(§ion->widgets[count], 1);
if(section->widgets[count].funcs->get_value)
{
section->widgets[count].funcs->get_value(§ion->widgets[count]);
if(section->widgets[count].info->flags & BG_PARAMETER_SYNC)
bg_gtk_change_callback_block(§ion->widgets[count], 0);
}
i++;
count++;
}
if(section->cfg_section)
{
/* Create action box */
gtk_table_resize(GTK_TABLE(table), row+1, num_columns);
restore_button = create_restore_button(section);
action_box = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(action_box), restore_button, FALSE, FALSE, 0);
if(!preset_section &&
(info[0].preset_path || (section->num_widgets && section->widgets[0].info->preset_path)))
{
section->preset_section = bg_cfg_section_copy(section->cfg_section);
section->preset_menu = bg_gtk_preset_menu_create(info[0].preset_path,
section->preset_section,
preset_load_callback,
preset_save_callback,
section);
gtk_box_pack_start(GTK_BOX(action_box),
bg_gtk_preset_menu_get_widget(section->preset_menu),
FALSE, FALSE, 0);
}
else
section->preset_section = preset_section;
gtk_widget_show(action_box);
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_end(GTK_BOX(vbox), action_box, FALSE, FALSE, 0);
gtk_widget_show(vbox);
gtk_table_attach_defaults(GTK_TABLE(table), vbox, 0,
num_columns-1, row, row+1);
}
gtk_widget_show(table);
return table;
}
static int count_sections(const bg_parameter_info_t * info)
{
int ret = 0;
int i = 0;
if(!info[0].name)
return 0;
if(info[0].type == BG_PARAMETER_SECTION)
{
while(info[i].name)
{
if(info[i].type == BG_PARAMETER_SECTION)
ret++;
i++;
}
return ret;
}
return 0;
}
static void preset_load_callback_global(void * data)
{
int i;
bg_dialog_t * d = data;
for(i = 0; i < d->root_section.num_children; i++)
preset_load_callback(d->root_section.children[i]);
}
static void preset_save_callback_global(void * data)
{
int i;
bg_dialog_t * d = data;
for(i = 0; i < d->root_section.num_children; i++)
preset_save_callback(d->root_section.children[i]);
}
bg_dialog_t * bg_dialog_create(bg_cfg_section_t * section,
bg_set_parameter_func_t set_param,
bg_get_parameter_func_t get_param,
void * callback_data,
const bg_parameter_info_t * info,
const char * title)
{
int i, index;
int num_sections;
GtkWidget * label;
GtkWidget * table;
bg_cfg_section_t * preset_subsection;
GtkTreeIter root_iter;
GtkTreeModel * model;
const char * translation_domain = NULL;
bg_dialog_t * ret = create_dialog(title);
// fprintf(stderr, "bg_dialog_create\n");
num_sections = count_sections(info);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(ret->treeview));
if((info->flags & BG_PARAMETER_GLOBAL_PRESET) &&
(info->preset_path))
{
ret->preset_section = bg_cfg_section_create(NULL);
ret->preset_menu = bg_gtk_preset_menu_create(info->preset_path,
ret->preset_section,
preset_load_callback_global,
preset_save_callback_global,
ret);
gtk_box_pack_start(GTK_BOX(ret->action_box),
bg_gtk_preset_menu_get_widget(ret->preset_menu),
FALSE, FALSE, 0);
gtk_widget_show(ret->action_box);
}
if(num_sections)
{
ret->root_section.num_children = num_sections;
ret->root_section.children =
calloc(ret->root_section.num_children, sizeof(*ret->root_section.children));
index = 0;
for(i = 0; i < ret->root_section.num_children; i++)
{
ret->root_section.children[i] =
calloc(1, sizeof(*ret->root_section.children[i]));
if(info[index].gettext_domain)
translation_domain = info[i].gettext_domain;
if(info[index].gettext_directory)
bg_bindtextdomain(translation_domain, info[i].gettext_directory);
label = gtk_label_new(TR_DOM(info[index].long_name));
gtk_widget_show(label);
gtk_tree_store_append(GTK_TREE_STORE(model), &root_iter, NULL);
gtk_tree_store_set(GTK_TREE_STORE(model), &root_iter, COLUMN_NAME,
TR_DOM(info[index].long_name), -1);
if(ret->preset_section)
preset_subsection =
bg_cfg_section_find_subsection(ret->preset_section, info[index].name);
else
preset_subsection = NULL;
table = create_section(ret->root_section.children[i], &info[index],
section, set_param, get_param, callback_data,
translation_domain, ret->plugin_reg, preset_subsection);
ret->root_section.children[i]->notebook_index =
gtk_notebook_get_n_pages(GTK_NOTEBOOK(ret->notebook));
gtk_notebook_append_page(GTK_NOTEBOOK(ret->notebook), table, label);
ret->root_section.children[i]->parent = &ret->root_section;
// while(info[index].type == BG_PARAMETER_SECTION)
index++;
while(info[index].name &&
(info[index].type != BG_PARAMETER_SECTION))
index++;
}
}
else
{
label = gtk_label_new(title);
gtk_widget_show(label);
ret->root_section.num_children = 1;
ret->root_section.children = calloc(ret->root_section.num_children,
sizeof(*ret->root_section.children));
ret->root_section.children[0] = calloc(1, sizeof(*ret->root_section.children[0]));
table =
create_section(*ret->root_section.children, info, section, set_param, get_param,
callback_data, NULL, ret->plugin_reg, NULL);
gtk_notebook_append_page(GTK_NOTEBOOK(ret->notebook), table, label);
gtk_notebook_set_current_page(GTK_NOTEBOOK(ret->notebook), 1);
gtk_widget_hide(ret->scrolledwindow);
}
return ret;
}
bg_dialog_t * bg_dialog_create_multi(const char * title)
{
bg_dialog_t * ret = create_dialog(title);
return ret;
}
void bg_dialog_add_child(bg_dialog_t *d, void * _parent,
const char * name,
bg_cfg_section_t * section,
bg_set_parameter_func_t set_param,
bg_get_parameter_func_t get_param,
void * callback_data,
const bg_parameter_info_t * info)
{
GtkTreeIter iter, parent_iter;
int num_items;
int num_sections;
GtkWidget * table;
GtkWidget * tab_label;
int i, item_index, section_index;
GtkTreeModel *model;
const char * translation_domain = NULL;
dialog_section_t * parent = (dialog_section_t*)_parent;
// fprintf(stderr, "bg_dialog_add_child %s %p\n", info->name, section);
num_items = 0;
num_sections = 0;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->treeview));
if(info)
{
while(info[num_items+num_sections].name)
{
if(info[num_items+num_sections].type == BG_PARAMETER_SECTION)
num_sections++;
else
num_items++;
}
}
if(!num_sections)
{
parent->children = realloc(parent->children,
(parent->num_children+1)*sizeof(*parent->children));
parent->children[parent->num_children] =
calloc(1, sizeof(*parent->children[parent->num_children]));
table = create_section(parent->children[parent->num_children],
info, section, set_param, get_param, callback_data,
NULL, d->plugin_reg, NULL);
tab_label = gtk_label_new(name);
gtk_widget_show(tab_label);
parent->children[parent->num_children]->notebook_index =
gtk_notebook_get_n_pages(GTK_NOTEBOOK(d->notebook));
gtk_notebook_append_page(GTK_NOTEBOOK(d->notebook),
table, tab_label);
if(parent == &d->root_section)
{
gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL);
}
else
{
section_to_iter(d, parent, &parent_iter);
gtk_tree_store_append(GTK_TREE_STORE(model), &iter, &parent_iter);
}
gtk_tree_store_set(GTK_TREE_STORE(model), &iter, COLUMN_NAME,
name, -1);
parent->children[parent->num_children]->parent = parent;
parent->num_children++;
}
else
{
parent->children = realloc(parent->children,
(parent->num_children+num_sections)*
sizeof(dialog_section_t));
item_index = 0;
section_index = parent->num_children;
for(i = 0; i < num_sections; i++)
{
parent->children[section_index] = calloc(1, sizeof(*parent->children[section_index]));
if(info[item_index].gettext_domain)
translation_domain = info[item_index].gettext_domain;
if(info[item_index].gettext_directory)
bg_bindtextdomain(translation_domain, info[item_index].gettext_directory);
tab_label = gtk_label_new(TR_DOM(info[item_index].long_name));
gtk_widget_show(tab_label);
if(parent == &d->root_section)
{
parent = (dialog_section_t*)_parent;
gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL);
}
else
{
section_to_iter(d, parent, &parent_iter);
gtk_tree_store_append(GTK_TREE_STORE(model), &iter, &parent_iter);
}
gtk_tree_store_set(GTK_TREE_STORE(model), &iter, COLUMN_NAME,
info[item_index].long_name, -1);
table = create_section(parent->children[section_index],
&info[item_index],
section, set_param, get_param, callback_data,
translation_domain, d->plugin_reg, NULL);
parent->children[section_index]->parent = parent;
parent->children[section_index]->notebook_index =
gtk_notebook_get_n_pages(GTK_NOTEBOOK(d->notebook));
gtk_notebook_append_page(GTK_NOTEBOOK(d->notebook),
table, tab_label);
item_index++;
while((info[item_index].name) &&
(info[item_index].type != BG_PARAMETER_SECTION))
item_index++;
section_index++;
}
parent->num_children += num_sections;
}
}
void bg_dialog_add(bg_dialog_t *d,
const char * name,
bg_cfg_section_t * section,
bg_set_parameter_func_t set_param,
bg_get_parameter_func_t get_param,
void * callback_data,
const bg_parameter_info_t * info)
{
bg_dialog_add_child(d, &d->root_section, name,
section, set_param, get_param, callback_data, info);
}
int bg_dialog_show(bg_dialog_t * d, void * parent)
{
GtkWidget * parent_w;
d->result = 0;
if(d->visible)
{
gtk_window_present(GTK_WINDOW(d->window));
return 0;
}
if(parent)
{
parent_w = (GtkWidget*)parent;
parent_w = bg_gtk_get_toplevel(parent_w);
if(parent_w)
gtk_window_set_transient_for(GTK_WINDOW(d->window),
GTK_WINDOW(parent_w));
}
d->visible = 1;
gtk_widget_show(d->window);
gtk_widget_grab_default(d->ok_button);
gtk_widget_grab_focus(d->ok_button);
gtk_main();
return d->result;
}
static void destroy_section(dialog_section_t * s)
{
int i;
if(s->num_widgets)
{
for(i = 0; i < s->num_widgets; i++)
{
s->widgets[i].funcs->destroy(&s->widgets[i]);
bg_parameter_value_free(&s->widgets[i].value,
s->widgets[i].info->type);
bg_parameter_value_free(&s->widgets[i].last_value,
s->widgets[i].info->type);
if(s->widgets[i].cfg_subsection_save)
bg_cfg_section_destroy(s->widgets[i].cfg_subsection_save);
}
free(s->widgets);
}
if(s->children)
{
for(i = 0; i < s->num_children; i++)
{
destroy_section(s->children[i]);
free(s->children[i]);
}
free(s->children);
}
if(s->preset_menu)
{
bg_gtk_preset_menu_destroy(s->preset_menu);
if(s->preset_section)
bg_cfg_section_destroy(s->preset_section);
}
if(s->private_cfg_section)
bg_cfg_section_destroy(s->cfg_section);
}
void bg_dialog_destroy(bg_dialog_t * d)
{
destroy_section(&d->root_section);
gtk_widget_destroy(d->window);
free(d);
}
void bg_gtk_change_callback(GtkWidget * gw, gpointer data)
{
bg_gtk_widget_t * w = (bg_gtk_widget_t*)data;
w->funcs->set_value(w);
if(w->change_callback)
{
w->change_callback(w->change_callback_data,
w->info->name, &w->value);
if(w->funcs->apply_sub_params)
w->funcs->apply_sub_params(w);
w->change_callback(w->change_callback_data,
NULL, NULL);
}
}
void bg_gtk_change_callback_block(bg_gtk_widget_t * w, int block)
{
if(block)
{
if(w->callback_widget)
g_signal_handler_block(w->callback_widget, w->callback_id);
if(w->callback_widget_2)
g_signal_handler_block(w->callback_widget_2, w->callback_id_2);
}
else
{
if(w->callback_widget)
g_signal_handler_unblock(w->callback_widget, w->callback_id);
if(w->callback_widget_2)
g_signal_handler_unblock(w->callback_widget_2, w->callback_id_2);
}
}
void * bg_dialog_add_parent(bg_dialog_t *d, void * _parent, const char * label)
{
GtkTreeIter iter, parent_iter;
dialog_section_t * parent;
GtkTreeModel *model;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(d->treeview));
if(_parent)
{
parent = (dialog_section_t*)_parent;
section_to_iter(d, parent, &parent_iter);
gtk_tree_store_append(GTK_TREE_STORE(model), &iter, &parent_iter);
}
else
{
parent = &d->root_section;
gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL);
}
gtk_tree_store_set(GTK_TREE_STORE(model), &iter, COLUMN_NAME,
label, -1);
parent->children = realloc(parent->children,
sizeof(*(parent->children))*(parent->num_children+1));
parent->children[parent->num_children] = calloc(1, sizeof(*parent->children[parent->num_children]));
parent->children[parent->num_children]->parent = parent;
parent->num_children++;
return parent->children[parent->num_children-1];
}
void bg_dialog_set_plugin_registry(bg_dialog_t * d, bg_plugin_registry_t * plugin_reg)
{
d->plugin_reg = plugin_reg;
}
gmerlin-1.2.0~dfsg/lib/gtk/multiinfo.c 0000644 0001750 0001750 00000011354 11764363406 017614 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct
{
GtkWidget * window;
GtkWidget * close_button;
bg_gtk_textview_t * textview1;
bg_gtk_textview_t * textview2;
} multiwindow_t;
static void button_callback(GtkWidget * w, gpointer data)
{
multiwindow_t * win;
win = (multiwindow_t*)data;
bg_gtk_textview_destroy(win->textview1);
bg_gtk_textview_destroy(win->textview2);
gtk_widget_hide(win->window);
gtk_widget_destroy(win->window);
free(win);
}
static gboolean delete_callback(GtkWidget * w, GdkEventAny * event,
gpointer data)
{
button_callback(w, data);
return TRUE;
}
static multiwindow_t *
multiwindow_create(const char * title, const char * properties, const char * description)
{
GtkWidget * table;
GtkWidget * frame;
multiwindow_t * ret;
ret = calloc(1, sizeof(*ret));
ret->window = bg_gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(ret->window), GTK_WIN_POS_CENTER_ON_PARENT);
g_signal_connect(G_OBJECT(ret->window), "delete_event",
G_CALLBACK(delete_callback), (gpointer)ret);
gtk_window_set_title(GTK_WINDOW(ret->window), title);
/* Create close button */
ret->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
bg_gtk_widget_set_can_default(ret->close_button, TRUE);
g_signal_connect(G_OBJECT(ret->close_button), "clicked",
G_CALLBACK(button_callback), (gpointer)ret);
gtk_widget_show(ret->close_button);
/* Create texts */
ret->textview1 = bg_gtk_textview_create();
bg_gtk_textview_update(ret->textview1, properties);
ret->textview2 = bg_gtk_textview_create();
bg_gtk_textview_update(ret->textview2, description);
table = gtk_table_new(3, 1, 0);
gtk_table_set_row_spacings(GTK_TABLE(table), 5);
gtk_table_set_col_spacings(GTK_TABLE(table), 5);
gtk_container_set_border_width(GTK_CONTAINER(table), 5);
frame = gtk_frame_new("Properties");
gtk_container_add(GTK_CONTAINER(frame),
bg_gtk_textview_get_widget(ret->textview1));
gtk_widget_show(frame);
gtk_table_attach_defaults(GTK_TABLE(table),
frame, 0, 1, 0, 1);
frame = gtk_frame_new("Description");
gtk_container_add(GTK_CONTAINER(frame),
bg_gtk_textview_get_widget(ret->textview2));
gtk_widget_show(frame);
gtk_table_attach_defaults(GTK_TABLE(table),
frame, 0, 1, 1, 2);
gtk_table_attach(GTK_TABLE(table), ret->close_button, 0, 1, 2, 3,
GTK_SHRINK, GTK_SHRINK, 0, 0);
gtk_widget_show(table);
gtk_container_add(GTK_CONTAINER(ret->window), table);
return ret;
}
static void multiwindow_show(multiwindow_t * w, int modal)
{
gtk_window_set_modal(GTK_WINDOW(w->window), modal);
gtk_widget_grab_default(w->close_button);
gtk_widget_show(w->window);
}
void bg_gtk_multi_info_show(const bg_parameter_info_t * info, int i,
const char * translation_domain, GtkWidget * parent)
{
char * text;
multiwindow_t * win;
text = bg_sprintf(TR("Name:\t %s\nLabel:\t %s"),
info->multi_names[i],
(info->multi_labels?TR_DOM(info->multi_labels[i]):info->multi_names[i]));
win = multiwindow_create(info->long_name,
text, info->multi_descriptions ? info->multi_descriptions[i] :
TR("Not available"));
free(text);
parent = bg_gtk_get_toplevel(parent);
if(parent)
gtk_window_set_transient_for(GTK_WINDOW(win->window), GTK_WINDOW(parent));
multiwindow_show(win, 1);
}
gmerlin-1.2.0~dfsg/lib/gtk/cfg_color.c 0000644 0001750 0001750 00000027426 11764363406 017552 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include "gtk_dialog.h"
#include
typedef struct
{
GtkWidget * button;
GtkWidget * label;
GtkWidget * drawingarea;
GtkWidget * colorsel;
GdkDrawable * background_pixmap;
GdkColor gdk_color_1;
GdkColor gdk_color_2;
GdkGC * gc;
int has_alpha;
GdkColor color;
GdkColor last_color;
guint16 alpha;
} color_t;
static void button_callback(GtkWidget * w, gpointer data);
static void destroy(bg_gtk_widget_t * w)
{
color_t * priv = (color_t *)w->priv;
if(priv->colorsel)
gtk_widget_destroy(priv->colorsel);
if(priv->gc)
gdk_gc_unref(priv->gc);
if(priv->background_pixmap)
gdk_drawable_unref(priv->background_pixmap);
free(priv);
}
static const guint16 background_color_1[3] = { 0xc0c0, 0xc0c0, 0xc0c0 };
static const guint16 background_color_2[3] = { 0x8080, 0x8080, 0x8080 };
static void set_button(color_t * c)
{
GdkRectangle rect;
guint32 i_tmp;
int depth;
if(!c->drawingarea->window)
return;
if(c->has_alpha)
{
i_tmp = (c->color.red * c->alpha + background_color_1[0] * (0xffff - c->alpha)) >> 16;
c->gdk_color_1.red = (guint16)(i_tmp);
i_tmp = (c->color.green * c->alpha + background_color_1[1] * (0xffff - c->alpha)) >> 16;
c->gdk_color_1.green = (guint16)(i_tmp);
i_tmp = (c->color.blue * c->alpha + background_color_1[2] * (0xffff - c->alpha)) >> 16;
c->gdk_color_1.blue = (guint16)(i_tmp);
c->gdk_color_1.pixel = (c->gdk_color_1.red >> 8) << 16 |
(c->gdk_color_1.green >> 8) << 8 | (c->gdk_color_1.blue >> 8);
i_tmp = (c->color.red * c->alpha + background_color_2[0] * (0xffff - c->alpha)) >> 16;
c->gdk_color_2.red = (guint16)(i_tmp);
i_tmp = (c->color.green * c->alpha + background_color_2[1] * (0xffff - c->alpha)) >> 16;
c->gdk_color_2.green = (guint16)(i_tmp);
i_tmp = (c->color.blue * c->alpha + background_color_2[2] * (0xffff - c->alpha)) >> 16;
c->gdk_color_2.blue = (guint16)(i_tmp);
c->gdk_color_2.pixel = (c->gdk_color_2.red >> 8) << 16 |
(c->gdk_color_2.green >> 8) << 8 | (c->gdk_color_2.blue >> 8);
gdk_color_alloc(gdk_window_get_colormap(c->drawingarea->window),
&c->gdk_color_1);
gdk_color_alloc(gdk_window_get_colormap(c->drawingarea->window),
&c->gdk_color_2);
gdk_gc_set_foreground(c->gc, &c->gdk_color_1);
gdk_draw_rectangle(c->background_pixmap, c->gc, 1, 0, 0, 16, 16);
gdk_draw_rectangle(c->background_pixmap, c->gc, 1, 16, 16, 16, 16);
gdk_gc_set_foreground(c->gc, &c->gdk_color_2);
gdk_draw_rectangle(c->background_pixmap, c->gc, 1, 16, 0, 16, 16);
gdk_draw_rectangle(c->background_pixmap, c->gc, 1, 0, 16, 16, 16);
}
else
{
gdk_color_alloc(gdk_window_get_colormap(c->drawingarea->window),
&c->color);
gdk_gc_set_foreground(c->gc, &c->color);
gdk_draw_rectangle(c->background_pixmap, c->gc, 1, 0, 0, 32, 32);
gdk_window_set_background(c->drawingarea->window, &c->color);
}
gdk_window_get_geometry(c->drawingarea->window,
&rect.x, &rect.y, &rect.width, &rect.height, &depth);
rect.x = 0;
rect.y = 0;
gdk_window_invalidate_rect(c->drawingarea->window, &rect,
TRUE);
}
static void get_value(bg_gtk_widget_t * w)
{
color_t * priv;
priv = (color_t*)(w->priv);
priv->color.red = (guint16)(w->value.val_color[0]*65535.0);
priv->color.green = (guint16)(w->value.val_color[1]*65535.0);
priv->color.blue = (guint16)(w->value.val_color[2]*65535.0);
if(priv->has_alpha)
priv->alpha = (guint16)(w->value.val_color[3]*65535.0);
else
priv->alpha = 0xffff;
set_button(priv);
}
static void set_value(bg_gtk_widget_t * w)
{
color_t * priv;
priv = (color_t*)(w->priv);
w->value.val_color[0] = (float)priv->color.red/65535.0;
w->value.val_color[1] = (float)priv->color.green/65535.0;
w->value.val_color[2] = (float)priv->color.blue/65535.0;
if(priv->has_alpha)
w->value.val_color[3] = (float)priv->alpha/65535.0;
else
w->value.val_color[3] = 1.0;
}
static void attach(void * priv, GtkWidget * table,
int * row,
int * num_columns)
{
color_t * c = (color_t*)priv;
if(*num_columns < 2)
*num_columns = 2;
gtk_table_resize(GTK_TABLE(table), *row+1, *num_columns);
gtk_table_attach(GTK_TABLE(table), c->label,
0, 1, *row, *row+1, GTK_FILL, GTK_FILL, 0, 0);
gtk_table_attach(GTK_TABLE(table), c->button,
1, 2, *row, *row+1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
(*row)++;
}
static const gtk_widget_funcs_t funcs =
{
.get_value = get_value,
.set_value = set_value,
.destroy = destroy,
.attach = attach
};
static void realize_callback(GtkWidget * _w, gpointer data)
{
int x, y, w, h, depth;
color_t * priv = (color_t*)data;
// if(priv->has_alpha)
// {
gdk_window_get_geometry(priv->drawingarea->window,
&x, &y, &w, &h, &depth);
priv->background_pixmap = gdk_pixmap_new(priv->drawingarea->window,
32, 32, depth);
priv->gc = gdk_gc_new(priv->drawingarea->window);
// gdk_window_set_back_pixmap(priv->drawingarea->window,
// priv->background_pixmap, 0);
bg_gtk_set_widget_bg_pixmap(priv->drawingarea, priv->background_pixmap);
// }
set_button(priv);
}
static void changed_callback(GtkWidget * w, gpointer data)
{
bg_gtk_widget_t * wid = (bg_gtk_widget_t*)data;
color_t * priv = (color_t*)wid->priv;
gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->colorsel), &priv->color);
priv->alpha = gtk_color_selection_get_current_alpha(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->colorsel));
bg_gtk_change_callback(NULL, wid);
set_button(priv);
}
static gboolean delete_callback(GtkWidget * w, GdkEventAny * event,
gpointer data)
{
button_callback(w, data);
return TRUE;
}
static void button_callback(GtkWidget * w, gpointer data)
{
bg_gtk_widget_t * wid = (bg_gtk_widget_t*)data;
GtkWidget * toplevel;
color_t * priv = (color_t*)wid->priv;
if(w == priv->button)
{
/* Save last color */
memcpy(&priv->last_color, &priv->color, sizeof(priv->color));
if(!priv->colorsel)
{
priv->colorsel =
gtk_color_selection_dialog_new("Select a color");
if(wid->info->flags & BG_PARAMETER_SYNC)
{
g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->colorsel),
"color-changed", G_CALLBACK(changed_callback), wid);
}
gtk_window_set_modal(GTK_WINDOW(priv->colorsel), TRUE);
toplevel = bg_gtk_get_toplevel(priv->button);
if(toplevel)
gtk_window_set_transient_for(GTK_WINDOW(priv->colorsel),
GTK_WINDOW(toplevel));
g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->ok_button),
"clicked", G_CALLBACK(button_callback),
(gpointer)wid);
g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->cancel_button),
"clicked", G_CALLBACK(button_callback),
(gpointer)wid);
g_signal_connect(G_OBJECT(priv->colorsel),
"delete_event", G_CALLBACK(delete_callback),
(gpointer)wid);
gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->help_button);
if(priv->has_alpha)
gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->colorsel),
TRUE);
}
gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->colorsel),
&priv->color);
if(priv->has_alpha)
gtk_color_selection_set_current_alpha(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->colorsel),
priv->alpha);
gtk_widget_show(priv->colorsel);
gtk_main();
}
else if(priv->colorsel)
{
if(w == GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->ok_button)
{
gtk_main_quit();
gtk_widget_hide(priv->colorsel);
gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->colorsel), &priv->color);
priv->alpha = gtk_color_selection_get_current_alpha(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->colorsel));
set_button(priv);
}
else if((w == GTK_COLOR_SELECTION_DIALOG(priv->colorsel)->cancel_button) ||
(w == priv->colorsel))
{
gtk_main_quit();
gtk_widget_hide(priv->colorsel);
if(wid->info->flags & BG_PARAMETER_SYNC)
{
/* Restore last color */
memcpy(&priv->color, &priv->last_color, sizeof(priv->color));
wid->funcs->set_value(wid);
if(wid->change_callback)
wid->change_callback(wid->change_callback_data,
wid->info->name, &wid->value);
set_button(priv);
}
}
}
}
void bg_gtk_create_color_rgba(bg_gtk_widget_t * w,
const char * translation_domain)
{
color_t * priv;
bg_gtk_create_color_rgb(w, translation_domain);
priv = (color_t*)(w->priv);
priv->has_alpha = 1;
}
void bg_gtk_create_color_rgb(bg_gtk_widget_t * w,
const char * translation_domain)
{
color_t * priv = calloc(1, sizeof(*priv));
w->funcs = &funcs;
w->value.val_color[0] = 0.0;
w->value.val_color[1] = 0.0;
w->value.val_color[2] = 0.0;
w->value.val_color[3] = 1.0;
priv->button = gtk_button_new();
priv->drawingarea = gtk_drawing_area_new();
gtk_widget_set_size_request(priv->drawingarea,
priv->drawingarea->requisition.width,
16);
gtk_widget_set_events(priv->drawingarea, GDK_EXPOSURE_MASK);
g_signal_connect(G_OBJECT(priv->drawingarea),
"realize", G_CALLBACK(realize_callback),
(gpointer)priv);
g_signal_connect(G_OBJECT(priv->button),
"clicked", G_CALLBACK(button_callback),
(gpointer)w);
gtk_widget_show(priv->drawingarea);
gtk_container_add(GTK_CONTAINER(priv->button), priv->drawingarea);
if(w->info->help_string)
{
bg_gtk_tooltips_set_tip(priv->button, w->info->help_string,
translation_domain);
}
gtk_widget_show(priv->button);
priv->label = gtk_label_new(TR_DOM(w->info->long_name));
gtk_misc_set_alignment(GTK_MISC(priv->label), 0.0, 0.5);
gtk_widget_show(priv->label);
w->priv = priv;
}
gmerlin-1.2.0~dfsg/lib/gtk/fileentry.c 0000644 0001750 0001750 00000014525 11764363406 017612 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
struct bg_gtk_file_entry_s
{
GtkWidget * entry;
GtkWidget * button;
int is_dir;
GtkWidget * fileselect;
void (*name_changed_callback)(bg_gtk_file_entry_t *,
void * data);
void * name_changed_callback_data;
};
static void
filesel_callback(GtkWidget *chooser,
gint response_id,
gpointer data)
{
char * tmp_string;
bg_gtk_file_entry_t * priv = (bg_gtk_file_entry_t*)data;
if(response_id == GTK_RESPONSE_OK)
{
if(priv->is_dir)
tmp_string = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(priv->fileselect));
else
tmp_string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->fileselect));
gtk_entry_set_text(GTK_ENTRY(priv->entry), tmp_string);
g_free(tmp_string);
}
gtk_widget_hide(priv->fileselect);
gtk_main_quit();
}
static gboolean delete_callback(GtkWidget * w, GdkEventAny * event,
gpointer data)
{
filesel_callback(w, GTK_RESPONSE_CANCEL, data);
return TRUE;
}
static void button_callback(GtkWidget * w, gpointer data)
{
bg_gtk_file_entry_t * priv = (bg_gtk_file_entry_t*)data;
GtkWidget * toplevel;
if(w == priv->button)
{
if(!priv->fileselect)
{
toplevel = bg_gtk_get_toplevel(w);
if(priv->is_dir)
{
priv->fileselect =
gtk_file_chooser_dialog_new(TR("Select a directory"),
GTK_WINDOW(toplevel),
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_OK,
GTK_RESPONSE_OK,
NULL);
}
else
{
priv->fileselect =
gtk_file_chooser_dialog_new(TR("Select a file"),
GTK_WINDOW(toplevel),
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_OK,
GTK_RESPONSE_OK,
NULL);
}
gtk_window_set_modal(GTK_WINDOW(priv->fileselect), TRUE);
g_signal_connect(priv->fileselect, "response",
G_CALLBACK(filesel_callback),
(gpointer)priv);
g_signal_connect(G_OBJECT(priv->fileselect),
"delete_event", G_CALLBACK(delete_callback),
(gpointer)priv);
}
if(priv->is_dir)
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(priv->fileselect),
gtk_entry_get_text(GTK_ENTRY(priv->entry)));
else
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(priv->fileselect),
gtk_entry_get_text(GTK_ENTRY(priv->entry)));
gtk_widget_show(priv->fileselect);
gtk_main();
}
else if(w == priv->entry)
{
priv->name_changed_callback(priv, priv->name_changed_callback_data);
}
}
bg_gtk_file_entry_t * bg_gtk_file_entry_create(int is_dir,
void (*name_changed_callback)(bg_gtk_file_entry_t *,
void * data),
void * name_changed_callback_data,
const char * help_string,
const char * translation_domain)
{
bg_gtk_file_entry_t * priv = calloc(1, sizeof(*priv));
priv->is_dir = is_dir;
priv->name_changed_callback = name_changed_callback;
priv->name_changed_callback_data = name_changed_callback_data;
priv->entry = gtk_entry_new();
if(help_string)
{
bg_gtk_tooltips_set_tip(priv->entry, help_string, translation_domain);
}
if(priv->name_changed_callback)
g_signal_connect(G_OBJECT(priv->entry), "changed",
G_CALLBACK(button_callback),
(gpointer)priv);
gtk_widget_show(priv->entry);
priv->button = gtk_button_new_with_label(TR("Browse..."));
g_signal_connect(G_OBJECT(priv->button),
"clicked", G_CALLBACK(button_callback),
(gpointer)priv);
gtk_widget_show(priv->button);
return priv;
}
void bg_gtk_file_entry_destroy(bg_gtk_file_entry_t * f)
{
if(f->fileselect)
gtk_widget_destroy(f->fileselect);
free(f);
}
const char * bg_gtk_file_entry_get_filename(bg_gtk_file_entry_t * f)
{
return gtk_entry_get_text(GTK_ENTRY(f->entry));
}
void bg_gtk_file_entry_set_filename(bg_gtk_file_entry_t * f, const char * s)
{
if(!s || (*s == '\0'))
{
gtk_entry_set_text(GTK_ENTRY(f->entry), "");
return;
}
gtk_entry_set_text(GTK_ENTRY(f->entry), s);
}
GtkWidget * bg_gtk_file_entry_get_entry(bg_gtk_file_entry_t * f)
{
return f->entry;
}
GtkWidget * bg_gtk_file_entry_get_button(bg_gtk_file_entry_t *f)
{
return f->button;
}
gmerlin-1.2.0~dfsg/lib/gtk/cfg_position.c 0000644 0001750 0001750 00000011752 11764363406 020273 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include "gtk_dialog.h"
#include
typedef struct
{
GtkWidget * label;
GtkWidget * box;
GtkWidget * spinbutton_x;
GtkWidget * spinbutton_y;
GtkObject * adj_x;
GtkObject * adj_y;
} spinbutton_t;
/*
typedef enum
{
GTK_EXPAND = 1 << 0,
GTK_SHRINK = 1 << 1,
GTK_FILL = 1 << 2
} GtkAttachOptions;
*/
static void
destroy(bg_gtk_widget_t * w)
{
spinbutton_t * s = w->priv;
free(s);
}
static void
get_value(bg_gtk_widget_t * w)
{
spinbutton_t * s = w->priv;
// Need to save this because the callback of the x coordinate
// will change w->value
float tmp = w->value.val_pos[1];
gtk_spin_button_set_value(GTK_SPIN_BUTTON(s->spinbutton_x),
w->value.val_pos[0]);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(s->spinbutton_y),
tmp);
}
static void
set_value(bg_gtk_widget_t * w)
{
spinbutton_t * s = w->priv;
w->value.val_pos[0] =
gtk_spin_button_get_value(GTK_SPIN_BUTTON(s->spinbutton_x));
w->value.val_pos[1] =
gtk_spin_button_get_value(GTK_SPIN_BUTTON(s->spinbutton_y));
}
static void
attach(void * priv, GtkWidget * table, int * row, int * num_columns)
{
spinbutton_t * s = priv;
if(*num_columns < 2)
*num_columns = 2;
gtk_table_resize(GTK_TABLE(table), *row + 1, *num_columns);
gtk_table_attach(GTK_TABLE(table), s->label,
0, 1, *row, *row+1, GTK_FILL, GTK_SHRINK, 0, 0);
gtk_table_attach(GTK_TABLE(table), s->box,
1, 2, *row, *row+1, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
*row += 1;
}
static const gtk_widget_funcs_t pos_funcs =
{
.get_value = get_value,
.set_value = set_value,
.destroy = destroy,
.attach = attach
};
void
bg_gtk_create_position(bg_gtk_widget_t * w,
const char * translation_domain)
{
GtkWidget * label;
spinbutton_t * s = calloc(1, sizeof(*s));
w->funcs = &pos_funcs;
s->label = gtk_label_new(TR_DOM(w->info->long_name));
gtk_widget_show(s->label);
gtk_misc_set_alignment(GTK_MISC(s->label), 0.0, 0.5);
s->adj_x = gtk_adjustment_new(0.0, 0.0, 1.0,
0.01, 0.0, 0.0);
s->adj_y = gtk_adjustment_new(0.0, 0.0, 1.0,
0.01, 0.0, 0.0);
s->spinbutton_x =
gtk_spin_button_new(GTK_ADJUSTMENT(s->adj_x), 0.1, 0);
s->spinbutton_y =
gtk_spin_button_new(GTK_ADJUSTMENT(s->adj_y), 0.1, 0);
if(w->info->flags & BG_PARAMETER_SYNC)
{
w->callback_id =
g_signal_connect(G_OBJECT(s->spinbutton_x), "value-changed",
G_CALLBACK(bg_gtk_change_callback), (gpointer)w);
w->callback_widget = s->spinbutton_x;
w->callback_id_2 =
g_signal_connect(G_OBJECT(s->spinbutton_y), "value-changed",
G_CALLBACK(bg_gtk_change_callback), (gpointer)w);
w->callback_widget_2 = s->spinbutton_y;
}
if(w->info->help_string)
{
bg_gtk_tooltips_set_tip(s->spinbutton_x,
w->info->help_string, translation_domain);
bg_gtk_tooltips_set_tip(s->spinbutton_y,
w->info->help_string, translation_domain);
}
gtk_widget_show(s->spinbutton_x);
gtk_widget_show(s->spinbutton_y);
gtk_widget_show(s->label);
s->box = gtk_hbox_new(0, 5);
label = gtk_label_new(TR("X"));
gtk_widget_show(label);
gtk_box_pack_start(GTK_BOX(s->box), label, FALSE, FALSE, 0);
bg_gtk_box_pack_start_defaults(GTK_BOX(s->box), s->spinbutton_x);
label = gtk_label_new(TR("Y"));
gtk_widget_show(label);
gtk_box_pack_start(GTK_BOX(s->box), label, FALSE, FALSE, 0);
bg_gtk_box_pack_start_defaults(GTK_BOX(s->box), s->spinbutton_y);
gtk_widget_show(s->box);
w->priv = s;
bg_gtk_change_callback_block(w, 1);
gtk_spin_button_set_digits(GTK_SPIN_BUTTON(s->spinbutton_x),
w->info->num_digits);
gtk_spin_button_set_digits(GTK_SPIN_BUTTON(s->spinbutton_y),
w->info->num_digits);
bg_gtk_change_callback_block(w, 0);
}
gmerlin-1.2.0~dfsg/lib/gtk/plugininfo.c 0000644 0001750 0001750 00000017477 11764363406 017774 0 ustar alessio alessio /*****************************************************************
* gmerlin - a general purpose multimedia framework and applications
*
* Copyright (c) 2001 - 2011 Members of the Gmerlin project
* gmerlin-general@lists.sourceforge.net
* http://gmerlin.sourceforge.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* *****************************************************************/
#include
#include