Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

page_capture.cc

Go to the documentation of this file.
00001 /*
00002 * page_capture.cc Notebook Firewire Capture Page Object
00003 * Copyright (C) 2001 Charles Yates <charles.yates@pandora.be>
00004 * Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org>
00005 *
00006 * This program is free software; you can redistribute it and/or modify
00007 * it under the terms of the GNU General Public License as published by
00008 * the Free Software Foundation; either version 2 of the License, or
00009 * (at your option) any later version.
00010 *
00011 * This program is distributed in the hope that it will be useful,
00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 * GNU General Public License for more details.
00015 *
00016 * You should have received a copy of the GNU General Public License
00017 * along with this program; if not, write to the Free Software Foundation,
00018 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <iostream>
00026 
00027 #include <gtk/gtk.h>
00028 #include <math.h>
00029 #include <limits.h>
00030 #include <unistd.h>
00031 
00032 #include "page_capture.h"
00033 #include "riff.h"
00034 #include "avi.h"
00035 #include "playlist.h"
00036 #include "filehandler.h"
00037 #include "ieee1394io.h"
00038 #include "framedisplayer.h"
00039 #include "message.h"
00040 #include "error.h"
00041 #include "frame.h"
00042 #include "page_editor.h"
00043 
00044 static FrameDisplayer *displayer = NULL;
00045 
00046 extern "C"
00047 {
00048 #include "support.h"
00049     #include "callbacks.h"
00050     #include "commands.h"
00051 
00052 #include <pthread.h>
00053 
00054     extern struct navigate_control g_nav_ctl;
00055 
00056     // reader changed to pointer to delay creation of Frame
00057     // objects so prefs dv decoder quality is properly read
00058     IEEE1394Reader *reader = NULL;
00059     AVC *avc = NULL;
00060     static FileHandler *writer = NULL;
00061     extern char cmd[];
00062     static char lastcmd[ 256 ];
00063     static gchar lastPreferenceFilename[ 512 ];
00064     extern KinoCommon *common;
00065     static quadlet_t avcStatus;
00066     char timecode[ 256 ];
00067 
00068     //
00069     // Preview rendering implementation
00070     //
00071 
00072     static void *captureThread( void * p );
00073     static void *avcThread( void * p );
00074     static void *videoThread( void * p );
00075     static pthread_t capturethread;
00076     static pthread_t avcthread;
00077     static pthread_t videothread;
00078     static gboolean audioOn = FALSE;
00079     static pthread_mutex_t writerlock = PTHREAD_MUTEX_INITIALIZER;
00080 
00081     // Define the size of the buffer
00082 #define BUFFERED_FRAMES     25
00083 
00084     // Buffer and positional information
00085     static int framePosition = -1;
00086     static Frame *frameBuffer[ BUFFERED_FRAMES ];
00087     static int frameCount = 0;
00088     static void TriggerAction( );
00089     static int framesCaptured = 0;
00090     static int prevFramesWritten = 0;
00091 }
00092 
00098 PageCapture::PageCapture( KinoCommon *common ) : isCapturing( false ), captureMutex( false ),
00099         avc_enabled( false ), driver_locked( false )
00100 {
00101     std::cerr << "> Creating Capture Page" << std::endl;
00102     this->common = common;
00103     this->frameArea = GTK_DRAWING_AREA( lookup_widget( common->getWidget(), "capture_drawingarea" ) );
00104     gtk_widget_set_double_buffered( GTK_WIDGET( frameArea ), FALSE );
00105     this->avcButton = GTK_TOGGLE_BUTTON( lookup_widget( common->getWidget(), "capture_page_avc_button" ) );
00106     this->recordButton = GTK_TOGGLE_BUTTON( lookup_widget( common->getWidget(), "capture_page_record_button" ) );
00107     this->stopButton = GTK_TOGGLE_BUTTON( lookup_widget( common->getWidget(), "capture_page_stop_button" ) );
00108     this->muteButton = GTK_TOGGLE_BUTTON( lookup_widget( common->getWidget(), "capture_page_mute_button" ) );
00109     this->snapshotButton = GTK_BUTTON( lookup_widget( common->getWidget(), "capture_page_snapshot_button" ) );
00110     this->fileEntry = GTK_ENTRY( lookup_widget( common->getWidget(), "entry_capture_file" ) );
00111     this->timecodeLabel = GTK_LABEL( lookup_widget( common->getWidget(), "position_label_current" ) );
00112     lastPreferenceFilename[ 0 ] = 0;
00113     memset( timecode, 0, sizeof( timecode ) );
00114 }
00115 
00119 PageCapture::~PageCapture()
00120 {
00121     std::cerr << "> Destroying Capture Page" << std::endl;
00122     delete reader;
00123     reader = NULL;
00124 }
00125 
00129 void PageCapture::newFile( )
00130 {
00131     strcpy( lastPreferenceFilename, "" );
00132 }
00133 
00137 void PageCapture::start()
00138 {
00139     std::cerr << ">> Starting Capture" << std::endl;
00140 
00141     // By default, AVC is always disabled
00142     driver_available = false;
00143     check_phyid = true;
00144     avc_enabled = false;
00145     validComponents = 0;
00146     gui_state_was = -1;
00147 
00148     gtk_toggle_button_set_active( avcButton, Preferences::getInstance().enableAVC );
00149     GtkLabel *label = GTK_LABEL( lookup_widget( common->getWidget(), "position_label_total" ) );
00150     gtk_label_set_text( label, "" );
00151 
00152     // Create the reader object
00153 #ifdef HAVE_IEC61883
00154     reader = new iec61883Reader( Preferences::getInstance().channel,
00155                                  Preferences::getInstance().dvCaptureBuffers );
00156 #else
00157     reader = new dv1394Reader( Preferences::getInstance().channel,
00158                                Preferences::getInstance().dvCaptureBuffers );
00159 #endif
00160 
00161     // Create the AV/C object
00162     avc = new AVC();
00163 
00164     // Determine the capture directory and file - rules are:
00165     // 1) if a full path is specified in the preferences, then use that
00166     // 2) if a non-full path is specified, then pick up the project directory and attach preference
00167 
00168     string capture_file_name = Preferences::getInstance().file;
00169     if ( capture_file_name[ 0 ] != '/' )
00170         capture_file_name = common->getPlayList( ) ->GetProjectDirectory( ) + "/" + capture_file_name;
00171 
00172     // If the preferences have changed, then update the filename
00173 
00174     if ( capture_file_name != lastPreferenceFilename )
00175     {
00176         strcpy( lastPreferenceFilename, capture_file_name.c_str( ) );
00177         gtk_entry_set_text( fileEntry, capture_file_name.c_str( ) );
00178     }
00179 
00180     // Have the GUI reflect the mute status
00181     gtk_toggle_button_set_active( this->muteButton, !audioOn );
00182 
00183     // Create the FrameDisplayer
00184     displayer = new FrameDisplayer;
00185 
00186     // Set up the preview rendering
00187     g_nav_ctl.capture_active = TRUE;
00188     pthread_create( &capturethread, NULL, captureThread, this );
00189     pthread_create( &avcthread, NULL, avcThread, this );
00190     pthread_create( &videothread, NULL, videoThread, lookup_widget( common->getWidget(), "capture_drawingarea" ) );
00191 
00192     common->activateWidgets();
00193     gtk_label_set_text( timecodeLabel, "" );
00194     common->packIt( "packer_capture", "packer_capture_outer" );
00195     common->toggleComponents( VIDEO_STOP, false );
00196     common->commitComponentState();
00197 
00198     gtk_notebook_set_page( GTK_NOTEBOOK( lookup_widget( common->getWidget(), "notebook_keyhelp" ) ), 1 );
00199     gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( lookup_widget( common->getWidget(), "menuitem_capture" ) ), TRUE );
00200 }
00201 
00205 void PageCapture::CheckDevices( )
00206 {
00207     static int avcRetry = 0;
00208 
00209     if ( driver_locked == true )
00210     {
00211         std::cerr << "Stopping thread" << std::endl;
00212         reader->StopThread( );
00213         driver_locked = false;
00214         driver_available = false;
00215         avc_enabled = false;
00216         check_phyid = true;
00217         TriggerAction( );
00218     }
00219     else if ( driver_available == false )
00220     {
00221         // Check if the id of the device is valid
00222         Preferences::getInstance( ).phyID = avc->getNodeId( Preferences::getInstance( ).avcGUID );
00223         Preferences::getInstance( ).phyID = avc->isPhyIDValid( Preferences::getInstance( ).phyID );
00224         check_phyid = Preferences::getInstance( ).phyID < 0;
00225 
00226         // Start the reader thread
00227         //std::cerr << "Starting thread" << std::endl;
00228         if ( reader->StartThread( ( avc->getPort() < 0 ) ? 0 : avc->getPort() ) )
00229         {
00230             // The drivers are installed
00231             driver_available = true;
00232             avc_enabled = false;
00233         }
00234         TriggerAction( );
00235     }
00236     else if ( check_phyid )
00237     {
00238         // Check that we're still valid.
00239         Preferences::getInstance( ).phyID = avc->isPhyIDValid( Preferences::getInstance( ).phyID );
00240         check_phyid = Preferences::getInstance( ).phyID < 0;
00241         avc_enabled = false;
00242         TriggerAction( );
00243     }
00244     else if ( avc_enabled )
00245     {
00246         quadlet_t status = avc->TransportStatus( Preferences::getInstance().phyID );
00247 
00248         if ( ( int ) status < 0 )
00249         {
00250             cerr << ">>> AVC status error" << std::endl;
00251             if ( ++avcRetry > 2 )
00252             {
00253                 avcRetry = 0;
00254                 avc_enabled = false;
00255                 check_phyid = true;
00256             }
00257         }
00258         else
00259         {
00260             char t[ 12 ];
00261 
00262             avcStatus = status;
00263             avcRetry = 0;
00264 
00265             if ( !isCapturing && avc->Timecode( Preferences::getInstance().phyID, t ) )
00266             {
00267                 strncpy( timecode, t, 12 );
00268             }
00269             TriggerAction();
00270         }
00271     }
00272     else
00273     {
00274         avc_enabled = Preferences::getInstance().enableAVC;
00275         gtk_toggle_button_set_active( this->avcButton, avc_enabled );
00276         avc->Noop();
00277     }
00278 }
00279 
00282 void PageCapture::applyAVCState( quadlet_t newAVCState )
00283 {
00284     if ( driver_available == false )
00285     {
00286         if ( gui_state_was != 0 )
00287         {
00288             gtk_widget_set_sensitive( GTK_WIDGET( avcButton ), false );
00289             gtk_widget_set_sensitive( GTK_WIDGET( recordButton ), false );
00290             gtk_widget_set_sensitive( GTK_WIDGET( stopButton ), false );
00291             gtk_widget_set_sensitive( GTK_WIDGET( snapshotButton ), false );
00292             gtk_widget_set_sensitive( GTK_WIDGET( muteButton ), false );
00293             validComponents = 0;
00294             common->activateWidgets( );
00295             common->commitComponentState();
00296 #ifdef HAVE_IEC61883
00297             common->setStatusBar( _( "WARNING: raw1394 kernel module not loaded or failure to read/write /dev/raw1394!" ) );
00298 #else
00299             common->setStatusBar( _( "WARNING: dv1394 kernel module not loaded or failure to read/write %s" ),
00300                 Preferences::getInstance().dvCaptureDevice );
00301 #endif
00302         }
00303         gui_state_was = 0;
00304     }
00305     else if ( check_phyid == true )
00306     {
00307         if ( gui_state_was != 1 )
00308         {
00309             gtk_widget_set_sensitive( GTK_WIDGET( avcButton ), false );
00310             gtk_widget_set_sensitive( GTK_WIDGET( recordButton ), true );
00311             gtk_widget_set_sensitive( GTK_WIDGET( stopButton ), true );
00312             gtk_widget_set_sensitive( GTK_WIDGET( snapshotButton ), true );
00313             gtk_widget_set_sensitive( GTK_WIDGET( muteButton ), true );
00314             validComponents = 0;
00315             common->activateWidgets( );
00316             common->commitComponentState();
00317             common->setStatusBar( _( "No AV/C compliant cam connected or not switched on?" ) );
00318         }
00319         gui_state_was = 1;
00320     }
00321     else if ( avc_enabled )
00322     {
00323         if ( gui_state_was != 2 )
00324         {
00325             gtk_widget_set_sensitive( GTK_WIDGET( avcButton ), true );
00326             gtk_widget_set_sensitive( GTK_WIDGET( recordButton ), true );
00327             gtk_widget_set_sensitive( GTK_WIDGET( stopButton ), true );
00328             gtk_widget_set_sensitive( GTK_WIDGET( snapshotButton ), true );
00329             gtk_widget_set_sensitive( GTK_WIDGET( muteButton ), true );
00330 
00331             validComponents = VIDEO_REWIND |
00332                               VIDEO_PLAY |
00333                               VIDEO_STOP |
00334                               VIDEO_FAST_FORWARD |
00335                               VIDEO_FORWARD |
00336                               VIDEO_BACK |
00337                               VIDEO_NEXT_SCENE |
00338                               VIDEO_START_OF_SCENE |
00339                               VIDEO_START_OF_MOVIE |
00340                               VIDEO_SHUTTLE |
00341                               VIDEO_END_OF_MOVIE |
00342                               INFO_FRAME;
00343 
00344             common->activateWidgets( );
00345 
00346             gui_state_was = 2;
00347 
00348             common->setStatusBar( "AV/C Controls Enabled" );
00349             cerr << ">>> AVC enabled " << std::endl;
00350 
00351             // Ensure that we reflect the current avc state when changing gui states
00352             avcState = true;
00353         }
00354 
00355         if ( avcState != newAVCState )
00356         {
00357             quadlet_t resp2 = AVC1394_MASK_RESPONSE_OPERAND( newAVCState, 2 );
00358             quadlet_t resp3 = AVC1394_MASK_RESPONSE_OPERAND( newAVCState, 3 );
00359             this->avcState = newAVCState;
00360 
00361             common->toggleComponents( common->getComponentState(), false );
00362             g_nav_ctl.active = FALSE;
00363 
00364             if ( resp2 == AVC1394_VCR_RESPONSE_TRANSPORT_STATE_PLAY )
00365             {
00366                 if ( resp3 >= AVC1394_VCR_OPERAND_PLAY_FAST_FORWARD_1
00367                         && resp3 <= AVC1394_VCR_OPERAND_PLAY_FASTEST_FORWARD )
00368                 {
00369                     common->toggleComponents( VIDEO_FAST_FORWARD, true );
00370                     g_nav_ctl.active = TRUE;
00371                 }
00372                 else if ( resp3 >= AVC1394_VCR_OPERAND_PLAY_FAST_REVERSE_1
00373                           && resp3 <= AVC1394_VCR_OPERAND_PLAY_FASTEST_REVERSE )
00374                 {
00375                     common->toggleComponents( VIDEO_REWIND, true );
00376                     g_nav_ctl.active = TRUE;
00377                 }
00378                 else if ( resp3 == AVC1394_VCR_OPERAND_PLAY_FORWARD_PAUSE )
00379                 {
00380                     common->toggleComponents( VIDEO_PLAY, false );
00381                 }
00382                 else
00383                 {
00384                     common->toggleComponents( VIDEO_PLAY, true );
00385                     g_nav_ctl.active = TRUE;
00386                 }
00387             }
00388             else if ( resp2 == AVC1394_VCR_RESPONSE_TRANSPORT_STATE_WIND )
00389             {
00390                 if ( resp3 == AVC1394_VCR_OPERAND_WIND_HIGH_SPEED_REWIND )
00391                 {
00392                     common->toggleComponents( VIDEO_START_OF_MOVIE, true );
00393                 }
00394                 else if ( resp3 == AVC1394_VCR_OPERAND_WIND_STOP )
00395                 {
00396                     stopCapture();
00397                     common->toggleComponents( VIDEO_STOP, true );
00398                 }
00399                 else if ( resp3 == AVC1394_VCR_OPERAND_WIND_REWIND )
00400                 {
00401                     common->toggleComponents( VIDEO_START_OF_MOVIE, true );
00402                 }
00403                 else if ( resp3 == AVC1394_VCR_OPERAND_WIND_FAST_FORWARD )
00404                 {
00405                     common->toggleComponents( VIDEO_END_OF_MOVIE, true );
00406                 }
00407                 else
00408                 {
00409                     std::cerr << "AVC Status: Unkown winding" << std::endl;
00410                 }
00411             }
00412             else if ( resp2 == AVC1394_VCR_RESPONSE_TRANSPORT_STATE_RECORD )
00413             {
00414                 if ( resp3 == AVC1394_VCR_OPERAND_RECORD_PAUSE )
00415                 {
00416                     common->toggleComponents( VIDEO_PLAY, false );
00417                 }
00418                 else
00419                 {
00420                     common->toggleComponents( VIDEO_PLAY, true );
00421                     g_nav_ctl.active = TRUE;
00422                 }
00423             }
00424             else
00425             {
00426                 std::cerr << "AVC Status: Unknown state" << std::endl;
00427             }
00428             common->commitComponentState();
00429         }
00430     }
00431     else
00432     {
00433         if ( gui_state_was != 3 )
00434         {
00435             gtk_widget_set_sensitive( GTK_WIDGET( avcButton ), true );
00436             gtk_widget_set_sensitive( GTK_WIDGET( recordButton ), true );
00437             gtk_widget_set_sensitive( GTK_WIDGET( stopButton ), true );
00438             gtk_widget_set_sensitive( GTK_WIDGET( snapshotButton ), true );
00439             gtk_widget_set_sensitive( GTK_WIDGET( muteButton ), true );
00440             validComponents = 0;
00441             common->activateWidgets( );
00442             common->commitComponentState();
00443             if ( !isCapturing )
00444                 common->setStatusBar( _( "AV/C Controls Not Enabled." ) );
00445             gui_state_was = 3;
00446         }
00447     }
00448 }
00449 
00453 gulong PageCapture::activate()
00454 {
00455     return this->validComponents | SCENE_LIST;
00456 }
00457 
00461 void PageCapture::clean()
00462 {
00463     std::cerr << ">> Leaving Capture" << std::endl;
00464     
00465     if ( g_nav_ctl.capture_active == TRUE )
00466     {
00467         // Stop the capture
00468         stopCapture();
00469 
00470         // Stop the video
00471         if ( avc_enabled && Preferences::getInstance( ).phyID >= 0 )
00472             videoStop();
00473 
00474         // Start the deactivation process for the backround processes
00475         g_nav_ctl.active = FALSE;
00476         g_nav_ctl.capture_active = FALSE;
00477 
00478         // Join with the capture thread to make sure we're definitely finished
00479         reader->TriggerAction();
00480         pthread_join( capturethread, NULL );
00481         pthread_join( avcthread, NULL );
00482 
00483         // Stop the video thread
00484         gdk_threads_leave();
00485         pthread_join( videothread, NULL );
00486         gdk_threads_enter();
00487 
00488         // Stop the reading thread
00489         reader->StopThread();
00490     }
00491 
00492     common->setStatusBar( "" );
00493 
00494     // Clean up objects created in start
00495     delete displayer;
00496     displayer = NULL;
00497     delete reader;
00498     reader = NULL;
00499     delete avc;
00500     avc = NULL;
00501 }
00502 
00503 
00507 void PageCapture::videoStartOfMovie()
00508 {
00509     if ( avc_enabled )
00510     {
00511         audioOn = FALSE;
00512         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00513         common->toggleComponents( VIDEO_START_OF_MOVIE, avc_enabled );
00514         if ( avc->Stop( Preferences::getInstance().phyID ) >= 0 )
00515             avc->Rewind( Preferences::getInstance().phyID );
00516     }
00517 }
00518 
00522 void PageCapture::videoPreviousScene()
00523 {
00524     if ( avc_enabled )
00525     {
00526         audioOn = FALSE;
00527         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00528         common->toggleComponents( VIDEO_START_OF_SCENE, avc_enabled );
00529         if ( avc->Pause( Preferences::getInstance().phyID ) >= 0 )
00530             avc->PreviousScene( Preferences::getInstance().phyID );
00531     }
00532 }
00533 
00537 void PageCapture::videoStartOfScene()
00538 {
00539     if ( avc_enabled )
00540     {
00541         audioOn = FALSE;
00542         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00543         common->toggleComponents( VIDEO_START_OF_SCENE, avc_enabled );
00544         if ( avc->Pause( Preferences::getInstance().phyID ) >= 0 )
00545             avc->PreviousScene( Preferences::getInstance().phyID );
00546     }
00547 }
00548 
00552 void PageCapture::videoRewind()
00553 {
00554     if ( avc_enabled )
00555     {
00556         audioOn = FALSE;
00557         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00558         common->toggleComponents( VIDEO_REWIND, avc_enabled );
00559         if ( avc->Pause( Preferences::getInstance().phyID ) >= 0 )
00560             avc->Rewind( Preferences::getInstance().phyID );
00561     }
00562 }
00563 
00567 void PageCapture::videoBack()
00568 {
00569     if ( avc_enabled )
00570     {
00571         audioOn = FALSE;
00572         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00573         common->toggleComponents( VIDEO_BACK, avc_enabled );
00574         if ( avc->Pause( Preferences::getInstance().phyID ) >= 0 )
00575             avc->Back( Preferences::getInstance().phyID );
00576     }
00577 }
00578 
00582 void PageCapture::videoPlay()
00583 {
00584     if ( avc_enabled )
00585     {
00586         if ( g_nav_ctl.active == TRUE )
00587         {
00588             audioOn = FALSE;
00589             gtk_toggle_button_set_active( this->muteButton, TRUE );
00590             common->toggleComponents( VIDEO_PLAY, false );
00591             avc->Pause( Preferences::getInstance().phyID );
00592         }
00593         else
00594         {
00595             audioOn = TRUE;
00596             gtk_toggle_button_set_active( this->muteButton, FALSE );
00597             common->toggleComponents( VIDEO_PLAY, true );
00598             avc->Play( Preferences::getInstance().phyID );
00599         }
00600     }
00601 }
00602 
00606 void PageCapture::videoPause()
00607 {
00608     if ( avc_enabled )
00609     {
00610         audioOn = FALSE;
00611         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00612         avc->Pause( Preferences::getInstance().phyID );
00613     }
00614 }
00615 
00619 void PageCapture::videoStop()
00620 {
00621     if ( avc_enabled )
00622     {
00623         audioOn = FALSE;
00624         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00625         common->toggleComponents( VIDEO_STOP, avc_enabled );
00626         avc->Stop( Preferences::getInstance().phyID );
00627     }
00628 }
00629 
00633 void PageCapture::videoForward()
00634 {
00635     if ( avc_enabled )
00636     {
00637         audioOn = FALSE;
00638         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00639         common->toggleComponents( VIDEO_FORWARD, avc_enabled );
00640         if ( avc->Pause( Preferences::getInstance().phyID ) >= 0 )
00641             avc->Forward( Preferences::getInstance().phyID );
00642     }
00643 }
00644 
00648 void PageCapture::videoFastForward()
00649 {
00650     if ( avc_enabled )
00651     {
00652         audioOn = FALSE;
00653         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00654         common->toggleComponents( VIDEO_FAST_FORWARD, avc_enabled );
00655         if ( avc->Pause( Preferences::getInstance().phyID ) >= 0 )
00656             avc->FastForward( Preferences::getInstance().phyID );
00657     }
00658 }
00659 
00663 void PageCapture::videoNextScene()
00664 {
00665     if ( avc_enabled )
00666     {
00667         audioOn = FALSE;
00668         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00669         common->toggleComponents( VIDEO_NEXT_SCENE, avc_enabled );
00670         if ( avc->Pause( Preferences::getInstance().phyID ) >= 0 )
00671             avc->NextScene( Preferences::getInstance().phyID );
00672     }
00673 }
00674 
00678 void PageCapture::videoEndOfScene()
00679 {
00680     if ( avc_enabled )
00681     {
00682         audioOn = FALSE;
00683         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00684         common->toggleComponents( VIDEO_NEXT_SCENE, avc_enabled );
00685         if ( avc->Pause( Preferences::getInstance().phyID ) >= 0 )
00686             avc->NextScene( Preferences::getInstance().phyID );
00687     }
00688 }
00689 
00693 void PageCapture::videoEndOfMovie()
00694 {
00695     if ( avc_enabled )
00696     {
00697         audioOn = FALSE;
00698         gtk_toggle_button_set_active( this->muteButton, !audioOn );
00699         common->toggleComponents( VIDEO_END_OF_MOVIE, avc_enabled );
00700         if ( avc->Stop( Preferences::getInstance().phyID ) >= 0 )
00701             avc->FastForward( Preferences::getInstance().phyID );
00702     }
00703 }
00704 
00712 void PageCapture::videoShuttle( int angle )
00713 {
00714     if ( avc_enabled )
00715     {
00716         audioOn = FALSE;
00717         if ( angle != 0 )
00718             avc->Shuttle( Preferences::getInstance().phyID, angle * 2 );
00719         else
00720             avc->Pause( Preferences::getInstance().phyID );
00721     }
00722 }
00723 
00724 
00730 gboolean PageCapture::processKeyboard( GdkEventKey *event )
00731 {
00732     gboolean ret = FALSE;
00733 
00734     // Only process while not escape mode
00735     if ( g_nav_ctl.escaped == FALSE )
00736     {
00737         if ( strcmp( lastcmd, "alt" ) == 0 )
00738         {
00739             strcpy( lastcmd, "" );
00740             return ret;
00741         }
00742 
00743         // Translate special keys to equivalent command
00744         switch ( event->keyval )
00745         {
00746         case GDK_Home:
00747             strcat( cmd, "gg");
00748             ret = TRUE;
00749             break;
00750         case GDK_End:
00751             strcat( cmd, "G");
00752             ret = TRUE;
00753             break;
00754         case GDK_BackSpace:
00755         case GDK_Left:
00756             strcat( cmd, "h" );
00757             ret = TRUE;
00758             break;
00759         case GDK_Up:
00760             strcat( cmd, "k" );
00761             ret = TRUE;
00762             break;
00763         case GDK_Right:
00764             strcat( cmd, "l" );
00765             ret = TRUE;
00766             break;
00767         case GDK_Return:
00768             startCapture();
00769             cmd[ 0 ] = 0;
00770             break;
00771         case GDK_Down:
00772             strcat( cmd, "j" );
00773             ret = TRUE;
00774             break;
00775         case GDK_Escape:
00776             if ( this->isCapturing )
00777             {
00778                 common->keyboardFeedback( "Esc", _( "Stop Capture" ) );
00779                 stopCapture();
00780             }
00781             else if ( common->getComponentState() & VIDEO_STOP )
00782             {
00783                 common->changePageRequest( PAGE_EDITOR );
00784             }
00785             else if ( avc_enabled && Preferences::getInstance( ).phyID >= 0 )
00786             {
00787                 common->keyboardFeedback( "Esc", _( "Stop" ) );
00788                 common->videoStop( );
00789             }
00790             else
00791             {
00792                 common->changePageRequest( PAGE_EDITOR );
00793             }
00794             cmd[ 0 ] = 0;
00795             return TRUE;
00796         case GDK_Alt_L:
00797         case GDK_Alt_R:
00798             strcpy( lastcmd, "alt" );
00799             return ret;
00800         default:
00801             strcat( cmd, event->string );
00802             break;
00803         }
00804 
00805         if ( !strcmp( cmd, "." ) )
00806             strcpy( cmd, lastcmd );
00807 
00808         processCommand( cmd );
00809 
00810     }
00811     else if ( event->keyval == GDK_Return || event->keyval == GDK_Escape )
00812     {
00813         g_nav_ctl.escaped = FALSE;
00814         gtk_entry_set_editable( fileEntry, FALSE );
00815         gtk_widget_grab_focus( common->getWidget() );
00816     }
00817     return ret;
00818 }
00819 
00825 gboolean PageCapture::processCommand( char *command )
00826 {
00827     int count = 1;
00828     char real[ 256 ] = "";
00829 
00830     strcpy( cmd, command );
00831 
00832     switch ( sscanf( cmd, "%d%s", &count, real ) )
00833     {
00834     case 1:
00835         // Numeric value only - return immediately if the cmd is not "0"
00836         if ( strcmp( cmd, "0" ) )
00837         {
00838             common->keyboardFeedback( cmd, "" );
00839             return FALSE;
00840         }
00841         break;
00842     case 0:
00843         sscanf( cmd, "%s", real );
00844         count = 1;
00845         break;
00846     }
00847 
00848     strcpy( lastcmd, cmd );
00849 
00850     /* mode switching */
00851     
00852     if ( strcmp( cmd, "t" ) == 0 )
00853     {
00854         common->keyboardFeedback( cmd, _( "Trim" ) );
00855         common->changePageRequest( PAGE_TRIM );
00856         cmd[ 0 ] = 0;
00857     }
00858 
00859     else if ( strcmp( cmd, "v" ) == 0 )
00860     {
00861         common->keyboardFeedback( cmd, _( "Timeline" ) );
00862         common->changePageRequest( PAGE_TIMELINE );
00863         cmd[ 0 ] = 0;
00864     }
00865 
00866     else if ( strcmp( cmd, "C" ) == 0 )
00867     {
00868         common->keyboardFeedback( cmd, _( "FX" ) );
00869         common->changePageRequest( PAGE_MAGICK );
00870         cmd[ 0 ] = 0;
00871     }
00872 
00873     /* switch to export mode */
00874 
00875     else if ( strcmp( cmd, ":W" ) == 0 )
00876     {
00877         common->keyboardFeedback( cmd, _( "Export" ) );
00878         common->changePageRequest( PAGE_EXPORT );
00879         cmd[ 0 ] = 0;
00880     }
00881 
00882     else if ( strcmp( cmd, "F2" ) == 0 )
00883     {
00884         common->keyboardFeedback( cmd, _( "Edit" ) );
00885         common->changePageRequest( PAGE_EDITOR );
00886         cmd[ 0 ] = 0;
00887     }
00888 
00889     /* write PlayList */
00890 
00891     else if ( strcmp( cmd, ":w" ) == 0 )
00892     {
00893         common->keyboardFeedback( cmd, _( "Write playlist" ) );
00894         common->savePlayList( );
00895         cmd[ 0 ] = 0;
00896     }
00897 
00898     /* quit */
00899 
00900     else if ( strcmp( cmd, ":q" ) == 0 )
00901     {
00902         common->keyboardFeedback( cmd, _( "quit" ) );
00903         kinoDeactivate();
00904         cmd[ 0 ] = 0;
00905     }
00906     
00907     else if ( strcmp( real, "Enter" ) == 0 && ! isCapturing )
00908     {
00909         common->keyboardFeedback( cmd, _( "Start Capture" ) );
00910         startCapture();
00911         cmd[ 0 ] = 0;
00912     }
00913 
00914     else if ( strcmp( real, "Esc" ) == 0 )
00915     {
00916         if ( this->isCapturing )
00917         {
00918             common->keyboardFeedback( "Esc", _( "Stop Capture" ) );
00919             stopCapture();
00920         }
00921         else if ( avc_enabled && Preferences::getInstance( ).phyID >= 0 )
00922         {
00923             common->keyboardFeedback( "Esc", _( "Stop" ) );
00924             common->videoStop( );
00925         }
00926         cmd[ 0 ] = 0;
00927     }
00928 
00929     /* Navigation */
00930 
00931     if ( validComponents == 0 || Preferences::getInstance( ).phyID < 0 )
00932         return FALSE;
00933 
00934     /* play, pause */
00935 
00936     if ( strcmp( cmd, " " ) == 0 )
00937     {
00938         if ( g_nav_ctl.active == FALSE )
00939         {
00940             common->keyboardFeedback( cmd, _( "Play" ) );
00941             common->videoPlay( );
00942         }
00943         else
00944         {
00945             common->keyboardFeedback( cmd, _( "Pause" ) );
00946             common->videoPause( );
00947         }
00948         cmd[ 0 ] = 0;
00949     }
00950 
00951     /* advance one frame */
00952 
00953     else if ( strcmp( real, "l" ) == 0 )
00954     {
00955         common->keyboardFeedback( cmd, _( "Move forward" ) );
00956         common->videoForward();
00957         cmd[ 0 ] = 0;
00958     }
00959 
00960     /* backspace one frame */
00961 
00962     else if ( strcmp( real, "h" ) == 0 )
00963     {
00964         common->keyboardFeedback( cmd, _( "Move backward" ) );
00965         common->videoBack();
00966         cmd[ 0 ] = 0;
00967     }
00968 
00969     /* advance one second */
00970 
00971     else if ( ( strcmp( real, "w" ) == 0 || strcmp( real, "W" ) == 0 ||
00972                 strcmp( real, "e" ) == 0 || strcmp( real, "E" ) == 0 ) )
00973     {
00974         common->keyboardFeedback( cmd, _( "Fast Forward" ) );
00975         common->videoFastForward();
00976         cmd[ 0 ] = 0;
00977     }
00978 
00979     /* backspace one second */
00980 
00981     else if ( ( strcmp( real, "b" ) == 0 || strcmp( real, "B" ) == 0 ) )
00982     {
00983         common->keyboardFeedback( cmd, _( "Rewind" ) );
00984         common->videoRewind();
00985         cmd[ 0 ] = 0;
00986     }
00987 
00988     /* start of scene */
00989 
00990     else if ( ( strcmp( cmd, "0" ) == 0 || strcmp( real, "^" ) == 0 ) )
00991     {
00992         common->videoStartOfScene( );
00993         for ( ; count > 1; count -- )
00994         {
00995             common->g_currentFrame --;
00996             videoStartOfScene( );
00997         }
00998         common->keyboardFeedback( cmd, _( "Move to start of scene" ) );
00999         cmd[ 0 ] = 0;
01000     }
01001 
01002     /* end of scene */
01003 
01004     else if ( strcmp( real, "$" ) == 0 )
01005     {
01006         common->videoEndOfScene( );
01007         for ( ; count > 1; count -- )
01008         {
01009             common->g_currentFrame ++;
01010             videoEndOfScene( );
01011         }
01012         common->keyboardFeedback( cmd, _( "Move to end of scene" ) );
01013         cmd[ 0 ] = 0;
01014     }
01015 
01016     /* start of next scene */
01017 
01018     else if ( ( strcmp( real, "j" ) == 0 || strcmp( real, "+" ) == 0 ) )
01019     {
01020         for ( ; count >= 1; count -- )
01021             common->videoNextScene( );
01022         common->keyboardFeedback( cmd, _( "Move to start of next scene" ) );
01023         cmd[ 0 ] = 0;
01024     }
01025 
01026     /* start of previous scene */
01027 
01028     else if ( ( strcmp( real, "k" ) == 0 || strcmp( real, "-" ) == 0 ) )
01029     {
01030         for ( ; count >= 1; count -- )
01031             common->videoPreviousScene( );
01032         common->keyboardFeedback( cmd, _( "Move to start of previous scene" ) );
01033         cmd[ 0 ] = 0;
01034     }
01035 
01036     /* first frame */
01037 
01038     else if ( strcmp( cmd, "gg" ) == 0 )
01039     {
01040         common->videoStartOfMovie( );
01041         common->keyboardFeedback( cmd, _( "Move to first frame" ) );
01042         cmd[ 0 ] = 0;
01043     }
01044 
01045     /* last frame */
01046 
01047     else if ( strcmp( cmd, "G" ) == 0 )
01048     {
01049         common->videoEndOfMovie( );
01050         common->keyboardFeedback( cmd, _( "Move to last frame" ) );
01051         cmd[ 0 ] = 0;
01052     }
01053 
01054     else
01055     {
01056         // Check for invalid commands
01057         if ( strlen( real ) > 3 )
01058             cmd[ 0 ] = 0;
01059         else if ( strchr( "dgy ", real[ strlen( real ) - 1 ] ) == NULL )
01060             cmd[ 0 ] = 0;
01061 
01062         common->keyboardFeedback( cmd, "" );
01063 
01064 #if 0
01065 
01066         printf( "send_event: %2.2x\n", event->send_event );
01067         printf( "time  : %8.8x\n", event->time );
01068         printf( "state : %8.8x\n", event->state );
01069         printf( "keyval: %8.8x\n", event->keyval );
01070         printf( "length: %8.8x\n", event->length );
01071         printf( "string: %s\n", event->string );
01072         printf( "(hex) : %2.2x\n", event->string[ 0 ] );
01073         printf( "cmd   : %s\n", cmd );
01074 #endif
01075 
01076     }
01077 
01078     return FALSE;
01079 }
01080 
01086 void PageCapture::startCapture()
01087 {
01088     if ( !captureMutex && !isCapturing )
01089     {
01090         captureMutex = true;
01091         isCapturing = true;
01092         gtk_toggle_button_set_active( recordButton, true );
01093         gtk_toggle_button_set_active( stopButton, false );
01094         gtk_widget_set_sensitive( GTK_WIDGET( snapshotButton ), false );
01095         gtk_widget_set_sensitive( GTK_WIDGET( avcButton ), false );
01096         captureMutex = false;
01097 
01098         char filename[ 512 ];
01099         strcpy( filename, gtk_entry_get_text( fileEntry ) );
01100 
01101         if ( !strcmp( filename, "" ) )
01102         {
01103             common->setStatusBar( _( "You must enter a filename to capture." ) );
01104             captureMutex = true;
01105             gtk_toggle_button_set_active( recordButton, false );
01106             gtk_toggle_button_set_active( stopButton, true );
01107             gtk_widget_set_sensitive( GTK_WIDGET( snapshotButton ), true );
01108             gtk_widget_set_sensitive( GTK_WIDGET( avcButton ), true );
01109             captureMutex = false;
01110             isCapturing = false;
01111 
01112         }
01113         else
01114         {
01115             Frame *frame = NULL;
01116             int retry;
01117             const int MAX_RETRIES = 10;
01118             struct timespec t =
01119                 {
01120                     1, 0UL
01121                 };
01122 
01123             if ( avc_enabled )
01124             {
01125                 g_nav_ctl.active = FALSE;
01126                 videoPlay();
01127                 g_nav_ctl.active = TRUE;
01128                 gui_state_was = 2;
01129                 validComponents = 0;
01130                 common->activateWidgets( );
01131                 common->commitComponentState();
01132                 gtk_widget_set_sensitive( GTK_WIDGET( recordButton ), true );
01133                 gtk_widget_set_sensitive( GTK_WIDGET( stopButton ), true );
01134                 gtk_widget_set_sensitive( GTK_WIDGET( muteButton ), true );
01135                 gtk_widget_set_sensitive( GTK_WIDGET( avcButton ), false );
01136                 gtk_widget_set_sensitive( GTK_WIDGET( snapshotButton ), false );
01137             }
01138 
01139             FileHandler *handler = NULL;
01140 
01141             switch ( Preferences::getInstance().fileFormat )
01142             {
01143             case RAW_FORMAT:
01144                 handler = new RawHandler();
01145                 break;
01146 #ifdef HAVE_LIBQUICKTIME
01147 
01148             case QT_FORMAT:
01149                 handler = new QtHandler();
01150                 break;
01151 #endif
01152 
01153             case AVI_DV1_FORMAT:
01154             case AVI_DV2_FORMAT:
01155                 AVIHandler *aviWriter = new AVIHandler( Preferences::getInstance().fileFormat );
01156                 handler = aviWriter;
01157                 aviWriter->SetOpenDML( Preferences::getInstance().isOpenDML ||
01158                                        Preferences::getInstance().maxFileSize == 0 ||
01159                                        Preferences::getInstance().maxFileSize > 1000 );
01160                 break;
01161             }
01162 
01163             /* set command line parameters to the handler object */
01164 
01165             handler->SetMaxFrameCount( Preferences::getInstance().frames );
01166             handler->SetAutoSplit( Preferences::getInstance().autoSplit );
01167             handler->SetTimeStamp( Preferences::getInstance().timeStamp );
01168             handler->SetEveryNthFrame( Preferences::getInstance().every );
01169             handler->SetMaxFileSize( ( off_t ) Preferences::getInstance().maxFileSize * ( off_t ) ( 1024 * 1024 ) );
01170             handler->SetBaseName( filename );
01171 
01172             // without this, if no video has yet played, then frameBuffer is
01173             // likely to be empty due to latency of avcPlay above
01174             for ( retry = MAX_RETRIES; retry > 0 && isCapturing; --retry )
01175             {
01176                 frame = frameBuffer[ framePosition ];
01177 
01178                 if ( frame != NULL )
01179                 {
01180                     // frame must be normal speed to get correct audio stream params for type2 AVI
01181                     if ( frame->IsNormalSpeed() )
01182                     {
01183                         reader->ResetDroppedFrames();
01184                         if ( handler->WriteFrame( *frame ) == false )
01185                         {
01186                             delete handler;
01187                             captureMutex = false;
01188                             stopCapture();
01189                             modal_message( _( "Failed to write to file. Is file name OK?" ) );
01190                         }
01191                         else
01192                         {
01193                             pthread_mutex_lock( &writerlock );
01194                             writer = handler;
01195                             pthread_mutex_unlock( &writerlock );
01196                             common->setStatusBar( ( string( _( "Capturing " ) ) + handler->GetFilename() ).c_str() );
01197                         }
01198                         break;
01199                     }
01200                 }
01201                 common->setStatusBar( _( "Waiting for DV %s%d ..." ),
01202                                       ( avc_enabled ? "" : _( "(press play on camera) " ) ), retry );
01203                 while ( gtk_events_pending() )
01204                     gtk_main_iteration();
01205                 nanosleep( &t, NULL );
01206             }
01207             if ( retry == 0 && isCapturing )
01208             {
01209                 // timed out waiting for iso stream, stop capture
01210                 this->stopCapture();
01211                 common->setStatusBar( _( "Capture failed." ) );
01212             }
01213             else if ( !isCapturing )
01214                 common->setStatusBar( _( "Capture stopped." ) );
01215         }
01216     }
01217     else if ( !captureMutex )
01218     {
01219         captureMutex = true;
01220         gtk_toggle_button_set_active( recordButton, true );
01221         captureMutex = false;
01222     }
01223 }
01224 
01230 void PageCapture::stopCapture()
01231 {
01232 
01233     if ( !captureMutex && isCapturing )
01234     {
01235         captureMutex = true;
01236 
01237         pthread_mutex_lock( &writerlock );
01238         if ( writer != NULL )
01239         {
01240             writer->Close();
01241             delete writer;
01242             writer = NULL;
01243         }
01244         pthread_mutex_unlock( &writerlock );
01245 
01246         gtk_toggle_button_set_active( recordButton, false );
01247         gtk_toggle_button_set_active( stopButton, true );
01248         gtk_widget_set_sensitive( GTK_WIDGET( snapshotButton ), true );
01249         gtk_widget_set_sensitive( GTK_WIDGET( avcButton ), true );
01250 
01251         audioOn = FALSE;
01252         gtk_toggle_button_set_active( this->muteButton, !audioOn );
01253 
01254         if ( avc_enabled )
01255         {
01256             gtk_toggle_button_set_active( avcButton, avc_enabled );
01257             gui_state_was = -1;
01258             videoPause( );
01259         }
01260         common->setStatusBar( "" );
01261         isCapturing = false;
01262         captureMutex = false;
01263     }
01264     else if ( !captureMutex )
01265     {
01266         captureMutex = true;
01267         gtk_toggle_button_set_active( stopButton, true );
01268         captureMutex = false;
01269     }
01270     
01271     // Pick up any captured files
01272     for ( unsigned int index = 0; index < FileTracker::GetInstance().Size(); index ++ )
01273     {
01274         char *file = FileTracker::GetInstance().Get( ( int ) index );
01275         switch ( FileTracker::GetInstance().GetMode() )
01276         {
01277         case CAPTURE_IGNORE:
01278             // Do nothing
01279             break;
01280         case CAPTURE_FRAME_INSERT:
01281             common->loadMediaObject( file, common->g_currentFrame == -1 ? 0 : common->g_currentFrame );
01282             break;
01283         case CAPTURE_FRAME_APPEND:
01284             common->loadMediaObject( file, common->g_currentFrame + 1 );
01285             break;
01286         case CAPTURE_MOVIE_APPEND:
01287             common->loadMediaObject( file, common->getPlayList() ->GetNumFrames() );
01288             break;
01289         }
01290         common->hasListChanged = TRUE;
01291     }
01292 
01293     // Clear the list to avoid reloading on a subsequent re-entry
01294     FileTracker::GetInstance().Clear();
01295 
01296     // Just to make sure we refresh what needs to be refreshed
01297     if ( common->hasListChanged )
01298     {
01299         GetEditorBackup()->Store( common->getPlayList() );
01300         common->setWindowTitle( );
01301         common->getPageEditor()->DrawBar( common->g_currentFrame );
01302         if ( common->g_currentFrame == -1 )
01303             common->g_currentFrame = 0;
01304     }
01305     
01306 }
01307 
01308 
01311 void PageCapture::saveFrame()
01312 {
01313     Frame * frame = frameBuffer[ framePosition ];
01314 
01315     if ( frame != NULL )
01316     {
01317         unsigned char pixels[ FRAME_MAX_WIDTH * FRAME_MAX_HEIGHT * 4 ];
01318         GError *gerror = NULL;
01319 
01320         frame->ExtractPreviewRGB( pixels );
01321         GdkPixbuf *im = gdk_pixbuf_new_from_data
01322                         ( pixels, GDK_COLORSPACE_RGB, FALSE, 8,
01323                           frame->GetWidth(), frame->GetHeight(),
01324                           frame->GetWidth() * 3, NULL, NULL );
01325 
01326         // resample pixel aspect
01327         {
01328             int width = frame->GetWidth();
01329             if ( frame->IsWide() )
01330                 width = frame->IsPAL() ? 1024 : 854;
01331             AspectRatioCalculator calc( width, frame->GetHeight(),
01332                                     frame->GetWidth(), frame->GetHeight(),
01333                                     frame->IsPAL(), frame->IsWide() );
01334             GdkPixbuf *scaled = gdk_pixbuf_scale_simple( im, calc.width, calc.height, GDK_INTERP_HYPER );
01335             g_object_unref( im );
01336             im = scaled;
01337         }
01338 
01339         char *filename = common->getFileToSave( _( "Save Still Frame" ) );
01340         if ( strcmp( filename, "" ) )
01341         {
01342             const std::string FileName( filename );
01343             const std::string suffix( FileName.begin() + FileName.rfind( "." ), FileName.end() );
01344             if ( suffix == ".png" )
01345                 gdk_pixbuf_save( im, filename, "png", &gerror, NULL );
01346             else
01347                 gdk_pixbuf_save( im, filename, "jpeg", &gerror, "quality", "80", NULL );
01348             if ( gerror != NULL )
01349             {
01350                 modal_message( gerror->message );
01351                 g_error_free( gerror );
01352             }
01353         }
01354 
01355         g_object_unref( im );
01356     }
01357 }
01358 
01359 void PageCapture::windowMoved()
01360 {
01361     if ( frameBuffer[ framePosition ] == NULL )
01362         common->clearPreview( frameArea );
01363     else
01364         displayer->Put( *frameBuffer[ framePosition ], GTK_WIDGET( frameArea ), TRUE );
01365 }
01366 
01367 
01370 void PageCapture::showFrameInfo( int i )
01371 {
01372     GtkLabel *total = GTK_LABEL( lookup_widget( common->getWidget(), "position_label_total" ) );
01373     string tc;
01374     char msg[ 128 ] = "";
01375     Frame *frame = getFrame();
01376     
01377     if ( isCapturing && frame )
01378     {
01379         common->getTime().setFramerate( frame->GetFrameRate() );
01380         string tc = "<span size=\"x-large\">" + common->getTime().parseFramesToString( i, common->getTimeFormat() ) + "</span>";
01381         gtk_label_set_markup( timecodeLabel, tc.c_str() );
01382         gtk_widget_set_redraw_on_allocate( GTK_WIDGET( timecodeLabel ), FALSE );
01383         snprintf( msg, 128, "%s %d", _("Dropped:"), reader->GetDroppedFrames() );
01384         gtk_label_set_markup( total, msg );
01385     }
01386     else if ( avc_enabled && common->getTimeFormat() != SMIL::Time::TIME_FORMAT_NONE )
01387     {
01388         snprintf( msg, 128, "<span size=\"x-large\">%s</span>", timecode );
01389         gtk_label_set_markup( timecodeLabel, msg );
01390         gtk_widget_set_redraw_on_allocate( GTK_WIDGET( timecodeLabel ), FALSE );
01391         gtk_label_set_markup( total, "" );
01392     }
01393 }
01394 
01397 Frame* PageCapture::getFrame()
01398 {
01399     return frameBuffer[ framePosition ];
01400 }
01401 
01402 
01403 void PageCapture::collectFiles( )
01404 {
01405         string file;
01406         
01407         // Pick up any captured files
01408         for ( unsigned int index = 0; index < FileTracker::GetInstance().Size(); index ++ )
01409         {
01410             file = FileTracker::GetInstance().Get( ( int ) index );
01411             if ( index == FileTracker::GetInstance().Size() - 1 )
01412                 break;
01413             cerr << ">>> File " << file << " returned from tracker" << endl;
01414             switch ( FileTracker::GetInstance().GetMode() )
01415             {
01416                 case CAPTURE_IGNORE:
01417                     // Do nothing
01418                     break;
01419                 case CAPTURE_FRAME_INSERT:
01420                     common->loadMediaObject( const_cast< char* >( file.c_str() ), common->g_currentFrame == -1 ? 0 : common->g_currentFrame );
01421                     break;
01422                 case CAPTURE_FRAME_APPEND:
01423                     common->loadMediaObject( const_cast< char* >( file.c_str() ), common->g_currentFrame + 1 );
01424                     break;
01425                 case CAPTURE_MOVIE_APPEND:
01426                     common->loadMediaObject( const_cast< char* >( file.c_str() ), common->getPlayList() ->GetNumFrames() );
01427                     break;
01428             }
01429             common->hasListChanged = TRUE;
01430         }
01431         
01432         // Clear the list to avoid reloading on a subsequent re-entry
01433         FileTracker::GetInstance().Clear();
01434 
01435         if ( file.length() )
01436             FileTracker::GetInstance().Add( file.c_str() );
01437 
01438         // Just to make sure we refresh what needs to be refreshed
01439         if ( common->hasListChanged )
01440         {
01441             gdk_threads_enter();
01442             common->getPageEditor()->DrawBar( common->g_currentFrame );
01443             gdk_threads_leave();
01444         }
01445 }
01446 
01447 /*
01448  * Callbacks
01449  */
01450 
01451 extern "C"
01452 {
01453     //
01454     // Threaded implementation
01455     //
01456 
01457     static pthread_mutex_t condition_mutex = PTHREAD_MUTEX_INITIALIZER;
01458     static pthread_cond_t condition = PTHREAD_COND_INITIALIZER;
01459 
01460     static int WaitForAction( int lastPosition )
01461     {
01462         if ( framePosition == lastPosition )
01463         {
01464             pthread_mutex_lock( &condition_mutex );
01465             pthread_cond_wait( &condition, &condition_mutex );
01466             pthread_mutex_unlock( &condition_mutex );
01467         }
01468 
01469         return framePosition;
01470     }
01471 
01472     static void TriggerAction( )
01473     {
01474         pthread_mutex_lock( &condition_mutex );
01475         pthread_cond_signal( &condition );
01476         pthread_mutex_unlock( &condition_mutex );
01477     }
01478 
01479     static void *captureThread( void * p )
01480     {
01481         Frame * frame = NULL;
01482 
01483         // Make sure we can tell the difference between a used and unused frame
01484         for ( int index = 0; index < BUFFERED_FRAMES; index ++ )
01485             frameBuffer[ index ] = NULL;
01486         
01487         framesCaptured = 0;
01488 
01489         // Loop until we're informed otherwise
01490         while ( g_nav_ctl.capture_active )
01491         {
01492             // Wait for the reader to indicate that something has happened
01493             reader->WaitForAction( );
01494 
01495             // Get the next frame
01496             frame = reader->GetFrame();
01497 
01498             // Check if the out queue is falling behind
01499             bool critical_mass = reader->GetOutQueueSize( ) > reader->GetInQueueSize( );
01500 
01501             // Make sure we return the oldest frame first
01502             if ( frame != NULL && frameCount == BUFFERED_FRAMES )
01503             {
01504                 int oldest = ( framePosition + 1 ) % BUFFERED_FRAMES;
01505                 frameBuffer[ oldest ] = NULL;
01506                 frameCount --;
01507             }
01508 
01509             // Put the last collected frame into the buffer so it can be picked up
01510             // by the video thread.
01511             if ( frame != NULL && !critical_mass )
01512             {
01513                 // Bung it in to the buffer
01514                 int newest = ( framePosition + 1 ) % BUFFERED_FRAMES;
01515                 frameBuffer[ newest ] = frame;
01516                 frameCount ++;
01517                 framePosition = newest;
01518                 TriggerAction( );
01519             }
01520 
01521             // Do with it what needs to be done
01522             if ( frame != NULL )
01523             {
01524                 TimeCode tc;
01525                 frame->GetTimeCode( tc );
01526                 if ( frame->IsNormalSpeed() &&
01527                      ! ( tc.hour == 0 && tc.min == 0 && tc.sec == 0 && tc.frame == 0 ) )
01528                 {
01529                     // Play audio
01530                     if ( audioOn && !critical_mass )
01531                         if ( writer == NULL || ( writer != NULL && Preferences::getInstance().preview_capture ) )
01532                             displayer->PutSound( *frame );
01533     
01534                     // All access to the writer is protected
01535                     pthread_mutex_lock( &writerlock );
01536                     if ( writer != NULL )
01537                     {
01538                         if ( !writer->WriteFrame( *frame ) )
01539                         {
01540                             pthread_mutex_unlock( &writerlock );
01541                             gdk_threads_enter();
01542                             common->getPageCapture() ->stopCapture();
01543                             common->setStatusBar( _( "WARNING: Capture stopped. Disk full?" ) );
01544                             gdk_threads_leave();
01545                         }
01546                         framesCaptured++;
01547                     }
01548                     pthread_mutex_unlock( &writerlock );
01549                 }
01550                 // Return the frame for reuse
01551                 reader->DoneWithFrame( frame );
01552 
01553             }
01554         }
01555 
01556         // Set the buffer positional vars to defaults to allow restart
01557         frameCount = 0;
01558         framePosition = -1;
01559 
01560         g_nav_ctl.capture_active = FALSE;
01561 
01562         TriggerAction( );
01563 
01564         return NULL;
01565     }
01566 
01567 
01568     static void *videoThread( gpointer p )
01569     {
01570         int lastPosition = -1;
01571 
01572         GtkWidget *drawingarea = ( GtkWidget* ) p;
01573         prevFramesWritten = 0;
01574 
01575         while ( g_nav_ctl.capture_active )
01576         {
01577             int position = WaitForAction( lastPosition );
01578 
01579             // Lock the gui
01580             gdk_threads_enter();
01581 
01582             // If we have a valid position and it's different to the last iteration
01583             if ( g_nav_ctl.capture_active && position != -1 && position != lastPosition )
01584             {
01585 
01586                 // ... display it - note, the displayer and the frame may be destroyed
01587                 // while waiting for the thread lock so additional checks are needed
01588                 if ( ( displayer != NULL && frameBuffer[ position ] != NULL ) )
01589                 {
01590                     if ( writer == NULL || ( writer != NULL && Preferences::getInstance().preview_capture ) )
01591                     {
01592                         displayer->Put( *frameBuffer[ position ], drawingarea, TRUE );
01593                     }
01594                 }
01595 
01596                 // Remember the buffer position of the last frame displayed
01597                 lastPosition = position;
01598             }
01599 
01600             // Apply the changes to the GUI
01601             common->getPageCapture() ->applyAVCState( avcStatus );
01602             common->getPageCapture()->showFrameInfo( framesCaptured );
01603 
01604             gdk_flush();
01605             gdk_threads_leave();
01606             
01607             pthread_mutex_lock( &writerlock );
01608             if ( writer )
01609             {
01610                 int framesWritten = writer->GetFramesWritten();
01611                 string filename = writer->GetFilename();
01612                 pthread_mutex_unlock( &writerlock );
01613                 
01614                 if ( framesWritten < prevFramesWritten )
01615                 {
01616                     gdk_threads_enter();
01617                     common->setStatusBar( ( string( _( "Capturing " ) ) + filename ).c_str() );
01618                     gdk_threads_leave();
01619                     common->getPageCapture()->collectFiles();
01620                 }
01621                 prevFramesWritten = framesWritten;
01622             }
01623             else
01624             {
01625                 pthread_mutex_unlock( &writerlock );
01626             }
01627 
01628             // Release temporarily
01629             struct timespec t = { 0, 0 };
01630             nanosleep( &t, NULL );
01631         }
01632 
01633         return NULL;
01634     }
01635 
01636     static void *avcThread( gpointer p )
01637     {
01638         while ( g_nav_ctl.capture_active )
01639         {
01640             // Check that device is still valid
01641             common->getPageCapture() ->CheckDevices( );
01642 
01643             // Release temporarily
01644             struct timespec t =
01645                 {
01646                     0, ( unsigned long ) Preferences::getInstance().avcPollIntervalMs
01647                     * 1000000UL
01648                 };
01649             nanosleep( &t, NULL );
01650         }
01651 
01652         return NULL;
01653     }
01654 
01655     void
01656     on_capture_page_record_button_clicked ( GtkButton * button,
01657                                             gpointer user_data )
01658     {
01659         startCapture();
01660     }
01661 
01662 
01663     void
01664     on_capture_page_stop_button_clicked ( GtkButton * button,
01665                                           gpointer user_data )
01666     {
01667         stopCapture();
01668     }
01669 
01670     void
01671     on_capture_page_snapshot_button_clicked ( GtkButton * button,
01672             gpointer user_data )
01673     {
01674         saveFrame();
01675     }
01676 
01677     gboolean
01678     on_entry_capture_file_focus_in_event   (GtkWidget       *widget,
01679                                             GdkEventFocus   *event,
01680                                             gpointer         user_data)
01681     {
01682         gtk_entry_set_editable( GTK_ENTRY( widget ), TRUE );
01683         g_nav_ctl.escaped = TRUE;
01684         return FALSE;
01685     }
01686 
01687 
01688     gboolean
01689     on_entry_capture_file_focus_out_event  (GtkWidget       *widget,
01690                                             GdkEventFocus   *event,
01691                                             gpointer         user_data)
01692     {
01693         gtk_entry_set_editable( GTK_ENTRY( widget ), FALSE );
01694         g_nav_ctl.escaped = FALSE;
01695         return FALSE;
01696     }
01697 
01698 
01699     void
01700     on_capture_file_button_clicked         (GtkButton       *button,
01701                                             gpointer         user_data)
01702     {
01703         g_nav_ctl.escaped = TRUE;
01704         gtk_entry_set_editable( GTK_ENTRY( lookup_widget( GTK_WIDGET( button ), "entry_capture_file" ) ), TRUE );
01705         gtk_widget_grab_focus( lookup_widget( GTK_WIDGET( button ), "entry_capture_file" ) );
01706         const char *filename = common->getFileToSave( _("Choose a DV file") );
01707         if ( strcmp( filename, "" ) )
01708             gtk_entry_set_text( GTK_ENTRY( lookup_widget( GTK_WIDGET( button ), "entry_capture_file" ) ), filename );
01709     }
01710 
01711     void
01712     on_entry_capture_file_grab_focus ( GtkWidget * widget,
01713                                        gpointer user_data )
01714     {
01715         gtk_entry_set_editable( GTK_ENTRY( widget ), TRUE );
01716         g_nav_ctl.escaped = TRUE;
01717     }
01718 
01719 
01720     static bool buttonGuard = true;
01721     void
01722     on_capture_page_button_pressed ( GtkButton * button,
01723                                      gpointer user_data )
01724     {
01725         buttonGuard = false;
01726     }
01727 
01728 
01729     void
01730     on_capture_page_button_released ( GtkButton * button,
01731                                       gpointer user_data )
01732     {
01733         buttonGuard = true;
01734     }
01735 
01736 
01737     void
01738     on_capture_page_mute_button_toggled ( GtkToggleButton * togglebutton,
01739                                           gpointer user_data )
01740     {
01741         if ( ! buttonGuard )
01742         {
01743             audioOn = ! audioOn;
01744             buttonGuard = true;
01745             gtk_toggle_button_set_active( togglebutton, !audioOn );
01746         }
01747     }
01748 
01749     void
01750     on_capture_page_avc_button_clicked( GtkButton *button,
01751                                                gpointer user_data)
01752     {
01753         avcStatus = !avcStatus;
01754         Preferences::getInstance().enableAVC = common->getPageCapture( ) ->avc_enabled = !common->getPageCapture( ) ->avc_enabled;
01755         std::cerr << ">> AV/C " << ( common->getPageCapture( ) ->avc_enabled ? _( "Enabled" ) : _( "Disabled" ) ) << std::endl;
01756         TriggerAction( );
01757     }
01758 }

Generated on Sun Mar 11 22:11:46 2007 for Kino by  doxygen 1.4.2