playlistinfo.m 17.9 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
    BOOL b_stats = config_GetInt(VLCIntf, "stats");
maxime Ripard's avatar
maxime Ripard committed
130
    [self initMediaPanelStats];
131 132 133 134
    if( b_stats )
    {
        o_statUpdateTimer = [NSTimer scheduledTimerWithTimeInterval: 1
            target: self selector: @selector(updateStatistics:)
135
            userInfo: nil repeats: YES];
136 137 138 139 140 141 142 143 144
        [o_statUpdateTimer fire];
        [o_statUpdateTimer retain];
    }
    else
    {
        if( [o_tab_view numberOfTabViewItems] > 2 )
            [o_tab_view removeTabViewItem: [o_tab_view tabViewItemAtIndex: 2]];
    }

145
    [self updatePanel];
146
    [o_info_window makeKeyAndOrderFront: self];
147 148
}

maxime Ripard's avatar
maxime Ripard committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
- (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];

}

175 176 177 178
- (void)updatePanel
{
    /* make sure that we got the current item and not an outdated one */
    intf_thread_t * p_intf = VLCIntf;
179
        playlist_t * p_playlist = pl_Yield( p_intf );
180

181 182
    p_item = p_playlist->status.p_item;
    vlc_object_release( p_playlist );
183

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

187
    /* fill uri info */
188
    if( input_item_GetURI( p_item->p_input ) != NULL )
189
    {
190
        [o_uri_txt setStringValue: [NSString stringWithUTF8String: input_item_GetURI( p_item->p_input ) ]];
191
    }
192

193 194
#define SET( foo, bar ) \
    char *psz_##foo = input_item_Get##bar ( p_item->p_input ); \
195
    [self setMeta: psz_##foo forLabel: o_##foo##_txt]; \
196
    FREENULL( psz_##foo );
197

198
    /* fill the other fields */
199 200 201 202 203 204 205 206 207 208
    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 );
209
    SET( description, Description );
210 211

#undef SET
212

213 214
    char *psz_meta;
    NSImage *o_image;
215
    psz_meta = input_item_GetArtURL( p_item->p_input );
216 217
    if( psz_meta && !strncmp( psz_meta, "file://", 7 ) )
        o_image = [[NSImage alloc] initWithContentsOfURL: [NSURL URLWithString: [NSString stringWithUTF8String: psz_meta]]];
218
    else
219 220 221
        o_image = [[NSImage imageNamed: @"noart.png"] retain];
    [o_image_well setImage: o_image];
    [o_image release];
222
    FREENULL( psz_meta );
223

224
    /* reload the advanced table */
225 226
    [[VLCInfoTreeItem rootItem] refresh];
    [o_outline_view reloadData];
227

228 229
    /* update the stats once to display p_item change faster */
    [self updateStatistics: nil];
Benjamin Pracht's avatar
Benjamin Pracht committed
230 231
}

232
- (void)setMeta: (char *)psz_meta forLabel: (id)theItem
233 234
{
    if( psz_meta != NULL && *psz_meta)
235
        [theItem setStringValue: [NSString stringWithUTF8String:psz_meta]];
236 237
    else
        [theItem setStringValue: @""];
238 239
}

240
- (void)updateStatistics:(NSTimer*)theTimer
241
{
242 243
    if( [self isItemInPlaylist: p_item] )
    {
244
        vlc_mutex_lock( &p_item->p_input->p_stats->lock );
245 246

        /* input */
247
        [o_read_bytes_txt setStringValue: [NSString stringWithFormat:
248
            @"%8.0f kB", (float)(p_item->p_input->p_stats->i_read_bytes)/1000]];
249
        [o_input_bitrate_txt setStringValue: [NSString stringWithFormat:
250
            @"%6.0f kb/s", (float)(p_item->p_input->p_stats->f_input_bitrate)*8000]];
251
        [o_demux_bytes_txt setStringValue: [NSString stringWithFormat:
252
            @"%8.0f kB", (float)(p_item->p_input->p_stats->i_demux_read_bytes)/1000]];
253
        [o_demux_bitrate_txt setStringValue: [NSString stringWithFormat:
254
            @"%6.0f kb/s", (float)(p_item->p_input->p_stats->f_demux_bitrate)*8000]];
255 256

        /* Video */
257 258 259 260
        [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;
261
        /* FIXME: input_Control( p_item->p_input, INPUT_GET_VIDEO_FPS, &f_fps ); */
262
        [o_fps_txt setFloatValue: f_fps];
263 264

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

        /* Audio */
272 273 274
        [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];
275

276
        vlc_mutex_unlock( &p_item->p_input->p_stats->lock );
277
    }
278 279
}

280 281 282 283 284
- (IBAction)metaFieldChanged:(id)sender
{
    [o_saveMetaData_btn setEnabled: YES];
}

285
- (IBAction)saveMetaData:(id)sender
Benjamin Pracht's avatar
Benjamin Pracht committed
286
{
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
287
    intf_thread_t * p_intf = VLCIntf;
288
    playlist_t * p_playlist = pl_Yield( p_intf );
Benjamin Pracht's avatar
Benjamin Pracht committed
289
    vlc_value_t val;
Benjamin Pracht's avatar
Benjamin Pracht committed
290

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
291
    if( [self isItemInPlaylist: p_item] )
Benjamin Pracht's avatar
Benjamin Pracht committed
292
    {
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
        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;
338

Benjamin Pracht's avatar
Benjamin Pracht committed
339
        val.b_bool = VLC_TRUE;
340
        var_Set( p_playlist, "intf-change", val );
341
        [self updatePanel];
Benjamin Pracht's avatar
Benjamin Pracht committed
342
    }
343 344 345 346

    end:
    vlc_object_release( p_playlist );
    [o_saveMetaData_btn setEnabled: NO];
347 348
}

349
- (playlist_item_t *)getItem
350
{
351 352 353
    return p_item;
}

354
- (BOOL)isItemInPlaylist:(playlist_item_t *)p_local_item
355 356
{
    intf_thread_t * p_intf = VLCIntf;
357
    playlist_t * p_playlist = pl_Yield( p_intf );
358 359
    int i;

360
    for( i = 0 ; i < p_playlist->all_items.i_size ; i++ )
361
    {
362
        if( ARRAY_VAL( p_playlist->all_items, i ) == p_local_item )
363
        {
364
            vlc_object_release( p_playlist );
365 366 367
            return YES;
        }
    }
368
    vlc_object_release( p_playlist );
369
    return NO;
370
}
Benjamin Pracht's avatar
Benjamin Pracht committed
371

372 373
- (BOOL)windowShouldClose:(id)sender
{
374
    if( [o_statUpdateTimer isValid] )
375
        [o_statUpdateTimer invalidate];
376 377

    if( o_statUpdateTimer )
378
        [o_statUpdateTimer release];
379

380 381 382
    return YES;
}

383 384
@end

385 386 387 388 389 390
@implementation VLCInfo (NSMenuValidation)

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

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
391
    intf_thread_t * p_intf = VLCIntf;
392 393 394
    input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
                                                       FIND_ANYWHERE );

395
    if( [[o_mi title] isEqualToString: _NS("Information")] )
396 397 398 399 400 401 402 403 404 405 406 407
    {
        if( p_input == NULL )
        {
            bEnabled = FALSE;
        }
    }
    if( p_input ) vlc_object_release( p_input );

    return( bEnabled );
}

@end
Benjamin Pracht's avatar
Benjamin Pracht committed
408

409
@implementation VLCInfo (NSTableDataSource)
410

411
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
412 413 414 415 416 417 418 419
{
    return (item == nil) ? [[VLCInfoTreeItem rootItem] numberOfChildren] : [item numberOfChildren];
}

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

420
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
421
{
422
    return (item == nil) ? [[VLCInfoTreeItem rootItem] childAtIndex:index] : (id)[item childAtIndex:index];
423 424 425 426 427 428 429 430 431 432 433 434 435 436
}

- (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];
    }
}

437

438 439 440 441 442 443 444 445 446 447 448
@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];
449

450 451 452 453 454 455
    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;
456 457 458 459
        if( [[VLCMain sharedInstance] getInfo] != nil )
            p_item = [[[VLCMain sharedInstance] getInfo] getItem];
        else
            p_item = NULL;
460 461 462 463 464
    }
    return( self );
}

+ (VLCInfoTreeItem *)rootItem {
465 466
    if( o_root_item == nil )
        o_root_item = [[VLCInfoTreeItem alloc] initWithName:@"main" value: @"" ID: 0 parent:nil];
467 468 469 470 471
    return o_root_item;
}

- (void)dealloc
{
472
    if( o_children != IsALeafNode ) [o_children release];
473 474 475 476 477 478 479 480 481 482 483 484
    [o_name release];
    [super dealloc];
}

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

485
        if( [[[VLCMain sharedInstance] getInfo] isItemInPlaylist: p_item] )
486
        {
487
            if( self == o_root_item )
488
            {
489
                vlc_mutex_lock( &p_item->p_input->lock );
490
                o_children = [[NSMutableArray alloc] initWithCapacity:
491 492
                                                p_item->p_input->i_categories];
                for (i = 0 ; i < p_item->p_input->i_categories ; i++)
493
                {
494
                    [o_children addObject:[[VLCInfoTreeItem alloc]
495
                        initWithName: [NSString stringWithUTF8String:
496
                            p_item->p_input->pp_categories[i]->psz_name]
497 498 499
                        value: @""
                        ID: i
                        parent: self]];
500
                }
501
                vlc_mutex_unlock( &p_item->p_input->lock );
502
            }
503
            else if( o_parent == o_root_item )
504
            {
505
                vlc_mutex_lock( &p_item->p_input->lock );
506
                o_children = [[NSMutableArray alloc] initWithCapacity:
507
                    p_item->p_input->pp_categories[i_object_id]->i_infos];
508

509
                for (i = 0 ; i < p_item->p_input->pp_categories[i_object_id]->i_infos ; i++)
510
                {
511 512
                    [o_children addObject:[[VLCInfoTreeItem alloc]
                    initWithName: [NSString stringWithUTF8String:
513
                            p_item->p_input->pp_categories[i_object_id]->pp_infos[i]->psz_name]
514
                        value: [NSString stringWithUTF8String:
515
                            p_item->p_input->pp_categories[i_object_id]->pp_infos[i]->psz_value ? : ""]
516 517
                        ID: i
                        parent: self]];
518
                }
519
                vlc_mutex_unlock( &p_item->p_input->lock );
520 521 522 523
            }
            else
            {
                o_children = IsALeafNode;
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
            }
        }
    }
    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];
546
    return ( i_tmp == IsALeafNode ) ? (-1) : (int)[i_tmp count];
547 548 549 550
}

- (void)refresh
{
551
    p_item = [[[VLCMain sharedInstance] getInfo] getItem];
552
    if( o_children != NULL )
553 554 555 556 557
    {
        [o_children release];
        o_children = NULL;
    }
}
Benjamin Pracht's avatar
Benjamin Pracht committed
558 559 560

@end