2 * dv1394app.c -- GTK+ 2 dv1394d client demo
3 * Copyright (C) 2002-2003 Charles Yates <charles.yates@pandora.be>
4 * Copyright (C) 2010 Dan Dennedy <dan@dennedy.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include <gdk/gdkkeysyms.h>
31 #include "interface.h"
33 #include "dv1394app.h"
35 #include "gtkenhancedscale.h"
36 #include <mvcp/mvcp_remote.h>
37 #include <melted/melted_local.h>
40 /** Window close event.
43 static gboolean
on_main_window_delete_event( GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
)
49 static gboolean
instance_connect( dv1394app
this, char *server
, char *port
)
51 if ( this->parser
== NULL
)
53 if ( strstr( server
, ".conf" ) == NULL
)
54 this->parser
= mvcp_parser_init_remote( server
, atoi( port
) );
56 this->parser
= melted_parser_init_local( );
58 this->command
= mvcp_init( this->parser
);
60 if ( strstr( server
, ".conf" ) != NULL
)
61 mvcp_run( this->command
, server
);
63 if ( mvcp_connect( this->command
) == mvcp_ok
)
65 struct timespec t
= { 1, 0 };
66 nanosleep( &t
, NULL
);
67 //gdk_threads_leave( );
68 dv1394app_connect( this );
69 //gdk_threads_enter( );
73 mvcp_close( this->command
);
74 mvcp_parser_close( this->parser
);
80 return this->parser
!= NULL
;
83 /** Connection window - Connect button pressed.
86 static gboolean
on_connect_pressed( GtkWidget
*button
, gpointer user_data
)
88 dv1394app
this = user_data
;
93 if ( this->parser
== NULL
)
95 widget
= lookup_widget( this->connect
, "entry_server" );
96 server
= ( char * )gtk_entry_get_text( GTK_ENTRY( widget
) );
97 widget
= lookup_widget( this->connect
, "entry_port" );
98 port
= ( char * )gtk_entry_get_text( GTK_ENTRY( widget
) );
99 instance_connect( this, server
, port
);
101 gtk_widget_destroy( this->connect
);
103 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( lookup_widget( this->window
, "button_connect" ) ), TRUE
);
104 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( lookup_widget( this->window
, "button_disconnect" ) ), FALSE
);
110 /** Connection window - Cancel button pressed.
113 static gboolean
on_cancel_pressed( GtkWidget
*button
, gpointer user_data
)
115 dv1394app
this = user_data
;
116 if ( this->guard
) return FALSE
;
117 gtk_widget_destroy( this->connect
);
121 /** Main window - connect menu item selected.
124 void on_item_connect_activate( GtkMenuItem
*menuitem
, gpointer user_data
)
126 dv1394app
this = user_data
;
129 if ( this->guard
) return;
130 this->connect
= create_window_connection( );
132 /* Connection set up handling */
133 widget
= lookup_widget( this->connect
, "button_connect" );
134 gtk_signal_connect( GTK_OBJECT( widget
), "clicked", GTK_SIGNAL_FUNC( on_connect_pressed
), this );
135 widget
= lookup_widget( this->connect
, "button_cancel" );
136 gtk_signal_connect( GTK_OBJECT( widget
), "clicked", GTK_SIGNAL_FUNC( on_cancel_pressed
), this );
138 gtk_widget_show( this->connect
);
141 /** Main window - disconnect menu item selected.
144 void on_item_disconnect_activate( GtkMenuItem
*menuitem
, gpointer user_data
)
146 dv1394app
this = user_data
;
148 if ( this->guard
) return;
149 dv1394app_disconnect( this );
152 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( lookup_widget( this->window
, "button_connect" ) ), FALSE
);
153 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( lookup_widget( this->window
, "button_disconnect" ) ), TRUE
);
157 /** Main window - quit menu item selected.
160 void on_item_quit_activate( GtkMenuItem
*menuitem
, gpointer user_data
)
165 static void playlist_open(dv1394app
this, char* filename
)
169 if( this->selected_unit
>= 0 && this->command
)
171 f
= fopen(filename
, "rt");
183 fgets(l
, sizeof(l
), f
);
185 /* remove newlines */
186 if( (s
= strchr(l
, '\n')) ) *s
= 0;
187 if( (s
= strchr(l
, '\r')) ) *s
= 0;
188 if( (s
= strchr(l
, '\t')) ) *s
= 0;
190 /* check for empty line */
192 mvcp_unit_append( this->command
, this->selected_unit
, l
, -1, -1 );
201 static void playlist_save(dv1394app
this, char* filename
)
204 mvcp_list_entry_t entry
;
208 if( this->selected_unit
>= 0 && this->command
)
210 f
= fopen(filename
, "wt");
213 list
= mvcp_list_init( this->command
, this->selected_unit
);
217 for ( index
= 0; list
&& index
< mvcp_list_count( list
); index
++ )
219 mvcp_list_get( list
, index
, &entry
);
220 fprintf(f
, "%s\n", entry
.full
);
222 mvcp_list_close( list
);
230 /** Main window - playlist open menu item selected.
233 static void on_item_open_playlist_activate( GtkMenuItem
*menuitem
, gpointer user_data
)
235 dv1394app
this = user_data
;
238 dialog
= gtk_file_chooser_dialog_new ("Open File",
239 GTK_WINDOW (this->window
),
240 GTK_FILE_CHOOSER_ACTION_OPEN
,
241 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
242 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
245 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog
),
246 (this->playlist_folder
)?
this->playlist_folder
:getenv("HOME"));
248 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
)
252 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
253 playlist_open(this, filename
);
255 if ( this->playlist_folder
)
256 g_free (this->playlist_folder
);
257 this->playlist_folder
= filename
;
258 if ( this->playlist_folder
)
260 char* e
= strrchr(this->playlist_folder
, '/');
265 gtk_widget_destroy (dialog
);
268 /** Main window - playlist save menu item selected.
271 static void on_item_save_playlist_activate( GtkMenuItem
*menuitem
, gpointer user_data
)
273 dv1394app
this = user_data
;
276 dialog
= gtk_file_chooser_dialog_new ("Save File",
277 GTK_WINDOW (this->window
),
278 GTK_FILE_CHOOSER_ACTION_SAVE
,
279 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
280 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
283 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
), TRUE
);
285 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog
),
286 (this->playlist_folder
)?
this->playlist_folder
:getenv("HOME"));
287 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog
), "Untitled document");
289 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
)
293 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
294 playlist_save(this, filename
);
296 if ( this->playlist_folder
)
297 g_free (this->playlist_folder
);
298 this->playlist_folder
= filename
;
299 if ( this->playlist_folder
)
301 char* e
= strrchr(this->playlist_folder
, '/');
306 gtk_widget_destroy (dialog
);
309 static gboolean
on_page_switch_pressed( GtkWidget
*button
, gpointer user_data
)
311 dv1394app
this = user_data
;
313 GtkWidget
*notebook
= lookup_widget( button
, "notebook1" );
315 if ( this->guard
) return TRUE
;
317 for ( index
= 0; index
< this->page_count
; index
++ )
318 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( this->page_buttons
[ index
] ), FALSE
);
319 for ( index
= 0; index
< this->page_count
; index
++ )
321 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( this->page_buttons
[ index
] ), FALSE
);
322 if ( this->page_buttons
[ index
] == button
)
325 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( this->page_buttons
[ index
] ), TRUE
);
326 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook
), index
);
332 static gboolean
on_transport_pressed( GtkWidget
*button
, gpointer data
)
335 dv1394app
this = ( dv1394app
)data
;
336 mvcp dv
= dv1394app_get_command( this );
337 int unit
= dv1394app_get_selected_unit( this );
339 for ( index
= 0; index
< TRANSPORT_BUTTONS_COUNT
; index
++ )
340 if ( this->buttons
[ index
] == button
)
346 mvcp_unit_clip_goto( dv
, unit
, mvcp_absolute
, 0, 0 );
350 mvcp_unit_goto( dv
, unit
, 0 );
354 mvcp_unit_rewind( dv
, unit
);
358 mvcp_unit_step( dv
, unit
, -1 );
362 mvcp_unit_pause( dv
, unit
);
366 mvcp_unit_play( dv
, unit
);
370 mvcp_unit_stop( dv
, unit
);
374 mvcp_unit_step( dv
, unit
, 1 );
378 mvcp_unit_fast_forward( dv
, unit
);
382 mvcp_unit_clip_goto( dv
, unit
, mvcp_relative
, 1, 0 );
386 mvcp_unit_clip_goto( dv
, unit
, mvcp_absolute
, 9999, -1 );
390 mvcp_unit_set( dv
, unit
, "eof", "loop");
391 this->eof
[this->selected_unit
] = 0;
395 mvcp_unit_set( dv
, unit
, "eof", "pause");
396 this->eof
[this->selected_unit
] = 0;
406 static void dv1394app_register_page( dv1394app
this, page item
)
408 GtkWidget
*toolbar
= lookup_widget( this->window
, "toolbar1" );
409 GtkIconSize size
= gtk_toolbar_get_icon_size( GTK_TOOLBAR( toolbar
) );
410 GtkWidget
*widget
= lookup_widget( this->window
, "notebook1" );
411 GtkWidget
*bin
= gtk_frame_new( NULL
);
414 this->pages
[ this->page_count
] = item
;
415 gtk_widget_reparent( GTK_BIN( page_get_widget( item
) )->child
, bin
);
416 gtk_container_add(GTK_CONTAINER( widget
), bin
);
417 gtk_frame_set_label_align( GTK_FRAME( bin
), 0, 0 );
418 gtk_frame_set_shadow_type( GTK_FRAME( bin
), GTK_SHADOW_NONE
);
419 gtk_widget_show( bin
);
421 page_get_toolbar_info( item
, size
, &widget
, &label
);
422 this->page_buttons
[ this->page_count
] = gtk_toolbar_append_element( GTK_TOOLBAR ( toolbar
), GTK_TOOLBAR_CHILD_TOGGLEBUTTON
, NULL
, label
, NULL
, NULL
, widget
, NULL
, NULL
);
423 gtk_label_set_use_underline( GTK_LABEL(((GtkToolbarChild
*)(g_list_last( GTK_TOOLBAR( toolbar
)->children
)->data
))->label
), TRUE
);
424 gtk_widget_show( widget
);
425 gtk_signal_connect( GTK_OBJECT( this->page_buttons
[ this->page_count
] ), "clicked", GTK_SIGNAL_FUNC( on_page_switch_pressed
), this );
426 gtk_signal_connect( GTK_OBJECT( this->page_buttons
[ this->page_count
] ), "activate", GTK_SIGNAL_FUNC( on_page_switch_pressed
), this );
431 static GtkAdjustment
*trim_adj
[3];
432 #define TRIM_ADJ_POS 0
433 #define TRIM_ADJ_IN 1
434 #define TRIM_ADJ_OUT 2
436 void dv1394app_show_status( dv1394app
this, mvcp_status status
)
439 for ( index
= 0; index
< this->page_count
; index
++ )
440 page_show_status( this->pages
[ index
], status
);
442 if ( status
->seek_flag
!= this->seek_flag
)
444 gtk_widget_set_sensitive( lookup_widget( dv1394app_get_widget( this ), "transport_2" ), status
->seek_flag
);
445 gtk_widget_set_sensitive( lookup_widget( dv1394app_get_widget( this ), "transport_3" ), status
->seek_flag
);
446 gtk_widget_set_sensitive( lookup_widget( dv1394app_get_widget( this ), "transport_7" ), status
->seek_flag
);
447 gtk_widget_set_sensitive( lookup_widget( dv1394app_get_widget( this ), "transport_8" ), status
->seek_flag
);
448 this->seek_flag
= status
->seek_flag
;
451 if ( !this->trim_in_use
)
453 int upper
= status
->length
> 0 ? status
->length
- 1 : 0;
454 trim_adj
[TRIM_ADJ_IN
]->upper
= upper
;
455 trim_adj
[TRIM_ADJ_IN
]->value
= status
->in
;
456 trim_adj
[TRIM_ADJ_OUT
]->upper
= upper
;
457 trim_adj
[TRIM_ADJ_OUT
]->value
= status
->out
;
458 trim_adj
[TRIM_ADJ_POS
]->upper
= upper
;
459 trim_adj
[TRIM_ADJ_POS
]->value
= status
->position
;
460 gtk_signal_emit_by_name( GTK_OBJECT(trim_adj
[TRIM_ADJ_POS
]), "value_changed" );
463 gtk_widget_set_sensitive( lookup_widget( dv1394app_get_widget( this ), "transport_11" ),
464 this->eof
[this->selected_unit
] == 'p' );
465 gtk_widget_set_sensitive( lookup_widget( dv1394app_get_widget( this ), "transport_12" ),
466 this->eof
[this->selected_unit
] == 'l' );
469 static gboolean
trim_pressed( GtkWidget
*button
, GdkEventButton
*event
, gpointer user_data
)
471 dv1394app
this = (dv1394app
)user_data
;
472 mvcp_unit_pause( dv1394app_get_command( this ), this->selected_unit
);
475 this->trim_in_use
= 1;
479 static gboolean
trim_released( GtkWidget
*button
, GdkEventButton
*event
, gpointer user_data
)
481 dv1394app
this = (dv1394app
)user_data
;
482 this->trim_in_use
= 0;
483 if ( this->trim_in
!= -1 )
484 mvcp_unit_set_in( dv1394app_get_command( this ), this->selected_unit
, this->trim_in
);
485 if ( this->trim_out
!= -1 )
486 mvcp_unit_set_out( dv1394app_get_command( this ), this->selected_unit
, this->trim_out
);
490 static gboolean
on_trim_value_changed_event( GtkWidget
*button
, gpointer user_data
)
492 dv1394app
this = (dv1394app
)user_data
;
493 if ( this->trim_in_use
)
496 g_object_get( G_OBJECT( button
), "user_data", &value
, NULL
);
498 if ( !strcmp( value
, "position" ) )
500 mvcp_unit_goto( dv1394app_get_command( this ), this->selected_unit
, trim_adj
[TRIM_ADJ_POS
]->value
);
502 else if ( !strcmp( value
, "in" ) )
504 this->trim_in
= trim_adj
[TRIM_ADJ_IN
]->value
;
505 mvcp_unit_goto( dv1394app_get_command( this ), this->selected_unit
, trim_adj
[TRIM_ADJ_IN
]->value
);
507 else if ( !strcmp( value
, "out" ) )
509 this->trim_out
= trim_adj
[TRIM_ADJ_OUT
]->value
;
510 mvcp_unit_goto( dv1394app_get_command( this ), this->selected_unit
, trim_adj
[TRIM_ADJ_OUT
]->value
);
513 gtk_widget_queue_draw (lookup_widget(this->window
, "vbox_trim") );
519 /** Initialiser for application state.
522 dv1394app
dv1394app_init( GtkWidget
*window
, char *instance
)
524 dv1394app
this = calloc( 1, sizeof( dv1394app_t
) );
530 this->window
= window
;
532 /* Window destroy event */
533 gtk_signal_connect( GTK_OBJECT( this->window
), "destroy", GTK_SIGNAL_FUNC( on_main_window_delete_event
), this );
535 /* Menu item signal handling */
536 // widget = lookup_widget( this->window, "item_connect" );
537 // gtk_signal_connect( GTK_OBJECT( widget ), "activate", GTK_SIGNAL_FUNC( on_item_connect_activate ), this );
538 widget
= lookup_widget( this->window
, "button_connect" );
539 gtk_signal_connect( GTK_OBJECT( widget
), "clicked", GTK_SIGNAL_FUNC( on_item_connect_activate
), this );
540 // widget = lookup_widget( this->window, "item_disconnect" );
541 // gtk_signal_connect( GTK_OBJECT( widget ), "activate", GTK_SIGNAL_FUNC( on_item_disconnect_activate ), this );
542 widget
= lookup_widget( this->window
, "button_disconnect" );
543 gtk_signal_connect( GTK_OBJECT( widget
), "clicked", GTK_SIGNAL_FUNC( on_item_disconnect_activate
), this );
544 // widget = lookup_widget( this->window, "item_quit" );
545 // gtk_signal_connect( GTK_OBJECT( widget ), "activate", GTK_SIGNAL_FUNC( on_item_quit_activate ), this );
546 // widget = lookup_widget( this->window, "button_quit" );
547 // gtk_signal_connect( GTK_OBJECT( widget ), "clicked", GTK_SIGNAL_FUNC( on_item_quit_activate ), this );
548 widget
= lookup_widget( this->window
, "button_open_playlist" );
549 gtk_signal_connect( GTK_OBJECT( widget
), "clicked", GTK_SIGNAL_FUNC( on_item_open_playlist_activate
), this );
550 widget
= lookup_widget( this->window
, "button_save_playlist" );
551 gtk_signal_connect( GTK_OBJECT( widget
), "clicked", GTK_SIGNAL_FUNC( on_item_save_playlist_activate
), this );
554 /* Initialise the pages. */
555 dv1394app_register_page( this, page_operate_init( this ) );
556 dv1394app_register_page( this, page_command_init( this ) );
558 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( this->page_buttons
[ 0 ] ), TRUE
);
561 /* Remove the empty page */
562 widget
= lookup_widget( this->window
, "notebook1" );
563 gtk_notebook_remove_page( GTK_NOTEBOOK( widget
), 0 );
565 guint keys
[ ] = { GDK_0
, GDK_1
, GDK_2
, GDK_3
, GDK_4
, GDK_5
, GDK_6
, GDK_7
, GDK_8
, GDK_9
, GDK_A
};
567 GtkAccelGroup
*accel_group
= gtk_accel_group_new( );
569 for ( index
= 0; index
< TRANSPORT_BUTTONS_COUNT
; index
++ )
572 sprintf( name
, "transport_%d", index
);
573 this->buttons
[ index
] = lookup_widget( dv1394app_get_widget( this ), name
);
574 gtk_signal_connect( GTK_OBJECT( this->buttons
[ index
] ), "clicked", GTK_SIGNAL_FUNC( on_transport_pressed
), this );
575 gtk_widget_add_accelerator( this->buttons
[ index
], "clicked", accel_group
, keys
[ index
], GDK_CONTROL_MASK
, GTK_ACCEL_VISIBLE
);
578 gtk_window_add_accel_group( GTK_WINDOW( dv1394app_get_widget( this ) ), accel_group
);
580 trim_adj
[TRIM_ADJ_POS
] = GTK_ADJUSTMENT( gtk_adjustment_new( 0, 0, 1000, 1, 10, 0 ) );
581 g_object_set( G_OBJECT( trim_adj
[TRIM_ADJ_POS
] ), "user_data", "position", NULL
);
582 trim_adj
[TRIM_ADJ_IN
] = GTK_ADJUSTMENT( gtk_adjustment_new( 0, 0, 1000, 1, 10, 0 ) );
583 g_object_set( G_OBJECT( trim_adj
[TRIM_ADJ_IN
] ), "user_data", "in", NULL
);
584 trim_adj
[TRIM_ADJ_OUT
] = GTK_ADJUSTMENT( gtk_adjustment_new( 0, 0, 1000, 1, 10, 0 ) );
585 g_object_set( G_OBJECT( trim_adj
[TRIM_ADJ_OUT
] ), "user_data", "out", NULL
);
588 for (i
= 0; i
< 3; i
++)
589 gtk_signal_connect (GTK_OBJECT (trim_adj
[i
]), "value_changed", GTK_SIGNAL_FUNC (on_trim_value_changed_event
), this );
591 GtkWidget
*trim
= gtk_enhanced_scale_new( (GtkObject
**) trim_adj
, 3);
594 gtk_widget_set_name (trim
, "trim");
595 GTK_WIDGET_UNSET_FLAGS( GTK_WIDGET( trim
), GTK_CAN_FOCUS
);
596 gtk_widget_ref(trim
);
597 gtk_object_set_data_full (GTK_OBJECT( this->window
), "trim", trim
, (GtkDestroyNotify
) gtk_widget_unref
);
598 GtkWidget
*vbox_trim
= lookup_widget(this->window
, "vbox_trim");
599 gtk_widget_show(trim
);
600 gtk_box_pack_start(GTK_BOX (vbox_trim
), trim
, TRUE
, TRUE
, 0);
601 gtk_signal_connect( GTK_OBJECT( trim
), "button_press_event", GTK_SIGNAL_FUNC (trim_pressed
), this );
602 gtk_signal_connect( GTK_OBJECT( trim
), "button_release_event", GTK_SIGNAL_FUNC (trim_released
), this );
606 this->playing
[0] = create_pixbuf("empty_16x16.png");
607 this->playing
[1] = create_pixbuf("item_playing_16x16_(Axialis_Team).png");
610 if ( instance
!= NULL
)
612 char *server
= strdup( instance
);
613 char *port
= strchr( server
, ':' );
619 if ( instance_connect( this, server
, port
) )
620 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( lookup_widget( this->window
, "button_connect" ) ), TRUE
);
622 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( lookup_widget( this->window
, "button_disconnect" ) ), TRUE
);
631 /** Get the app window widget.
634 GtkWidget
*dv1394app_get_widget( dv1394app
this )
639 /** Get the applications parser.
642 mvcp_parser
dv1394app_get_parser( dv1394app
this )
647 /** Return the command parser.
650 mvcp
dv1394app_get_command( dv1394app
this )
652 return this->command
;
655 /** Issue a connect to all pages.
658 void dv1394app_connect( dv1394app
this )
661 for ( index
= 0; index
< this->page_count
; index
++ )
662 page_on_connect( this->pages
[ index
] );
665 /** Inform all pages that the unit has changed.
668 void dv1394app_on_unit_change( dv1394app
this, int unit
)
671 this->selected_unit
= unit
;
672 for ( index
= 0; index
< this->page_count
; index
++ )
673 page_on_unit_change( this->pages
[ index
], unit
);
676 /** Return the selected unit.
679 int dv1394app_get_selected_unit( dv1394app
this )
681 return this->selected_unit
;
684 /** Issue a disconnect to all pages.
687 void dv1394app_disconnect( dv1394app
this )
690 if ( this->parser
!= NULL
)
692 for ( index
= 0; index
< this->page_count
; index
++ )
693 page_on_disconnect( this->pages
[ index
] );
694 mvcp_close( this->command
);
695 this->command
= NULL
;
696 mvcp_parser_close( this->parser
);
701 /** Close application.
704 void dv1394app_close( dv1394app
this )
706 dv1394app_disconnect( this );
707 while ( this->page_count
> 0 )
708 page_close( this->pages
[ -- this->page_count
] );
712 char* frames2tc( int f
, float fps
, char* buf
)
714 int tc
[4] = { 0, 0, 0, 0 };
723 tc
[0] = (d
- t
) * fps
;
724 tc
[1] = t
% 60; t
/= 60;
725 tc
[2] = t
% 60; t
/= 60;
729 sprintf(buf
, "%.2d:%.2d:%.2d:%.2d", tc
[3], tc
[2], tc
[1], tc
[0]);