playlistinfo.m 16.8 KB
Newer Older
Benjamin Pracht's avatar
Benjamin Pracht committed
1
/*****************************************************************************
2
 r playlistinfo.m: MacOS X interface module
Benjamin Pracht's avatar
Benjamin Pracht committed
3
 *****************************************************************************
4
 * Copyright (C) 2002-2008 the VideoLAN team
5
 * $Id$
Benjamin Pracht's avatar
Benjamin Pracht committed
6
 *
7
 * Authors: Benjamin Pracht <bigben at videolan dot org>
8
 *          Felix Paul Kühne <fkuehne at videolan dot org>
Benjamin Pracht's avatar
Benjamin Pracht committed
9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
14
 *
Benjamin Pracht's avatar
Benjamin Pracht committed
15 16 17 18 19 20 21
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Benjamin Pracht's avatar
Benjamin Pracht committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#include "intf.h"
#include "playlistinfo.h"
#include "playlist.h"

/*****************************************************************************
 * VLCPlaylistInfo Implementation
 *****************************************************************************/

37 38
@implementation VLCInfo

39 40 41
static VLCInfo *_o_sharedInstance = nil;

+ (VLCInfo *)sharedInstance
42
{
43 44
    return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
}
45

46 47 48 49 50 51 52 53 54 55 56 57
- (id)init
{
    if( _o_sharedInstance ) {
        [self dealloc];
    } else {
        _o_sharedInstance = [super init];
        
        if( _o_sharedInstance != nil )
        {
            p_item = NULL;
            o_statUpdateTimer = nil;
        }
58
    }
59 60

    return _o_sharedInstance;
61 62
}

Benjamin Pracht's avatar
Benjamin Pracht committed
63 64 65 66
- (void)awakeFromNib
{
    [o_info_window setExcludedFromWindowsMenu: TRUE];

67 68
    [o_info_window setTitle: _NS("Media Information")];
    [o_uri_lbl setStringValue: _NS("Location")];
Benjamin Pracht's avatar
Benjamin Pracht committed
69
    [o_title_lbl setStringValue: _NS("Title")];
70 71 72
    [o_author_lbl setStringValue: _NS("Artist")];
    [o_saveMetaData_btn setStringValue: _NS("Save Metadata" )];

73
    [[o_tab_view tabViewItemAtIndex: 0] setLabel: _NS("General")];
74
    [[o_tab_view tabViewItemAtIndex: 1] setLabel: _NS("Codec Details")];
75 76 77 78 79 80 81 82 83 84 85 86 87
    [[o_tab_view tabViewItemAtIndex: 2] setLabel: _NS("Statistics")];
    [o_tab_view selectTabViewItemAtIndex: 0];

    /* constants defined in vlc_meta.h */
    [o_genre_lbl setStringValue: _NS(VLC_META_GENRE)];
    [o_copyright_lbl setStringValue: _NS(VLC_META_COPYRIGHT)];
    [o_collection_lbl setStringValue: _NS(VLC_META_COLLECTION)];
    [o_seqNum_lbl setStringValue: _NS(VLC_META_SEQ_NUM)];
    [o_description_lbl setStringValue: _NS(VLC_META_DESCRIPTION)];
    [o_date_lbl setStringValue: _NS(VLC_META_DATE)];
    [o_language_lbl setStringValue: _NS(VLC_META_LANGUAGE)];
    [o_nowPlaying_lbl setStringValue: _NS(VLC_META_NOW_PLAYING)];
    [o_publisher_lbl setStringValue: _NS(VLC_META_PUBLISHER)];
88

89 90 91 92 93 94
    /* statistics */
    [o_input_box setTitle: _NS("Input")];
    [o_read_bytes_lbl setStringValue: _NS("Read at media")];
    [o_input_bitrate_lbl setStringValue: _NS("Input bitrate")];
    [o_demux_bytes_lbl setStringValue: _NS("Demuxed")];
    [o_demux_bitrate_lbl setStringValue: _NS("Stream bitrate")];
95

96 97 98 99
    [o_video_box setTitle: _NS("Video")];
    [o_video_decoded_lbl setStringValue: _NS("Decoded blocks")];
    [o_displayed_lbl setStringValue: _NS("Displayed frames")];
    [o_lost_frames_lbl setStringValue: _NS("Lost frames")];
100
	[o_fps_lbl setStringValue: _NS("Frames per Second")];
101

102 103 104 105
    [o_sout_box setTitle: _NS("Streaming")];
    [o_sent_packets_lbl setStringValue: _NS("Sent packets")];
    [o_sent_bytes_lbl setStringValue: _NS("Sent bytes")];
    [o_sent_bitrate_lbl setStringValue: _NS("Send rate")];
106

107 108 109 110
    [o_audio_box setTitle: _NS("Audio")];
    [o_audio_decoded_lbl setStringValue: _NS("Decoded blocks")];
    [o_played_abuffers_lbl setStringValue: _NS("Played buffers")];
    [o_lost_abuffers_lbl setStringValue: _NS("Lost buffers")];
111 112

    [o_info_window setInitialFirstResponder: o_uri_txt];
Benjamin Pracht's avatar
Benjamin Pracht committed
113 114
}

115 116
- (void)dealloc
{
117 118 119 120
    /* make sure that the timer is released in any case */
    if( [o_statUpdateTimer isValid] )
        [o_statUpdateTimer invalidate];

121 122
    if ( o_statUpdateTimer )
        [o_statUpdateTimer release];
123

124 125
    [super dealloc];
}
Benjamin Pracht's avatar
Benjamin Pracht committed
126

127
- (void)initPanel
128
{
129 130 131 132 133
    BOOL b_stats = config_GetInt(VLCIntf, "stats");
    if( b_stats )
    {
        o_statUpdateTimer = [NSTimer scheduledTimerWithTimeInterval: 1
            target: self selector: @selector(updateStatistics:)
134
            userInfo: nil repeats: YES];
135 136 137 138 139 140 141 142 143
        [o_statUpdateTimer fire];
        [o_statUpdateTimer retain];
    }
    else
    {
        if( [o_tab_view numberOfTabViewItems] > 2 )
            [o_tab_view removeTabViewItem: [o_tab_view tabViewItemAtIndex: 2]];
    }

144
    [self updatePanel];
145
    [o_info_window makeKeyAndOrderFront: self];
146 147 148 149 150 151
}

- (void)updatePanel
{
    /* make sure that we got the current item and not an outdated one */
    intf_thread_t * p_intf = VLCIntf;
152
        playlist_t * p_playlist = pl_Yield( p_intf );
153

154 155
    p_item = p_playlist->status.p_item;
    vlc_object_release( p_playlist );
156

157
    /* check whether our item is valid, because we would crash if not */
158 159
    if(! [self isItemInPlaylist: p_item] ) return;

160
    /* fill uri info */
161
    if( input_item_GetURI( p_item->p_input ) != NULL )
162
    {
163
        [o_uri_txt setStringValue: [NSString stringWithUTF8String: input_item_GetURI( p_item->p_input ) ]];
164
    }
165

166 167
#define SET( foo, bar ) \
    char *psz_##foo = input_item_Get##bar ( p_item->p_input ); \
168
    [self setMeta: psz_##foo forLabel: o_##foo##_txt]; \
169
    FREENULL( psz_##foo );
170

171
    /* fill the other fields */
172 173 174 175 176 177 178 179 180 181
    SET( title, Title );
    SET( author, Artist );
    SET( collection, Album );
    SET( seqNum, TrackNum );
    SET( genre, Genre );
    SET( copyright, Copyright );
    SET( publisher, Publisher );
    SET( nowPlaying, NowPlaying );
    SET( language, Language );
    SET( date, Date );
182
    SET( description, Description );
183 184

#undef SET
185

186 187
    char *psz_meta;
    NSImage *o_image;
188
    psz_meta = input_item_GetArtURL( p_item->p_input );
189 190
    if( psz_meta && !strncmp( psz_meta, "file://", 7 ) )
        o_image = [[NSImage alloc] initWithContentsOfURL: [NSURL URLWithString: [NSString stringWithUTF8String: psz_meta]]];
191
    else
192 193 194
        o_image = [[NSImage imageNamed: @"noart.png"] retain];
    [o_image_well setImage: o_image];
    [o_image release];
195
    FREENULL( psz_meta );
196

197
    /* reload the advanced table */
198 199
    [[VLCInfoTreeItem rootItem] refresh];
    [o_outline_view reloadData];
200

201 202
    /* update the stats once to display p_item change faster */
    [self updateStatistics: nil];
Benjamin Pracht's avatar
Benjamin Pracht committed
203 204
}

205
- (void)setMeta: (char *)psz_meta forLabel: (id)theItem
206 207
{
    if( psz_meta != NULL && *psz_meta)
208
        [theItem setStringValue: [NSString stringWithUTF8String:psz_meta]];
209 210
    else
        [theItem setStringValue: @""];
211 212
}

213
- (void)updateStatistics:(NSTimer*)theTimer
214
{
215 216
    if( [self isItemInPlaylist: p_item] )
    {
217
        vlc_mutex_lock( &p_item->p_input->p_stats->lock );
218 219

        /* input */
220
        [o_read_bytes_txt setStringValue: [NSString stringWithFormat:
221
            @"%8.0f kB", (float)(p_item->p_input->p_stats->i_read_bytes)/1000]];
222
        [o_input_bitrate_txt setStringValue: [NSString stringWithFormat:
223
            @"%6.0f kb/s", (float)(p_item->p_input->p_stats->f_input_bitrate)*8000]];
224
        [o_demux_bytes_txt setStringValue: [NSString stringWithFormat:
225
            @"%8.0f kB", (float)(p_item->p_input->p_stats->i_demux_read_bytes)/1000]];
226
        [o_demux_bitrate_txt setStringValue: [NSString stringWithFormat:
227
            @"%6.0f kb/s", (float)(p_item->p_input->p_stats->f_demux_bitrate)*8000]];
228 229

        /* Video */
230 231 232 233
        [o_video_decoded_txt setIntValue: p_item->p_input->p_stats->i_decoded_video];
        [o_displayed_txt setIntValue: p_item->p_input->p_stats->i_displayed_pictures];
        [o_lost_frames_txt setIntValue: p_item->p_input->p_stats->i_lost_pictures];
        float f_fps = 0;
234
        /* FIXME: input_Control( p_item->p_input, INPUT_GET_VIDEO_FPS, &f_fps ); */
235
        [o_fps_txt setFloatValue: f_fps];
236 237

        /* Sout */
238
        [o_sent_packets_txt setIntValue: p_item->p_input->p_stats->i_sent_packets];
239
        [o_sent_bytes_txt setStringValue: [NSString stringWithFormat: @"%8.0f kB",
240
            (float)(p_item->p_input->p_stats->i_sent_bytes)/1000]];
241
        [o_sent_bitrate_txt setStringValue: [NSString stringWithFormat:
242
            @"%6.0f kb/s", (float)(p_item->p_input->p_stats->f_send_bitrate*8)*1000]];
243 244

        /* Audio */
245 246 247
        [o_audio_decoded_txt setIntValue: p_item->p_input->p_stats->i_decoded_audio];
        [o_played_abuffers_txt setIntValue: p_item->p_input->p_stats->i_played_abuffers];
        [o_lost_abuffers_txt setIntValue: p_item->p_input->p_stats->i_lost_abuffers];
248

249
        vlc_mutex_unlock( &p_item->p_input->p_stats->lock );
250
    }
251 252
}

253 254 255 256 257
- (IBAction)metaFieldChanged:(id)sender
{
    [o_saveMetaData_btn setEnabled: YES];
}

258
- (IBAction)saveMetaData:(id)sender
Benjamin Pracht's avatar
Benjamin Pracht committed
259
{
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
260
    intf_thread_t * p_intf = VLCIntf;
261
    playlist_t * p_playlist = pl_Yield( p_intf );
Benjamin Pracht's avatar
Benjamin Pracht committed
262
    vlc_value_t val;
Benjamin Pracht's avatar
Benjamin Pracht committed
263

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
264
    if( [self isItemInPlaylist: p_item] )
Benjamin Pracht's avatar
Benjamin Pracht committed
265
    {
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
        meta_export_t p_export;
        p_export.p_item = p_item->p_input;

        if( p_item->p_input == NULL )
            goto end;

        /* we can write meta data only in a file */
        vlc_mutex_lock( &p_item->p_input->lock );
        int i_type = p_item->p_input->i_type;
        vlc_mutex_unlock( &p_item->p_input->lock );
        if( i_type == ITEM_TYPE_FILE )
        {
            char *psz_uri_orig = input_item_GetURI( p_item->p_input );
            char *psz_uri = psz_uri_orig;
            if( !strncmp( psz_uri, "file://", 7 ) )
                psz_uri += 7; /* strlen("file://") = 7 */
            
            p_export.psz_file = strndup( psz_uri, PATH_MAX );
            free( psz_uri_orig );
        }
        else
            goto end;

        #define utf8( o_blub ) \
            [[o_blub stringValue] UTF8String]

        input_item_SetName( p_item->p_input, utf8( o_title_txt ) );
        input_item_SetTitle( p_item->p_input, utf8( o_title_txt ) );
        input_item_SetArtist( p_item->p_input, utf8( o_author_txt ) );
        input_item_SetAlbum( p_item->p_input, utf8( o_collection_txt ) );
        input_item_SetGenre( p_item->p_input, utf8( o_genre_txt ) );
        input_item_SetTrackNum( p_item->p_input, utf8( o_seqNum_txt ) );
        input_item_SetDate( p_item->p_input, utf8( o_date_txt ) );
        input_item_SetCopyright( p_item->p_input, utf8( o_copyright_txt ) );
        input_item_SetPublisher( p_item->p_input, utf8( o_publisher_txt ) );
        input_item_SetDescription( p_item->p_input, utf8( o_description_txt ) );
        input_item_SetLanguage( p_item->p_input, utf8( o_language_txt ) );

        PL_LOCK;
        p_playlist->p_private = &p_export;

        module_t *p_mod = module_Need( p_playlist, "meta writer", NULL, 0 );
        if( p_mod )
            module_Unneed( p_playlist, p_mod );
        PL_UNLOCK;
311

Benjamin Pracht's avatar
Benjamin Pracht committed
312
        val.b_bool = VLC_TRUE;
313
        var_Set( p_playlist, "intf-change", val );
314
        [self updatePanel];
Benjamin Pracht's avatar
Benjamin Pracht committed
315
    }
316 317 318 319

    end:
    vlc_object_release( p_playlist );
    [o_saveMetaData_btn setEnabled: NO];
320 321
}

322
- (playlist_item_t *)getItem
323
{
324 325 326
    return p_item;
}

327
- (BOOL)isItemInPlaylist:(playlist_item_t *)p_local_item
328 329
{
    intf_thread_t * p_intf = VLCIntf;
330
    playlist_t * p_playlist = pl_Yield( p_intf );
331 332
    int i;

333
    for( i = 0 ; i < p_playlist->all_items.i_size ; i++ )
334
    {
335
        if( ARRAY_VAL( p_playlist->all_items, i ) == p_local_item )
336
        {
337
            vlc_object_release( p_playlist );
338 339 340
            return YES;
        }
    }
341
    vlc_object_release( p_playlist );
342
    return NO;
343
}
Benjamin Pracht's avatar
Benjamin Pracht committed
344

345 346
- (BOOL)windowShouldClose:(id)sender
{
347
    if( [o_statUpdateTimer isValid] )
348
        [o_statUpdateTimer invalidate];
349 350

    if( o_statUpdateTimer )
351
        [o_statUpdateTimer release];
352

353 354 355
    return YES;
}

356 357
@end

358 359 360 361 362 363
@implementation VLCInfo (NSMenuValidation)

- (BOOL)validateMenuItem:(NSMenuItem *)o_mi
{
    BOOL bEnabled = TRUE;

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
364
    intf_thread_t * p_intf = VLCIntf;
365 366 367
    input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
                                                       FIND_ANYWHERE );

368
    if( [[o_mi title] isEqualToString: _NS("Information")] )
369 370 371 372 373 374 375 376 377 378 379 380
    {
        if( p_input == NULL )
        {
            bEnabled = FALSE;
        }
    }
    if( p_input ) vlc_object_release( p_input );

    return( bEnabled );
}

@end
Benjamin Pracht's avatar
Benjamin Pracht committed
381

382
@implementation VLCInfo (NSTableDataSource)
383

384
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
385 386 387 388 389 390 391 392
{
    return (item == nil) ? [[VLCInfoTreeItem rootItem] numberOfChildren] : [item numberOfChildren];
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
    return ([item numberOfChildren] > 0);
}

393
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
394
{
395
    return (item == nil) ? [[VLCInfoTreeItem rootItem] childAtIndex:index] : (id)[item childAtIndex:index];
396 397 398 399 400 401 402 403 404 405 406 407 408 409
}

- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
    if ([[tableColumn identifier] isEqualToString:@"0"])
    {
        return (item == nil) ? @"" : (id)[item getName];
    }
    else
    {
        return (item == nil) ? @"" : (id)[item getValue];
    }
}

410

411 412 413 414 415 416 417 418 419 420 421
@end

@implementation VLCInfoTreeItem

static VLCInfoTreeItem *o_root_item = nil;

#define IsALeafNode ((id)-1)

- (id)initWithName: (NSString *)o_item_name value: (NSString *)o_item_value ID: (int)i_id parent:(VLCInfoTreeItem *)o_parent_item
{
    self = [super init];
422

423 424 425 426 427 428
    if( self != nil )
    {
        o_name = [o_item_name copy];
        o_value = [o_item_value copy];
        i_object_id = i_id;
        o_parent = o_parent_item;
429 430 431 432
        if( [[VLCMain sharedInstance] getInfo] != nil )
            p_item = [[[VLCMain sharedInstance] getInfo] getItem];
        else
            p_item = NULL;
433 434 435 436 437
    }
    return( self );
}

+ (VLCInfoTreeItem *)rootItem {
438 439
    if( o_root_item == nil )
        o_root_item = [[VLCInfoTreeItem alloc] initWithName:@"main" value: @"" ID: 0 parent:nil];
440 441 442 443 444
    return o_root_item;
}

- (void)dealloc
{
445
    if( o_children != IsALeafNode ) [o_children release];
446 447 448 449 450 451 452 453 454 455 456 457
    [o_name release];
    [super dealloc];
}

/* Creates and returns the array of children
 * Loads children incrementally */
- (NSArray *)children
{
    if (o_children == NULL)
    {
        int i;

458
        if( [[[VLCMain sharedInstance] getInfo] isItemInPlaylist: p_item] )
459
        {
460
            if( self == o_root_item )
461
            {
462
                vlc_mutex_lock( &p_item->p_input->lock );
463
                o_children = [[NSMutableArray alloc] initWithCapacity:
464 465
                                                p_item->p_input->i_categories];
                for (i = 0 ; i < p_item->p_input->i_categories ; i++)
466
                {
467
                    [o_children addObject:[[VLCInfoTreeItem alloc]
468
                        initWithName: [NSString stringWithUTF8String:
469
                            p_item->p_input->pp_categories[i]->psz_name]
470 471 472
                        value: @""
                        ID: i
                        parent: self]];
473
                }
474
                vlc_mutex_unlock( &p_item->p_input->lock );
475
            }
476
            else if( o_parent == o_root_item )
477
            {
478
                vlc_mutex_lock( &p_item->p_input->lock );
479
                o_children = [[NSMutableArray alloc] initWithCapacity:
480
                    p_item->p_input->pp_categories[i_object_id]->i_infos];
481

482
                for (i = 0 ; i < p_item->p_input->pp_categories[i_object_id]->i_infos ; i++)
483
                {
484 485
                    [o_children addObject:[[VLCInfoTreeItem alloc]
                    initWithName: [NSString stringWithUTF8String:
486
                            p_item->p_input->pp_categories[i_object_id]->pp_infos[i]->psz_name]
487
                        value: [NSString stringWithUTF8String:
488
                            p_item->p_input->pp_categories[i_object_id]->pp_infos[i]->psz_value ? : ""]
489 490
                        ID: i
                        parent: self]];
491
                }
492
                vlc_mutex_unlock( &p_item->p_input->lock );
493 494 495 496
            }
            else
            {
                o_children = IsALeafNode;
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
            }
        }
    }
    return o_children;
}

- (NSString *)getName
{
    return o_name;
}

- (NSString *)getValue
{
    return o_value;
}

- (VLCInfoTreeItem *)childAtIndex:(int)i_index {
    return [[self children] objectAtIndex:i_index];
}

- (int)numberOfChildren {
    id i_tmp = [self children];
519
    return ( i_tmp == IsALeafNode ) ? (-1) : (int)[i_tmp count];
520 521 522 523
}

- (void)refresh
{
524
    p_item = [[[VLCMain sharedInstance] getInfo] getItem];
525
    if( o_children != NULL )
526 527 528 529 530
    {
        [o_children release];
        o_children = NULL;
    }
}
Benjamin Pracht's avatar
Benjamin Pracht committed
531 532 533

@end