1/***************************************************************************** 2 * VLCPlaylistInfo.m: Controller for the codec info panel 3 ***************************************************************************** 4 * Copyright (C) 2002-2015 VLC authors and VideoLAN 5 * $Id: 53b05221edd4d1ac582045e69d093802b0663098 $ 6 * 7 * Authors: Benjamin Pracht <bigben at videolan dot org> 8 * Felix Paul Kühne <fkuehne at videolan dot org> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 23 ******************************************************************************/ 24 25#import "CompatibilityFixes.h" 26#import "VLCMain.h" 27#import "VLCPlaylistInfo.h" 28#import "VLCPlaylist.h" 29#import <vlc_url.h> 30 31@interface VLCInfo () <NSOutlineViewDataSource> 32{ 33 VLCInfoTreeItem *rootItem; 34 35 input_item_t *p_item; 36 37 BOOL b_stats; 38} 39@end 40 41@implementation VLCInfo 42 43- (id)init 44{ 45 self = [super initWithWindowNibName:@"MediaInfo"]; 46 return self; 47} 48 49- (void)windowDidLoad 50{ 51 [self.window setExcludedFromWindowsMenu: YES]; 52 [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary]; 53 54 [self.window setTitle: _NS("Media Information")]; 55 56 _outlineView.dataSource = self; 57 58 [_uriLabel setStringValue: _NS("Location")]; 59 [_titleLabel setStringValue: _NS("Title")]; 60 [_authorLabel setStringValue: _NS("Artist")]; 61 [_saveMetaDataButton setStringValue: _NS("Save Metadata")]; 62 63 [_segmentedView setLabel:_NS("General") forSegment:0]; 64 [_segmentedView setLabel:_NS("Codec Details") forSegment:1]; 65 [_segmentedView setLabel:_NS("Statistics") forSegment:2]; 66 67 /* constants defined in vlc_meta.h */ 68 [_genreLabel setStringValue: _NS(VLC_META_GENRE)]; 69 [_copyrightLabel setStringValue: _NS(VLC_META_COPYRIGHT)]; 70 [_collectionLabel setStringValue: _NS(VLC_META_ALBUM)]; 71 [_seqNumLabel setStringValue: _NS(VLC_META_TRACK_NUMBER)]; 72 [_descriptionLabel setStringValue: _NS(VLC_META_DESCRIPTION)]; 73 [_dateLabel setStringValue: _NS(VLC_META_DATE)]; 74 [_languageLabel setStringValue: _NS(VLC_META_LANGUAGE)]; 75 [_nowPlayingLabel setStringValue: _NS(VLC_META_NOW_PLAYING)]; 76 [_publisherLabel setStringValue: _NS(VLC_META_PUBLISHER)]; 77 [_encodedbyLabel setStringValue: _NS(VLC_META_ENCODED_BY)]; 78 79 /* statistics */ 80 [_inputLabel setStringValue: _NS("Input")]; 81 [_readBytesLabel setStringValue: _NS("Read at media")]; 82 [_inputBitrateLabel setStringValue: _NS("Input bitrate")]; 83 [_demuxBytesLabel setStringValue: _NS("Demuxed")]; 84 [_demuxBitrateLabel setStringValue: _NS("Stream bitrate")]; 85 86 [_videoLabel setStringValue: _NS("Video")]; 87 [_videoDecodedLabel setStringValue: _NS("Decoded blocks")]; 88 [_displayedLabel setStringValue: _NS("Displayed frames")]; 89 [_lostFramesLabel setStringValue: _NS("Lost frames")]; 90 91 [_audioLabel setStringValue: _NS("Audio")]; 92 [_audioDecodedLabel setStringValue: _NS("Decoded blocks")]; 93 [_playedAudioBuffersLabel setStringValue: _NS("Played buffers")]; 94 [_lostAudioBuffersLabel setStringValue: _NS("Lost buffers")]; 95 96 [self.window setInitialFirstResponder: _uriLabel]; 97 98 b_stats = var_InheritBool(getIntf(), "stats"); 99 if (!b_stats) { 100 if ([_segmentedView segmentCount] >= 3) 101 [_segmentedView setSegmentCount: 2]; 102 } 103 else 104 [self initMediaPanelStats]; 105 106 107 /* We may be awoken from nib way after initialisation 108 *Update ourselves */ 109 [self updatePanelWithItem:p_item]; 110} 111 112 113- (void)dealloc 114{ 115 if (p_item) 116 input_item_Release(p_item); 117} 118 119- (void)updateCocoaWindowLevel:(NSInteger)i_level 120{ 121 if (self.isWindowLoaded && [self.window isVisible] && [self.window level] != i_level) 122 [self.window setLevel: i_level]; 123} 124 125- (IBAction)toggleWindow:(id)sender 126{ 127 if ([self.window isKeyWindow]) 128 [self.window orderOut:sender]; 129 else { 130 [self.window setLevel: [[[VLCMain sharedInstance] voutController] currentStatusWindowLevel]]; 131 [self.window makeKeyAndOrderFront:sender]; 132 } 133} 134 135- (void)initMediaPanelStats 136{ 137 //Initializing Input Variables 138 [_readBytesTextField setStringValue: [NSString stringWithFormat:_NS("%.1f KiB"), (float)0]]; 139 [_inputBitrateTextField setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]]; 140 [_demuxBytesTextField setStringValue: [NSString stringWithFormat:_NS("%.1f KiB"), (float)0]]; 141 [_demuxBitrateTextField setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]]; 142 143 //Initializing Video Variables 144 [_videoDecodedTextField setIntValue:0]; 145 [_displayedTextField setIntValue:0]; 146 [_lostFramesTextField setIntValue:0]; 147 148 //Initializing Audio Variables 149 [_audioDecodedTextField setIntValue:0]; 150 [_playedAudioBuffersTextField setIntValue: 0]; 151 [_lostAudioBuffersTextField setIntValue: 0]; 152} 153 154- (void)updateMetadata 155{ 156 if (!p_item) 157 return; 158 159 [self updatePanelWithItem:p_item]; 160} 161 162- (void)updatePanelWithItem:(input_item_t *)_p_item; 163{ 164 if (_p_item != p_item) { 165 if (p_item) 166 input_item_Release(p_item); 167 [_saveMetaDataButton setEnabled: NO]; 168 if (_p_item) 169 input_item_Hold(_p_item); 170 p_item = _p_item; 171 } 172 173 if (!self.isWindowLoaded) 174 return; 175 176 if (!p_item) { 177 /* Erase */ 178#define SET( foo ) \ 179[_##foo##TextField setStringValue:@""]; 180 SET( uri ); 181 SET( title ); 182 SET( author ); 183 SET( collection ); 184 SET( seqNum ); 185 SET( genre ); 186 SET( copyright ); 187 SET( publisher ); 188 SET( nowPlaying ); 189 SET( language ); 190 SET( date ); 191 SET( description ); 192 SET( encodedby ); 193#undef SET 194 [_imageWell setImage: [NSImage imageNamed: @"noart.png"]]; 195 } else { 196 if (!input_item_IsPreparsed(p_item)) 197 libvlc_MetadataRequest(getIntf()->obj.libvlc, p_item, META_REQUEST_OPTION_NONE, -1, NULL); 198 199 /* fill uri info */ 200 char *psz_url = vlc_uri_decode(input_item_GetURI(p_item)); 201 [_uriTextField setStringValue:toNSStr(psz_url)]; 202 free(psz_url); 203 204 /* fill title info */ 205 char *psz_title = input_item_GetTitle(p_item); 206 if (!psz_title) 207 psz_title = input_item_GetName(p_item); 208 [_titleTextField setStringValue:toNSStr(psz_title)]; 209 free(psz_title); 210 211#define SET( foo, bar ) \ 212char *psz_##foo = input_item_Get##bar ( p_item ); \ 213[_##foo##TextField setStringValue:toNSStr(psz_##foo)]; \ 214FREENULL( psz_##foo ); 215 216 /* fill the other fields */ 217 SET( author, Artist ); 218 SET( collection, Album ); 219 SET( seqNum, TrackNum ); 220 SET( genre, Genre ); 221 SET( copyright, Copyright ); 222 SET( publisher, Publisher ); 223 SET( nowPlaying, NowPlaying ); 224 SET( language, Language ); 225 SET( date, Date ); 226 SET( description, Description ); 227 SET( encodedby, EncodedBy ); 228 229#undef SET 230 231 char *psz_meta; 232 NSImage *image; 233 psz_meta = input_item_GetArtURL(p_item); 234 235 /* FIXME Can also be attachment:// */ 236 if (psz_meta && strncmp(psz_meta, "attachment://", 13)) 237 image = [[NSImage alloc] initWithContentsOfURL: [NSURL URLWithString:toNSStr(psz_meta)]]; 238 else 239 image = [NSImage imageNamed: @"noart.png"]; 240 [_imageWell setImage: image]; 241 FREENULL(psz_meta); 242 } 243 244 /* reload the codec details table */ 245 [self updateStreamsList]; 246 247 /* update the stats once to display p_item change faster */ 248 [self updateStatistics]; 249} 250 251- (void)updateStatistics 252{ 253 if (!self.isWindowLoaded || !b_stats) 254 return; 255 256 if (!p_item || !p_item->p_stats) { 257 [self initMediaPanelStats]; 258 return; 259 } 260 261 if (![self.window isVisible]) 262 return; 263 264 vlc_mutex_lock(&p_item->p_stats->lock); 265 266 /* input */ 267 [_readBytesTextField setStringValue: [NSString stringWithFormat: 268 @"%8.0f KiB", (float)(p_item->p_stats->i_read_bytes)/1024]]; 269 [_inputBitrateTextField setStringValue: [NSString stringWithFormat: 270 @"%6.0f kb/s", (float)(p_item->p_stats->f_input_bitrate)*8000]]; 271 [_demuxBytesTextField setStringValue: [NSString stringWithFormat: 272 @"%8.0f KiB", (float)(p_item->p_stats->i_demux_read_bytes)/1024]]; 273 [_demuxBitrateTextField setStringValue: [NSString stringWithFormat: 274 @"%6.0f kb/s", (float)(p_item->p_stats->f_demux_bitrate)*8000]]; 275 276 /* Video */ 277 [_videoDecodedTextField setIntValue: p_item->p_stats->i_decoded_video]; 278 [_displayedTextField setIntValue: p_item->p_stats->i_displayed_pictures]; 279 [_lostFramesTextField setIntValue: p_item->p_stats->i_lost_pictures]; 280 281 /* Audio */ 282 [_audioDecodedTextField setIntValue: p_item->p_stats->i_decoded_audio]; 283 [_playedAudioBuffersTextField setIntValue: p_item->p_stats->i_played_abuffers]; 284 [_lostAudioBuffersTextField setIntValue: p_item->p_stats->i_lost_abuffers]; 285 286 vlc_mutex_unlock(&p_item->p_stats->lock); 287} 288 289- (void)updateStreamsList 290{ 291 rootItem = [[VLCInfoTreeItem alloc] init]; 292 293 if (p_item) { 294 vlc_mutex_lock(&p_item->lock); 295 // build list of streams 296 NSMutableArray *streams = [NSMutableArray array]; 297 298 for (int i = 0; i < p_item->i_categories; i++) { 299 info_category_t *cat = p_item->pp_categories[i]; 300 301 VLCInfoTreeItem *subItem = [[VLCInfoTreeItem alloc] init]; 302 subItem.propertyName = toNSStr(cat->psz_name); 303 304 // Build list of codec details 305 NSMutableArray *infos = [NSMutableArray array]; 306 307 for (int j = 0; j < cat->i_infos; j++) { 308 VLCInfoTreeItem *infoItem = [[VLCInfoTreeItem alloc] init]; 309 infoItem.propertyName = toNSStr(cat->pp_infos[j]->psz_name); 310 infoItem.propertyValue = toNSStr(cat->pp_infos[j]->psz_value); 311 [infos addObject:infoItem]; 312 } 313 314 subItem.children = [infos copy]; 315 [streams addObject:subItem]; 316 } 317 318 rootItem.children = [streams copy]; 319 vlc_mutex_unlock(&p_item->lock); 320 } 321 322 [_outlineView reloadData]; 323 [_outlineView expandItem:nil expandChildren:YES]; 324} 325 326- (IBAction)metaFieldChanged:(id)sender 327{ 328 [_saveMetaDataButton setEnabled: YES]; 329} 330 331- (IBAction)saveMetaData:(id)sender 332{ 333 if (!p_item) { 334 NSAlert *alert = [[NSAlert alloc] init]; 335 [alert setMessageText:_NS("Error while saving meta")]; 336 [alert setInformativeText:_NS("VLC was unable to save the meta data.")]; 337 [alert addButtonWithTitle:_NS("OK")]; 338 [alert runModal]; 339 return; 340 } 341 342 #define utf8( _blub ) \ 343 [[_blub stringValue] UTF8String] 344 345 input_item_SetName( p_item, utf8( _titleTextField ) ); 346 input_item_SetTitle( p_item, utf8( _titleTextField ) ); 347 input_item_SetArtist( p_item, utf8( _authorTextField ) ); 348 input_item_SetAlbum( p_item, utf8( _collectionTextField ) ); 349 input_item_SetGenre( p_item, utf8( _genreTextField ) ); 350 input_item_SetTrackNum( p_item, utf8( _seqNumTextField ) ); 351 input_item_SetDate( p_item, utf8( _dateTextField ) ); 352 input_item_SetCopyright( p_item, utf8( _copyrightTextField ) ); 353 input_item_SetPublisher( p_item, utf8( _publisherTextField ) ); 354 input_item_SetDescription( p_item, utf8( _descriptionTextField ) ); 355 input_item_SetLanguage( p_item, utf8( _languageTextField ) ); 356 357 playlist_t *p_playlist = pl_Get(getIntf()); 358 input_item_WriteMeta(VLC_OBJECT(p_playlist), p_item); 359 360 [self updatePanelWithItem: p_item]; 361 362 [_saveMetaDataButton setEnabled: NO]; 363} 364 365- (IBAction)downloadCoverArt:(id)sender 366{ 367 if (p_item) 368 libvlc_ArtRequest(getIntf()->obj.libvlc, p_item, META_REQUEST_OPTION_NONE); 369} 370 371@end 372 373 374@implementation VLCInfo (NSTableDataSource) 375 376- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item 377{ 378 return (item == nil) ? [rootItem children].count : [item children].count; 379} 380 381- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { 382 return ([item children].count > 0); 383} 384 385- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item 386{ 387 return (item == nil) ? [[rootItem children] objectAtIndex:index] : [[item children]objectAtIndex:index]; 388} 389 390- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item 391{ 392 if (!item) 393 return @""; 394 395 if ([[tableColumn identifier] isEqualToString:@"0"]) 396 return [item propertyName]; 397 else 398 return [item propertyValue]; 399} 400 401@end 402 403 404@implementation VLCInfoTreeItem 405 406@end 407