1 /*****************************************************************************
2 * intf.c: interface for CMML annotations/hyperlinks
3 *****************************************************************************
4 * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research
5 * Organisation (CSIRO) Australia
6 * Copyright (C) 2004 the VideoLAN team
10 * Authors: Andre Pang <Andre.Pang@csiro.au>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
42 #include <vlc_codec.h>
43 #include <vlc_input.h>
44 #include <vlc_interface.h>
46 #include <vlc_playlist.h>
51 #include "browser_open.h"
56 #undef CMML_INTF_USE_TIMED_URIS
58 #undef CMML_INTF_DEBUG
59 #undef CMML_INTF_HISTORY_DEBUG
61 /*****************************************************************************
62 * intf_sys_t: description and status of interface
63 *****************************************************************************/
66 decoder_t * p_cmml_decoder;
67 input_thread_t * p_input;
72 struct navigation_history_t
78 /*****************************************************************************
80 *****************************************************************************/
82 int OpenIntf ( vlc_object_t * );
83 void CloseIntf ( vlc_object_t * );
85 static int InitThread ( intf_thread_t * );
86 static int MouseEvent ( vlc_object_t *, char const *,
87 vlc_value_t, vlc_value_t, void * );
88 static int KeyEvent ( vlc_object_t *, char const *,
89 vlc_value_t, vlc_value_t, void * );
91 static void FollowAnchor ( intf_thread_t * );
92 static void GoBack ( intf_thread_t * );
93 static void GoForward ( intf_thread_t * );
95 static int FollowAnchorCallback ( vlc_object_t *, char const *,
96 vlc_value_t, vlc_value_t, void * );
97 static int GoBackCallback ( vlc_object_t *, char const *,
98 vlc_value_t, vlc_value_t, void * );
99 static int GoForwardCallback ( vlc_object_t *, char const *,
100 vlc_value_t, vlc_value_t, void * );
102 static char *GetTimedURLFromPlaylistItem( intf_thread_t *, playlist_item_t * );
103 static char *GetTimedURIFragmentForTime ( int );
104 static int GetCurrentTimeInSeconds ( input_thread_t * );
105 static int DisplayAnchor ( intf_thread_t *, vout_thread_t *,
107 static int DisplayPendingAnchor ( intf_thread_t *, vout_thread_t * );
108 static history_t * GetHistory ( playlist_t * );
109 static void ReplacePlaylistItem ( playlist_t *, char * );
111 /* Exported functions */
112 static void RunIntf ( intf_thread_t *p_intf );
114 /*****************************************************************************
115 * OpenIntf: initialize CMML interface
116 *****************************************************************************/
117 int OpenIntf ( vlc_object_t *p_this )
119 intf_thread_t *p_intf = (intf_thread_t *)p_this;
121 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
122 if( p_intf->p_sys == NULL )
127 p_intf->pf_run = RunIntf;
129 var_AddCallback( p_intf->p_libvlc, "key-action", KeyEvent, p_intf );
130 /* we also need to add the callback for "mouse-clicked", but do that later
131 * when we've found a p_vout */
133 var_Create( p_intf->p_libvlc, "browse-go-back", VLC_VAR_VOID );
134 var_AddCallback( p_intf->p_libvlc, "browse-go-back",
135 GoBackCallback, p_intf );
136 var_Create( p_intf->p_libvlc, "browse-go-forward", VLC_VAR_VOID );
137 var_AddCallback( p_intf->p_libvlc, "browse-go-forward",
138 GoForwardCallback, p_intf );
139 var_Create( p_intf->p_libvlc, "browse-follow-anchor", VLC_VAR_VOID );
140 var_AddCallback( p_intf->p_libvlc, "browse-follow-anchor",
141 FollowAnchorCallback, p_intf );
146 /*****************************************************************************
147 * CloseIntf: destroy dummy interface
148 *****************************************************************************/
149 void CloseIntf ( vlc_object_t *p_this )
151 intf_thread_t * p_intf = (intf_thread_t *)p_this;
152 vout_thread_t * p_vout;
154 #ifdef CMML_INTF_DEBUG
155 msg_Dbg( p_intf, "freeing CMML interface" );
158 /* erase the anchor text description from the video output if it exists */
159 p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
162 /* enable CMML as a subtitle track */
163 spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
164 vlc_object_release( p_vout );
167 var_DelCallback( p_intf->p_libvlc, "key-action", KeyEvent, p_intf );
169 vlc_object_release( p_intf->p_sys->p_cmml_decoder );
171 free( p_intf->p_sys );
175 /*****************************************************************************
177 *****************************************************************************/
178 static void RunIntf( intf_thread_t *p_intf )
180 vout_thread_t * p_vout = NULL;
182 if( InitThread( p_intf ) < 0 )
184 msg_Err( p_intf, "can't initialize CMML interface" );
187 #ifdef CMML_INTF_DEBUG
188 msg_Dbg( p_intf, "CMML intf initialized" );
191 /* if video output is dying, disassociate ourselves from it */
192 if( p_vout && p_vout->b_die )
194 var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
195 vlc_object_release( p_vout );
200 while( !p_intf->b_die )
203 /* find a video output if we currently don't have one */
206 p_vout = vlc_object_find( p_intf->p_sys->p_input,
207 VLC_OBJECT_VOUT, FIND_CHILD );
210 #ifdef CMML_INTF_DEBUG
211 msg_Dbg( p_intf, "found vout thread" );
213 var_AddCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
217 vlc_mutex_lock( &p_intf->change_lock );
222 switch( p_intf->p_sys->i_key_action )
224 case ACTIONID_NAV_ACTIVATE:
225 FollowAnchor( p_intf );
227 case ACTIONID_HISTORY_BACK:
230 case ACTIONID_HISTORY_FORWARD:
236 p_intf->p_sys->i_key_action = 0;
237 vlc_mutex_unlock( &p_intf->change_lock );
239 (void) DisplayPendingAnchor( p_intf, p_vout );
242 msleep( INTF_IDLE_SLEEP );
245 /* if we're here, the video output is dying: release the vout object */
249 var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
250 vlc_object_release( p_vout );
253 vlc_object_release( p_intf->p_sys->p_input );
256 /*****************************************************************************
257 * DisplayPendingAnchor: get a pending anchor description/URL from the CMML
258 * decoder and display it on screen
259 *****************************************************************************/
260 static int DisplayPendingAnchor( intf_thread_t *p_intf, vout_thread_t *p_vout )
262 decoder_t *p_cmml_decoder;
263 char *psz_description = NULL;
264 char *psz_url = NULL;
266 intf_thread_t *p_primary_intf;
269 p_cmml_decoder = p_intf->p_sys->p_cmml_decoder;
270 if( var_Get( p_cmml_decoder, "psz-current-anchor-description", &val )
279 psz_description = val.p_address;
281 if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val )
284 psz_url = val.p_address;
289 /* don't display anchor if main interface can display it */
290 p_primary_intf = vlc_object_find( p_intf->p_libvlc, VLC_OBJECT_INTF,
295 if( var_Get( p_primary_intf, "intf-displays-cmml-description", &val )
298 if( val.b_bool == true )
300 vlc_object_release( p_primary_intf );
305 vlc_object_release( p_primary_intf );
308 /* display anchor as subtitle on-screen */
309 if( DisplayAnchor( p_intf, p_vout, psz_description, psz_url )
312 /* text render unsuccessful: do nothing */
316 /* text render successful: clear description */
317 val.p_address = NULL;
318 if( var_Set( p_cmml_decoder, "psz-current-anchor-description", val )
322 "reset of psz-current-anchor-description failed" );
324 free( psz_description );
332 /*****************************************************************************
334 *****************************************************************************/
335 static int InitThread( intf_thread_t * p_intf )
337 /* We might need some locking here */
340 input_thread_t * p_input;
341 decoder_t *p_cmml_decoder;
343 p_cmml_decoder = vlc_object_find( p_intf, VLC_OBJECT_DECODER, FIND_PARENT );
344 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_PARENT );
346 #ifdef CMML_INTF_DEBUG
347 msg_Dbg( p_intf, "cmml decoder at %p, input thread at %p",
348 p_cmml_decoder, p_input );
351 /* Maybe the input just died */
352 if( p_input == NULL )
357 vlc_mutex_lock( &p_intf->change_lock );
359 p_intf->p_sys->p_input = p_input;
360 p_intf->p_sys->p_cmml_decoder = p_cmml_decoder;
362 p_intf->p_sys->i_key_action = 0;
364 vlc_mutex_unlock( &p_intf->change_lock );
374 /*****************************************************************************
375 * MouseEvent: callback for mouse events
376 *****************************************************************************/
377 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
378 vlc_value_t oldval, vlc_value_t newval, void *p_data )
380 VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
381 VLC_UNUSED(oldval); VLC_UNUSED(newval);
383 /* TODO: handle mouse clicks on the anchor text */
388 /*****************************************************************************
389 * KeyEvent: callback for keyboard events
390 *****************************************************************************/
391 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
392 vlc_value_t oldval, vlc_value_t newval, void *p_data )
394 VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
395 VLC_UNUSED(oldval); VLC_UNUSED(newval);
396 intf_thread_t *p_intf = (intf_thread_t *)p_data;
397 vlc_mutex_lock( &p_intf->change_lock );
399 p_intf->p_sys->i_key_action = newval.i_int;
401 vlc_mutex_unlock( &p_intf->change_lock );
406 /*****************************************************************************
407 * FollowAnchor: follow the current anchor being displayed to the user
408 *****************************************************************************/
409 static void FollowAnchor ( intf_thread_t *p_intf )
412 decoder_t *p_cmml_decoder;
413 char *psz_url = NULL;
416 msg_Dbg( p_intf, "User followed anchor" );
418 p_sys = p_intf->p_sys;
419 p_cmml_decoder = p_sys->p_cmml_decoder;
421 if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val ) ==
424 if( val.p_address ) psz_url = val.p_address;
427 #ifdef CMML_INTF_DEBUG
428 msg_Dbg( p_intf, "Current URL is \"%s\"", psz_url );
433 playlist_t *p_playlist;
434 playlist_item_t *p_current_item;
435 char *psz_uri_to_load;
439 p_playlist = (playlist_t *) vlc_object_find( p_intf,
440 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
443 msg_Warn( p_intf, "can't find playlist" );
448 p_current_item = p_playlist->status.p_item;
449 char *psz_uri = input_item_GetURI( p_current_item->p_input );
450 #ifdef CMML_INTF_DEBUG
451 msg_Dbg( p_intf, "Current playlist item URL is \"%s\"", psz_uri );
454 psz_uri_to_load = XURL_Concat( psz_uri, psz_url );
457 #ifdef CMML_INTF_DEBUG
458 msg_Dbg( p_intf, "URL to load is \"%s\"", psz_uri_to_load );
461 if( var_Get( p_intf->p_sys->p_input, "time", &time ) )
463 msg_Dbg( p_intf, "couldn't get time from current clip" );
466 i_seconds = time.i_time / 1000000;
467 #ifdef CMML_INTF_DEBUG
468 msg_Dbg( p_intf, "Current time is \"%lld\"", i_seconds );
471 /* TODO: we need a (much) more robust way of detecting whether
472 * the file's a media file ... */
473 if( strstr( psz_uri_to_load, ".anx" ) != NULL )
475 history_t *p_history = NULL;
476 history_item_t *p_history_item = NULL;
479 p_history = GetHistory( p_playlist );
481 /* create history item */
482 psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item );
483 p_history_item = historyItem_New( psz_timed_url, psz_timed_url );
484 free( psz_timed_url );
486 if( !p_history_item )
488 msg_Warn( p_intf, "could not initialise history item" );
492 #ifdef CMML_INTF_DEBUG
493 msg_Dbg( p_intf, "history pre-index %d", p_history->i_index );
495 history_PruneAndInsert( p_history, p_history_item );
496 #ifdef CMML_INTF_DEBUG
497 msg_Dbg( p_intf, "new history item at %p, uri is \"%s\"",
498 p_history_item, p_history_item->psz_uri );
499 msg_Dbg( p_intf, "history index now %d", p_history->i_index );
503 /* free current-anchor-url */
505 val.p_address = NULL;
506 if( var_Set( p_cmml_decoder, "psz-current-anchor-url", val ) !=
509 msg_Dbg( p_intf, "couldn't reset psz-current-anchor-url" );
512 ReplacePlaylistItem( p_playlist, psz_uri_to_load );
516 #ifdef CMML_INTF_DEBUG
517 msg_Dbg( p_intf, "calling browser_Open with \"%s\"", psz_url );
519 (void) browser_Open( psz_url );
520 playlist_Control( p_playlist, PLAYLIST_PAUSE, true, 0 );
523 free( psz_uri_to_load );
525 vlc_object_release( p_playlist );
530 char *GetTimedURLFromPlaylistItem( intf_thread_t *p_intf,
531 playlist_item_t *p_current_item )
533 #ifdef CMML_INTF_USE_TIMED_URIS
534 char *psz_url = NULL;
535 char *psz_return_value = NULL;
536 char *psz_seconds = NULL;
539 char *psz_uri = input_item_GetURI( p_current_item->p_input );
540 psz_url = XURL_GetWithoutFragment( psz_uri );
543 /* Get current time as a string */
544 if( XURL_IsFileURL( psz_url ) == true )
545 psz_url = xstrcat( psz_url, "#" );
547 psz_url = xstrcat( psz_url, "?" );
549 /* jump back to 2 seconds before where we are now */
550 i_seconds = GetCurrentTimeInSeconds( p_intf->p_sys->p_input ) - 2;
551 psz_seconds = GetTimedURIFragmentForTime( i_seconds < 0 ? 0 : i_seconds );
554 psz_url = xstrcat( psz_url, psz_seconds );
556 psz_return_value = psz_url;
559 return psz_return_value;
564 /* Suppress warning messages about unused functions */
565 p = GetTimedURIFragmentForTime; /* unused */
566 p = GetCurrentTimeInSeconds; /* unused */
568 return input_item_GetURI( p_current_item->p_input );
574 * Get the current time, rounded down to the nearest second
576 * http://www.ietf.org/internet-drafts/draft-pfeiffer-temporal-fragments-02.txt
579 int GetCurrentTimeInSeconds( input_thread_t *p_input )
584 var_Get( p_input, "time", &time );
585 i_seconds = time.i_time / 1000000;
590 char *GetTimedURIFragmentForTime( int seconds )
594 asprintf( &psz_time, "%d", seconds );
599 int GoBackCallback( vlc_object_t *p_this, char const *psz_var,
600 vlc_value_t oldval, vlc_value_t newval, void *p_data )
602 VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
603 VLC_UNUSED(oldval); VLC_UNUSED(newval);
604 intf_thread_t *p_intf = (intf_thread_t *) p_data;
610 int GoForwardCallback( vlc_object_t *p_this, char const *psz_var,
611 vlc_value_t oldval, vlc_value_t newval, void *p_data )
613 VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
614 VLC_UNUSED(oldval); VLC_UNUSED(newval);
615 intf_thread_t *p_intf = (intf_thread_t *) p_data;
621 int FollowAnchorCallback( vlc_object_t *p_this, char const *psz_var,
622 vlc_value_t oldval, vlc_value_t newval,
625 VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
626 VLC_UNUSED(oldval); VLC_UNUSED(newval);
627 intf_thread_t *p_intf = (intf_thread_t *) p_data;
628 FollowAnchor( p_intf );
633 void GoBack( intf_thread_t *p_intf )
636 history_t *p_history = NULL;
637 history_item_t *p_history_item = NULL;
638 history_item_t *p_new_history_item = NULL;
639 playlist_t *p_playlist = NULL;
640 char *psz_timed_url = NULL;
641 playlist_item_t *p_current_item;
643 #ifdef CMML_INTF_DEBUG
644 msg_Dbg( p_intf, "Going back in navigation history" );
647 /* Find the playlist */
648 p_playlist = (playlist_t *) vlc_object_find( p_intf,
649 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
652 msg_Warn( p_intf, "can't find playlist" );
656 /* Retrieve navigation history from playlist */
657 if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS ||
660 /* History doesn't exist yet: ignore user's request */
661 msg_Warn( p_intf, "can't go back: no history exists yet" );
662 vlc_object_release( p_playlist );
666 p_history = history.p_address;
667 #ifdef CMML_INTF_DEBUG
668 msg_Dbg( p_intf, "back: nav history retrieved from %p", p_history );
669 msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index,
670 p_history->p_xarray );
673 /* Check whether we can go back in the history */
674 if( history_CanGoBack( p_history ) == false )
676 msg_Warn( p_intf, "can't go back: already at beginning of history" );
677 vlc_object_release( p_playlist );
681 p_current_item = p_playlist->status.p_item;
683 /* Save the currently-playing media in a new history item */
684 psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item );
685 p_new_history_item = historyItem_New( psz_timed_url, psz_timed_url );
686 free( psz_timed_url );
688 if( !p_new_history_item )
690 #ifdef CMML_INTF_DEBUG
691 msg_Dbg( p_intf, "back: could not initialise new history item" );
693 vlc_object_release( p_playlist );
697 /* Go back in the history, saving the currently-playing item */
698 (void) history_GoBackSavingCurrentItem( p_history, p_new_history_item );
699 p_history_item = history_Item( p_history );
701 #ifdef CMML_INTF_DEBUG
702 msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index );
703 msg_Dbg( p_intf, "got previous history item: %p", p_history_item );
704 msg_Dbg( p_intf, "prev history item URL: \"%s\"", p_history_item->psz_uri );
707 ReplacePlaylistItem( p_playlist, p_history_item->psz_uri );
708 vlc_object_release( p_playlist );
712 void GoForward( intf_thread_t *p_intf )
715 history_t *p_history = NULL;
716 history_item_t *p_history_item = NULL;
717 history_item_t *p_new_history_item = NULL;
718 playlist_t *p_playlist = NULL;
719 playlist_item_t *p_current_item;
721 #ifdef CMML_INTF_DEBUG
722 msg_Dbg( p_intf, "Going forward in navigation history" );
725 /* Find the playlist */
726 p_playlist = (playlist_t *) vlc_object_find( p_intf,
727 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
730 msg_Warn( p_intf, "can't find playlist" );
734 /* Retrieve navigation history from playlist */
735 if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS ||
738 /* History doesn't exist yet: ignore user's request */
739 msg_Warn( p_intf, "can't go back: no history exists yet" );
740 vlc_object_release( p_playlist );
744 p_history = history.p_address;
745 #ifdef CMML_INTF_DEBUG
746 msg_Dbg( p_intf, "forward: nav history retrieved from %p", p_history );
747 msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index,
748 p_history->p_xarray );
751 /* Check whether we can go forward in the history */
752 if( history_CanGoForward( p_history ) == false )
754 msg_Warn( p_intf, "can't go forward: already at end of history" );
755 vlc_object_release( p_playlist );
759 /* Save the currently-playing media in a new history item */
760 p_new_history_item = malloc( sizeof(history_item_t) );
761 if( !p_new_history_item )
763 #ifdef CMML_INTF_DEBUG
764 msg_Dbg( p_intf, "forward: could not initialise new history item" );
766 vlc_object_release( p_playlist );
769 p_current_item = p_playlist->status.p_item;
770 p_new_history_item->psz_uri = GetTimedURLFromPlaylistItem( p_intf,
772 p_new_history_item->psz_name = p_new_history_item->psz_uri;
774 /* Go forward in the history, saving the currently-playing item */
775 (void) history_GoForwardSavingCurrentItem( p_history, p_new_history_item );
776 p_history_item = history_Item( p_history );
778 #ifdef CMML_INTF_DEBUG
779 msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index );
780 msg_Dbg( p_intf, "got next history item: %p", p_history_item );
781 msg_Dbg( p_intf, "next history item URL: \"%s\"", p_history_item->psz_uri );
784 ReplacePlaylistItem( p_playlist, p_history_item->psz_uri );
785 vlc_object_release( p_playlist );
788 static void ReplacePlaylistItem( playlist_t *p_playlist, char *psz_uri )
790 playlist_Stop( p_playlist );
791 (void) playlist_Add( p_playlist, psz_uri, psz_uri,
792 PLAYLIST_INSERT /* FIXME: used to be PLAYLIST_REPLACE */, PLAYLIST_END|PLAYLIST_GO, true /* FIXME: p_playlist->status.i_index */,
796 /****************************************************************************
797 * DisplayAnchor: displays an anchor on the given video output
798 ****************************************************************************/
799 static int DisplayAnchor( intf_thread_t *p_intf,
800 vout_thread_t *p_vout,
801 char *psz_anchor_description,
802 char *psz_anchor_url )
804 int i_margin_h, i_margin_v;
816 /* Should display subtitle underlined and in blue, but it looks
817 * like VLC doesn't implement any text styles yet. D'oh! */
818 // p_style = &blue_with_underline;
822 /* TODO: p_subpicture doesn't have the proper i_x and i_y
823 * coordinates. Need to look at the subpicture display system to
825 if ( vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
826 psz_anchor_description, NULL, OSD_ALIGN_BOTTOM,
827 i_margin_h, i_margin_v, i_now, 0 ) == VLC_SUCCESS )
829 /* Displayed successfully */
838 msg_Dbg( p_intf, "DisplayAnchor couldn't find a video output" );
845 static history_t * GetHistory( playlist_t *p_playlist )
848 history_t *p_history = NULL;
850 if( var_Get( p_playlist, "navigation-history", &val ) != VLC_SUCCESS )
852 /* history doesn't exist yet: need to create it */
853 history_t *new_history = history_New();
854 val.p_address = new_history;
855 var_Create( p_playlist, "navigation-history",
856 VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
857 if( var_Set( p_playlist, "navigation-history", val ) != VLC_SUCCESS )
859 msg_Warn( p_playlist, "could not initialise history" );
863 p_history = new_history;
864 #ifdef CMML_INTF_HISTORY_DEBUG
865 msg_Dbg( p_playlist, "nav history created at %p", new_history );
866 msg_Dbg( p_playlist, "nav history index:%d, p_xarray:%p",
867 p_history->i_index, p_history->p_xarray );
873 p_history = val.p_address;
874 #ifdef CMML_INTF_HISTORY_DEBUG
875 msg_Dbg( p_playlist, "nav history retrieved from %p", p_history );