1 /* dvd_access.c: DVD access plugin.
2 *****************************************************************************
3 * This plugins should handle all the known specificities of the DVD format,
4 * especially the 2048 bytes logical block size.
6 * -libdvdcss for access and unscrambling
7 * -dvd_ifo for ifo parsing and analyse
8 * -dvd_udf to find files
9 *****************************************************************************
10 * Copyright (C) 1998-2001 VideoLAN
11 * $Id: dvd_access.c,v 1.2 2002/03/06 12:26:35 stef Exp $
13 * Author: Stéphane Borel <stef@via.ecp.fr>
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
28 *****************************************************************************/
30 /*****************************************************************************
32 *****************************************************************************/
37 #include <videolan/vlc.h>
44 #include <sys/types.h>
49 #ifdef STRNCASECMP_IN_STRINGS_H
54 # include "dummy_dvdcss.h"
56 # include <videolan/dvdcss.h>
59 #include "stream_control.h"
60 #include "input_ext-intf.h"
61 #include "input_ext-dec.h"
62 #include "input_ext-plugins.h"
68 #include "dvd_summary.h"
73 /*****************************************************************************
75 *****************************************************************************/
77 /* called from outside */
78 static int DVDOpen ( struct input_thread_s * );
79 static void DVDClose ( struct input_thread_s * );
80 static int DVDSetArea ( struct input_thread_s *, struct input_area_s * );
81 static int DVDSetProgram ( struct input_thread_s *, pgrm_descriptor_t * );
82 static int DVDRead ( struct input_thread_s *, byte_t *, size_t );
83 static void DVDSeek ( struct input_thread_s *, off_t );
85 static char * DVDParse( input_thread_t * );
87 /*****************************************************************************
88 * Functions exported as capabilities. They are declared as static so that
89 * we don't pollute the namespace too much.
90 *****************************************************************************/
91 void _M( access_getfunctions)( function_list_t * p_function_list )
93 #define input p_function_list->functions.access
94 input.pf_open = DVDOpen;
95 input.pf_close = DVDClose;
96 input.pf_read = DVDRead;
97 input.pf_set_area = DVDSetArea;
98 input.pf_set_program = DVDSetProgram;
99 input.pf_seek = DVDSeek;
104 * Data access functions
107 /*****************************************************************************
109 *****************************************************************************/
110 static int DVDOpen( struct input_thread_s *p_input )
113 dvdcss_handle dvdhandle;
114 thread_dvd_data_t * p_dvd;
115 input_area_t * p_area;
118 p_dvd = malloc( sizeof(thread_dvd_data_t) );
121 intf_ErrMsg( "dvd error: out of memory" );
124 p_input->p_access_data = (void *)p_dvd;
126 /* Parse command line */
127 if( !( psz_device = DVDParse( p_input ) ) )
141 dvdhandle = dvdcss_open( psz_device );
143 /* free allocated string */
147 if( dvdhandle == NULL )
149 intf_ErrMsg( "dvd error: dvdcss can't open device" );
153 p_dvd->dvdhandle = (dvdcss_handle) dvdhandle;
155 if( dvdcss_seek( p_dvd->dvdhandle, 0, DVDCSS_NOFLAGS ) < 0 )
157 intf_ErrMsg( "dvd error: %s", dvdcss_error( p_dvd->dvdhandle ) );
161 /* Ifo allocation & initialisation */
162 if( IfoCreate( p_dvd ) < 0 )
164 intf_ErrMsg( "dvd error: allcation error in ifo" );
169 if( IfoInit( p_dvd->p_ifo ) < 0 )
171 intf_ErrMsg( "dvd error: fatal failure in ifo" );
172 IfoDestroy( p_dvd->p_ifo );
177 /* Set stream and area data */
178 vlc_mutex_lock( &p_input->stream.stream_lock );
180 p_input->stream.i_method = INPUT_METHOD_DVD;
181 p_input->stream.b_pace_control = 1;
182 p_input->stream.b_seekable = 1;
183 p_input->stream.p_selected_area->i_size = 0;
184 p_input->stream.p_selected_area->i_tell = 0;
186 /* Initialize ES structures */
187 input_InitStream( p_input, sizeof( stream_ps_data_t ) );
189 #define title_inf p_dvd->p_ifo->vmg.title_inf
190 intf_WarnMsg( 3, "dvd info: number of titles: %d", title_inf.i_title_nb );
192 #define area p_input->stream.pp_areas
193 /* We start from 1 here since the default area 0
194 * is reserved for video_ts.vob */
195 for( i = 1 ; i <= title_inf.i_title_nb ; i++ )
197 input_AddArea( p_input );
199 /* Titles are Program Chains */
202 /* Absolute start offset and size
203 * We can only set that with vts ifo, so we do it during the
204 * first call to DVDSetArea */
205 area[i]->i_start = 0;
208 /* Number of chapters */
209 area[i]->i_part_nb = title_inf.p_attr[i-1].i_chapter_nb;
212 /* Offset to vts_i_0.ifo */
213 area[i]->i_plugin_data = p_dvd->p_ifo->i_start +
214 title_inf.p_attr[i-1].i_start_sector;
218 p_dvd->i_title = p_dvd->i_title <= title_inf.i_title_nb ?
222 p_area = p_input->stream.pp_areas[p_dvd->i_title];
224 p_dvd->i_chapter = p_dvd->i_chapter < p_area->i_part_nb ?
225 p_dvd->i_chapter : 1;
226 p_area->i_part = p_dvd->i_chapter;
228 p_dvd->i_audio_nb = 0;
231 /* set title, chapter, audio and subpic */
232 if( DVDSetArea( p_input, p_area ) )
234 vlc_mutex_unlock( &p_input->stream.stream_lock );
238 vlc_mutex_unlock( &p_input->stream.stream_lock );
240 p_input->psz_demux = "dvd";
245 /*****************************************************************************
246 * DVDClose: close dvd
247 *****************************************************************************/
248 static void DVDClose( struct input_thread_s *p_input )
250 thread_dvd_data_t * p_dvd;
252 p_dvd = (thread_dvd_data_t*)p_input->p_access_data;
254 IfoDestroy( p_dvd->p_ifo );
256 p_input->p_access_data = (void *)(p_dvd->dvdhandle);
259 /* Clean up libdvdcss */
260 dvdcss_close( (dvdcss_handle) p_input->p_access_data );
263 /*****************************************************************************
264 * DVDSetProgram: used to change angle
265 *****************************************************************************/
266 static int DVDSetProgram( input_thread_t * p_input,
267 pgrm_descriptor_t * p_program )
269 if( p_input->stream.p_selected_program != p_program )
271 thread_dvd_data_t * p_dvd;
274 p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
275 i_angle = p_program->i_number;
277 /* DVD is actually mono-program: we only need the current angle
278 * number, so copy the data between programs */
279 memcpy( p_program, p_input->stream.p_selected_program,
280 sizeof(pgrm_descriptor_t) );
281 p_program->i_number = i_angle;
282 p_input->stream.p_selected_program = p_program;
285 p_dvd->p_ifo->vts.title_unit.p_title[p_dvd->i_title_id-1].title
286 if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
288 if( ( p_program->i_number - p_dvd->i_angle ) < 0 )
292 p_dvd->i_prg_cell += ( p_program->i_number - p_dvd->i_angle );
293 p_dvd->i_angle = p_program->i_number;
295 DVDFindSector( p_dvd );
296 p_dvd->i_cell += p_dvd->i_angle_cell;
300 p_dvd->i_angle = p_program->i_number;
303 intf_WarnMsg( 3, "dvd info: angle %d selected", p_dvd->i_angle );
309 /*****************************************************************************
310 * DVDSetArea: initialize input data for title x, chapter y.
311 * It should be called for each user navigation request.
312 *****************************************************************************
313 * Take care that i_title starts from 0 (vmg) and i_chapter start from 1.
314 * Note that you have to take the lock before entering here.
315 *****************************************************************************/
316 static int DVDSetArea( input_thread_t * p_input, input_area_t * p_area )
318 thread_dvd_data_t * p_dvd;
322 p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
324 /* we can't use the interface slider until initilization is complete */
325 p_input->stream.b_seekable = 0;
327 if( p_area != p_input->stream.p_selected_area )
329 /* Reset the Chapter position of the old title */
330 p_input->stream.p_selected_area->i_part = 0;
331 p_input->stream.p_selected_area = p_area;
334 * We have to load all title information
336 /* Change the default area */
338 /* title number: it is not vts nb!,
339 * it is what appears in the interface list */
340 p_dvd->i_title = p_area->i_id;
341 p_dvd->p_ifo->i_title = p_dvd->i_title;
343 /* set number of chapters of current title */
344 p_dvd->i_chapter_nb = p_area->i_part_nb;
347 if( IfoTitleSet( p_dvd->p_ifo ) < 0 )
349 intf_ErrMsg( "dvd error: fatal error in vts ifo" );
351 p_input->b_error = 1;
355 #define vmg p_dvd->p_ifo->vmg
356 #define vts p_dvd->p_ifo->vts
357 /* title position inside the selected vts */
358 i_vts_title = vmg.title_inf.p_attr[p_dvd->i_title-1].i_title_num;
360 vts.title_inf.p_title_start[i_vts_title-1].i_title_id;
362 intf_WarnMsg( 3, "dvd: title %d vts_title %d pgc %d",
363 p_dvd->i_title, i_vts_title, p_dvd->i_title_id );
367 * Set selected title start and size
370 /* title set offset XXX: convert to block values */
371 p_dvd->i_title_start =
372 vts.i_pos + vts.manager_inf.i_title_vob_start_sector;
374 /* last video cell */
376 p_dvd->i_prg_cell = -1 +
377 vts.title_unit.p_title[p_dvd->i_title_id-1].title.i_cell_nb;
379 if( DVDFindCell( p_dvd ) < 0 )
381 intf_ErrMsg( "dvd error: can't find title end" );
382 p_input->b_error = 1;
386 /* temporary hack to fix size in some dvds */
387 if( p_dvd->i_cell >= vts.cell_inf.i_cell_nb )
389 p_dvd->i_cell = vts.cell_inf.i_cell_nb - 1;
393 p_dvd->i_size = vts.cell_inf.p_cell_map[p_dvd->i_cell].i_end_sector;
395 if( DVDChapterSelect( p_dvd, 1 ) < 0 )
397 intf_ErrMsg( "dvd error: can't find first chapter" );
398 p_input->b_error = 1;
402 /* Force libdvdcss to check its title key.
403 * It is only useful for title cracking method. Methods using the
404 * decrypted disc key are fast enough to check the key at each seek */
406 if( dvdcss_seek( p_dvd->dvdhandle, p_dvd->i_start,
407 DVDCSS_SEEK_KEY ) < 0 )
409 intf_ErrMsg( "dvd error: %s", dvdcss_error( p_dvd->dvdhandle ) );
413 p_dvd->i_size -= p_dvd->i_sector + 1;
415 IfoPrintTitle( p_dvd );
417 /* Area definition */
418 p_input->stream.p_selected_area->i_start = LB2OFF( p_dvd->i_start );
419 p_input->stream.p_selected_area->i_size = LB2OFF( p_dvd->i_size );
422 * Destroy obsolete ES by reinitializing programs
423 * and find all ES in title with ifo data
425 if( p_input->stream.pp_programs != NULL )
427 /* We don't use input_EndStream here since
428 * we keep area structures */
429 while( p_input->stream.i_es_number )
431 input_DelES( p_input, p_input->stream.pp_es[0] );
434 while( p_input->stream.i_pgrm_number )
436 input_DelProgram( p_input, p_input->stream.pp_programs[0] );
439 if( p_input->stream.pp_selected_es )
441 free( p_input->stream.pp_selected_es );
442 p_input->stream.pp_selected_es = NULL;
444 p_input->stream.i_selected_es_number = 0;
448 * Angle management: angles are handled through programs
450 p_dvd->i_angle_nb = vmg.title_inf.p_attr[p_dvd->i_title-1].i_angle_nb;
451 if( ( p_dvd->i_angle <= 0 ) || p_dvd->i_angle > p_dvd->i_angle_nb )
456 input_AddProgram( p_input, 1, sizeof( stream_ps_data_t ) );
457 p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
459 for( i = 1 ; i < p_dvd->i_angle_nb ; i++ )
461 input_AddProgram( p_input, i+1, 0 );
464 DVDSetProgram( p_input,
465 p_input->stream.pp_programs[p_dvd->i_angle-1] );
468 /* No PSM to read in DVD mode, we already have all information */
469 p_input->stream.p_selected_program->b_is_ok = 1;
471 DVDReadVideo( p_input );
473 DVDReadAudio( p_input );
475 DVDReadSPU( p_input );
477 /* FIXME: hack to check that the demuxer is ready, and set
479 if( p_input->p_demux_module )
481 DVDLaunchDecoders( p_input );
487 p_area = p_input->stream.p_selected_area;
496 if( p_area->i_part != p_dvd->i_chapter )
498 if( ( p_area->i_part > 0 ) &&
499 ( p_area->i_part <= p_area->i_part_nb ))
501 if( DVDChapterSelect( p_dvd, p_area->i_part ) < 0 )
503 intf_ErrMsg( "dvd error: can't set chapter in area" );
504 p_input->b_error = 1;
508 p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
509 p_input->stream.p_selected_area->i_tell =
510 LB2OFF( p_dvd->i_start ) - p_area->i_start;
512 intf_WarnMsg( 4, "dvd info: chapter %d start at: %lld",
513 p_area->i_part, p_area->i_tell );
518 p_dvd->i_chapter = 1;
522 /* warn interface that something has changed */
523 p_input->stream.b_seekable = 1;
524 p_input->stream.b_changed = 1;
530 p_dvd->p_ifo->vts.title_unit.p_title[p_dvd->i_title_id-1].title
532 /*****************************************************************************
533 * DVDRead: reads data packets.
534 *****************************************************************************
535 * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
537 *****************************************************************************/
538 static int DVDRead( input_thread_t * p_input,
539 byte_t * p_buffer, size_t i_count )
541 thread_dvd_data_t * p_dvd;
549 p_dvd = (thread_dvd_data_t *)(p_input->p_access_data);
556 i_blocks = OFF2LB(i_count);
560 i_sector = p_dvd->i_title_start + p_dvd->i_sector;
561 i_block_once = p_dvd->i_end_sector - p_dvd->i_sector + 1;
563 /* Get the position of the next cell if we're at cell end */
564 if( i_block_once <= 0 )
569 p_dvd->i_angle_cell++;
571 /* Find cell index in adress map */
572 if( DVDFindSector( p_dvd ) < 0 )
574 intf_ErrMsg( "dvd error: can't find next cell" );
578 /* Position the fd pointer on the right address */
579 if( ( i_sector = dvdcss_seek( p_dvd->dvdhandle,
580 p_dvd->i_title_start + p_dvd->i_sector,
581 DVDCSS_SEEK_MPEG ) ) < 0 )
583 intf_ErrMsg( "dvd error: %s",
584 dvdcss_error( p_dvd->dvdhandle ) );
588 /* update chapter : it will be easier when we have navigation
590 if( p_dvd->i_chapter < ( p_dvd->i_chapter_nb - 1 ) )
592 if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
594 i_angle = p_dvd->i_angle - 1;
600 if( title.chapter_map.pi_start_cell[p_dvd->i_chapter] <=
601 ( p_dvd->i_prg_cell - i_angle + 1 ) )
608 i_block_once = p_dvd->i_end_sector - p_dvd->i_sector + 1;
611 /* The number of blocks read is the max between the requested
612 * value and the leaving block in the cell */
613 if( i_block_once > i_blocks )
615 i_block_once = i_blocks;
619 i_read_blocks = dvdcss_read( p_dvd->dvdhandle, p_buffer,
620 i_block_once, DVDCSS_READ_DECRYPT );
622 i_blocks -= i_read_blocks;
623 p_buffer += LB2OFF( i_read_blocks );
624 i_read_total += i_read_blocks;
626 /* Update global position */
627 p_dvd->i_sector += i_read_blocks;
630 vlc_mutex_lock( &p_input->stream.stream_lock );
632 p_input->stream.p_selected_area->i_tell =
633 LB2OFF( i_sector + i_read_total ) -
634 p_input->stream.p_selected_area->i_start;
637 /* We modify i_part only at end of chapter not to erase
638 * some modification from the interface */
639 p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
642 if( p_input->stream.p_selected_area->i_tell
643 >= p_input->stream.p_selected_area->i_size )
645 if( ( p_dvd->i_title + 1 ) >= p_input->stream.i_area_nb )
648 vlc_mutex_unlock( &p_input->stream.stream_lock );
653 intf_WarnMsg( 4, "dvd info: new title" );
655 DVDSetArea( p_input, p_input->stream.pp_areas[p_dvd->i_title] );
658 vlc_mutex_unlock( &p_input->stream.stream_lock );
660 return LB2OFF( i_read_total );
663 /*****************************************************************************
664 * DVDSeek : Goes to a given position on the stream.
665 *****************************************************************************
666 * This one is used by the input and translate chronological position from
667 * input to logical position on the device.
668 * The lock should be taken before calling this function.
669 *****************************************************************************/
670 static void DVDSeek( input_thread_t * p_input, off_t i_off )
672 thread_dvd_data_t * p_dvd;
679 p_dvd = ( thread_dvd_data_t * )(p_input->p_access_data);
681 vlc_mutex_lock( &p_input->stream.stream_lock );
682 /* we have to take care of offset of beginning of title */
683 p_dvd->i_sector = OFF2LB(i_off + p_input->stream.p_selected_area->i_start)
684 - p_dvd->i_title_start;
685 vlc_mutex_unlock( &p_input->stream.stream_lock );
690 /* parse vobu address map to find program cell */
691 while( title.p_cell_play[i_prg_cell].i_end_sector < p_dvd->i_sector )
696 p_dvd->i_prg_cell = i_prg_cell;
698 if( DVDChooseAngle( p_dvd ) < 0 )
700 p_input->b_error = 1;
706 /* Find first title cell which is inside program cell */
707 if( DVDFindCell( p_dvd ) < 0 )
709 /* no following cell : we're at eof */
710 intf_ErrMsg( "dvd error: cell seeking failed" );
711 p_input->b_error = 1;
715 i_cell = p_dvd->i_cell;
717 #define cell p_dvd->p_ifo->vts.cell_inf.p_cell_map[i_cell]
718 /* parse cell address map to find title cell containing sector */
719 while( cell.i_end_sector < p_dvd->i_sector )
724 p_dvd->i_cell = i_cell;
726 /* if we're inside a multi-angle zone, we have to choose i_sector
727 * in the current angle ; we can't do it all the time since cells
728 * can be very wide out of such zones */
729 if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
731 p_dvd->i_sector = __MAX(
733 title.p_cell_play[p_dvd->i_prg_cell].i_start_sector );
736 p_dvd->i_end_sector = __MIN(
738 title.p_cell_play[p_dvd->i_prg_cell].i_end_sector );
741 if( title.p_cell_play[p_dvd->i_prg_cell].i_category & 0xf000 )
743 i_angle = p_dvd->i_angle - 1;
749 if( p_dvd->i_chapter_nb > 1 )
751 while( ( title.chapter_map.pi_start_cell[i_chapter] <=
752 ( p_dvd->i_prg_cell - i_angle + 1 ) ) &&
753 ( i_chapter < ( p_dvd->i_chapter_nb - 1 ) ) )
763 p_dvd->i_chapter = i_chapter;
765 if( ( i_block = dvdcss_seek( p_dvd->dvdhandle,
766 p_dvd->i_title_start + p_dvd->i_sector,
767 DVDCSS_SEEK_MPEG ) ) < 0 )
769 intf_ErrMsg( "dvd error: %s", dvdcss_error( p_dvd->dvdhandle ) );
770 p_input->b_error = 1;
774 vlc_mutex_lock( &p_input->stream.stream_lock );
775 p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
776 p_input->stream.p_selected_area->i_tell =
777 LB2OFF ( i_block ) - p_input->stream.p_selected_area->i_start;
778 vlc_mutex_unlock( &p_input->stream.stream_lock );
780 intf_WarnMsg( 4, "Program Cell: %d Cell: %d Chapter: %d",
781 p_dvd->i_prg_cell, p_dvd->i_cell, p_dvd->i_chapter );
786 /*****************************************************************************
787 * DVDParse: parse command line
788 *****************************************************************************/
789 static char * DVDParse( input_thread_t * p_input )
791 thread_dvd_data_t * p_dvd;
792 struct stat stat_info;
797 boolean_t b_options = 0;
803 p_dvd = (thread_dvd_data_t*)(p_input->p_access_data);
805 psz_parser = psz_device = strdup( p_input->psz_name );
811 /* Parse input string :
812 * [device][@rawdevice][@[title][,[chapter][,angle]]] */
813 while( *psz_parser && *psz_parser != '@' )
818 if( *psz_parser == '@' )
820 /* Maybe found raw device or option list */
822 psz_raw = ++psz_parser;
829 if( *psz_parser && !strtol( psz_parser, NULL, 10 ) )
831 /* what we've found is either a raw device or a partial option
832 * list e.g. @,29 or both a device and a list ; search end of string */
833 while( *psz_parser && *psz_parser != '@' )
838 if( *psz_parser == '@' )
840 /* found end of raw device, and beginning of options */
847 psz_parser = psz_raw + 1;
848 for( i=0 ; i<3 ; i++ )
852 /* we have only a raw device */
855 if( strtol( psz_parser, NULL, 10 ) )
857 /* we have only a partial list of options, no device */
858 psz_parser = psz_raw;
869 /* found beginning of options ; no raw device specified */
877 i_title = (int)strtol( psz_parser, &psz_next, 10 );
880 psz_parser = psz_next + 1;
881 i_chapter = (int)strtol( psz_parser, &psz_next, 10 );
884 i_angle = (int)strtol( psz_next + 1, NULL, 10 );
888 p_dvd->i_title = i_title ? i_title : 1;
889 p_dvd->i_chapter = i_chapter ? i_chapter : 1;
890 p_dvd->i_angle = i_angle ? i_angle : 1;
897 /* check the raw device */
898 if( stat( psz_raw, &stat_info ) == -1 )
900 intf_WarnMsg( 3, "dvd warning: cannot stat() raw"
902 psz_raw, strerror(errno));
904 *(psz_raw - 1) = '@';
912 if( !S_ISCHR(stat_info.st_mode) )
914 intf_WarnMsg( 3, "dvd warning: raw device %s is"
915 " not a valid char device", psz_raw );
917 *(psz_raw - 1) = '@';
923 psz_env = malloc( strlen("DVDCSS_RAW_DEVICE=")
924 + strlen( psz_raw ) + 1 );
925 sprintf( psz_env, "DVDCSS_RAW_DEVICE=%s", psz_raw );
940 if( !p_input->psz_access )
942 /* no device and no access specified: we probably don't want DVD */
945 psz_device = config_GetPszVariable( INPUT_DVD_DEVICE_VAR );
948 /* check block device */
949 if( stat( psz_device, &stat_info ) == -1 )
951 intf_ErrMsg( "input error: cannot stat() device `%s' (%s)",
952 psz_device, strerror(errno));
957 if( !S_ISBLK(stat_info.st_mode) && !S_ISCHR(stat_info.st_mode) )
959 intf_WarnMsg( 3, "input: DVD plugin discarded"
960 " (not a valid block device)" );
965 intf_WarnMsg( 2, "input: dvd=%s raw=%s title=%d chapter=%d angle=%d",
966 psz_device, psz_raw, p_dvd->i_title,
967 p_dvd->i_chapter, p_dvd->i_angle );