]> git.sesse.net Git - mlt/blob - src/miracle/miracle_unit.c
Initial revision
[mlt] / src / miracle / miracle_unit.c
1 /*
2  * dvunit.c -- DV Transmission Unit Implementation
3  * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
4  * Author: Dan Dennedy <dan@dennedy.org>
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <sys/ioctl.h>
28 #include <sys/mman.h>
29 #include <sys/poll.h>
30 #include <sys/types.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <signal.h>
36
37 #include <libdv/dv1394.h>
38 #include <libraw1394/raw1394.h>
39 #include <libavc1394/avc1394_vcr.h>
40 #include <sys/mman.h>
41
42 #include "dvunit.h"
43 #include "dvframe.h"
44 #include "dvframepool.h"
45 #include "dvqueue.h"
46 #include "dvpump.h"
47 #include "dverror.h"
48 #include "dvplayer.h"
49 #include "raw1394util.h"
50 #include "log.h"
51 #include "dvlocal.h"
52
53 /* Forward references */
54 static void dv_unit_status_communicate( dv_unit );
55
56 /** dv1394 device file names based upon devfs default names. */
57
58 static char *devices[4][4] = {
59         {
60         "/dev/ieee1394/dv/host0/NTSC/in",
61         "/dev/ieee1394/dv/host0/NTSC/out",
62         "/dev/ieee1394/dv/host0/PAL/in",
63         "/dev/ieee1394/dv/host0/PAL/out",
64         },{
65         "/dev/ieee1394/dv/host1/NTSC/in",
66         "/dev/ieee1394/dv/host1/NTSC/out",
67         "/dev/ieee1394/dv/host1/PAL/in",
68         "/dev/ieee1394/dv/host1/PAL/out"
69         },{
70         "/dev/ieee1394/dv/host2/NTSC/in",
71         "/dev/ieee1394/dv/host2/NTSC/out",
72         "/dev/ieee1394/dv/host2/PAL/in",
73         "/dev/ieee1394/dv/host2/PAL/out"
74         },{
75         "/dev/ieee1394/dv/host3/NTSC/in",
76         "/dev/ieee1394/dv/host3/NTSC/out",
77         "/dev/ieee1394/dv/host3/PAL/in",
78         "/dev/ieee1394/dv/host3/PAL/out"
79         }
80 };
81
82 static int device_count[4] = {0,0,0,0};
83
84 /** Allocate a new DV transmission unit.
85
86     \param dv1394d_fd The file descriptor of a dv1394 device file to 
87                       use for transmission.
88     \param guid The node GUID of the receiving device.
89     \param channel The channel to use for transmission.
90     \return A new dv_unit handle.
91 */
92
93 dv_unit dv_unit_init( octlet_t guid, int channel )
94 {
95         dv_unit unit = malloc( sizeof( dv_unit_t ) );
96         if ( unit != NULL )
97         {
98                 int node_id;
99                 
100                 memset( unit, 0, sizeof( dv_unit_t ) );
101                 unit->guid = guid;
102                 unit->buffer_size = 25;
103                 unit->is_terminated = 1;
104                 unit->channel = channel;
105                 unit->dv1394_fd = -1;
106                 unit->n_frames = DV1394_MAX_FRAMES / 2;
107                 unit->n_fill = 1;
108
109                 /* get a raw1394 handle for plug control */
110                 if ( ( node_id = raw1394_find_node( &(unit->raw1394), guid ) ) != -1 )
111                 {
112                         if ( dv_unit_online( unit ) == 1 )
113                                 dv1394d_log( LOG_DEBUG, "Added online unit with GUID %08x%08x", 
114                                         (quadlet_t) (unit->guid>>32), (quadlet_t) (unit->guid & 0xffffffff) );
115                         else
116                         {
117                                 dv_unit_close( unit );
118                                 unit = NULL;
119                         }
120                 }
121                 else
122                 {
123                         dv1394d_log( LOG_DEBUG, "Added offline unit with GUID %08x%08x", 
124                                 (quadlet_t) (unit->guid>>32), (quadlet_t) (unit->guid & 0xffffffff) );
125                 }
126         }
127         return unit;
128 }
129
130 /** Allow stdin to feed the unit (redundant now that senddv has been dropped).
131 */
132
133 void dv_unit_allow_stdin( dv_unit unit, int flag )
134 {
135         unit->allow_stdin = flag;
136 }
137
138 /** Override the default buffer/pump size - this must be done prior to the pumps
139         creation.
140 */
141
142 void dv_unit_set_buffer_size( dv_unit unit, int size )
143 {
144         if ( size > 0 )
145         {
146                 if ( unit->pump == NULL )
147                         unit->buffer_size = size;
148                 else
149                         unit->buffer_size = dv_pump_resize( unit->pump, size );
150         }
151 }
152
153 int dv_unit_get_buffer_size( dv_unit unit )
154 {
155         return unit->buffer_size;
156 }
157
158 void dv_unit_set_n_frames( dv_unit unit, int size )
159 {
160         if ( size > 0 && size <= DV1394_MAX_FRAMES / 2 )
161                 unit->n_frames = size;
162 }
163
164 int dv_unit_get_n_frames( dv_unit unit )
165 {
166         return unit->n_frames;
167 }
168
169 void dv_unit_set_n_fill( dv_unit unit, int size )
170 {
171         unit->n_fill = size;
172 }
173
174 int dv_unit_get_n_fill( dv_unit unit )
175 {
176         return unit->n_fill;
177 }
178
179 /** Set the notifier info
180 */
181
182 void dv_unit_set_notifier( dv_unit this, dv1394_notifier notifier, char *root_dir )
183 {
184         this->notifier = notifier;
185         this->root_dir = root_dir;
186         dv_unit_status_communicate( this );
187 }
188
189 /** Communicate the current status to all threads waiting on the notifier.
190 */
191
192 static void dv_unit_status_communicate( dv_unit unit )
193 {
194         if ( unit != NULL && unit->notifier != NULL && unit->root_dir != NULL )
195         {
196                 dv1394_status_t status;
197                 if ( dv_unit_get_status( unit, &status ) == 0 )
198                         if ( !( ( status.status == unit_playing || status.status == unit_paused ) &&
199                                         strcmp( status.clip, "" ) && 
200                                     !strcmp( status.tail_clip, "" ) && 
201                                         status.position == 0 && 
202                                         status.in == 0 && 
203                                         status.out == 0 ) )
204                                 dv1394_notifier_put( unit->notifier, &status );
205         }
206 }
207
208 /** Load a clip into the unit clearing existing play list.
209
210     \todo error handling
211     \param unit A dv_unit handle.
212     \param clip The absolute file name of the clip to load.
213     \param in   The starting frame (-1 for 0)
214         \param out  The ending frame (-1 for maximum)
215 */
216
217 dv_error_code dv_unit_load( dv_unit unit, const char *clip, long in, long out, int flush )
218 {
219         dv_player player = dv_unit_get_dv_player( unit );
220         dv_error_code error = dv_player_get_error( player );
221         if ( error == dv_pump_ok )
222         {
223                 error = dv_player_replace_file( player, (char*) clip, in, out, flush );
224                 dv1394d_log( LOG_DEBUG, "loaded clip %s", clip );
225                 if ( unit->is_terminated )
226                         dv_unit_status_communicate( unit );
227         }
228         return error;
229 }
230
231 dv_error_code dv_unit_insert( dv_unit unit, const char *clip, int index, long in, long out )
232 {
233         dv_player player = dv_unit_get_dv_player( unit );
234         dv_error_code error = dv_player_get_error( player );
235         if ( error == dv_pump_ok )
236         {
237                 error = dv_player_insert_file( player, (char*) clip, index, in, out );
238                 dv1394d_log( LOG_DEBUG, "inserted clip %s", clip );
239                 if ( unit->is_terminated )
240                         dv_unit_status_communicate( unit );
241         }
242         return error;
243 }
244
245 dv_error_code dv_unit_remove( dv_unit unit, int index )
246 {
247         dv_player player = dv_unit_get_dv_player( unit );
248         dv_error_code error = dv_player_get_error( player );
249         if ( error == dv_pump_ok )
250         {
251                 error = dv_player_remove_clip( player, index );
252                 dv1394d_log( LOG_DEBUG, "removed clip %d", index );
253                 if ( unit->is_terminated )
254                         dv_unit_status_communicate( unit );
255         }
256         return error;
257 }
258
259 dv_error_code dv_unit_clean( dv_unit unit )
260 {
261         dv_player player = dv_unit_get_dv_player( unit );
262         dv_error_code error = dv_player_get_error( player );
263         if ( error == dv_pump_ok )
264         {
265                 error = dv_player_clean( player );
266                 dv1394d_log( LOG_DEBUG, "Cleaned playlist" );
267                 if ( unit->is_terminated )
268                         dv_unit_status_communicate( unit );
269         }
270         return error;
271 }
272
273 dv_error_code dv_unit_move( dv_unit unit, int src, int dest )
274 {
275         dv_player player = dv_unit_get_dv_player( unit );
276         dv_error_code error = dv_player_get_error( player );
277         if ( error == dv_pump_ok )
278         {
279                 error = dv_player_move_clip( player, src, dest );
280                 dv1394d_log( LOG_DEBUG, "moved clip %d to %d", src, dest );
281                 if ( unit->is_terminated )
282                         dv_unit_status_communicate( unit );
283         }
284         return error;
285 }
286
287 /** Add a clip to the unit play list.
288
289     \todo error handling
290     \param unit A dv_unit handle.
291     \param clip The absolute file name of the clip to load.
292     \param in   The starting frame (-1 for 0)
293         \param out  The ending frame (-1 for maximum)
294 */
295
296 dv_error_code dv_unit_append( dv_unit unit, const char *clip, long in, long out )
297 {
298         dv_player player = dv_unit_get_dv_player( unit );
299         dv_error_code error = dv_player_add_file( player, (char*) clip, in, out );
300         dv_unit_status_communicate( unit );
301         return error;
302 }
303
304 void *output_cleanup( void *arg )
305 {
306         dv_unit unit = arg;
307         if ( unit != NULL && unit->mmap != NULL )
308         {
309                 unit->is_terminated = 1;
310                 dv_unit_status_communicate( unit );
311                 munmap( unit->mmap, unit->mmap_length );
312                 /* this actually stops transmission as opposed to allowing the 
313                    last frame to loop in the OHCI DMA context. */
314                 ioctl( unit->dv1394_fd, DV1394_SHUTDOWN, NULL );
315         }
316
317         return NULL;
318 }
319
320 /** The dv1394 transmission thread.
321
322     \param arg A dv_unit handle.
323 */
324
325 static void *output( void *arg )
326 {
327         dv_unit unit = arg;
328         dv_frame frames[ DV1394_MAX_FRAMES ];
329         int frames_dropped = 0; /* count of total frames dropped (repeated) */
330         struct dv1394_status status;
331         char errstr[64];
332         int n_fill = unit->n_fill;
333         int n_frames = unit->n_frames;
334         
335         /* Determine the number of frames to wait for/fill on each iteration */
336         if ( n_fill < 1 )
337                 n_fill = 1;
338         else if ( n_fill > unit->n_frames )
339                 n_fill = n_frames / 2;
340
341         unit->mmap = mmap( NULL,unit->mmap_length,PROT_WRITE,MAP_SHARED,unit->dv1394_fd,0 );
342         if ( unit->mmap == MAP_FAILED || unit->mmap == NULL )
343         {
344                 perror( "mmap" );
345                 return NULL;
346         }
347
348         pthread_cleanup_push( output_cleanup, (void *)arg );
349
350         while ( dv_pump_get_available_output_count( unit->pump ) || 
351                 !( dv_unit_has_terminated( unit ) || dv_pump_has_terminated( unit->pump) ) )
352         {
353                 int available = 0;
354
355                 if ( ioctl( unit->dv1394_fd, DV1394_WAIT_FRAMES, n_fill ) < 0)
356                         perror( "DV1394_WAIT_FRAMES" );
357
358                 pthread_testcancel();
359
360                 /* update the status for the next iteration and detect dropped frames */
361                 if ( ioctl( unit->dv1394_fd, DV1394_GET_STATUS, &status ) >= 0)
362                 {
363                         pthread_testcancel();
364
365                         /*
366                         printf( "dv1394 status: active=%02d, #clear=%02d, first clear=%02d\n", 
367                                 status.active_frame, status.n_clear_frames, status.first_clear_frame);
368                         */
369                 
370                         /* report dropped frames */
371                         if( status.dropped_frames > 0 )
372                         {
373                                 frames_dropped += status.dropped_frames;
374                                 dv1394d_log( LOG_WARNING, "dv1394 repeated %d frames with %d available.", 
375                                                          status.dropped_frames, dv_pump_get_available_output_count( unit->pump ) );
376                         }
377
378                         available = dv_pump_get_output_block( unit->pump, (void **)frames, n_fill );
379
380                         dv_unit_status_communicate( unit );
381                         
382                         /* The only time we get 0 frames is when the unit is being stopped. */
383                         if ( available != 0 )
384                         {
385                                 int size = dv_frame_size( frames[ 0 ] );
386                                 int pos = status.first_clear_frame;
387                                 int index = 0;
388
389                                 for ( index = 0; index < available; index ++ )
390                                         memcpy( unit->mmap + ( ( pos + index ) % n_frames ) * size, dv_frame_data( frames[ index ] ), size );
391
392                                 if ( ioctl( unit->dv1394_fd, DV1394_SUBMIT_FRAMES, available ) >= 0)
393                                 {
394                                         for ( index = 0; index < available - 1; index ++ )
395                                         {
396                                                 dv_frame_clear_error( frames[ index ] );
397                                                 dv_frame_id_clear( dv_frame_get_id( frames[ index ] ) );
398                                         }                       
399                                         dv_pump_return_output_block( unit->pump );
400                                         pthread_testcancel();
401                                 }
402                                 else
403                                 {
404                                         dv1394d_log( LOG_ERR, "failed to write frames to dv1394: %s.", strerror_r( errno, errstr, 63 ) );
405                                         dv_pump_terminate( unit->pump );
406                                         dv_pump_flush( unit->pump );
407                                         pthread_testcancel();
408                                 }
409                         }
410                 }
411                 else
412                 {
413                         dv1394d_log( LOG_ERR, "failed to get dv1394 status: %s.", strerror_r( errno, errstr, 63 ) );
414                         dv_pump_return_used_output( unit->pump );
415                 }
416         }
417
418         if ( frames_dropped > 0 )
419                 dv1394d_log( LOG_WARNING, "dv1394 repeated %d frames total during this transmission.", frames_dropped );
420
421         pthread_cleanup_pop( 1 );
422
423         return NULL;
424 }
425
426 /** Start playing the clip.
427
428     Start a dv-pump and commence dv1394 transmission.
429
430     \todo error handling
431     \param unit A dv_unit handle.
432     \param speed An integer that specifies the playback rate as a
433                  percentage multiplied by 100.
434 */
435
436 void dv_unit_play( dv_unit_t *unit, int speed )
437 {
438         dv_player player = dv_unit_get_dv_player( unit );
439
440         if ( unit->is_terminated == 1 && ( dv_player_get_total_frames( player ) > 0 || unit->allow_stdin ) )
441         {
442                 int retval;
443                 dv_frame frame = NULL;
444                 struct dv1394_init setup =
445                 {
446                         api_version: DV1394_API_VERSION,
447                         channel: unit->channel,
448                         /* this only sets the *requested* size of the ringbuffer,
449                            in frames */ 
450                         n_frames: unit->n_frames,
451                         /* we set the format later */
452                         cip_n: unit->dv1394_cip_n,
453                         cip_d: unit->dv1394_cip_d,
454                         syt_offset: unit->dv1394_syt_offset
455                 };
456                 pthread_attr_t attr;
457
458                 if ( unit->in == NULL )
459                 {
460                         if ( !unit->allow_stdin || dv_player_get_total_frames( player ) != 0 )
461                                 unit->in = dv_player_get_dv_input( player );
462                         else
463                                 unit->in = dv_input_init( unit->pump );
464                 }
465                 else
466                 {
467                         dv_input_join_thread( unit->in );
468                         pthread_join( unit->out, NULL );
469                 }
470
471                 unit->is_terminated = 0;
472                 dv_pump_restart( unit->pump );
473                 dv_input_start_thread( unit->in );
474                 dv_player_set_speed( player, (double) speed/1000.0 );
475
476                 /* first we read a little data to see if this is PAL or NTSC
477                    so we can initialize dv1394 properly */
478                 frame = dv_pump_get_available_output( unit->pump );
479         
480                 /* initialize dv1394 */
481                 setup.format = dv_frame_is_pal(frame) ? DV1394_PAL : DV1394_NTSC;
482                 
483                 retval = ioctl( unit->dv1394_fd, DV1394_INIT, &setup );
484                 if (retval < 0)
485                 {
486                         perror( "DV1394_INIT" );
487                         return;
488                 }
489
490                 unit->mmap_length = unit->n_frames * dv_frame_size( frame );
491
492                 pthread_attr_init( &attr );
493                 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
494                 pthread_attr_setinheritsched( &attr, PTHREAD_INHERIT_SCHED );
495                 pthread_create( &unit->out, &attr, output, unit );
496         }
497         else 
498         {
499                 dv_player_set_speed( player, (double) speed/1000.0 );
500         }
501         dv_unit_status_communicate( unit );
502 }
503
504 /** Stop playback.
505
506     Terminates the dv_pump and halts dv1394 transmission.
507
508     \param unit A dv_unit handle.
509 */
510
511 void dv_unit_terminate( dv_unit unit )
512 {
513         unit->is_terminated = 1;
514         if ( unit->pump != NULL )
515         {
516                 dv_pump_terminate( unit->pump );
517                 dv_pump_flush( unit->pump );
518         }
519 }
520
521 /** Query the status of unit playback.
522
523     \param unit A dv_unit handle.
524     \return 1 if the unit is not playing, 0 if playing.
525 */
526
527 int dv_unit_has_terminated( dv_unit unit )
528 {
529         return unit->is_terminated;
530 }
531
532 /** Get the dv_player from the dv_unit.
533
534     \param unit A dv_unit handle.
535     \return A dv_player handle.
536 */
537
538 dv_player dv_unit_get_dv_player( dv_unit unit )
539 {
540         if ( unit != NULL )
541         {
542                 if ( unit->pump == NULL )
543                 {
544                         unit->pump = dv_pump_init( unit->buffer_size );
545                         if ( unit->pump != NULL )
546                                 unit->player = dv_player_init( unit->pump );
547                 }
548                 return unit->player;
549         }
550         return NULL;
551 }
552
553
554 /** Transfer the currently loaded clip to another unit
555 */
556
557 int dv_unit_transfer( dv_unit dest_unit, dv_unit src_unit )
558 {
559         dv_player src_player = dv_unit_get_dv_player( src_unit );
560         dv_player dest_player = dv_unit_get_dv_player( dest_unit );
561
562         if( dest_player != NULL && src_player != NULL )
563                 dv_player_replace_player( dest_player, src_player );
564
565         return 0;
566 }
567
568 /** Get the guid associated to this unit.
569 */
570
571 octlet_t dv_unit_get_guid( dv_unit unit )
572 {
573         return unit->guid;
574 }
575
576 /** Get the node id associated to this unit.
577 */
578
579 int dv_unit_get_nodeid( dv_unit unit )
580 {
581         return (unit->node_id & 0x3f);
582 }
583
584 /** Get the channel associated to this unit.
585 */
586
587 int dv_unit_get_channel( dv_unit unit )
588 {
589         return (unit->channel);
590 }
591
592 /** Turn unit online.
593 */
594
595 int dv_unit_online( dv_unit unit )
596 {
597         int result = 0;
598         int port, node_id;
599         
600         if ( unit->raw1394 != NULL )
601                 raw1394_close( unit->raw1394 );
602         
603         node_id = raw1394_find_node( &(unit->raw1394), unit->guid );
604         if ( node_id != -1 )
605         {
606                 unit->node_id = 0xffc0 | node_id;       
607                 port = dv_unit_get_port( unit );
608         
609                 unit->dv1394_fd = open( devices[ port ][ device_count[port] ], O_RDWR );
610                 if ( unit->dv1394_fd < 0 )
611                 {
612                         dv1394d_log( LOG_ERR, "failed to open dv1394 device - %s\n", devices[ port ][ device_count[port] ] );
613                         dv_unit_close( unit );
614                 }
615                 else
616                 {
617                         device_count[ port ] ++;
618                         if ( establish_p2p_connection( unit->raw1394, unit->node_id, (unsigned int *) &(unit->channel) ) )
619                         {
620                                 avc1394_vcr_record( unit->raw1394, unit->node_id );
621                                 unit->online = 1;
622                                 dv_unit_status_communicate( unit );
623                                 result = 1;
624                         }
625                 }
626         }
627                                 
628         return result;
629 }
630
631 /** Turn unit offline.
632 */
633
634 void dv_unit_offline( dv_unit unit )
635 {
636         if ( unit->online == 1 )
637         {
638                 if ( unit->is_terminated == 0 )
639                         dv_unit_terminate( unit );
640                 unit->online = 0;
641                 if ( unit->raw1394 != NULL )
642                 {
643                         avc1394_vcr_stop( unit->raw1394, unit->node_id );
644                         break_p2p_connection( unit->raw1394, unit->node_id, unit->channel );
645                 }
646                 if ( unit->dv1394_fd > -1 )
647                 {
648                         close( unit->dv1394_fd );
649                         device_count[ dv_unit_get_port( unit ) ] --;
650                 }
651                 dv_unit_status_communicate( unit );
652                 dv1394d_log( LOG_DEBUG, "Unit with GUID %08x%08x is now offline.",
653                         (quadlet_t) (unit->guid>>32), (quadlet_t) (unit->guid & 0xffffffff) );
654         }
655 }
656
657 /** Determine if unit is offline.
658 */
659
660 int dv_unit_is_offline( dv_unit unit )
661 {
662         return (unit->online == 0);
663 }
664
665 /** Obtain the status for a given unit
666 */
667
668 int dv_unit_get_status( dv_unit unit, dv1394_status status )
669 {
670         int error = -1;
671
672         memset( status, 0, sizeof( dv1394_status_t ) );
673
674         if ( unit != NULL )
675         {
676                 dv_player player = dv_unit_get_dv_player( unit );
677
678                 error = 0;
679
680                 if ( player != NULL )
681                 {
682                         dv_frame head = dv_pump_get_head( player->pump );
683                         dv_frame tail = dv_pump_get_tail( player->pump );
684
685                         status->speed = (int)( dv_player_get_speed( player ) * 1000.0 );
686                         status->fps = dv_player_frames_per_second( player, 0 );
687
688                         if ( head != NULL )
689                         {
690                                 dv_frame_id id = dv_frame_get_id( head );
691                                 if ( id->resource != NULL )
692                                 {
693                                         const char *resource = id->resource;
694                                         if ( resource != NULL && unit->root_dir != NULL )
695                                                 resource += strlen( unit->root_dir ) - ( unit->root_dir[ strlen( unit->root_dir ) - 1 ] == '/' );
696                                         strncpy( status->clip, resource, sizeof( status->clip ) );
697                                 }
698                                 else
699                                 {
700                                         char *title = dv_player_get_name( player, dv_player_get_clip_containing( player, 0 ), unit->root_dir );
701                                         if ( title != NULL )
702                                                 strncpy( status->clip, title, sizeof( status->clip ) );
703                                 }
704
705                                 status->position = id->relative;
706                                 status->in = id->in;
707                                 status->out = id->out;
708                                 status->length = id->length;
709                                 status->seek_flag = id->seek_flag;
710                         }
711                         else
712                         {
713                                 char *title = dv_player_get_name( player, dv_player_get_clip_containing( player, 0 ), unit->root_dir );
714                                 if ( title != NULL )
715                                         strncpy( status->clip, title, sizeof( status->clip ) );
716                         }
717
718                         if ( tail != NULL )
719                         {
720                                 dv_frame_id id = dv_frame_get_id( tail );
721                                 const char *resource = id->resource;
722                                 if ( resource != NULL && unit->root_dir != NULL )
723                                         resource += strlen( unit->root_dir ) - ( unit->root_dir[ strlen( unit->root_dir ) - 1 ] == '/' );
724                                 if ( resource != NULL )
725                                         strncpy( status->tail_clip, resource, sizeof( status->clip ) );
726                                 status->tail_position = id->relative;
727                                 status->tail_in = id->in;
728                                 status->tail_out = id->out;
729                                 status->tail_length = id->length;
730                         }
731                         
732                         status->generation = player->generation;
733                         status->clip_index = dv_unit_get_current_clip( unit );
734                 }
735
736                 if ( dv_unit_is_offline( unit ) )
737                         status->status = unit_offline;
738                 else if ( !strcmp( status->clip, "" ) )
739                         status->status = unit_not_loaded;
740                 else if ( dv_unit_has_terminated( unit ) )
741                         status->status = unit_stopped;
742                 else if ( status->speed == 0 )
743                         status->status = unit_paused;
744                 else
745                         status->status = unit_playing;
746         }
747         else
748         {
749                 status->status = unit_undefined;
750         }
751
752         status->unit = unit->unit;
753
754         return error;
755 }
756
757 /** Change position in the playlist.
758 */
759
760 void dv_unit_change_position( dv_unit unit, int clip, long position )
761 {
762         dv_player player = dv_unit_get_dv_player( unit );
763         dv_player_set_clip_position( player, clip, position );
764         dv_unit_status_communicate( unit );
765 }
766
767 /** Change speed.
768 */
769
770 void dv_unit_change_speed( dv_unit unit, int speed )
771 {
772         if ( dv_unit_has_terminated( unit ) )
773                 dv_unit_change_position( unit, 0, 0 );
774         else
775                 dv_unit_play( unit, speed );
776 }
777
778 int     dv_unit_get_current_clip( dv_unit unit )
779 {
780         dv_player player = dv_unit_get_dv_player( unit );
781         unsigned long position = dv_player_get_position( player );
782         return dv_player_get_clip_containing( player, position );
783 }
784
785 /** Set a clip's in point
786 */
787
788 int dv_unit_set_clip_in( dv_unit unit, int index, long position )
789 {
790         int error = 0;
791         dv_player player = dv_unit_get_dv_player( unit );
792
793         if ( player != NULL )
794         {
795                 dv_unit_change_speed( unit, 0 );
796                 if ( dv_player_set_in_point( player, index, (unsigned long) position ) == position )
797                         dv_player_set_clip_position( player, index, position );
798                 else
799                         error = -2;
800         }
801         else
802         {
803                 error = -1;
804         }
805
806         dv_unit_status_communicate( unit );
807
808         return error;
809
810 }
811
812 /** Set a clip's out point.
813 */
814
815 int dv_unit_set_clip_out( dv_unit unit, int index, long position )
816 {
817         int error = 0;
818         dv_player player = dv_unit_get_dv_player( unit );
819
820         if ( player != NULL )
821         {
822                 dv_unit_change_speed( unit, 0 );
823                 if ( dv_player_set_out_point( player, index, position ) == position )
824                         dv_player_set_clip_position( player, index, position );
825                 else
826                         error = -2;
827         }
828         else
829         {
830                 error = -1;
831         }
832
833         dv_unit_status_communicate( unit );
834
835         return error;
836 }
837
838 /** Step by specified position.
839 */
840
841 void dv_unit_step( dv_unit unit, int offset )
842 {
843         dv_player player = dv_unit_get_dv_player( unit );
844         dv_player_change_position( player, dv_seek_relative, offset );
845 }
846
847 /** Set the unit's clip mode regarding in and out points.
848 */
849
850 void dv_unit_set_mode( dv_unit unit, dv_player_clip_mode mode )
851 {
852         dv_player player = dv_unit_get_dv_player( unit );
853         if ( player != NULL )
854                 dv_player_set_clip_mode( player, mode );
855         dv_unit_status_communicate( unit );
856 }
857
858 /** Get the unit's clip mode regarding in and out points.
859 */
860
861 dv_player_clip_mode dv_unit_get_mode( dv_unit unit )
862 {
863         dv_player player = dv_unit_get_dv_player( unit );
864         return dv_player_get_clip_mode( player );
865 }
866
867 /** Set the unit's clip mode regarding eof handling.
868 */
869
870 void dv_unit_set_eof_action( dv_unit unit, dv_player_eof_action action )
871 {
872         dv_player player = dv_unit_get_dv_player( unit );
873         dv_player_set_eof_action( player, action );
874         dv_unit_status_communicate( unit );
875 }
876
877 /** Get the unit's clip mode regarding eof handling.
878 */
879
880 dv_player_eof_action dv_unit_get_eof_action( dv_unit unit )
881 {
882         dv_player player = dv_unit_get_dv_player( unit );
883         return dv_player_get_eof_action( player );
884 }
885
886 /** Release the unit
887
888     \todo error handling
889     \param unit A dv_unit handle.
890 */
891
892 void dv_unit_close( dv_unit unit )
893 {
894         if ( unit != NULL )
895         {
896                 dv1394d_log( LOG_DEBUG, "closing unit..." );
897                 dv_unit_offline( unit );
898                 if ( unit->pump != NULL )
899                 {
900                         dv_pump_terminate( unit->pump );
901                         dv_pump_flush( unit->pump );
902                         dv_pump_return_used_output( unit->pump );
903                         dv_input_join_thread( unit->in );
904                         if ( !unit->is_terminated )
905                                 pthread_join( unit->out, NULL );
906                         dv_pump_close( unit->pump );
907                         unit->pump = NULL;
908                 }
909                 raw1394_close( unit->raw1394 );
910                 free( unit );
911                 dv1394d_log( LOG_DEBUG, "... unit closed." );
912         }
913 }
914
915 /** Get the raw1394 port associated to this unit.
916 */
917
918 int dv_unit_get_port( dv_unit unit )
919 {
920         if ( unit->raw1394 != NULL )
921                 return (int) raw1394_get_userdata( unit->raw1394 );
922         else
923                 return -1;
924 }
925
926 /** Set the dv1394 file descriptor for the unit.
927 */
928
929 void dv_unit_set_dv1394_fd( dv_unit unit, int fd )
930 {
931         unit->dv1394_fd = fd;
932 }
933
934 /** Get the dv1394 syt_offset (timestamp latency) property.
935 */
936
937 unsigned int dv_unit_get_syt_offset( dv_unit unit )
938 {
939         return unit->dv1394_syt_offset;
940 }
941
942 /** Get the dv1394 cip_n (timing numerator) property.
943 */
944
945 unsigned int dv_unit_get_cip_n( dv_unit unit )
946 {
947         return unit->dv1394_cip_n;
948 }
949
950 /** Get the dv1394 cip_d (timing denominator) property.
951 */
952
953 unsigned int dv_unit_get_cip_d( dv_unit unit )
954 {
955         return unit->dv1394_cip_d;
956 }
957
958 /** Set the dv1394 syt_offset (timestamp latency) property.
959
960     Stops and restarts the unit if playing.
961 */
962
963 void dv_unit_set_syt_offset( dv_unit unit, unsigned int syt_offset )
964 {
965         int restart = !unit->is_terminated;
966         int speed = (int)( dv_player_get_speed( dv_unit_get_dv_player(unit) ) * 1000.0 );
967         
968         dv_unit_terminate( unit );
969         unit->dv1394_syt_offset = syt_offset;
970         if ( restart )
971                 dv_unit_play( unit, speed );
972 }
973         
974 /** Set the dv1394 cip_n (timing numerator) property.
975
976     Stops and restarts the unit if playing.
977 */
978
979 void dv_unit_set_cip_n( dv_unit unit, unsigned int cip_n )
980 {
981         int restart = !unit->is_terminated;
982         int speed = (int)( dv_player_get_speed( dv_unit_get_dv_player(unit) ) * 1000.0 );
983         
984         dv_unit_terminate( unit );
985         unit->dv1394_cip_n = cip_n;
986         if ( restart )
987                 dv_unit_play( unit, speed );
988 }
989
990 /** Set the dv1394 cip_d (timing denominator) property.
991
992     Stops and restarts the unit if playing.
993 */
994
995 void dv_unit_set_cip_d( dv_unit unit, unsigned int cip_d )
996 {
997         int restart = !unit->is_terminated;
998         int speed = (int)( dv_player_get_speed( dv_unit_get_dv_player(unit) ) * 1000.0 );
999         
1000         dv_unit_terminate( unit );
1001         unit->dv1394_cip_d = cip_d;
1002         if ( restart )
1003                 dv_unit_play( unit, speed );
1004 }
1005
1006 /** Terminate, but only the output thread and close dv1394.
1007 */
1008
1009 void dv_unit_suspend( dv_unit unit )
1010 {
1011         if ( unit->is_terminated == 0 )
1012         {
1013                 unit->is_terminated = 1;
1014                 unit->is_suspended = 1;
1015                 dv_pump_terminate( unit->pump );
1016                 dv_pump_flush( unit->pump );
1017                 pthread_cancel( unit->out );
1018         }
1019         if ( unit->dv1394_fd > -1 )
1020         {
1021                 close( unit->dv1394_fd );
1022                 device_count[ dv_unit_get_port( unit ) ] --;
1023         }
1024         unit->dv1394_fd = -1;
1025         dv_unit_status_communicate( unit );
1026 }
1027
1028
1029 /** Restore unit on the bus, re-open dv1394, start playback if pump is running.
1030 */
1031
1032 void dv_unit_restore( dv_unit unit )
1033 {
1034         int result = 0;
1035         int port, node_id;
1036         
1037         if ( unit->raw1394 != NULL )
1038                 raw1394_close( unit->raw1394 );
1039         
1040         node_id = raw1394_find_node( &(unit->raw1394), unit->guid );
1041         if ( node_id != -1 )
1042         {
1043                 unit->node_id = 0xffc0 | node_id;       
1044                 port = dv_unit_get_port( unit );
1045         
1046                 unit->dv1394_fd = open( devices[ port ][ device_count[port] ], O_RDWR );
1047                 if ( unit->dv1394_fd < 0 )
1048                 {
1049                         dv1394d_log( LOG_ERR, "failed to open dv1394 device - %s\n", devices[ port ][ device_count[port] ] );
1050                         dv_unit_close( unit );
1051                 }
1052                 else
1053                 {
1054                         device_count[ port ] ++;
1055                         break_p2p_connection( unit->raw1394, unit->node_id, unit->channel );
1056                         if ( establish_p2p_connection( unit->raw1394, unit->node_id, (unsigned int *) &(unit->channel) ) )
1057                         {
1058                                 avc1394_vcr_record( unit->raw1394, unit->node_id );
1059                                 unit->online = 1;
1060                                 result = 1;
1061                         }
1062                 }
1063         }
1064         if ( unit->is_suspended == 1 )
1065         {
1066                 int retval;
1067                 dv_frame frame = dv_pump_get_available_output( unit->pump );
1068                 struct dv1394_init setup =
1069                 {
1070                         api_version: DV1394_API_VERSION,
1071                         channel: unit->channel,
1072                         /* this only sets the *requested* size of the ringbuffer,
1073                            in frames */ 
1074                         n_frames: unit->n_frames,
1075                         format: dv_frame_is_pal(frame) ? DV1394_PAL : DV1394_NTSC,
1076                         cip_n: unit->dv1394_cip_n,
1077                         cip_d: unit->dv1394_cip_d,
1078                         syt_offset: unit->dv1394_syt_offset
1079                 };
1080                 pthread_attr_t attr;
1081
1082                 dv_input_join_thread( unit->in );
1083                 unit->is_terminated = 0;
1084                 unit->is_suspended = 0;
1085                 dv_pump_restart( unit->pump );
1086                 dv_input_start_thread( unit->in );
1087                 
1088                 /* initialize dv1394 */
1089                 retval = ioctl( unit->dv1394_fd, DV1394_INIT, &setup );
1090                 if ( retval < 0 )
1091                         return;
1092                 
1093                 pthread_attr_init( &attr );
1094                 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
1095                 pthread_attr_setinheritsched( &attr, PTHREAD_INHERIT_SCHED );
1096                 /* pthread_attr_setschedpolicy( &attr, SCHED_RR ); */
1097                 pthread_create( &unit->out, &attr, output, unit );
1098         }
1099         dv_unit_status_communicate( unit );
1100 }