Files
veejay/veejay-current/gveejay-reloaded/multitrack.c
2006-01-25 22:03:54 +00:00

1401 lines
37 KiB
C

/* Gveejay Reloaded - graphical interface for VeeJay
* (C) 2002-2005 Niels Elburg <nelburg@looze.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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <libvjnet/vj-client.h>
#include <libvjmsg/vj-common.h>
#include <veejay/vims.h>
#include <gtk/gtk.h>
#include <string.h>
#include <gdk/gdkx.h>
#include <glib.h>
#include <gdk/gdk.h>
#include "sequence.h"
#include "tracksources.h"
#define SEQ_BUTTON_CLOSE 0
#define SEQ_BUTTON_RULE 1
#include <assert.h>
#include <gveejay-reloaded/multitrack.h>
#include <gveejay-reloaded/common.h>
#include <gveejay-reloaded/utils.h>
#include <gveejay-reloaded/widgets/gtktimeselection.h>
#define WTIME 50000
#define G_ERRORCHECK_MUTEXES 1
#define G_DEBUG_LOCKS 1
G_LOCK_DEFINE(mt_lock);
#define __MAX_TRACKS 64
typedef struct
{
GtkWidget *event_box;
GtkWidget *frame;
GtkWidget *main_vbox;
GtkWidget *panel;
GtkWidget *hbox;
GtkWidget *area;
GtkWidget *sub_frame;
GtkWidget *sub_hbox;
GtkWidget *toggle;
GtkWidget *buttons[8];
GtkWidget *icons[8];
GtkWidget *button_box;
GtkWidget *timeline_;
GtkWidget *labels_[4];
GtkWidget *sliders_[4];
GtkWidget *button_box2;
GtkWidget *buttons2[8];
void *tracks;
gint dim[2];
} sequence_view_t;
typedef struct
{
int num;
sequence_view_t *view;
void *sequence;
char *hostname;
int port_num;
void *backlink;
int preview;
int active;
int used;
gint timeout;
int status_lock;
int history[4][18];
int status_cache[18];
char *tracks[__MAX_TRACKS];
} mt_priv_t;
typedef struct
{
mt_priv_t *pt[__MAX_TRACKS+1];
int stop_mt;
GThread *thread;
} all_priv_t;
typedef struct
{
GtkWidget *main_window;
GtkWidget *main_box;
GtkWidget *status_bar;
GtkWidget *scroll;
void *data;
int selected;
int sensitive;
int quit;
} multitracker_t;
static void (*img_cb)(GdkPixbuf *p);
static void (*gui_cb)(int, char*, int);
static int mt_new_connection_dialog(multitracker_t *mt, char *hostname,int len, int *port_num);
static void add_buttons( mt_priv_t *p, sequence_view_t *seqv , GtkWidget *w);
static void add_buttons2( mt_priv_t *p, sequence_view_t *seqv , GtkWidget *w);
static int num_tracks_active( multitracker_t * mt );
void *mt_preview( gpointer user_data );
static int preview_width_ = 0;
static int preview_height_ = 0;
static volatile int MAX_TRACKS = 4;
static volatile int LAST_TRACK = 0;
void multitrack_preview_master(void *data, int status)
{
multitracker_t *mt = (multitracker_t*) data;
all_priv_t *pt = (all_priv_t*) mt->data;
mt_priv_t *last = pt->pt[LAST_TRACK];
if(status == last->preview)
return;
G_LOCK(mt_lock);
last->preview = status;
G_UNLOCK(mt_lock);
}
static void status_print(multitracker_t *mt, const char format[], ... )
{
char buf[1024];
va_list args;
bzero(buf,1024);
va_start(args,format);
vsnprintf( buf,sizeof(buf), format, args );
int nr,nw;
gchar *text = g_locale_to_utf8( buf, -1, &nr, &nw, NULL );
text[strlen(text)-1] = '\0';
gtk_statusbar_push( GTK_STATUSBAR(mt->status_bar), 0, text);
g_free(text);
va_end(args);
}
void multitrack_sync_start(void *data)
{
multitracker_t *mt = (multitracker_t*)data;
all_priv_t *pt = (all_priv_t*)mt->data;
gint i;
G_LOCK(mt_lock);
for( i = 0;i < MAX_TRACKS; i ++ )
{
mt_priv_t *p = pt->pt[i];
if(p->active)
{
veejay_sequence_send( p->sequence,VIMS_VIDEO_PLAY_STOP, NULL,NULL);
veejay_sequence_send( p->sequence,VIMS_VIDEO_GOTO_START , NULL ,NULL);
veejay_sequence_send(p->sequence, VIMS_VIDEO_PLAY_FORWARD, NULL ,NULL);
}
}
G_UNLOCK(mt_lock);
}
void multitrack_sync_simple_cmd(void *data, int vims_id, int value)
{
multitracker_t *mt = (multitracker_t*)data;
all_priv_t *pt = (all_priv_t*)mt->data;
gint i;
G_LOCK(mt_lock);
for( i = 0;i < MAX_TRACKS; i ++ )
{
mt_priv_t *p = pt->pt[i];
if(p->active)
veejay_sequence_send( p->sequence, vims_id, (value > 0 ? "%d": NULL),
(value > 0 ? value :NULL));
}
G_UNLOCK(mt_lock);
}
int * sequence_get_track_status(void *priv)
{
mt_priv_t *p = (mt_priv_t*) priv;
multitracker_t *mt = (multitracker_t*) p->backlink;
all_priv_t *a = (all_priv_t*) mt->data;
gint i;
int *result = (int*) malloc(sizeof(int) * MAX_TRACKS );
for( i = 0;i < MAX_TRACKS ; i ++ )
{
mt_priv_t *q = a->pt[i];
result[i] = 0; // reset it
if(q->active)
{
// find in tracklist
gint j;
gint num = -1;
for( j = 0; j < MAX_TRACKS; j ++ )
{
if(p->tracks[j])
{
char hostname[255];
int port_num = 0;
char *str = p->tracks[j];
int tag_id = 0;
if(sscanf(str, "%s %d %d", hostname, &port_num, &tag_id ))
{
if(strncasecmp( hostname, q->hostname,strlen(hostname)) == 0 && port_num ==
q->port_num )
{
num = i;
break;
}
}
}
}
if( num >= 0 )
result[i] = 1;
}
}
return result;
}
static void seq_gotostart(GtkWidget *w, gpointer user_data )
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(p->active)
veejay_sequence_send( p->sequence,VIMS_VIDEO_GOTO_START , NULL ,NULL);
}
static void seq_reverse(GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(p->active)
veejay_sequence_send(p->sequence, VIMS_VIDEO_PLAY_BACKWARD, NULL ,NULL);
}
static void seq_pause(GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(p->active)
veejay_sequence_send(p->sequence, VIMS_VIDEO_PLAY_STOP, NULL ,NULL);
}
static void seq_play( GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(p->active)
veejay_sequence_send(p->sequence, VIMS_VIDEO_PLAY_FORWARD, NULL ,NULL);
}
static void seq_gotoend(GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(p->active)
veejay_sequence_send(p->sequence, VIMS_VIDEO_GOTO_END , NULL,NULL );
}
static void seq_speeddown(GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
int n = p->status_cache[SAMPLE_SPEED];
if( n < 0 ) n += 1;
if( n > 0 ) n -= 1;
if(p->active)
veejay_sequence_send( p->sequence, VIMS_VIDEO_SET_SPEED, "%d", n );
}
static void seq_speedup(GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
int n = p->status_cache[SAMPLE_SPEED];
if( n < 0 ) n -= 1;
if( n > 0 ) n += 1;
if(p->active)
veejay_sequence_send( p->sequence, VIMS_VIDEO_SET_SPEED, "%d", n );
}
static void seq_prevframe(GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(p->active)
veejay_sequence_send( p->sequence, VIMS_VIDEO_PREV_FRAME, NULL,NULL );
}
static void seq_nextframe(GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(p->active)
veejay_sequence_send( p->sequence, VIMS_VIDEO_SKIP_FRAME, NULL,NULL );
}
static void seq_speed( GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(p->status_lock)
return;
gdouble value = GTK_ADJUSTMENT( GTK_RANGE(w)->adjustment )->value;
gint speed = (gint) value;
if(p->active && speed != 0)
veejay_sequence_send( p->sequence, VIMS_VIDEO_SET_SPEED, "%d", speed );
}
static void seq_opacity( GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(p->status_lock)
return;
gdouble value = GTK_ADJUSTMENT( GTK_RANGE(w)->adjustment )->value;
gint opacity = (gint)( value * 255.0);
if(p->active)
veejay_sequence_send( p->sequence, VIMS_CHAIN_MANUAL_FADE, "%d %d",0, opacity );
}
#define FIRST_ROW_END 5
static struct
{
const char *name;
int vims_id;
const char *file;
void (*f)();
} button_template_t[] =
{
{ "button_gotostart", VIMS_VIDEO_GOTO_START, "button_gotostart.png", seq_gotostart },
{ "button_reverse", VIMS_VIDEO_PLAY_BACKWARD, "button_reverse.png" , seq_reverse },
{ "button_pauseplay", VIMS_VIDEO_PLAY_STOP, "button_pause.png", seq_pause},
{ "button_play", VIMS_VIDEO_PLAY_FORWARD, "button_play.png", seq_play },
{ "button_gotoend", VIMS_VIDEO_GOTO_END, "button_gotoend.png",seq_gotoend },
{ "button_speeddown", VIMS_VIDEO_SET_SPEED, "button_down.png", seq_speeddown },
{ "button_speedup", VIMS_VIDEO_SET_SPEED, "button_up.png", seq_speedup },
{ "button_prevframe", VIMS_VIDEO_PREV_FRAME, "button_prev.png", seq_prevframe },
{ "button_nextframe", VIMS_VIDEO_SKIP_FRAME, "button_skip.png", seq_nextframe },
{ NULL , 0 , NULL },
};
static void add_buttons( mt_priv_t *p, sequence_view_t *seqv , GtkWidget *w)
{
int i;
for( i = 0; i < FIRST_ROW_END;i ++ )
{
char path[1024];
bzero(path,1024);
get_gd(path,NULL, button_template_t[i].file );
seqv->icons[i] = gtk_image_new_from_file( path );
seqv->buttons[i] = gtk_button_new_with_label(" ");
gtk_widget_set_size_request( seqv->icons[i],24,20 );
gtk_button_set_image( GTK_BUTTON(seqv->buttons[i]), seqv->icons[i] );
gtk_widget_set_size_request( seqv->buttons[i],24,20 );
// gtk_container_add( GTK_CONTAINER( w ), seqv->buttons[i] );
gtk_box_pack_start( GTK_BOX(w), seqv->buttons[i], TRUE,TRUE, 0 );
g_signal_connect( G_OBJECT( seqv->buttons[i] ), "clicked", G_CALLBACK( button_template_t[i].f),
(gpointer*)p );
gtk_widget_show( seqv->buttons[i] );
}
// gtk_widget_set_sensitive( w, FALSE );
}
static void add_buttons2( mt_priv_t *p, sequence_view_t *seqv , GtkWidget *w)
{
int i;
for( i = FIRST_ROW_END; button_template_t[i].name != NULL ;i ++ )
{
char path[1024];
bzero(path,1024);
get_gd(path,NULL, button_template_t[i].file );
seqv->icons[i] = gtk_image_new_from_file( path );
seqv->buttons2[i] = gtk_button_new_with_label(" ");
gtk_widget_set_size_request( seqv->icons[i],24,20 );
gtk_button_set_image( GTK_BUTTON(seqv->buttons2[i]), seqv->icons[i] );
gtk_widget_set_size_request( seqv->buttons2[i],24,20 );
gtk_box_pack_start( GTK_BOX(w), seqv->buttons2[i], TRUE,TRUE, 0 );
g_signal_connect( G_OBJECT( seqv->buttons2[i] ), "clicked", G_CALLBACK( button_template_t[i].f),
(gpointer*)p );
gtk_widget_show( seqv->buttons2[i] );
}
// gtk_widget_set_sensitive( w, FALSE );
}
static void update_timeline( mt_priv_t *p, gint total, gint current )
{
timeline_set_length( GTK_WIDGET(p->view->timeline_),
(gdouble) total,
(gdouble) current );
char *total = format_time( total,25.0f );
gtk_label_set_text( p->view->labels_[1], total );
g_free(total);
}
static void update_pos( mt_priv_t *p, gint current )
{
timeline_set_pos( p->view->timeline_, current );
char *now = format_time( current ,25.0f);
gtk_label_set_text( p->view->labels_[0], now );
g_free(now);
}
static void update_speed( mt_priv_t *p, gint speed )
{
gtk_adjustment_set_value( GTK_ADJUSTMENT( GTK_RANGE( p->view->sliders_[0] )->adjustment), (gdouble) speed );
}
static gboolean update_track_list( mt_priv_t *p )
{
if(p->active)
{
int len = 0;
gchar *buf = veejay_sequence_get_track_list( p->sequence, 5, &len );
int i = 0;
int it = 0;
char *ptr = buf;
// clear existing buffer
for( i = 0; i < MAX_TRACKS ; i ++ )
{
if( p->tracks[i] )
free(p->tracks[i]);
p->tracks[i] = NULL;
}
if( !buf )
return TRUE;
i = 0;
while( i < len )
{
int dlen = 0;
char tmp_len[4];
bzero( tmp_len, 4 );
strncpy( tmp_len, ptr , 3 );
sscanf( tmp_len , "%d", &dlen );
if(dlen>0)
{
ptr += 3;
p->tracks[it] = strndup( ptr, dlen );
it++;
ptr += dlen;
}
i += ( 3 + dlen );
}
free( buf );
return TRUE;
}
return FALSE;
}
static void playmode_sensitivity( mt_priv_t *p, gint pm )
{
if( pm == MODE_STREAM )
{
gtk_widget_set_sensitive( GTK_WIDGET( p->view->button_box2 ), FALSE );
gtk_widget_set_sensitive( GTK_WIDGET( p->view->button_box ), FALSE );
gtk_widget_set_sensitive( GTK_WIDGET( p->view->sliders_[0] ), FALSE );
gtk_widget_set_sensitive( GTK_WIDGET( p->view->timeline_ ), FALSE );
gtk_widget_set_sensitive( GTK_WIDGET( p->view->sliders_[1] ), TRUE );
}
else
{
if( pm == MODE_SAMPLE || pm == MODE_PLAIN )
{
gtk_widget_set_sensitive( GTK_WIDGET( p->view->button_box2 ), TRUE );
gtk_widget_set_sensitive( GTK_WIDGET( p->view->button_box ), TRUE );
gtk_widget_set_sensitive( GTK_WIDGET( p->view->sliders_[0] ), TRUE );
gtk_widget_set_sensitive( GTK_WIDGET( p->view->timeline_ ), TRUE );
}
if( pm == MODE_SAMPLE )
gtk_widget_set_sensitive( GTK_WIDGET( p->view->sliders_[1] ), TRUE );
else
gtk_widget_set_sensitive( GTK_WIDGET( p->view->sliders_[1] ), FALSE );
}
}
static void update_widgets(int *status, mt_priv_t *p, int pm)
{
int *h = p->history[pm];
gdk_threads_enter();
playmode_sensitivity( p, pm );
if( pm == MODE_SAMPLE || pm == MODE_PLAIN )
{
// if( h[TOTAL_FRAMES] != status[TOTAL_FRAMES] )
// update_timeline( p, status[TOTAL_FRAMES], status[FRAME_NUM] );
if( h[FRAME_NUM] != status[FRAME_NUM] )
update_pos( p, status[FRAME_NUM] );
if( h[SAMPLE_SPEED] != status[SAMPLE_SPEED] )
update_speed( p, status[SAMPLE_SPEED] );
}
if( h[TOTAL_SLOTS] != status[TOTAL_SLOTS])
{
if(update_track_list( p ))
update_track_view( MAX_TRACKS, get_track_tree( p->view->tracks ), (void*)p );
}
gdk_threads_leave();
}
static gboolean update_sequence_widgets( gpointer data )
{
mt_priv_t *p = (mt_priv_t*) data;
char status[100];
int array[18];
veejay_get_status( p->sequence, status );
int n = status_to_arr( status, array );
if( n != 17 )
{
printf("status error\n");
}
p->status_lock = 1;
int pm = array[PLAY_MODE];
int i;
for( i = 0; i < 18; i ++ )
p->status_cache[i] = array[i];
update_widgets(array, p, pm);
int *his = p->history[ pm ];
for( i = 0; i < 18; i ++ )
his[i] = array[i];
p->status_lock = 0;
return TRUE;
}
static int free_slot(void *data)
{
int i = 0;
all_priv_t *pt = (all_priv_t*)data;
for( i = 0; i < MAX_TRACKS; i ++ )
{
mt_priv_t *p = pt->pt[i];
if(p->used == 0)
return i;
}
return -1;
}
static void store_data( void *data, int index,char *hostname, int port_num )
{
all_priv_t *pt = (all_priv_t*) data;
if(pt->pt[index]->hostname) free(pt->pt[index]->hostname);
pt->pt[index]->hostname = strdup(hostname);
pt->pt[index]->port_num = port_num;
}
static void free_data( mt_priv_t *p )
{
sequence_view_t *v = p->view;
void *b = p->backlink;
int n = p->num;
if( p->timeout )
g_source_remove( p->timeout );
p->timeout = 0;
veejay_abort_sequence( p->sequence );
if( p->hostname )
free( p->hostname );
memset( p, 0,sizeof(mt_priv_t));
p->view = v;
p->backlink = b;
p->num = n;
}
static void delete_data( void *data, int index )
{
all_priv_t *pt = (all_priv_t*)data;
char track_title[100];
mt_priv_t *p = pt->pt[index];
if(p->used)
{
// gtk_widget_set_sensitive( GTK_WIDGET(p->view->toggle), FALSE );
gtk_widget_set_sensitive( GTK_WIDGET(p->view->panel),FALSE);
set_logo( p->view->area );
free_data( p );
}
}
static void set_logo(GtkWidget *area)
{
char path[1024];
bzero(path,1024);
get_gd(path,NULL, "veejay-logo.png");
GdkPixbuf *buf = gdk_pixbuf_new_from_file( path,NULL );
GdkPixbuf *buf2 = gdk_pixbuf_scale_simple( buf,preview_width_,preview_height_, GDK_INTERP_BILINEAR );
gtk_image_set_from_pixbuf( GTK_IMAGE(area), buf2 );
gdk_pixbuf_unref( buf );
gdk_pixbuf_unref( buf2 );
}
static sequence_view_t *get_sequence_view(void *data,int index)
{
all_priv_t *pt = (all_priv_t*)data;
return pt->pt[index]->view;
}
static int mt_new_connection_dialog(multitracker_t *mt, char *hostname,int len, int *port_num)
{
GtkWidget *dialog = gtk_dialog_new_with_buttons(
"New Track",
GTK_WINDOW( mt->main_window ),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CANCEL,
GTK_RESPONSE_REJECT,
GTK_STOCK_OK,
GTK_RESPONSE_ACCEPT,
NULL );
GtkWidget *text_entry = gtk_entry_new();
gtk_entry_set_text( GTK_ENTRY(text_entry), "localhost" );
gtk_editable_set_editable( GTK_ENTRY(text_entry), TRUE );
gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_REJECT );
gtk_window_set_resizable( GTK_WINDOW( dialog ), FALSE );
gint base = 3490;
gint dport = base + (1000 * num_tracks_active( mt ));
GtkObject *adj = gtk_adjustment_new( dport,1024,65535,5,10,0);
GtkWidget *num_entry = gtk_spin_button_new( adj, 5.0, 0 );
GtkWidget *text_label = gtk_label_new( "Hostname" );
GtkWidget *num_label = gtk_label_new( "Port" );
g_signal_connect( G_OBJECT(dialog), "response",
G_CALLBACK( gtk_widget_hide ), G_OBJECT( dialog ) );
GtkWidget *vbox = gtk_vbox_new( FALSE, 4 );
gtk_container_add( GTK_CONTAINER( vbox ), text_label );
gtk_container_add( GTK_CONTAINER( vbox ), text_entry );
gtk_container_add( GTK_CONTAINER( vbox ), num_label );
gtk_container_add( GTK_CONTAINER( vbox ), num_entry );
gtk_container_add( GTK_CONTAINER( GTK_DIALOG(dialog)->vbox), vbox );
gtk_widget_show_all( dialog );
gint res = gtk_dialog_run( GTK_DIALOG(dialog) );
if( res == GTK_RESPONSE_ACCEPT )
{
gchar *host = gtk_entry_get_text( GTK_ENTRY( text_entry ) );
gint port = gtk_spin_button_get_value( GTK_SPIN_BUTTON(num_entry ));
strncpy( hostname, host, len );
*port_num = port;
}
gtk_widget_destroy( dialog );
return res;
}
void setup_geometry( int w, int h, int n_tracks )
{
LAST_TRACK = n_tracks + 1;
MAX_TRACKS = n_tracks;
}
void *multitrack_new( void (*f)(int,char*,int), void (*g)(GdkPixbuf *),GtkWidget *win, GtkWidget *box ,GtkWidget *msg, gint max_w, gint max_h, GtkWidget *main_preview_area)
{
max_w = 352;
max_h = 288;
multitracker_t *mt = NULL;
all_priv_t *pt = NULL;
mt = (multitracker_t*) malloc(sizeof(multitracker_t));
memset( mt, 0, sizeof(multitracker_t));
mt->main_window = win;
mt->main_box = box;
mt->status_bar = msg;
gui_cb = f;
img_cb = g;
pt = (all_priv_t*) malloc(sizeof(all_priv_t));
memset(pt,0,sizeof(pt));
mt->scroll = gtk_scrolled_window_new(NULL,NULL);
preview_width_ = max_w / 2;
preview_height_ = max_h / 2;
gdouble mw = preview_width_ * 5;
gtk_widget_set_size_request(mt->scroll,(int)mw,max_h*1.2);
gtk_container_set_border_width(GTK_CONTAINER(mt->scroll),2);
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(mt->scroll),GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
GtkWidget *table = gtk_table_new( 1, MAX_TRACKS, FALSE );
gtk_box_pack_start( GTK_BOX( mt->main_box ), mt->scroll , FALSE,FALSE, 0 );
gtk_widget_show(mt->scroll);
int c = 0;
for( c = 0; c < MAX_TRACKS; c ++ )
{
mt_priv_t *p = (mt_priv_t*) malloc(sizeof( mt_priv_t));
memset( p, 0, sizeof(mt_priv_t));
p->num = c;
p->view = new_sequence_view( p,0,0,0, main_preview_area );
p->backlink = (void*) mt;
pt->pt[c] = p;
gtk_table_attach_defaults( table, p->view->event_box, c, c+1, 0, 1 );
}
max_w = 352;
max_h = 288;
gtk_scrolled_window_add_with_viewport(
GTK_SCROLLED_WINDOW( mt->scroll ), table );
gtk_widget_show(table);
mt_priv_t *lt = (mt_priv_t*) malloc(sizeof( mt_priv_t));
memset( lt, 0, sizeof(mt_priv_t));
lt->num = LAST_TRACK;
lt->backlink = (void*) mt;
lt->view = new_sequence_view( lt, 0, 0,1 , main_preview_area);
pt->pt[LAST_TRACK] = lt;
gtk_container_add( GTK_CONTAINER( mt->main_box ), lt->view->event_box );
mt->data = (void*) pt;
GError *err = NULL;
pt->thread = g_thread_create( (GThreadFunc) mt_preview, (gpointer*) mt ,FALSE,&err);
if(!pt->thread)
{
status_print( mt, "%s while starting image thread", err->message);
return NULL;
}
return (void*) mt;
}
void multitrack_open(void *data)
{
multitracker_t *mt = (multitracker_t*) data;
G_LOCK(mt_lock);
mt->sensitive = 1;
G_UNLOCK(mt_lock);
}
void multitrack_close( void *data )
{
multitracker_t *mt = (multitracker_t*) data;
G_LOCK(mt_lock);
mt->sensitive = 0;
G_UNLOCK(mt_lock);
}
void multitrack_quit( void *data )
{
multitracker_t *mt = (multitracker_t*) data;
G_LOCK(mt_lock);
mt->quit = 1;
G_UNLOCK(mt_lock);
all_priv_t *a = (all_priv_t*) mt->data;
int i;
for( i = 0; i <MAX_TRACKS; i ++ )
{
mt_priv_t *p = a->pt[i];
if(p) free_data(p);
}
}
int multitrack_add_track( void *data )
{
multitracker_t *mt = (multitracker_t*) data;
if(mt)
{
// open input dialog, query hostname and postnum etc
char *hostname = g_new0(char , 100 );
int port_num = 0;
if( mt_new_connection_dialog( mt, hostname, 100, &port_num ) == GTK_RESPONSE_ACCEPT )
{
all_priv_t *pt = (all_priv_t*)mt->data;
int track = free_slot( mt->data );
if( track == -1 )
{
status_print(mt, "No free Tracks available!");
g_free(hostname);
return 0;
}
int i;
int found = 0;
for( i = 0; i < MAX_TRACKS ; i ++ )
{
mt_priv_t *p = pt->pt[i];
if(p->active)
{
if(strncasecmp(hostname,p->hostname,strlen(hostname)) == 0 && port_num == p->port_num )
{
found = 1;
break;
}
}
}
void *seq = NULL;
if(found)
{
status_print( mt, "Track %d: '%s' '%d' already in Track %d\n",
track, hostname,port_num, i );
g_free(hostname);
return 0;
}
seq = veejay_sequence_init( port_num, hostname, preview_width_*2, preview_height_*2 );
if(seq == NULL )
{
status_print( mt, "Error while connecting to '%s' : '%d'", hostname, port_num );
g_free(hostname);
pt->pt[track]->sequence = NULL;
pt->pt[track]->active = 0;
pt->pt[track]->used = 0;
return 0;
}
pt->pt[track]->timeout = gtk_timeout_add( 300, update_sequence_widgets, (gpointer*) pt->pt[track] );
veejay_configure_sequence( seq, preview_width_, preview_height_ );
G_LOCK(mt_lock);
pt->pt[track]->sequence = seq;
pt->pt[track]->active = 1;
pt->pt[track]->used = 1;
store_data( mt->data, track, hostname, port_num );
G_UNLOCK(mt_lock);
mt_priv_t *p = pt->pt[track];
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( p->view->toggle ), 1 );
status_print( mt, "Track %d: Connection established with '%s' port %d\n",
track, hostname, port_num );
gtk_widget_set_sensitive( GTK_WIDGET(p->view->panel),TRUE);
}
g_free(hostname);
return 1;
}
return 0;
}
void multitrack_close_track( void *data )
{
multitracker_t *mt = (multitracker_t*) data;
}
int multrack_audoadd( void *data, char *hostname, int port_num )
{
multitracker_t *mt = (multitracker_t*) data;
all_priv_t *a = (all_priv_t*)mt->data;
G_LOCK(mt_lock);
int track = free_slot( mt->data );
void *seq = veejay_sequence_init( port_num, hostname, preview_width_*2, preview_height_*2 );
if(seq == NULL )
{
status_print( mt, "Error while connecting to '%s' : '%d'", hostname, port_num );
g_free(hostname);
a->pt[track]->sequence = NULL;
a->pt[track]->active = 0;
a->pt[track]->used = 0;
G_UNLOCK(mt_lock);
}
a->pt[track]->timeout = gtk_timeout_add( 300, update_sequence_widgets, (gpointer*) a->pt[track] );
veejay_configure_sequence( seq, preview_width_, preview_height_ );
a->pt[track]->sequence = seq;
a->pt[track]->active = 1;
a->pt[track]->used = 1;
store_data( mt->data, track, hostname, port_num );
multitrack_set_current( data, hostname, port_num , preview_width_*2, preview_height_*2);
gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( a->pt[track]->view->toggle ), 1 );
status_print( mt, "Track %d: Connection established with '%s' port %d\n",
track, hostname, port_num );
gtk_widget_set_sensitive( GTK_WIDGET(a->pt[track]->view->panel),TRUE);
// gtk_widget_set_sensitive( GTK_WIDGET(a->pt[track]->view->toggle),TRUE);
G_UNLOCK(mt_lock);
return track;
}
int multitrack_autoload( void *data, char *hostname, int port_num )
{
multitrack_set_current( data, hostname, port_num , preview_width_*2, preview_height_*2);
return 1;
}
void multitrack_set_current2( void *data, char *hostname, int port_num , int width, int height)
{
}
static int num_tracks_active( multitracker_t * mt )
{
int i;
int sum = 0;
all_priv_t *a = (all_priv_t*) mt->data;
for( i = 0; i < MAX_TRACKS ; i ++ )
{
mt_priv_t *p = a->pt[i];
if(p->active)
sum ++;
}
return sum;
}
static int find_track( multitracker_t *mt, const char *host, int port )
{
int i;
all_priv_t *a = (all_priv_t*) mt->data;
if(!host || port <= 0 || port > 65535 )
return -1;
for( i = 0; i < MAX_TRACKS ; i ++ )
{
mt_priv_t *p = a->pt[i];
if(p->active)
{
if(strncasecmp( p->hostname, host, strlen(host)) == 0 && port ==
p->port_num )
return i;
}
}
return -1;
}
static int find_sequence( all_priv_t *a )
{
mt_priv_t *c = a->pt[LAST_TRACK];
if(!c->active)
return -1;
int i;
for( i= 0; i < MAX_TRACKS; i ++ )
{// if( c == a->pt[i]->sequence ) return i;
if(a->pt[i]->active)
{
if(strncasecmp(c->hostname, a->pt[i]->hostname, strlen(a->pt[i]->hostname) ) == 0 &&
c->port_num == a->pt[i]->port_num )
return i;
}
}
return -1;
}
static int find_vtrack_id( mt_priv_t *p, mt_priv_t *q, int *got_tag_id )
{
if(!q->active)
return -1;
if(!p->active)
return -1;
char hostname[255];
int port_num = 0;
int tag_id = 0;
int i;
for( i = 0; i < MAX_TRACKS; i++ )
{
if(p->tracks[i])
{
char *str = p->tracks[ i ];
if(sscanf(str, "%s %d %d", hostname, &port_num, &tag_id ))
{
if(strncasecmp( q->hostname,hostname,strlen(hostname) ) == 0 &&
port_num == q->port_num )
{
*got_tag_id = tag_id;
return i;
}
}
}
}
return -1;
}
void multitrack_release_track(void *data, int id, int release_this )
{
char hostname[255];
int port_num = 0;
int tag_id = 0;
if( release_this < 0 || release_this > MAX_TRACKS )
return;
if( id < 0 || id > MAX_TRACKS )
return;
G_LOCK(mt_lock);
multitracker_t *mt = (multitracker_t*) data;
all_priv_t *a = (all_priv_t*) mt->data;
mt_priv_t *p = a->pt[id];
mt_priv_t *q = a->pt[release_this];
int stream_id = find_vtrack_id( p,q, &tag_id );
if( stream_id >= 0 )
veejay_sequence_send( p->sequence , VIMS_STREAM_DELETE, "%d", tag_id);
G_UNLOCK(mt_lock);
}
void multitrack_bind_track( void *data, int id, int bind_this )
{
if( bind_this < 0 || bind_this > MAX_TRACKS )
return;
if( id < 0 || id > MAX_TRACKS )
return;
G_LOCK(mt_lock);
multitracker_t *mt = (multitracker_t*) data;
all_priv_t *a = (all_priv_t*) mt->data;
mt_priv_t *p = a->pt[id];
mt_priv_t *q = a->pt[bind_this];
if(!q->active)
{
G_UNLOCK(mt_lock);
status_print(mt,"Track %d is empty\n", bind_this );
return;
}
if(!p->active)
{
//@@@ fatal
G_UNLOCK(mt_lock);
return;
}
if( strncasecmp( q->hostname, p->hostname, strlen(q->hostname)) == 0 &&
q->port_num == p->port_num )
{
G_UNLOCK(mt_lock);
status_print(mt, "Track %d: Cannot bind to myself", bind_this);
return;
}
// connect q to p
veejay_sequence_send( p->sequence, VIMS_STREAM_NEW_UNICAST, "%d %s", q->port_num,q->hostname );
G_UNLOCK(mt_lock);
status_print(mt, "Veejay '%s:%d' retrieving frames from Veejay '%s:%d",
p->hostname,p->port_num,q->hostname,q->port_num );
}
// set_current opens Main preview
void multitrack_set_current( void *data, char *hostname, int port_num , int width, int height)
{
multitracker_t *mt = (multitracker_t*)data;
all_priv_t *a = (all_priv_t*) mt->data;
mt_priv_t *last_track = a->pt[LAST_TRACK];
if( last_track->active )
{
// make sure to reset width/height back to small
veejay_configure_sequence( last_track->sequence, preview_width_, preview_height_ );
}
int id = find_track( mt, hostname, port_num );
if(id >= 0 )
{
last_track->used = 1;
last_track->active = 1;
last_track->sequence = a->pt[id]->sequence;
if(last_track->hostname)
free(last_track->hostname);
last_track->hostname = strdup(hostname);
last_track->port_num = port_num;
assert( last_track->sequence != NULL );
veejay_configure_sequence( last_track->sequence, 352, 288 );
gtk_widget_set_size_request( GTK_WIDGET( last_track->view->area ), width,height );
}
else
{
all_priv_t *a = (all_priv_t*) mt->data;
mt_priv_t *last_track = a->pt[LAST_TRACK];
last_track->active = 0;
}
}
void multitrack_restart(void *data)
{
}
static gboolean seqv_mouse_press_event ( GtkWidget *w, GdkEventButton *event, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
multitracker_t *mt = (multitracker_t*) p->backlink;
if( p == NULL )
{
status_print((multitracker_t*)p->backlink,"Track %d is empty\n", p->num);
return FALSE;
}
if( gveejay_busy() )
{
status_print( (multitracker_t*)p->backlink,
"Already connecting to Track %d", mt->selected );
return FALSE;
}
// mt->selected = p->num;
if(event->type == GDK_2BUTTON_PRESS)
{
mt->selected = p->num;
gui_cb( 0, strdup(p->hostname), p->port_num );
G_LOCK(mt_lock);
multitrack_set_current( (void*) mt, p->hostname, p->port_num ,preview_width_*2,preview_height_*2 );
/*
all_priv_t *a = (all_priv_t*) mt->data;
mt_priv_t *last_track = a->pt[LAST_TRACK];
last_track->used = 1;
last_track->active = 1;
last_track->sequence = p->sequence;
assert( last_track->sequence != NULL );
veejay_configure_sequence( last_track->sequence, 352, 288 );*/
G_UNLOCK(mt_lock);
}
if( event->type == GDK_BUTTON_PRESS )
{
status_print((multitracker_t*)p->backlink,"Selected Track %d\n", p->num);
mt->selected = p->num;
}
return FALSE;
}
static void sequence_preview_cb(GtkWidget *widget, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
veejay_toggle_image_loader( p->sequence,
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
}
static gint seqv_image_expose( GtkWidget *w, gpointer user_data )
{
mt_priv_t *p = (mt_priv_t*) user_data;
return FALSE;
}
static void sequence_set_current_frame(GtkWidget *w, gpointer user_data)
{
mt_priv_t *p = (mt_priv_t*) user_data;
if(!p->status_lock)
{
gdouble pos = timeline_get_pos( TIMELINE_SELECTION(w) );
veejay_sequence_send(p->sequence, VIMS_VIDEO_SET_FRAME, "%d" , (gint) pos);
}
}
static sequence_view_t *new_sequence_view( mt_priv_t *p,gint w, gint h, gint last, GtkWidget *main_area )
{
sequence_view_t *seqv = (sequence_view_t*) malloc(sizeof(sequence_view_t));
memset( seqv, 0,sizeof( sequence_view_t ));
if(!last || main_area == NULL)
{
seqv->event_box = gtk_event_box_new();
gtk_event_box_set_visible_window( seqv->event_box, TRUE );
GTK_WIDGET_SET_FLAGS( seqv->event_box, GTK_CAN_FOCUS );
if( w == 0 && h == 0 )
g_signal_connect( G_OBJECT( seqv->event_box ),
"button_press_event",
G_CALLBACK( seqv_mouse_press_event ),
(gpointer*) p );
gtk_widget_show( GTK_WIDGET( seqv->event_box ) );
gchar *track_title = g_new0( gchar, 20 );
sprintf(track_title, "Track %d", p->num );
seqv->frame = gtk_frame_new( track_title );
g_free(track_title);
gtk_container_set_border_width( GTK_CONTAINER( seqv->frame) , 1 );
gtk_widget_show( GTK_WIDGET( seqv->frame ) );
gtk_container_add( GTK_CONTAINER( seqv->event_box), seqv->frame );
seqv->main_vbox = gtk_vbox_new(FALSE,0);
gtk_container_add( GTK_CONTAINER( seqv->frame ), seqv->main_vbox );
gtk_widget_show( GTK_WIDGET( seqv->main_vbox ) );
seqv->area = gtk_image_new();
}
else
seqv->area = main_area;
set_logo( seqv->area );
g_signal_connect( G_OBJECT( seqv->area ),
"expose_event",
G_CALLBACK( seqv_image_expose ),
(gpointer*) p );
if(!last || !main_area)
{
gtk_box_pack_start( GTK_BOX(seqv->main_vbox),GTK_WIDGET( seqv->area), FALSE,FALSE,0);
gtk_widget_set_size_request( seqv->area, w == 0 ? preview_width_ : w, h == 0 ? preview_height_: h );
}
if(!last)
{
seqv->panel = gtk_frame_new(NULL);
seqv->toggle = gtk_toggle_button_new_with_label( "preview" );
g_signal_connect( G_OBJECT( seqv->toggle ), "toggled", G_CALLBACK(sequence_preview_cb),
(gpointer*)p );
//gtk_widget_set_sensitive( GTK_WIDGET(seqv->toggle), FALSE );
//gtk_container_add( GTK_CONTAINER( seqv->main_vbox ), seqv->toggle );
gtk_box_pack_start( GTK_BOX(seqv->main_vbox), seqv->toggle,FALSE,FALSE, 0 );
gtk_widget_show( seqv->toggle );
GtkWidget *vvbox = gtk_vbox_new(FALSE, 0);
seqv->button_box = gtk_hbox_new(FALSE,0);
gtk_box_pack_start( GTK_BOX(vvbox), seqv->button_box ,FALSE,FALSE, 0 );
if( w== 0 && h == 0 )
add_buttons( p,seqv,seqv->button_box );
gtk_widget_show( seqv->button_box );
gtk_container_add( GTK_CONTAINER( seqv->main_vbox ), seqv->panel );
seqv->button_box2 = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start( GTK_BOX(vvbox), seqv->button_box2, FALSE,FALSE, 0 );
if( w== 0 && h == 0)
add_buttons2( p,seqv,seqv->button_box2 );
gtk_widget_show( seqv->button_box2 );
gtk_container_add( GTK_CONTAINER( seqv->panel ), vvbox );
gtk_widget_show(vvbox);
GtkWidget *box = gtk_vbox_new(FALSE,0);
seqv->timeline_ = timeline_new();
gtk_widget_set_size_request( seqv->panel, 180 ,14);
gtk_widget_show( seqv->panel );
gtk_box_pack_start( GTK_BOX( box ), seqv->timeline_, FALSE,FALSE, 0 );
//gtk_container_add( GTK_CONTAINER(seqv->panel), box );
gtk_box_pack_start( GTK_BOX( vvbox ), box , FALSE,FALSE,0);
gtk_widget_show(seqv->timeline_);
g_signal_connect( seqv->timeline_, "pos_changed",
(GCallback) sequence_set_current_frame, (gpointer*) p );
/* tree */
GtkWidget *scroll = gtk_scrolled_window_new(NULL,NULL);
// gtk_srolled_window_set_placement( GTK_SCROLLED_WINDOW(scroll), GTK_CORNER_TOP_RIGHT );
gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_ETCHED_IN );
gtk_widget_set_size_request(scroll,30,70);
gtk_container_set_border_width(GTK_CONTAINER(scroll),0);
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scroll),GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
GtkWidget *vvvbox = gtk_hbox_new(FALSE,0);
seqv->tracks = create_track_view(p->num, MAX_TRACKS, (void*) p );
gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( get_track_tree(seqv->tracks)) , FALSE );
gtk_widget_set_size_request( get_track_tree(seqv->tracks),20,80 );
gtk_widget_show(scroll);
gtk_scrolled_window_add_with_viewport(
GTK_SCROLLED_WINDOW( scroll ), get_track_tree(seqv->tracks) );
gtk_widget_show( get_track_tree(seqv->tracks));
gtk_box_pack_start( GTK_BOX(vvvbox), scroll, TRUE,TRUE, 0);
GtkWidget *hhbox = gtk_hbox_new(FALSE,0);
seqv->sliders_[0] = gtk_vscale_new_with_range( -12.0,12.0,1.0 );
seqv->sliders_[1] = gtk_vscale_new_with_range( 0.0, 1.0, 0.01 );
gtk_scale_set_digits( GTK_SCALE(seqv->sliders_[1]), 2 );
g_signal_connect( G_OBJECT( seqv->sliders_[0] ), "value_changed", G_CALLBACK( seq_speed ),
(gpointer*)p );
g_signal_connect( G_OBJECT( seqv->sliders_[1] ), "value_changed", G_CALLBACK( seq_opacity ),
(gpointer*)p );
gtk_box_pack_start( GTK_BOX( hhbox ), seqv->sliders_[0], TRUE, TRUE, 0 );
gtk_box_pack_start( GTK_BOX( hhbox ), seqv->sliders_[1], TRUE, TRUE, 0 );
gtk_widget_show( seqv->sliders_[0] );
gtk_widget_show( seqv->sliders_[1] );
// gtk_container_add( GTK_CONTAINER( vvvbox ), hhbox );
gtk_box_pack_start( GTK_BOX(vvvbox), hhbox, TRUE,TRUE, 0 );
gtk_widget_show( hhbox );
gtk_container_add( GTK_CONTAINER( box ), vvvbox );
gtk_widget_show( vvvbox );
gtk_widget_show( box );
GtkWidget *hbox = gtk_hbox_new(FALSE,0);
gtk_box_set_spacing( hbox, 10 );
seqv->labels_[0] = gtk_label_new( "00:00:00:00" );
seqv->labels_[1] = gtk_label_new( "00:00:00:00" );
gtk_box_pack_start( GTK_BOX( hbox ), seqv->labels_[0], FALSE, FALSE, 0 );
gtk_box_pack_start( GTK_BOX( hbox ), seqv->labels_[1], FALSE, FALSE, 0 );
gtk_widget_show( seqv->labels_[0] );
gtk_widget_show( seqv->labels_[1] );
gtk_box_pack_start( GTK_BOX(seqv->main_vbox), hbox, FALSE,FALSE, 0 );
gtk_widget_show( hbox );
gtk_widget_set_sensitive(GTK_WIDGET(seqv->panel), FALSE );
}
gtk_widget_show( GTK_WIDGET( seqv->area ) );
seqv->dim[0] = w;
seqv->dim[1] = h;
return seqv;
}
void *mt_preview( gpointer user_data )
{
multitracker_t *mt = (multitracker_t*) user_data;
all_priv_t *a = (all_priv_t*) mt->data;
gint i = 0;
GdkPixbuf *cache[MAX_TRACKS+2];
for( ;; )
{
G_LOCK(mt_lock);
mt_priv_t *lt = a->pt[LAST_TRACK];
gint error = 0;
memset( cache, 0, (MAX_TRACKS+2) * sizeof(GdkPixbuf*));
if(mt->quit)
{
G_UNLOCK(mt_lock);
g_thread_exit(NULL);
}
if(lt->active && lt->preview)
{
cache[LAST_TRACK] = veejay_get_image( lt->sequence, &error );
if( error )
delete_data( mt->data, LAST_TRACK );
}
int ref = find_sequence( a );
if( mt->sensitive )
for( i = 0; i < MAX_TRACKS ; i ++ )
{
mt_priv_t *p = a->pt[i];
if( p->active && ref != i)
{
cache[i] = veejay_get_image(
p->sequence,
&error );
if( error )
cache[i] = 0;
}
}
G_UNLOCK(mt_lock);
if( ref >= 0 && cache[LAST_TRACK] && lt->preview)
{
cache[ref] = gdk_pixbuf_scale_simple(
cache[LAST_TRACK],
preview_width_,
preview_height_,
GDK_INTERP_BILINEAR );
}
gdk_threads_enter();
if( mt->sensitive )
for( i = 0; i < MAX_TRACKS ; i ++ )
{
mt_priv_t *p = a->pt[i];
if(cache[i])
{
GtkImage *image = GTK_IMAGE( p->view->area );
gtk_image_set_from_pixbuf( image, cache[i] );
}
}
if(mt->quit)
{
gdk_threads_leave();
g_thread_exit(NULL);
}
else
if(lt->active && cache[LAST_TRACK])
{
GtkImage *image = GTK_IMAGE( lt->view->area );
gtk_image_set_from_pixbuf( image, cache[LAST_TRACK] );
img_cb( cache[LAST_TRACK] );
gdk_pixbuf_unref( cache[LAST_TRACK] );
}
//@@ cleanup
for( i = 0; i < MAX_TRACKS ; i ++ )
{
mt_priv_t *p = a->pt[i];
if(cache[i])
{
gdk_pixbuf_unref(cache[i]);
}
cache[i] = NULL;
}
gdk_threads_leave();
}
}