You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

459 lines
17 KiB

/*****************************************************************************
* VLCInformationWindowController.m: Controller for the codec info panel
*****************************************************************************
* Copyright (C) 2002-2019 VLC authors and VideoLAN
*
* Authors: Benjamin Pracht <bigben at videolan dot org>
* Felix Paul Kühne <fkuehne at videolan dot org>
*
* 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.
*
* 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
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
******************************************************************************/
#import "VLCInformationWindowController.h"
#import "extensions/NSString+Helpers.h"
#import "main/VLCMain.h"
#import "playlist/VLCPlaylistController.h"
#import "playlist/VLCPlayerController.h"
#import "windows/video/VLCVideoOutputProvider.h"
#import "library/VLCInputItem.h"
#import "views/VLCImageView.h"
#pragma mark - data storage object
@interface VLCCodecInformationTreeItem : NSObject
@property (readwrite) NSString *propertyName;
@property (readwrite) NSString *propertyValue;
@property (readwrite) NSArray *children;
@end
@implementation VLCCodecInformationTreeItem
- (NSString *)description
{
return [NSString stringWithFormat:@"%@: name: %@ value: %@ children: %lu", NSStringFromClass([self class]), self.propertyName, self.propertyValue, self.children.count];
}
@end
#pragma mark - window controller
@interface VLCInformationWindowController () <NSOutlineViewDataSource>
{
VLCCodecInformationTreeItem *_rootCodecInformationItem;
BOOL _statisticsEnabled;
}
@end
@implementation VLCInformationWindowController
- (id)init
{
self = [super initWithWindowNibName:@"VLCInformationWindow"];
return self;
}
- (void)dealloc
{
[NSNotificationCenter.defaultCenter removeObserver:self];
}
- (void)awakeFromNib
{
[self.window setExcludedFromWindowsMenu: YES];
[self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
[self.window setInitialFirstResponder: _uriLabel];
_outlineView.dataSource = self;
NSNotificationCenter *notificationCenter = NSNotificationCenter.defaultCenter;
if (_mainMenuInstance) {
[notificationCenter addObserver:self
selector:@selector(currentPlayingItemChanged:)
name:VLCPlayerCurrentMediaItemChanged
object:nil];
[notificationCenter addObserver:self
selector:@selector(updateStatistics:)
name:VLCPlayerStatisticsUpdated
object:nil];
}
[notificationCenter addObserver:self
selector:@selector(updateCocoaWindowLevel:)
name:VLCWindowShouldUpdateLevel
object:nil];
[notificationCenter addObserver:self
selector:@selector(mediaItemWasParsed:)
name:VLCInputItemParsingSucceeded
object:nil];
[notificationCenter addObserver:self
selector:@selector(mediaItemWasParsed:)
name:VLCInputItemPreparsingSucceeded
object:nil];
[notificationCenter postNotificationName:VLCPlayerStatisticsUpdated object:self];
[self initStrings];
_statisticsEnabled = var_InheritBool(getIntf(), "stats");
if (!_statisticsEnabled || !_mainMenuInstance) {
if ([_segmentedView segmentCount] >= 3)
[_segmentedView setSegmentCount: 2];
} else {
[self initMediaPanelStats];
}
[self updateRepresentation];
}
- (void)initStrings
{
[self.window setTitle: _NS("Media Information")];
[_uriLabel setStringValue: _NS("Location")];
[_titleLabel setStringValue: NSTR(VLC_META_TITLE)];
[_artistLabel setStringValue: NSTR(VLC_META_ARTIST)];
[_saveMetaDataButton setStringValue: _NS("Save Metadata")];
[_segmentedView setLabel:_NS("General") forSegment:0];
[_segmentedView setLabel:_NS("Codec Details") forSegment:1];
[_segmentedView setLabel:_NS("Statistics") forSegment:2];
/* constants defined in vlc_meta.h */
[_genreLabel setStringValue: NSTR(VLC_META_GENRE)];
[_copyrightLabel setStringValue: NSTR(VLC_META_COPYRIGHT)];
[_albumLabel setStringValue: NSTR(VLC_META_ALBUM)];
[_trackNumberLabel setStringValue: NSTR(VLC_META_TRACK_NUMBER)];
[_trackTotalLabel setStringValue: _NS("Track Total")];
[_descriptionLabel setStringValue: NSTR(VLC_META_DESCRIPTION)];
[_dateLabel setStringValue: NSTR(VLC_META_DATE)];
[_languageLabel setStringValue: NSTR(VLC_META_LANGUAGE)];
[_nowPlayingLabel setStringValue: NSTR(VLC_META_NOW_PLAYING)];
[_publisherLabel setStringValue: NSTR(VLC_META_PUBLISHER)];
[_encodedbyLabel setStringValue: NSTR(VLC_META_ENCODED_BY)];
[_showNameLabel setStringValue: NSTR(VLC_META_SHOW_NAME)];
[_episodeLabel setStringValue: NSTR(VLC_META_EPISODE)];
[_seasonLabel setStringValue: NSTR(VLC_META_SEASON)];
[_actorsLabel setStringValue: NSTR(VLC_META_ACTORS)];
[_directorLabel setStringValue: NSTR(VLC_META_DIRECTOR)];
/* statistics */
[_inputLabel setStringValue: _NS("Input")];
[_inputReadBytesLabel setStringValue: _NS("Read at media")];
[_inputBitrateLabel setStringValue: _NS("Input bitrate")];
[_inputReadPacketsLabel setStringValue: _NS("Read packets")];
[_demuxReadBytesLabel setStringValue: _NS("Demuxed")];
[_demuxReadPacketsLabel setStringValue: _NS("Demuxed packets")];
[_demuxBitrateLabel setStringValue: _NS("Stream bitrate")];
[_demuxCorruptedLabel setStringValue: _NS("Corrupted")];
[_demuxDiscontinuitiesLabel setStringValue: _NS("Discontinuities")];
[_videoLabel setStringValue: _NS("Video")];
[_videoDecodedLabel setStringValue: _NS("Decoded blocks")];
[_displayedLabel setStringValue: _NS("Displayed frames")];
[_lostFramesLabel setStringValue: _NS("Lost frames")];
[_lateFramesLabel setStringValue: _NS("Late frames")];
[_audioLabel setStringValue: _NS("Audio")];
[_audioDecodedLabel setStringValue: _NS("Decoded blocks")];
[_playedAudioBuffersLabel setStringValue: _NS("Played buffers")];
[_lostAudioBuffersLabel setStringValue: _NS("Lost buffers")];
}
- (void)updateCocoaWindowLevel:(NSNotification *)aNotification
{
NSInteger windowLevel = [aNotification.userInfo[VLCWindowLevelKey] integerValue];
if ([self.window isVisible] && [self.window level] != windowLevel)
[self.window setLevel: windowLevel];
}
- (IBAction)toggleWindow:(id)sender
{
if ([self.window isKeyWindow])
[self.window orderOut:sender];
else {
[self.window setLevel: VLCMain.sharedInstance.voutProvider.currentStatusWindowLevel];
[self.window makeKeyAndOrderFront:sender];
}
}
- (void)initMediaPanelStats
{
//Initializing Input Variables
[_inputReadBytesTextField setStringValue: [NSString stringWithFormat:_NS("%.1f KiB"), (float)0]];
[_inputReadPacketsTextField setIntValue: 0];
[_inputBitrateTextField setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]];
[_demuxReadBytesTextField setStringValue: [NSString stringWithFormat:_NS("%.1f KiB"), (float)0]];
[_demuxReadPacketsTextField setIntValue: 0];
[_demuxBitrateTextField setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]];
[_demuxDiscontinuitiesTextField setIntValue: 0];
[_demuxCorruptedTextField setIntValue: 0];
//Initializing Video Variables
[_videoDecodedTextField setIntValue:0];
[_displayedTextField setIntValue:0];
[_lateFramesTextField setIntValue:0];
[_lostFramesTextField setIntValue:0];
//Initializing Audio Variables
[_audioDecodedTextField setIntValue:0];
[_playedAudioBuffersTextField setIntValue: 0];
[_lostAudioBuffersTextField setIntValue: 0];
}
- (void)currentPlayingItemChanged:(NSNotification *)aNotification
{
VLCPlayerController * const playerController = VLCMain.sharedInstance.playlistController.playerController;
VLCInputItem * const currentMediaItem = playerController.currentMedia;
[self setRepresentedInputItem:currentMediaItem];
}
- (void)setRepresentedInputItem:(VLCInputItem *)representedInputItem
{
_representedInputItem = representedInputItem;
[self updateRepresentation];
}
- (void)mediaItemWasParsed:(NSNotification *)aNotification
{
[self updateRepresentation];
}
- (void)updateStatistics:(NSNotification *)aNotification
{
if (!_statisticsEnabled)
return;
if (![self.window isVisible])
return;
VLCInputStats *inputStats = aNotification.userInfo[VLCPlayerInputStats];
if (!inputStats) {
[self initMediaPanelStats];
return;
}
/* input */
[_inputReadBytesTextField setStringValue: [NSString stringWithFormat:
@"%8.0f KiB", (float)(inputStats.inputReadBytes)/1024]];
[_inputReadPacketsTextField setIntegerValue:inputStats.inputReadPackets];
[_inputBitrateTextField setStringValue: [NSString stringWithFormat:
@"%6.0f kb/s", (float)(inputStats.inputBitrate)*8000]];
[_demuxReadBytesTextField setStringValue: [NSString stringWithFormat:
@"%8.0f KiB", (float)(inputStats.demuxReadBytes)/1024]];
[_demuxReadPacketsTextField setIntegerValue:inputStats.demuxReadPackets];
[_demuxBitrateTextField setStringValue: [NSString stringWithFormat:
@"%6.0f kb/s", (float)(inputStats.demuxBitrate)*8000]];
[_demuxCorruptedTextField setIntegerValue:inputStats.demuxCorrupted];
[_demuxDiscontinuitiesTextField setIntegerValue:inputStats.demuxDiscontinuity];
/* Video */
[_videoDecodedTextField setIntegerValue: inputStats.decodedVideo];
[_displayedTextField setIntegerValue: inputStats.displayedPictures];
[_lateFramesTextField setIntegerValue: inputStats.latePictures];
[_lostFramesTextField setIntegerValue: inputStats.lostPictures];
/* Audio */
[_audioDecodedTextField setIntegerValue: inputStats.decodedAudio];
[_playedAudioBuffersTextField setIntegerValue: inputStats.playedAudioBuffers];
[_lostAudioBuffersTextField setIntegerValue: inputStats.lostAudioBuffers];
}
- (void)updateRepresentation
{
[_saveMetaDataButton setEnabled: NO];
if (!_representedInputItem) {
/* Erase */
#define SET( foo ) \
[_##foo##TextField setStringValue:@""];
SET( uri );
SET( title );
SET( artist );
SET( album );
SET( trackNumber );
SET( trackTotal );
SET( genre );
SET( season );
SET( episode );
SET( actors );
SET( director );
SET( showName );
SET( copyright );
SET( publisher );
SET( nowPlaying );
SET( language );
SET( date );
SET( description );
SET( encodedby );
#undef SET
[_artworkImageView setImage: [NSImage imageNamed:@"noart.png"]];
} else {
if (!_representedInputItem.preparsed) {
[_representedInputItem preparseInputItem];
}
_uriTextField.stringValue = _representedInputItem.decodedMRL;
_titleTextField.stringValue = _representedInputItem.title;
_artistTextField.stringValue = _representedInputItem.artist;
_albumTextField.stringValue = _representedInputItem.albumName;
_trackNumberTextField.stringValue = _representedInputItem.trackNumber;
_trackTotalTextField.stringValue = _representedInputItem.trackTotal;
_genreTextField.stringValue = _representedInputItem.genre;
_seasonTextField.stringValue = _representedInputItem.season;
_episodeTextField.stringValue = _representedInputItem.episode;
_actorsTextField.stringValue = _representedInputItem.actors;
_directorTextField.stringValue = _representedInputItem.director;
_showNameTextField.stringValue = _representedInputItem.showName;
_copyrightTextField.stringValue = _representedInputItem.copyright;
_publisherTextField.stringValue = _representedInputItem.publisher;
_nowPlayingTextField.stringValue = _representedInputItem.nowPlaying;
_languageTextField.stringValue = _representedInputItem.language;
_dateTextField.stringValue = _representedInputItem.date;
_descriptionTextField.stringValue = _representedInputItem.contentDescription;
_encodedbyTextField.stringValue = _representedInputItem.encodedBy;
NSURL *artworkURL = _representedInputItem.artworkURL;
NSImage *placeholderImage = [NSImage imageNamed: @"noart.png"];
[_artworkImageView setImageURL:artworkURL placeholderImage:placeholderImage];
if (!_mainMenuInstance) {
[self.window setTitle:_representedInputItem.title];
}
}
/* reload the codec details table */
[self updateStreamsList];
}
- (void)updateStreamsList
{
_rootCodecInformationItem = [[VLCCodecInformationTreeItem alloc] init];
if (_representedInputItem) {
input_item_t *p_input = _representedInputItem.vlcInputItem;
vlc_mutex_lock(&p_input->lock);
// build list of streams
NSMutableArray *streams = [NSMutableArray array];
info_category_t *cat;
vlc_list_foreach(cat, &p_input->categories, node) {
info_t *info;
if (info_category_IsHidden(cat))
continue;
VLCCodecInformationTreeItem *subItem = [[VLCCodecInformationTreeItem alloc] init];
subItem.propertyName = toNSStr(cat->psz_name);
// Build list of codec details
NSMutableArray *infos = [NSMutableArray array];
info_foreach(info, &cat->infos) {
VLCCodecInformationTreeItem *infoItem = [[VLCCodecInformationTreeItem alloc] init];
infoItem.propertyName = toNSStr(info->psz_name);
infoItem.propertyValue = toNSStr(info->psz_value);
[infos addObject:infoItem];
}
subItem.children = [infos copy];
[streams addObject:subItem];
}
_rootCodecInformationItem.children = [streams copy];
vlc_mutex_unlock(&p_input->lock);
}
[_outlineView reloadData];
[_outlineView expandItem:nil expandChildren:YES];
}
- (IBAction)metaFieldChanged:(id)sender
{
[_saveMetaDataButton setEnabled: YES];
}
- (IBAction)saveMetaData:(id)sender
{
if (!_representedInputItem) {
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:_NS("Error while saving meta")];
[alert setInformativeText:_NS("VLC was unable to save the meta data.")];
[alert addButtonWithTitle:_NS("OK")];
[alert runModal];
return;
}
_representedInputItem.name = _titleTextField.stringValue;
_representedInputItem.title = _titleTextField.stringValue;
_representedInputItem.artist = _artistTextField.stringValue;
_representedInputItem.albumName = _albumTextField.stringValue;
_representedInputItem.genre = _genreTextField.stringValue;
_representedInputItem.trackNumber = _trackNumberTextField.stringValue;
_representedInputItem.date = _dateTextField.stringValue;
_representedInputItem.copyright = _copyrightTextField.stringValue;
_representedInputItem.publisher = _publisherTextField.stringValue;
_representedInputItem.contentDescription = _descriptionTextField.stringValue;
_representedInputItem.language = _languageTextField.stringValue;
_representedInputItem.showName = _showNameTextField.stringValue;
_representedInputItem.actors = _actorsTextField.stringValue;
_representedInputItem.director = _directorTextField.stringValue;
[_representedInputItem writeMetadataToFile];
[_saveMetaDataButton setEnabled: NO];
}
@end
@implementation VLCInformationWindowController (NSTableDataSource)
- (NSInteger)outlineView:(NSOutlineView *)outlineView
numberOfChildrenOfItem:(id)item
{
return (item == nil) ? [_rootCodecInformationItem children].count : [item children].count;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView
isItemExpandable:(id)item
{
return ([item children].count > 0);
}
- (id)outlineView:(NSOutlineView *)outlineView
child:(NSInteger)index
ofItem:(id)item
{
return (item == nil) ? [_rootCodecInformationItem children][index] : [item children][index];
}
- (id)outlineView:(NSOutlineView *)outlineView
objectValueForTableColumn:(NSTableColumn *)tableColumn
byItem:(id)item
{
if (!item)
return @"";
if ([[tableColumn identifier] isEqualToString:@"0"]) {
return [item propertyName];
} else {
return [item propertyValue];
}
}
@end