playlistinfo.m 18 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
- (id)init
{
    if( _o_sharedInstance ) {
        [self dealloc];
    } else {
        _o_sharedInstance = [super init];
        
        if( _o_sharedInstance != nil )
        {
            p_item = NULL;
            o_statUpdateTimer = nil;
57 58
            [self updatePanelWithItem: NULL];
            rootItem = [[VLCInfoTreeItem alloc] init];
59
        }
60
    }
61 62

    return _o_sharedInstance;
63 64
}

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

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

75
    [[o_tab_view tabViewItemAtIndex: 0] setLabel: _NS("General")];
76
    [[o_tab_view tabViewItemAtIndex: 1] setLabel: _NS("Codec Details")];
77 78 79 80 81 82
    [[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)];
Rafaël Carré's avatar
Rafaël Carré committed
83 84
    [o_collection_lbl setStringValue: _NS(VLC_META_ALBUM)];
    [o_seqNum_lbl setStringValue: _NS(VLC_META_TRACK_NUMBER)];
85 86 87 88 89
    [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)];
90

91 92 93 94 95 96
    /* 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")];
97

98 99 100 101
    [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")];
102
	[o_fps_lbl setStringValue: _NS("Frames per Second")];
103

104 105 106 107
    [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")];
108

109 110 111 112
    [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")];
113 114

    [o_info_window setInitialFirstResponder: o_uri_txt];
115 116 117 118 119 120 121
    [o_info_window setDelegate: self];

    /* We may be awoken from nib way after initialiwation
     * Update ourselves */
    [self updatePanelWithItem:p_item];
}

122
- (void)stopTimers
123 124 125 126 127 128 129 130 131
{
    /* make sure that the timer is released in any case */
    if( o_statUpdateTimer && [o_statUpdateTimer isValid] )
        [o_statUpdateTimer invalidate];

    [rootItem release];

    [o_statUpdateTimer release];
    o_statUpdateTimer = nil;
Benjamin Pracht's avatar
Benjamin Pracht committed
132 133
}

134 135
- (void)dealloc
{
136
    /* make sure that the timer is released in any case */
137
    if( o_statUpdateTimer && [o_statUpdateTimer isValid] )
138 139
        [o_statUpdateTimer invalidate];

140 141 142
    [o_statUpdateTimer release];

    if( p_item ) vlc_gc_decref( p_item );
143

144 145
    [super dealloc];
}
Benjamin Pracht's avatar
Benjamin Pracht committed
146

147
- (void)windowDidBecomeKey:(NSNotification *)notification
148
{
149 150 151
    BOOL b_stats = config_GetInt(VLCIntf, "stats");
    if( b_stats )
    {
152 153 154 155 156 157
        if( o_statUpdateTimer )
        {
            [o_statUpdateTimer invalidate];
            [o_statUpdateTimer release];
            o_statUpdateTimer = nil;
        }
158 159
        o_statUpdateTimer = [NSTimer scheduledTimerWithTimeInterval: 1
            target: self selector: @selector(updateStatistics:)
160
            userInfo: nil repeats: YES];
161 162 163
        [o_statUpdateTimer fire];
        [o_statUpdateTimer retain];
    }
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
}

- (BOOL)windowShouldClose:(id)sender
{
    if( [o_statUpdateTimer isValid] )
        [o_statUpdateTimer invalidate];

    if( o_statUpdateTimer )
        [o_statUpdateTimer release];
    o_statUpdateTimer = nil;
    return YES;
}

- (void)initPanel
{
    BOOL b_stats = config_GetInt(VLCIntf, "stats");
    if( !b_stats )
181 182 183 184 185
    {
        if( [o_tab_view numberOfTabViewItems] > 2 )
            [o_tab_view removeTabViewItem: [o_tab_view tabViewItemAtIndex: 2]];
    }

186
    [self initMediaPanelStats];
187
    [o_info_window makeKeyAndOrderFront: self];
188 189
}

maxime Ripard's avatar
maxime Ripard committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
- (void)initMediaPanelStats
{
    //Initializing Input Variables
    [o_read_bytes_txt setStringValue: [NSString stringWithFormat:@"%8.0f kB", (float)0]];
    [o_input_bitrate_txt setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]];
    [o_demux_bytes_txt setStringValue: [NSString stringWithFormat:@"%8.0f kB", (float)0]];
    [o_demux_bitrate_txt setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]];
    
    //Initializing Video Variables
    [o_video_decoded_txt setIntValue:0];
    [o_displayed_txt setIntValue:0];
    [o_lost_frames_txt setIntValue:0];
    [o_fps_txt setFloatValue:0];

    //Initializing Output Variables
    [o_sent_packets_txt setIntValue: 0];
    [o_sent_bytes_txt setStringValue: [NSString stringWithFormat:@"%8.0f kB", (float)0]];
    [o_sent_bitrate_txt setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]];

    //Initializing Audio Variables
    [o_audio_decoded_txt setIntValue:0];
    [o_played_abuffers_txt setIntValue: 0];
    [o_lost_abuffers_txt setIntValue: 0];

}

216
- (void)updatePanelWithItem:(input_item_t *)_p_item;
217
{
218
    if( _p_item != p_item )
219
    {
220
        if( p_item ) vlc_gc_decref( p_item );
221
        [o_saveMetaData_btn setEnabled: NO];
222 223
        if( _p_item ) vlc_gc_incref( _p_item );
        p_item = _p_item;
224
    }
225

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    if( !p_item )
    {
        /* Erase */
    #define SET( foo ) \
        [self setMeta: "" forLabel: o_##foo##_txt];
        SET( uri );
        SET( title );
        SET( author );
        SET( collection );
        SET( seqNum );
        SET( genre );
        SET( copyright );
        SET( publisher );
        SET( nowPlaying );
        SET( language );
        SET( date );
        SET( description );
    #undef SET
        [o_image_well setImage: [NSImage imageNamed: @"noart.png"]];
    }
246
    else
247 248 249 250 251 252 253 254
    {
        if( !input_item_IsPreparsed( p_item ) )
        {
            playlist_t * p_playlist = pl_Yield( VLCIntf );
            playlist_PreparseEnqueue( p_playlist, p_item );
            pl_Release( VLCIntf );
        }

255 256 257
        /* fill uri info */
        char * psz_url = input_item_GetURI( p_item );
        [o_uri_txt setStringValue: [NSString stringWithUTF8String: psz_url ? psz_url : ""  ]];
258 259
        free( psz_url );

260 261 262 263 264 265 266
        /* fill title info */
        char * psz_title = input_item_GetTitle( p_item );
        if( !psz_title )
            psz_title = input_item_GetName( p_item );
        [o_title_txt setStringValue: [NSString stringWithUTF8String: psz_title ? : ""  ]];
        free( psz_title );

267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
    #define SET( foo, bar ) \
        char *psz_##foo = input_item_Get##bar ( p_item ); \
        [self setMeta: psz_##foo forLabel: o_##foo##_txt]; \
        FREENULL( psz_##foo );

        /* fill the other fields */
        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 );
        SET( description, Description );

    #undef SET

        char *psz_meta;
        NSImage *o_image;
        psz_meta = input_item_GetArtURL( p_item );
        if( psz_meta && !strncmp( psz_meta, "file://", 7 ) )
290
            o_image = [[NSImage alloc] initWithContentsOfFile: [NSString stringWithUTF8String: psz_meta+7]];
291 292 293 294 295 296
        else
            o_image = [[NSImage imageNamed: @"noart.png"] retain];
        [o_image_well setImage: o_image];
        [o_image release];
        FREENULL( psz_meta );
    }
297

298
    /* reload the advanced table */
299
    [rootItem refresh];
300
    [o_outline_view reloadData];
301

302 303
    /* update the stats once to display p_item change faster */
    [self updateStatistics: nil];
304

Benjamin Pracht's avatar
Benjamin Pracht committed
305 306
}

307
- (void)setMeta: (char *)psz_meta forLabel: (id)theItem
308 309
{
    if( psz_meta != NULL && *psz_meta)
310
        [theItem setStringValue: [NSString stringWithUTF8String:psz_meta]];
311 312
    else
        [theItem setStringValue: @""];
313 314
}

315
- (void)updateStatistics:(NSTimer*)theTimer
316
{
317
    if( !p_item || !p_item->p_stats )
318
    {
319 320
        [self initMediaPanelStats];
        return;
321
    }
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355

    vlc_mutex_lock( &p_item->p_stats->lock );

    /* input */
    [o_read_bytes_txt setStringValue: [NSString stringWithFormat:
        @"%8.0f kB", (float)(p_item->p_stats->i_read_bytes)/1000]];
    [o_input_bitrate_txt setStringValue: [NSString stringWithFormat:
        @"%6.0f kb/s", (float)(p_item->p_stats->f_input_bitrate)*8000]];
    [o_demux_bytes_txt setStringValue: [NSString stringWithFormat:
        @"%8.0f kB", (float)(p_item->p_stats->i_demux_read_bytes)/1000]];
    [o_demux_bitrate_txt setStringValue: [NSString stringWithFormat:
        @"%6.0f kb/s", (float)(p_item->p_stats->f_demux_bitrate)*8000]];

    /* Video */
    [o_video_decoded_txt setIntValue: p_item->p_stats->i_decoded_video];
    [o_displayed_txt setIntValue: p_item->p_stats->i_displayed_pictures];
    [o_lost_frames_txt setIntValue: p_item->p_stats->i_lost_pictures];
    float f_fps = 0;
    /* FIXME: input_Control( p_item, INPUT_GET_VIDEO_FPS, &f_fps ); */
    [o_fps_txt setFloatValue: f_fps];

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

    /* Audio */
    [o_audio_decoded_txt setIntValue: p_item->p_stats->i_decoded_audio];
    [o_played_abuffers_txt setIntValue: p_item->p_stats->i_played_abuffers];
    [o_lost_abuffers_txt setIntValue: p_item->p_stats->i_lost_abuffers];

    vlc_mutex_unlock( &p_item->p_stats->lock );
356 357
}

358 359 360 361 362
- (IBAction)metaFieldChanged:(id)sender
{
    [o_saveMetaData_btn setEnabled: YES];
}

363
- (IBAction)saveMetaData:(id)sender
Benjamin Pracht's avatar
Benjamin Pracht committed
364
{
365
    playlist_t * p_playlist = pl_Yield( VLCIntf );
Benjamin Pracht's avatar
Benjamin Pracht committed
366
    vlc_value_t val;
Benjamin Pracht's avatar
Benjamin Pracht committed
367

368
    if( !p_item ) goto error;
369

370 371
    meta_export_t p_export;
    p_export.p_item = p_item;
372

373 374 375 376
    /* we can write meta data only in a file */
    vlc_mutex_lock( &p_item->lock );
    int i_type = p_item->i_type;
    vlc_mutex_unlock( &p_item->lock );
377

378 379
    if( i_type != ITEM_TYPE_FILE )
        goto error;
380

381 382 383 384
    char *psz_uri_orig = input_item_GetURI( p_item );
    char *psz_uri = psz_uri_orig;
    if( !strncmp( psz_uri, "file://", 7 ) )
        psz_uri += 7; /* strlen("file://") = 7 */
385

386 387
    p_export.psz_file = strndup( psz_uri, PATH_MAX );
    free( psz_uri_orig );
388

389 390
    #define utf8( o_blub ) \
        [[o_blub stringValue] UTF8String]
Benjamin Pracht's avatar
Benjamin Pracht committed
391

392 393 394 395 396 397 398 399 400 401 402
    input_item_SetName( p_item, utf8( o_title_txt ) );
    input_item_SetTitle( p_item, utf8( o_title_txt ) );
    input_item_SetArtist( p_item, utf8( o_author_txt ) );
    input_item_SetAlbum( p_item, utf8( o_collection_txt ) );
    input_item_SetGenre( p_item, utf8( o_genre_txt ) );
    input_item_SetTrackNum( p_item, utf8( o_seqNum_txt ) );
    input_item_SetDate( p_item, utf8( o_date_txt ) );
    input_item_SetCopyright( p_item, utf8( o_copyright_txt ) );
    input_item_SetPublisher( p_item, utf8( o_publisher_txt ) );
    input_item_SetDescription( p_item, utf8( o_description_txt ) );
    input_item_SetLanguage( p_item, utf8( o_language_txt ) );
403

404 405
    PL_LOCK;
    p_playlist->p_private = &p_export;
406

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
    module_t *p_mod = module_Need( p_playlist, "meta writer", NULL, 0 );
    if( p_mod )
        module_Unneed( p_playlist, p_mod );
    PL_UNLOCK;

    val.b_bool = true;
    var_Set( p_playlist, "intf-change", val );
    [self updatePanelWithItem: p_item];

    pl_Release( VLCIntf );
    [o_saveMetaData_btn setEnabled: NO];
    return;

error:
    pl_Release( VLCIntf );
    NSRunAlertPanel(_NS("Error while saving meta"),
        _NS("Impossible to save the meta data."),
        _NS("OK"), nil, nil);
}

- (input_item_t *)item
{
    vlc_gc_incref( p_item );
    return p_item;
431 432
}

433 434
@end

435 436 437 438 439 440
@implementation VLCInfo (NSMenuValidation)

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

441
    if( [[o_mi title] isEqualToString: _NS("Information")] )
442
    {
443
        return ![[[VLCMain sharedInstance] getPlaylist] isSelectionEmpty];
444 445
    }

446
    return TRUE;
447 448 449
}

@end
Benjamin Pracht's avatar
Benjamin Pracht committed
450

451
@implementation VLCInfo (NSTableDataSource)
452

453
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
454
{
455
    return (item == nil) ? [rootItem numberOfChildren] : [item numberOfChildren];
456 457 458 459 460 461
}

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

462
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
463
{
464
    return (item == nil) ? [rootItem childAtIndex:index] : (id)[item childAtIndex:index];
465 466 467 468 469 470
}

- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
    if ([[tableColumn identifier] isEqualToString:@"0"])
    {
471
        return (item == nil) ? @"" : (id)[item name];
472 473 474
    }
    else
    {
475
        return (item == nil) ? @"" : (id)[item value];
476 477 478
    }
}

479

480 481 482 483 484 485
@end

@implementation VLCInfoTreeItem

#define IsALeafNode ((id)-1)

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

491 492 493 494 495 496
    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;
497 498
        p_item = [[[VLCMain sharedInstance] getInfo] item];
        o_children = nil;
499 500 501 502
    }
    return( self );
}

503 504 505
- (id)init
{
    return [self initWithName:@"main" value:@"" ID:-1 parent:nil];
506 507 508 509
}

- (void)dealloc
{
510
    if( o_children != IsALeafNode ) [o_children release];
511
    [o_name release];
512 513
    [o_value release];
    if( p_item ) vlc_gc_decref( p_item );
514 515 516 517 518 519 520
    [super dealloc];
}

/* Creates and returns the array of children
 * Loads children incrementally */
- (NSArray *)children
{
521 522
    if( !p_item ) return nil;

523 524 525 526
    if (o_children == NULL)
    {
        int i;

527
        if( i_object_id == -1 )
528
        {
529 530 531
            vlc_mutex_lock( &p_item->lock );
            o_children = [[NSMutableArray alloc] initWithCapacity: p_item->i_categories];
            for (i = 0 ; i < p_item->i_categories ; i++)
532
            {
533 534 535 536
                NSString * name = [NSString stringWithUTF8String: p_item->pp_categories[i]->psz_name];
                VLCInfoTreeItem * item = [[VLCInfoTreeItem alloc] initWithName:name value:@"" ID:i parent:self];
                [item autorelease];
                [o_children addObject:item];
537
            }
538 539 540 541 542 543 544 545
            vlc_mutex_unlock( &p_item->lock );
        }
        else if( o_parent->i_object_id == -1 )
        {
            vlc_mutex_lock( &p_item->lock );
            info_category_t * cat = p_item->pp_categories[i_object_id];
            o_children = [[NSMutableArray alloc] initWithCapacity: cat->i_infos];
            for (i = 0 ; i < cat->i_infos ; i++)
546
            {
547 548 549 550 551
                NSString * name = [NSString stringWithUTF8String: cat->pp_infos[i]->psz_name];
                NSString * value = [NSString stringWithUTF8String: cat->pp_infos[i]->psz_value ? : ""];
                VLCInfoTreeItem * item = [[VLCInfoTreeItem alloc] initWithName:name value:value ID:i parent:self];
                [item autorelease];
                [o_children addObject:item];
552
            }
553 554 555 556 557
            vlc_mutex_unlock( &p_item->lock );
        }
        else
        {
            o_children = IsALeafNode;
558 559 560 561 562
        }
    }
    return o_children;
}

563
- (NSString *)name
564
{
565
    return [[o_name retain] autorelease];
566 567
}

568
- (NSString *)value
569
{
570
    return [[o_value retain] autorelease];
571 572 573 574
}

- (void)refresh
{
575 576
    if( p_item ) vlc_gc_decref( p_item );
    p_item = [[[VLCMain sharedInstance] getInfo] item];
577
    if( o_children != NULL )
578 579 580 581 582
    {
        [o_children release];
        o_children = NULL;
    }
}
Benjamin Pracht's avatar
Benjamin Pracht committed
583

584 585 586 587 588 589 590 591 592 593
- (VLCInfoTreeItem *)childAtIndex:(int)i_index {
    return [[self children] objectAtIndex:i_index];
}

- (int)numberOfChildren {

    id i_tmp = [self children];
    return ( i_tmp == IsALeafNode ) ? (-1) : (int)[i_tmp count];
}

Benjamin Pracht's avatar
Benjamin Pracht committed
594 595
@end