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.
1828 lines
59 KiB
1828 lines
59 KiB
/*****************************************************************************
|
|
* VLCPlayerController.m: MacOS X interface module
|
|
*****************************************************************************
|
|
* Copyright (C) 2019 VLC authors and VideoLAN
|
|
*
|
|
* Authors: Felix Paul Kühne <fkuehne # 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 "VLCPlayerController.h"
|
|
|
|
#import <vlc_configuration.h>
|
|
#import <vlc_url.h>
|
|
|
|
#import "extensions/NSString+Helpers.h"
|
|
#import "main/VLCMain.h"
|
|
#import "os-integration/VLCRemoteControlService.h"
|
|
#import "os-integration/iTunes.h"
|
|
#import "os-integration/Spotify.h"
|
|
#import "library/VLCInputItem.h"
|
|
|
|
#import "windows/video/VLCVoutView.h"
|
|
#import "windows/video/VLCMainVideoViewController.h"
|
|
#import "windows/video/VLCVideoWindowCommon.h"
|
|
|
|
NSString *VLCPlayerElementaryStreamID = @"VLCPlayerElementaryStreamID";
|
|
NSString *VLCTick = @"VLCTick";
|
|
NSString *VLCPlayerCurrentMediaItemChanged = @"VLCPlayerCurrentMediaItemChanged";
|
|
NSString *VLCPlayerMetadataChangedForCurrentMedia = @"VLCPlayerMetadataChangedForCurrentMedia";
|
|
NSString *VLCPlayerStateChanged = @"VLCPlayerStateChanged";
|
|
NSString *VLCPlayerErrorChanged = @"VLCPlayerErrorChanged";
|
|
NSString *VLCPlayerBufferFill = @"VLCPlayerBufferFill";
|
|
NSString *VLCPlayerBufferChanged = @"VLCPlayerBufferChanged";
|
|
NSString *VLCPlayerRateChanged = @"VLCPlayerRateChanged";
|
|
NSString *VLCPlayerCapabilitiesChanged = @"VLCPlayerCapabilitiesChanged";
|
|
NSString *VLCPlayerTimeAndPositionChanged = @"VLCPlayerTimeAndPositionChanged";
|
|
NSString *VLCPlayerLengthChanged = @"VLCPlayerLengthChanged";
|
|
NSString *VLCPlayerTitleSelectionChanged = @"VLCPlayerTitleSelectionChanged";
|
|
NSString *VLCPlayerTitleListChanged = @"VLCPlayerTitleListChanged";
|
|
NSString *VLCPlayerChapterSelectionChanged = @"VLCPlayerChapterSelectionChanged";
|
|
NSString *VLCPlayerProgramSelectionChanged = @"VLCPlayerProgramSelectionChanged";
|
|
NSString *VLCPlayerProgramListChanged = @"VLCPlayerProgramListChanged";
|
|
NSString *VLCPlayerABLoopStateChanged = @"VLCPlayerABLoopStateChanged";
|
|
NSString *VLCPlayerTeletextMenuAvailable = @"VLCPlayerTeletextMenuAvailable";
|
|
NSString *VLCPlayerTeletextEnabled = @"VLCPlayerTeletextEnabled";
|
|
NSString *VLCPlayerTeletextPageChanged = @"VLCPlayerTeletextPageChanged";
|
|
NSString *VLCPlayerTeletextTransparencyChanged = @"VLCPlayerTeletextTransparencyChanged";
|
|
NSString *VLCPlayerAudioDelayChanged = @"VLCPlayerAudioDelayChanged";
|
|
NSString *VLCPlayerSubtitlesDelayChanged = @"VLCPlayerSubtitlesDelayChanged";
|
|
NSString *VLCPlayerDelayChangedForSpecificElementaryStream = @"VLCPlayerDelayChangedForSpecificElementaryStream";
|
|
NSString *VLCPlayerSubtitlesFPSChanged = @"VLCPlayerSubtitlesFPSChanged";
|
|
NSString *VLCPlayerSubtitleTextScalingFactorChanged = @"VLCPlayerSubtitleTextScalingFactorChanged";
|
|
NSString *VLCPlayerRecordingChanged = @"VLCPlayerRecordingChanged";
|
|
NSString *VLCPlayerRendererChanged = @"VLCPlayerRendererChanged";
|
|
NSString *VLCPlayerInputStats = @"VLCPlayerInputStats";
|
|
NSString *VLCPlayerStatisticsUpdated = @"VLCPlayerStatisticsUpdated";
|
|
NSString *VLCPlayerTrackListChanged = @"VLCPlayerTrackListChanged";
|
|
NSString *VLCPlayerTrackSelectionChanged = @"VLCPlayerTrackSelectionChanged";
|
|
NSString *VLCPlayerFullscreenChanged = @"VLCPlayerFullscreenChanged";
|
|
NSString *VLCPlayerWallpaperModeChanged = @"VLCPlayerWallpaperModeChanged";
|
|
NSString *VLCPlayerListOfVideoOutputThreadsChanged = @"VLCPlayerListOfVideoOutputThreadsChanged";
|
|
NSString *VLCPlayerVolumeChanged = @"VLCPlayerVolumeChanged";
|
|
NSString *VLCPlayerMuteChanged = @"VLCPlayerMuteChanged";
|
|
|
|
const CGFloat VLCVolumeMaximum = 2.;
|
|
const CGFloat VLCVolumeDefault = 1.;
|
|
|
|
@interface VLCPlayerController ()
|
|
{
|
|
vlc_player_t *_p_player;
|
|
vlc_player_listener_id *_playerListenerID;
|
|
vlc_player_aout_listener_id *_playerAoutListenerID;
|
|
vlc_player_vout_listener_id *_playerVoutListenerID;
|
|
vlc_player_title_list *_currentTitleList;
|
|
NSNotificationCenter *_defaultNotificationCenter;
|
|
|
|
/* remote control support */
|
|
VLCRemoteControlService *_remoteControlService;
|
|
|
|
/* iTunes/Apple Music/Spotify play/pause support */
|
|
BOOL _iTunesPlaybackWasPaused;
|
|
BOOL _appleMusicPlaybackWasPaused;
|
|
BOOL _spotifyPlaybackWasPaused;
|
|
|
|
NSTimer *_playbackHasTruelyEndedTimer;
|
|
}
|
|
|
|
@property (readwrite, atomic) iTunesApplication *appleMusicApp;
|
|
@property (readwrite, atomic) iTunesApplication *iTunesApp;
|
|
@property (readwrite, atomic) SpotifyApplication *spotifyApp;
|
|
|
|
- (void)currentMediaItemChanged:(input_item_t *)newMediaItem;
|
|
- (void)stateChanged:(enum vlc_player_state)state;
|
|
- (void)errorChanged:(enum vlc_player_error)error;
|
|
- (void)newBufferingValue:(float)bufferValue;
|
|
- (void)newRateValue:(float)rateValue;
|
|
- (void)capabilitiesChanged:(int)newCapabilities;
|
|
- (void)position:(float)position andTimeChanged:(vlc_tick_t)time;
|
|
- (void)lengthChanged:(vlc_tick_t)length;
|
|
- (void)titleListChanged:(vlc_player_title_list *)p_titles;
|
|
- (void)selectedTitleChanged:(size_t)selectedTitle;
|
|
- (void)selectedChapterChanged:(size_t)chapterIndex;
|
|
- (void)teletextAvailibilityChanged:(BOOL)hasTeletextMenu;
|
|
- (void)teletextEnabledChanged:(BOOL)teletextOn;
|
|
- (void)teletextPageChanged:(unsigned int)page;
|
|
- (void)teletextTransparencyChanged:(BOOL)isTransparent;
|
|
- (void)audioDelayChanged:(vlc_tick_t)audioDelay;
|
|
- (void)rendererChanged:(vlc_renderer_item_t *)newRendererItem;
|
|
- (void)subtitlesDelayChanged:(vlc_tick_t)subtitlesDelay;
|
|
- (void)delayChanged:(vlc_tick_t)trackDelay forTrack:(vlc_es_id_t *)esID;
|
|
- (void)subtitlesFPSChanged:(float)subtitlesFPS;
|
|
- (void)recordingChanged:(BOOL)recording;
|
|
- (void)inputStatsUpdated:(VLCInputStats *)inputStats;
|
|
- (void)trackSelectionChanged;
|
|
- (void)trackListChanged;
|
|
- (void)programListChanged;
|
|
- (void)programSelectionChanged:(int)selectedID;
|
|
- (void)ABLoopStateChanged:(enum vlc_player_abloop)abLoopState;
|
|
- (void)stopActionChanged:(enum vlc_player_media_stopped_action)stoppedAction;
|
|
- (void)metaDataChangedForInput:(input_item_t *)inputItem;
|
|
- (void)voutListUpdated;
|
|
|
|
/* video */
|
|
- (void)fullscreenChanged:(BOOL)isFullscreen;
|
|
- (void)wallpaperModeChanged:(BOOL)wallpaperModeValue;
|
|
|
|
/* audio */
|
|
- (void)volumeChanged:(float)volume;
|
|
- (void)muteChanged:(BOOL)mute;
|
|
@end
|
|
|
|
#pragma mark - player callback implementations
|
|
|
|
static void cb_player_current_media_changed(vlc_player_t *p_player, input_item_t *p_newMediaItem, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController currentMediaItemChanged:p_newMediaItem];
|
|
});
|
|
}
|
|
|
|
static void cb_player_state_changed(vlc_player_t *p_player, enum vlc_player_state state, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController stateChanged:state];
|
|
});
|
|
}
|
|
|
|
static void cb_player_error_changed(vlc_player_t *p_player, enum vlc_player_error error, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController errorChanged:error];
|
|
});
|
|
}
|
|
|
|
static void cb_player_buffering(vlc_player_t *p_player, float newBufferValue, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController newBufferingValue:newBufferValue];
|
|
});
|
|
}
|
|
|
|
static void cb_player_rate_changed(vlc_player_t *p_player, float newRateValue, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController newRateValue:newRateValue];
|
|
});
|
|
}
|
|
|
|
static void cb_player_capabilities_changed(vlc_player_t *p_player, int oldCapabilities, int newCapabilities, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player); VLC_UNUSED(oldCapabilities);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController capabilitiesChanged:newCapabilities];
|
|
});
|
|
}
|
|
|
|
static void cb_player_position_changed(vlc_player_t *p_player, vlc_tick_t time, double position, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController position:position andTimeChanged:time];
|
|
});
|
|
}
|
|
|
|
static void cb_player_length_changed(vlc_player_t *p_player, vlc_tick_t newLength, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController lengthChanged:newLength];
|
|
});
|
|
}
|
|
|
|
static void cb_player_titles_changed(vlc_player_t *p_player,
|
|
vlc_player_title_list *p_titles,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
if (p_titles)
|
|
vlc_player_title_list_Hold(p_titles);
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController titleListChanged:p_titles];
|
|
});
|
|
}
|
|
|
|
static void cb_player_title_selection_changed(vlc_player_t *p_player,
|
|
const struct vlc_player_title *p_new_title,
|
|
size_t selectedIndex,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
VLC_UNUSED(p_new_title);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController selectedTitleChanged:selectedIndex];
|
|
});
|
|
}
|
|
|
|
static void cb_player_chapter_selection_changed(vlc_player_t *p_player,
|
|
const struct vlc_player_title *p_title, size_t title_idx,
|
|
const struct vlc_player_chapter *p_new_chapter, size_t new_chapter_idx,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
VLC_UNUSED(p_title);
|
|
VLC_UNUSED(title_idx);
|
|
VLC_UNUSED(p_new_chapter);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController selectedChapterChanged:new_chapter_idx];
|
|
});
|
|
}
|
|
|
|
static void cb_player_teletext_menu_availability_changed(vlc_player_t *p_player, bool hasTeletextMenu, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController teletextAvailibilityChanged:hasTeletextMenu];
|
|
});
|
|
}
|
|
|
|
static void cb_player_teletext_enabled_changed(vlc_player_t *p_player, bool teletextEnabled, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController teletextEnabledChanged:teletextEnabled];
|
|
});
|
|
}
|
|
|
|
static void cb_player_teletext_page_changed(vlc_player_t *p_player, unsigned page, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController teletextPageChanged:page];
|
|
});
|
|
}
|
|
|
|
static void cb_player_teletext_transparency_changed(vlc_player_t *p_player, bool isTransparent, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController teletextTransparencyChanged:isTransparent];
|
|
});
|
|
}
|
|
|
|
static void cb_player_category_delay_changed(vlc_player_t *p_player, enum es_format_category_e cat,
|
|
vlc_tick_t newDelay, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
switch (cat)
|
|
{
|
|
case AUDIO_ES:
|
|
[playerController audioDelayChanged:newDelay];
|
|
break;
|
|
case SPU_ES:
|
|
[playerController subtitlesDelayChanged:newDelay];
|
|
break;
|
|
default:
|
|
vlc_assert_unreachable();
|
|
}
|
|
});
|
|
}
|
|
|
|
static void cb_player_associated_subs_fps_changed(vlc_player_t *p_player, float subs_fps, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController subtitlesFPSChanged:subs_fps];
|
|
});
|
|
}
|
|
|
|
static void cb_player_renderer_changed(vlc_player_t *p_player,
|
|
vlc_renderer_item_t *p_new_renderer,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController rendererChanged:p_new_renderer];
|
|
});
|
|
}
|
|
|
|
static void cb_player_record_changed(vlc_player_t *p_player, bool recording, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController recordingChanged:recording];
|
|
});
|
|
}
|
|
|
|
static void cb_player_stats_changed(vlc_player_t *p_player,
|
|
const struct input_stats_t *p_stats,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
|
|
/* the provided structure is valid in this context only, so copy all data to our own */
|
|
VLCInputStats *inputStats = [[VLCInputStats alloc] initWithStatsStructure:p_stats];
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController inputStatsUpdated:inputStats];
|
|
});
|
|
}
|
|
|
|
static void cb_player_track_list_changed(vlc_player_t *p_player,
|
|
enum vlc_player_list_action action,
|
|
const struct vlc_player_track *track,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player); VLC_UNUSED(action); VLC_UNUSED(track);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController trackListChanged];
|
|
});
|
|
}
|
|
|
|
static void cb_player_track_selection_changed(vlc_player_t *p_player,
|
|
vlc_es_id_t *unselected_id,
|
|
vlc_es_id_t *selected_id,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player); VLC_UNUSED(unselected_id); VLC_UNUSED(selected_id);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController trackSelectionChanged];
|
|
});
|
|
}
|
|
|
|
static void cb_player_track_delay_changed(vlc_player_t *p_player,
|
|
vlc_es_id_t *es_id,
|
|
vlc_tick_t delay,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController delayChanged:delay forTrack:es_id];
|
|
});
|
|
}
|
|
|
|
static void cb_player_program_list_changed(vlc_player_t *p_player,
|
|
enum vlc_player_list_action action,
|
|
const struct vlc_player_program *prgm,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player); VLC_UNUSED(action); VLC_UNUSED(prgm);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController programListChanged];
|
|
});
|
|
}
|
|
|
|
static void cb_player_program_selection_changed(vlc_player_t *p_player,
|
|
int unselected_id,
|
|
int selected_id,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player); VLC_UNUSED(unselected_id);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController programSelectionChanged:selected_id];
|
|
});
|
|
}
|
|
|
|
static void cb_player_atobloop_changed(vlc_player_t *p_player,
|
|
enum vlc_player_abloop new_state,
|
|
vlc_tick_t time, double pos,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController ABLoopStateChanged:new_state];
|
|
});
|
|
}
|
|
|
|
static void cb_player_media_stopped_action_changed(vlc_player_t *p_player,
|
|
enum vlc_player_media_stopped_action newAction,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController stopActionChanged:newAction];
|
|
});
|
|
}
|
|
|
|
static void cb_player_item_meta_changed(vlc_player_t *p_player,
|
|
input_item_t *p_mediaItem,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
input_item_Hold(p_mediaItem);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController metaDataChangedForInput:p_mediaItem];
|
|
});
|
|
}
|
|
|
|
static void cb_player_vout_changed(vlc_player_t *p_player,
|
|
enum vlc_player_vout_action action,
|
|
vout_thread_t *p_vout,
|
|
enum vlc_vout_order order,
|
|
vlc_es_id_t *es_id,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_player);
|
|
VLC_UNUSED(p_vout);
|
|
VLC_UNUSED(order);
|
|
|
|
if (vlc_es_id_GetCat(es_id) != VIDEO_ES)
|
|
return;
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController voutListUpdated];
|
|
});
|
|
}
|
|
|
|
static const struct vlc_player_cbs player_callbacks = {
|
|
cb_player_current_media_changed,
|
|
cb_player_state_changed,
|
|
cb_player_error_changed,
|
|
cb_player_buffering,
|
|
cb_player_rate_changed,
|
|
cb_player_capabilities_changed,
|
|
cb_player_position_changed,
|
|
cb_player_length_changed,
|
|
cb_player_track_list_changed,
|
|
cb_player_track_selection_changed,
|
|
cb_player_track_delay_changed,
|
|
cb_player_program_list_changed,
|
|
cb_player_program_selection_changed,
|
|
cb_player_titles_changed,
|
|
cb_player_title_selection_changed,
|
|
cb_player_chapter_selection_changed,
|
|
cb_player_teletext_menu_availability_changed,
|
|
cb_player_teletext_enabled_changed,
|
|
cb_player_teletext_page_changed,
|
|
cb_player_teletext_transparency_changed,
|
|
cb_player_category_delay_changed,
|
|
cb_player_associated_subs_fps_changed,
|
|
cb_player_renderer_changed,
|
|
cb_player_record_changed,
|
|
NULL, //cb_player_signal_changed,
|
|
cb_player_stats_changed,
|
|
cb_player_atobloop_changed,
|
|
cb_player_media_stopped_action_changed,
|
|
cb_player_item_meta_changed,
|
|
NULL, //cb_player_item_epg_changed,
|
|
NULL, //cb_player_subitems_changed,
|
|
cb_player_vout_changed,
|
|
NULL, //on_cork_changed
|
|
};
|
|
|
|
#pragma mark - video specific callback implementations
|
|
|
|
static void cb_player_vout_fullscreen_changed(vout_thread_t *p_vout, bool isFullscreen, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_vout);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController fullscreenChanged:isFullscreen];
|
|
});
|
|
}
|
|
|
|
static void cb_player_vout_wallpaper_mode_changed(vout_thread_t *p_vout, bool wallpaperModeEnabled, void *p_data)
|
|
{
|
|
VLC_UNUSED(p_vout);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController wallpaperModeChanged:wallpaperModeEnabled];
|
|
});
|
|
}
|
|
|
|
static const struct vlc_player_vout_cbs player_vout_callbacks = {
|
|
cb_player_vout_fullscreen_changed,
|
|
cb_player_vout_wallpaper_mode_changed,
|
|
};
|
|
|
|
#pragma mark - video specific callback implementations
|
|
|
|
static void cb_player_aout_volume_changed(audio_output_t *aout, float volume, void *p_data)
|
|
{
|
|
VLC_UNUSED(aout);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController volumeChanged:volume];
|
|
});
|
|
}
|
|
|
|
static void cb_player_aout_mute_changed(audio_output_t *aout, bool muted, void *p_data)
|
|
{
|
|
VLC_UNUSED(aout);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController muteChanged:muted];
|
|
});
|
|
}
|
|
|
|
static const struct vlc_player_aout_cbs player_aout_callbacks = {
|
|
cb_player_aout_volume_changed,
|
|
cb_player_aout_mute_changed,
|
|
NULL,
|
|
};
|
|
|
|
static int BossCallback(vlc_object_t *p_this,
|
|
const char *psz_var,
|
|
vlc_value_t oldval,
|
|
vlc_value_t new_val,
|
|
void *p_data)
|
|
{
|
|
VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval); VLC_UNUSED(new_val);
|
|
@autoreleasepool {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
VLCPlayerController *playerController = (__bridge VLCPlayerController *)p_data;
|
|
[playerController pause];
|
|
[[NSApplication sharedApplication] hide:nil];
|
|
});
|
|
return VLC_SUCCESS;
|
|
}
|
|
}
|
|
|
|
#pragma mark - controller initialization
|
|
|
|
@implementation VLCPlayerController
|
|
|
|
- (instancetype)initWithPlayer:(vlc_player_t *)player
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
_defaultNotificationCenter = [NSNotificationCenter defaultCenter];
|
|
[_defaultNotificationCenter addObserver:self
|
|
selector:@selector(applicationWillTerminate:)
|
|
name:NSApplicationWillTerminateNotification
|
|
object:nil];
|
|
[_defaultNotificationCenter addObserver:self
|
|
selector:@selector(applicationDidFinishLaunching:)
|
|
name:NSApplicationDidFinishLaunchingNotification
|
|
object:nil];
|
|
_position = -1.f;
|
|
_time = VLC_TICK_INVALID;
|
|
_p_player = player;
|
|
vlc_player_Lock(_p_player);
|
|
// FIXME: initialize state machine here
|
|
_playerListenerID = vlc_player_AddListener(_p_player,
|
|
&player_callbacks,
|
|
(__bridge void *)self);
|
|
vlc_player_Unlock(_p_player);
|
|
_playerAoutListenerID = vlc_player_aout_AddListener(_p_player,
|
|
&player_aout_callbacks,
|
|
(__bridge void *)self);
|
|
_playerVoutListenerID = vlc_player_vout_AddListener(_p_player,
|
|
&player_vout_callbacks,
|
|
(__bridge void *)self);
|
|
|
|
_volume = VLCVolumeDefault;
|
|
|
|
libvlc_int_t *libvlc = vlc_object_instance(getIntf());
|
|
var_AddCallback(libvlc, "intf-boss", BossCallback, (__bridge void *)self);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
{
|
|
if (@available(macOS 10.12.2, *)) {
|
|
_remoteControlService = [[VLCRemoteControlService alloc] init];
|
|
[_remoteControlService subscribeToRemoteCommands];
|
|
}
|
|
|
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
|
|
if (@available(macOS 10.15, *)) {
|
|
self.appleMusicApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.Music"];
|
|
} else {
|
|
self.iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
|
|
}
|
|
|
|
self.spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (self->_playerState == VLC_PLAYER_STATE_PLAYING || self->_playerState == VLC_PLAYER_STATE_STARTED) {
|
|
[self stopOtherAudioPlaybackApps];
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
|
{
|
|
libvlc_int_t *libvlc = vlc_object_instance(getIntf());
|
|
var_DelCallback(libvlc, "intf-boss", BossCallback, (__bridge void *)self);
|
|
|
|
[self onPlaybackHasTruelyEnded:nil];
|
|
if (@available(macOS 10.12.2, *)) {
|
|
[_remoteControlService unsubscribeFromRemoteCommands];
|
|
}
|
|
if (_currentTitleList) {
|
|
vlc_player_title_list_Release(_currentTitleList);
|
|
}
|
|
if (_p_player) {
|
|
if (_playerListenerID) {
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_RemoveListener(_p_player, _playerListenerID);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
if (_playerAoutListenerID) {
|
|
vlc_player_aout_RemoveListener(_p_player, _playerAoutListenerID);
|
|
}
|
|
if (_playerVoutListenerID) {
|
|
vlc_player_vout_RemoveListener(_p_player, _playerVoutListenerID);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_defaultNotificationCenter removeObserver:self];
|
|
}
|
|
|
|
#pragma mark - playback control methods
|
|
|
|
- (int)start
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
int ret = vlc_player_Start(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
return ret;
|
|
}
|
|
|
|
- (void)startInPausedState:(BOOL)startPaused
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetStartPaused(_p_player, startPaused);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)pause
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_Pause(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)resume
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_Resume(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)togglePlayPause
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
if (_playerState == VLC_PLAYER_STATE_PLAYING) {
|
|
vlc_player_Pause(_p_player);
|
|
} else if (_playerState == VLC_PLAYER_STATE_PAUSED) {
|
|
vlc_player_Resume(_p_player);
|
|
} else
|
|
vlc_player_Start(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)stop
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_Stop(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)stopActionChanged:(enum vlc_player_media_stopped_action)stoppedAction
|
|
{
|
|
_actionAfterStop = stoppedAction;
|
|
}
|
|
|
|
- (void)setActionAfterStop:(enum vlc_player_media_stopped_action)actionAfterStop
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetMediaStoppedAction(_p_player, actionAfterStop);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)metaDataChangedForInput:(input_item_t *)inputItem
|
|
{
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerMetadataChangedForCurrentMedia
|
|
object:self];
|
|
}
|
|
|
|
- (void)nextVideoFrame
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_NextVideoFrame(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
#pragma mark - player callback delegations
|
|
|
|
- (VLCInputItem *)currentMedia
|
|
{
|
|
VLCInputItem *inputItem;
|
|
input_item_t *p_input;
|
|
vlc_player_Lock(_p_player);
|
|
p_input = vlc_player_GetCurrentMedia(_p_player);
|
|
if (p_input) {
|
|
inputItem = [[VLCInputItem alloc] initWithInputItem:p_input];
|
|
}
|
|
vlc_player_Unlock(_p_player);
|
|
return inputItem;
|
|
}
|
|
|
|
- (int)setCurrentMedia:(VLCInputItem *)currentMedia
|
|
{
|
|
if (currentMedia == NULL) {
|
|
return VLC_ENOENT;
|
|
}
|
|
vlc_player_Lock(_p_player);
|
|
int ret = vlc_player_SetCurrentMedia(_p_player, currentMedia.vlcInputItem);
|
|
vlc_player_Unlock(_p_player);
|
|
return ret;
|
|
}
|
|
|
|
- (void)currentMediaItemChanged:(input_item_t *)newMediaItem
|
|
{
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerCurrentMediaItemChanged
|
|
object:self];
|
|
}
|
|
|
|
- (vlc_tick_t)durationOfCurrentMediaItem
|
|
{
|
|
if (!self.currentMedia) {
|
|
return -1;
|
|
}
|
|
|
|
return self.currentMedia.duration;
|
|
}
|
|
|
|
- (NSURL *)URLOfCurrentMediaItem;
|
|
{
|
|
if (!self.currentMedia) {
|
|
return nil;
|
|
}
|
|
|
|
return [NSURL URLWithString:self.currentMedia.MRL];
|
|
}
|
|
|
|
- (NSString*)nameOfCurrentMediaItem;
|
|
{
|
|
if (!self.currentMedia) {
|
|
return nil;
|
|
}
|
|
|
|
NSString *name = self.currentMedia.name;
|
|
if (!name) {
|
|
NSURL *url = [NSURL URLWithString:self.currentMedia.MRL];
|
|
if ([url isFileURL])
|
|
name = [[NSFileManager defaultManager] displayNameAtPath:[url path]];
|
|
else
|
|
name = [url absoluteString];
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
- (void)stateChanged:(enum vlc_player_state)state
|
|
{
|
|
/* instead of using vlc_player_GetState, we cache the state and provide it through a synthesized getter
|
|
* as the direct call might not reflect the actual state due the asynchronous API nature */
|
|
_playerState = state;
|
|
|
|
/* we seem to start (over), don't start other players */
|
|
if (_playerState != VLC_PLAYER_STATE_STOPPED) {
|
|
if (_playbackHasTruelyEndedTimer) {
|
|
[_playbackHasTruelyEndedTimer invalidate];
|
|
_playbackHasTruelyEndedTimer = nil;
|
|
}
|
|
}
|
|
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerStateChanged
|
|
object:self];
|
|
|
|
/* notify third party apps through an informal protocol that our state changed */
|
|
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"VLCPlayerStateDidChange"
|
|
object:nil
|
|
userInfo:nil
|
|
deliverImmediately:YES];
|
|
|
|
/* schedule a timer to restart iTunes / Spotify because we are done here */
|
|
if (_playerState == VLC_PLAYER_STATE_STOPPED) {
|
|
if (_playbackHasTruelyEndedTimer) {
|
|
[_playbackHasTruelyEndedTimer invalidate];
|
|
}
|
|
_playbackHasTruelyEndedTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
|
|
target:self
|
|
selector:@selector(onPlaybackHasTruelyEnded:)
|
|
userInfo:nil
|
|
repeats:NO];
|
|
}
|
|
|
|
/* pause external players */
|
|
if (_playerState == VLC_PLAYER_STATE_PLAYING || _playerState == VLC_PLAYER_STATE_STARTED) {
|
|
[self stopOtherAudioPlaybackApps];
|
|
}
|
|
}
|
|
|
|
// Called when playback has truly ended and likely no subsequent media will start playing
|
|
- (void)onPlaybackHasTruelyEnded:(id)sender
|
|
{
|
|
msg_Dbg(getIntf(), "Playback has been ended");
|
|
|
|
[self resumeOtherAudioPlaybackApps];
|
|
_playbackHasTruelyEndedTimer = nil;
|
|
}
|
|
|
|
- (void)stopOtherAudioPlaybackApps
|
|
{
|
|
intf_thread_t *p_intf = getIntf();
|
|
int64_t controlOtherPlayers = var_InheritInteger(p_intf, "macosx-control-itunes");
|
|
if (controlOtherPlayers <= 0)
|
|
return;
|
|
|
|
// Don't bother looking for Apple Music on macOS below 10.15, and conversely
|
|
// don't bother looking for iTunes on macOS above 10.15
|
|
if (@available(macOS 10.15, *)) {
|
|
// Pause Apple Music playback
|
|
iTunesApplication *appleMusic = self.appleMusicApp;
|
|
if (appleMusic != nil &&
|
|
!_appleMusicPlaybackWasPaused &&
|
|
[appleMusic isRunning] &&
|
|
[appleMusic playerState] == iTunesEPlSPlaying) {
|
|
|
|
msg_Dbg(p_intf, "pausing Apple Music");
|
|
[appleMusic pause];
|
|
_appleMusicPlaybackWasPaused = YES;
|
|
}
|
|
} else {
|
|
iTunesApplication *iTunes = self.iTunesApp;
|
|
if (iTunes != nil &&
|
|
!_iTunesPlaybackWasPaused &&
|
|
[iTunes isRunning] &&
|
|
[iTunes playerState] == iTunesEPlSPlaying) {
|
|
// Pause iTunes playback
|
|
msg_Dbg(p_intf, "pausing iTunes");
|
|
[iTunes pause];
|
|
_iTunesPlaybackWasPaused = YES;
|
|
}
|
|
}
|
|
|
|
// Pause Spotify
|
|
SpotifyApplication *spotify = self.spotifyApp;
|
|
if (spotify != nil &&
|
|
!_spotifyPlaybackWasPaused &&
|
|
[spotify respondsToSelector:@selector(isRunning)] &&
|
|
[spotify respondsToSelector:@selector(playerState)] &&
|
|
[spotify isRunning] &&
|
|
[spotify playerState] == kSpotifyPlayerStatePlaying) {
|
|
|
|
msg_Dbg(p_intf, "pausing Spotify");
|
|
[spotify pause];
|
|
_spotifyPlaybackWasPaused = YES;
|
|
}
|
|
}
|
|
|
|
- (void)resumeOtherAudioPlaybackApps
|
|
{
|
|
intf_thread_t *p_intf = getIntf();
|
|
if (var_InheritInteger(p_intf, "macosx-control-itunes") > 1) {
|
|
if (@available(macOS 10.15, *)) {
|
|
iTunesApplication *appleMusic = self.appleMusicApp;
|
|
if (appleMusic != nil &&
|
|
_appleMusicPlaybackWasPaused &&
|
|
[appleMusic isRunning] &&
|
|
[appleMusic playerState] == iTunesEPlSPaused) {
|
|
|
|
msg_Dbg(p_intf, "unpausing Apple Music");
|
|
[appleMusic playpause];
|
|
}
|
|
} else {
|
|
iTunesApplication *iTunes = self.iTunesApp;
|
|
if (iTunes != nil &&
|
|
_iTunesPlaybackWasPaused &&
|
|
[iTunes isRunning] &&
|
|
[iTunes playerState] == iTunesEPlSPaused) {
|
|
|
|
msg_Dbg(p_intf, "unpausing iTunes");
|
|
[iTunes playpause];
|
|
}
|
|
}
|
|
|
|
SpotifyApplication *spotify = self.spotifyApp;
|
|
if (spotify != nil &&
|
|
_spotifyPlaybackWasPaused &&
|
|
[spotify respondsToSelector:@selector(isRunning)] &&
|
|
[spotify respondsToSelector:@selector(playerState)] &&
|
|
[spotify isRunning] &&
|
|
[spotify playerState] == kSpotifyPlayerStatePaused) {
|
|
|
|
msg_Dbg(p_intf, "unpausing Spotify");
|
|
[spotify play];
|
|
}
|
|
}
|
|
|
|
_iTunesPlaybackWasPaused = NO;
|
|
_appleMusicPlaybackWasPaused = NO;
|
|
_spotifyPlaybackWasPaused = NO;
|
|
}
|
|
|
|
- (void)errorChanged:(enum vlc_player_error)error
|
|
{
|
|
/* instead of using vlc_player_GetError, we cache the error and provide it through a synthesized getter
|
|
* as the direct call might not reflect the actual error due the asynchronous API nature */
|
|
_error = error;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerErrorChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)newBufferingValue:(float)bufferValue
|
|
{
|
|
_bufferFill = bufferValue;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerBufferChanged
|
|
object:self
|
|
userInfo:@{VLCPlayerBufferFill : @(bufferValue)}];
|
|
}
|
|
|
|
- (void)newRateValue:(float)rateValue
|
|
{
|
|
_playbackRate = rateValue;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerRateChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setPlaybackRate:(float)playbackRate
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_ChangeRate(_p_player, playbackRate);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)incrementPlaybackRate
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_IncrementRate(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)decrementPlaybackRate
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_DecrementRate(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)capabilitiesChanged:(int)newCapabilities
|
|
{
|
|
_seekable = newCapabilities & VLC_PLAYER_CAP_SEEK ? YES : NO;
|
|
_rewindable = newCapabilities & VLC_PLAYER_CAP_REWIND ? YES : NO;
|
|
_pausable = newCapabilities & VLC_PLAYER_CAP_PAUSE ? YES : NO;
|
|
_recordable = YES;
|
|
_rateChangable = newCapabilities & VLC_PLAYER_CAP_CHANGE_RATE ? YES : NO;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerCapabilitiesChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)position:(float)position andTimeChanged:(vlc_tick_t)time
|
|
{
|
|
_position = position;
|
|
_time = time;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerTimeAndPositionChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setTimeFast:(vlc_tick_t)time
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SeekByTime(_p_player, time, VLC_PLAYER_SEEK_FAST, VLC_PLAYER_WHENCE_ABSOLUTE);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)setTimePrecise:(vlc_tick_t)time
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SeekByTime(_p_player, time, VLC_PLAYER_SEEK_PRECISE, VLC_PLAYER_WHENCE_ABSOLUTE);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)setPositionFast:(float)position
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SeekByPos(_p_player, position, VLC_PLAYER_SEEK_FAST, VLC_PLAYER_WHENCE_ABSOLUTE);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)setPositionPrecise:(float)position
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SeekByPos(_p_player, position, VLC_PLAYER_SEEK_PRECISE, VLC_PLAYER_WHENCE_ABSOLUTE);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)displayPosition
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_DisplayPosition(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)jumpWithValue:(char *)p_userDefinedJumpSize forward:(BOOL)shallJumpForward
|
|
{
|
|
int64_t interval = var_InheritInteger(getIntf(), p_userDefinedJumpSize);
|
|
if (interval <= 0)
|
|
return;
|
|
|
|
vlc_tick_t jumptime = vlc_tick_from_sec(interval);
|
|
if (!shallJumpForward)
|
|
jumptime = jumptime * -1;
|
|
|
|
vlc_player_Lock(_p_player);
|
|
/* No fask seek for jumps. Indeed, jumps can seek to the current position
|
|
* if not precise enough or if the jump value is too small. */
|
|
vlc_player_SeekByTime(_p_player,
|
|
jumptime,
|
|
VLC_PLAYER_SEEK_PRECISE,
|
|
VLC_PLAYER_WHENCE_RELATIVE);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)jumpForwardExtraShort
|
|
{
|
|
[self jumpWithValue:"extrashort-jump-size" forward:YES];
|
|
}
|
|
|
|
- (void)jumpBackwardExtraShort
|
|
{
|
|
[self jumpWithValue:"extrashort-jump-size" forward:NO];
|
|
}
|
|
|
|
- (void)jumpForwardShort
|
|
{
|
|
[self jumpWithValue:"short-jump-size" forward:YES];
|
|
}
|
|
|
|
- (void)jumpBackwardShort
|
|
{
|
|
[self jumpWithValue:"short-jump-size" forward:NO];
|
|
}
|
|
|
|
- (void)jumpForwardMedium
|
|
{
|
|
[self jumpWithValue:"medium-jump-size" forward:YES];
|
|
}
|
|
|
|
- (void)jumpBackwardMedium
|
|
{
|
|
[self jumpWithValue:"medium-jump-size" forward:NO];
|
|
}
|
|
|
|
- (void)jumpForwardLong
|
|
{
|
|
[self jumpWithValue:"long-jump-size" forward:YES];
|
|
}
|
|
|
|
- (void)jumpBackwardLong
|
|
{
|
|
[self jumpWithValue:"long-jump-size" forward:NO];
|
|
}
|
|
|
|
- (void)lengthChanged:(vlc_tick_t)length
|
|
{
|
|
_length = length;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerLengthChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)titleListChanged:(vlc_player_title_list *)p_titles
|
|
{
|
|
if (_currentTitleList) {
|
|
vlc_player_title_list_Release(_currentTitleList);
|
|
}
|
|
/* the new list was already hold earlier */
|
|
_currentTitleList = p_titles;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerTitleListChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)selectedTitleChanged:(size_t)selectedTitle
|
|
{
|
|
_selectedTitleIndex = selectedTitle;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerTitleSelectionChanged
|
|
object:self];
|
|
}
|
|
|
|
- (const struct vlc_player_title *)selectedTitle
|
|
{
|
|
if (_selectedTitleIndex >= 0 && _selectedTitleIndex < [self numberOfTitlesOfCurrentMedia]) {
|
|
return vlc_player_title_list_GetAt(_currentTitleList, _selectedTitleIndex);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
- (void)setSelectedTitleIndex:(size_t)selectedTitleIndex
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SelectTitleIdx(_p_player, selectedTitleIndex);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (const struct vlc_player_title *)titleAtIndexForCurrentMedia:(size_t)index
|
|
{
|
|
return vlc_player_title_list_GetAt(_currentTitleList, index);
|
|
}
|
|
|
|
- (size_t)numberOfTitlesOfCurrentMedia
|
|
{
|
|
if (!_currentTitleList) {
|
|
return 0;
|
|
}
|
|
return vlc_player_title_list_GetCount(_currentTitleList);
|
|
}
|
|
|
|
- (void)selectedChapterChanged:(size_t)chapterIndex
|
|
{
|
|
_selectedChapterIndex = chapterIndex;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerChapterSelectionChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setSelectedChapterIndex:(size_t)selectedChapterIndex
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SelectChapterIdx(_p_player, selectedChapterIndex);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)selectNextChapter
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SelectNextChapter(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)selectPreviousChapter
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SelectPrevChapter(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (size_t)numberOfChaptersForCurrentTitle
|
|
{
|
|
const struct vlc_player_title *p_current_title = [self selectedTitle];
|
|
if (p_current_title == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return p_current_title->chapter_count;
|
|
}
|
|
|
|
- (const struct vlc_player_chapter *)chapterAtIndexForCurrentTitle:(size_t)index
|
|
{
|
|
const struct vlc_player_title *p_current_title = [self selectedTitle];
|
|
if (p_current_title == NULL || !p_current_title->chapter_count) {
|
|
return NULL;
|
|
}
|
|
|
|
return &p_current_title->chapters[index];
|
|
}
|
|
|
|
- (void)teletextAvailibilityChanged:(BOOL)hasTeletextMenu
|
|
{
|
|
_teletextMenuAvailable = hasTeletextMenu;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerTeletextTransparencyChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)teletextEnabledChanged:(BOOL)teletextOn
|
|
{
|
|
_teletextEnabled = teletextOn;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerTeletextEnabled
|
|
object:self];
|
|
}
|
|
|
|
- (void)setTeletextEnabled:(BOOL)teletextEnabled
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetTeletextEnabled(_p_player, teletextEnabled);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)teletextPageChanged:(unsigned int)page
|
|
{
|
|
_teletextPage = page;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerTeletextPageChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setTeletextPage:(unsigned int)teletextPage
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SelectTeletextPage(_p_player, teletextPage);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)teletextTransparencyChanged:(BOOL)isTransparent
|
|
{
|
|
_teletextTransparent = isTransparent;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerTeletextTransparencyChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setTeletextTransparent:(BOOL)teletextTransparent
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetTeletextTransparency(_p_player, teletextTransparent);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)audioDelayChanged:(vlc_tick_t)audioDelay
|
|
{
|
|
_audioDelay = audioDelay;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerAudioDelayChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setAudioDelay:(vlc_tick_t)audioDelay
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetAudioDelay(_p_player, audioDelay, VLC_PLAYER_WHENCE_ABSOLUTE);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)subtitlesDelayChanged:(vlc_tick_t)subtitlesDelay
|
|
{
|
|
_subtitlesDelay = subtitlesDelay;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerSubtitlesDelayChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setSubtitlesDelay:(vlc_tick_t)subtitlesDelay
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetSubtitleDelay(_p_player, subtitlesDelay, VLC_PLAYER_WHENCE_ABSOLUTE);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)delayChanged:(vlc_tick_t)trackDelay forTrack:(vlc_es_id_t *)esID
|
|
{
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerDelayChangedForSpecificElementaryStream
|
|
object:self
|
|
userInfo:@{ VLCPlayerElementaryStreamID : [NSValue valueWithPointer:esID],
|
|
VLCTick : [NSNumber numberWithLongLong:trackDelay] }];
|
|
|
|
}
|
|
|
|
- (vlc_tick_t)delayForElementaryStreamID:(vlc_es_id_t *)esID
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_tick_t delay = vlc_player_GetEsIdDelay(_p_player, esID);
|
|
vlc_player_Unlock(_p_player);
|
|
return delay;
|
|
}
|
|
|
|
- (int)setDelay:(vlc_tick_t)delay forElementaryStreamID:(vlc_es_id_t *)esID relativeWhence:(BOOL)relative
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
int returnValue = vlc_player_SetEsIdDelay(_p_player, esID, delay, relative ? VLC_PLAYER_WHENCE_RELATIVE : VLC_PLAYER_WHENCE_ABSOLUTE);
|
|
vlc_player_Unlock(_p_player);
|
|
return returnValue;
|
|
}
|
|
|
|
- (void)subtitlesFPSChanged:(float)subtitlesFPS
|
|
{
|
|
_subtitlesFPS = subtitlesFPS;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerSubtitlesFPSChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setSubtitlesFPS:(float)subtitlesFPS
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetAssociatedSubsFPS(_p_player, subtitlesFPS);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (unsigned int)subtitleTextScalingFactor
|
|
{
|
|
unsigned int ret = 100;
|
|
vlc_player_Lock(_p_player);
|
|
ret = vlc_player_GetSubtitleTextScale(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
return ret;
|
|
}
|
|
|
|
- (void)setSubtitleTextScalingFactor:(unsigned int)subtitleTextScalingFactor
|
|
{
|
|
if (subtitleTextScalingFactor < 10)
|
|
subtitleTextScalingFactor = 10;
|
|
if (subtitleTextScalingFactor > 500)
|
|
subtitleTextScalingFactor = 500;
|
|
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetSubtitleTextScale(_p_player, subtitleTextScalingFactor);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (int)addAssociatedMediaToCurrentFromURL:(NSURL *)URL
|
|
ofCategory:(enum es_format_category_e)category
|
|
shallSelectTrack:(BOOL)selectTrack
|
|
shallDisplayOSD:(BOOL)showOSD
|
|
shallVerifyExtension:(BOOL)verifyExtension
|
|
{
|
|
int ret;
|
|
vlc_player_Lock(_p_player);
|
|
ret = vlc_player_AddAssociatedMedia(_p_player, category, [[URL absoluteString] UTF8String], selectTrack, showOSD, verifyExtension);
|
|
vlc_player_Unlock(_p_player);
|
|
return ret;
|
|
}
|
|
|
|
- (void)rendererChanged:(vlc_renderer_item_t *)newRenderer
|
|
{
|
|
_rendererItem = newRenderer;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerRendererChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setRendererItem:(vlc_renderer_item_t *)rendererItem
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetRenderer(_p_player, rendererItem);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)navigateInInteractiveContent:(enum vlc_player_nav)navigationAction
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_Navigate(_p_player, navigationAction);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)recordingChanged:(BOOL)recording
|
|
{
|
|
_enableRecording = recording;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerRecordingChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)inputStatsUpdated:(VLCInputStats *)inputStats
|
|
{
|
|
_statistics = inputStats;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerStatisticsUpdated
|
|
object:self
|
|
userInfo:@{VLCPlayerInputStats : inputStats}];
|
|
}
|
|
|
|
#pragma mark - track selection
|
|
- (void)trackSelectionChanged
|
|
{
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerTrackSelectionChanged object:self];
|
|
}
|
|
|
|
- (void)trackListChanged
|
|
{
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerTrackListChanged object:nil];
|
|
}
|
|
|
|
- (void)selectTrack:(VLCTrackMetaData *)track exclusively:(BOOL)exclusiveSelection
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
const enum es_format_category_e formatCategory = vlc_es_id_GetCat(track.esID);
|
|
vlc_player_SelectEsId(_p_player, track.esID, (formatCategory == AUDIO_ES || exclusiveSelection) ? VLC_PLAYER_SELECT_EXCLUSIVE : VLC_PLAYER_SELECT_SIMULTANEOUS);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)unselectTrack:(VLCTrackMetaData *)track
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_UnselectEsId(_p_player, track.esID);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)unselectTracksFromCategory:(enum es_format_category_e)category
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_UnselectTrackCategory(_p_player, category);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)selectPreviousTrackForCategory:(enum es_format_category_e)category
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SelectPrevTrack(_p_player, category);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)selectNextTrackForCategory:(enum es_format_category_e)category
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SelectNextTrack(_p_player, category);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (NSArray<VLCTrackMetaData *> *)tracksForCategory:(enum es_format_category_e)category
|
|
{
|
|
size_t numberOfTracks = 0;
|
|
NSMutableArray *tracks;
|
|
|
|
vlc_player_Lock(_p_player);
|
|
numberOfTracks = vlc_player_GetTrackCount(_p_player, category);
|
|
if (numberOfTracks == 0) {
|
|
vlc_player_Unlock(_p_player);
|
|
return nil;
|
|
}
|
|
|
|
tracks = [[NSMutableArray alloc] initWithCapacity:numberOfTracks];
|
|
for (size_t x = 0; x < numberOfTracks; x++) {
|
|
const struct vlc_player_track *p_track = vlc_player_GetTrackAt(_p_player, category, x);
|
|
VLCTrackMetaData *trackMetadata = [[VLCTrackMetaData alloc] initWithTrackStructure:p_track];
|
|
[tracks addObject:trackMetadata];
|
|
}
|
|
vlc_player_Unlock(_p_player);
|
|
|
|
return [tracks copy];
|
|
}
|
|
|
|
- (NSArray<VLCTrackMetaData *> *)audioTracks
|
|
{
|
|
return [self tracksForCategory:AUDIO_ES];
|
|
}
|
|
|
|
- (NSArray<VLCTrackMetaData *> *)videoTracks
|
|
{
|
|
return [self tracksForCategory:VIDEO_ES];
|
|
}
|
|
|
|
- (NSArray<VLCTrackMetaData *> *)subtitleTracks
|
|
{
|
|
return [self tracksForCategory:SPU_ES];
|
|
}
|
|
|
|
- (void)programListChanged
|
|
{
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerProgramListChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)programSelectionChanged:(int)selectedID
|
|
{
|
|
_selectedProgramID = selectedID;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerProgramSelectionChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)selectProgram:(VLCProgramMetaData *)program
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SelectProgram(_p_player, program.group_id);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (size_t)numberOfPrograms
|
|
{
|
|
size_t ret = 0;
|
|
vlc_player_Lock(_p_player);
|
|
ret = vlc_player_GetProgramCount(_p_player);
|
|
vlc_player_Unlock(_p_player);
|
|
return ret;
|
|
}
|
|
|
|
- (nullable VLCProgramMetaData *)programAtIndex:(size_t)index
|
|
{
|
|
VLCProgramMetaData *programMetaData = nil;
|
|
vlc_player_Lock(_p_player);
|
|
const struct vlc_player_program *p_program = vlc_player_GetProgramAt(_p_player, index);
|
|
if (p_program != NULL) {
|
|
programMetaData = [[VLCProgramMetaData alloc] initWithProgramStructure:p_program];
|
|
}
|
|
vlc_player_Unlock(_p_player);
|
|
return programMetaData;
|
|
}
|
|
|
|
- (nullable VLCProgramMetaData *)programForID:(int)programID
|
|
{
|
|
VLCProgramMetaData *programMetaData = nil;
|
|
vlc_player_Lock(_p_player);
|
|
const struct vlc_player_program *p_program = vlc_player_GetProgram(_p_player, programID);
|
|
if (p_program != NULL) {
|
|
programMetaData = [[VLCProgramMetaData alloc] initWithProgramStructure:p_program];
|
|
}
|
|
vlc_player_Unlock(_p_player);
|
|
return programMetaData;
|
|
}
|
|
|
|
- (void)ABLoopStateChanged:(enum vlc_player_abloop)abLoopState
|
|
{
|
|
_abLoopState = abLoopState;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerABLoopStateChanged
|
|
object:self];
|
|
}
|
|
|
|
- (int)setABLoop
|
|
{
|
|
int ret = 0;
|
|
|
|
vlc_player_Lock(_p_player);
|
|
switch (_abLoopState) {
|
|
case VLC_PLAYER_ABLOOP_A:
|
|
ret = vlc_player_SetAtoBLoop(_p_player, VLC_PLAYER_ABLOOP_B);
|
|
break;
|
|
|
|
case VLC_PLAYER_ABLOOP_B:
|
|
ret = vlc_player_SetAtoBLoop(_p_player, VLC_PLAYER_ABLOOP_NONE);
|
|
break;
|
|
|
|
default:
|
|
ret = vlc_player_SetAtoBLoop(_p_player, VLC_PLAYER_ABLOOP_A);
|
|
break;
|
|
}
|
|
vlc_player_Unlock(_p_player);
|
|
|
|
return ret;
|
|
}
|
|
|
|
- (int)disableABLoop
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
int ret = vlc_player_SetAtoBLoop(_p_player, VLC_PLAYER_ABLOOP_NONE);
|
|
vlc_player_Unlock(_p_player);
|
|
return ret;
|
|
}
|
|
|
|
- (void)setEnableRecording:(BOOL)enableRecording
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetRecordingEnabled(_p_player, enableRecording, NULL);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
- (void)toggleRecord
|
|
{
|
|
vlc_player_Lock(_p_player);
|
|
vlc_player_SetRecordingEnabled(_p_player, !_enableRecording, NULL);
|
|
vlc_player_Unlock(_p_player);
|
|
}
|
|
|
|
#pragma mark - video specific delegation
|
|
|
|
- (void)fullscreenChanged:(BOOL)isFullscreen
|
|
{
|
|
_fullscreen = isFullscreen;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerFullscreenChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setFullscreen:(BOOL)fullscreen
|
|
{
|
|
vlc_player_vout_SetFullscreen(_p_player, fullscreen);
|
|
}
|
|
|
|
- (void)toggleFullscreen
|
|
{
|
|
vlc_player_vout_SetFullscreen(_p_player, !_fullscreen);
|
|
}
|
|
|
|
- (void)wallpaperModeChanged:(BOOL)wallpaperModeValue
|
|
{
|
|
_wallpaperMode = wallpaperModeValue;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerWallpaperModeChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setWallpaperMode:(BOOL)wallpaperMode
|
|
{
|
|
vlc_player_vout_SetWallpaperModeEnabled(_p_player, wallpaperMode);
|
|
}
|
|
|
|
- (void)takeSnapshot
|
|
{
|
|
vlc_player_vout_Snapshot(_p_player);
|
|
}
|
|
|
|
- (void)voutListUpdated
|
|
{
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerListOfVideoOutputThreadsChanged
|
|
object:self];
|
|
}
|
|
|
|
- (vout_thread_t *)mainVideoOutputThread
|
|
{
|
|
return vlc_player_vout_Hold(_p_player);
|
|
}
|
|
|
|
- (vout_thread_t *)videoOutputThreadForKeyWindow
|
|
{
|
|
vout_thread_t *p_vout = nil;
|
|
|
|
id currentWindow = [NSApp keyWindow];
|
|
if ([currentWindow respondsToSelector:@selector(videoView)]) {
|
|
VLCVideoWindowCommon *videoWindow = (VLCVideoWindowCommon *)currentWindow;
|
|
VLCVoutView *videoView = videoWindow.videoViewController.voutView;
|
|
if (videoView) {
|
|
p_vout = [videoView voutThread];
|
|
}
|
|
}
|
|
|
|
if (!p_vout)
|
|
p_vout = [self mainVideoOutputThread];
|
|
|
|
return p_vout;
|
|
}
|
|
|
|
- (NSArray<NSValue *> *)allVideoOutputThreads
|
|
{
|
|
size_t numberOfVoutThreads = 0;
|
|
vout_thread_t **pp_vouts = vlc_player_vout_HoldAll(_p_player, &numberOfVoutThreads);
|
|
if (numberOfVoutThreads == 0) {
|
|
return nil;
|
|
}
|
|
|
|
NSMutableArray<NSValue *> *vouts = [NSMutableArray arrayWithCapacity:numberOfVoutThreads];
|
|
|
|
for (size_t i = 0; i < numberOfVoutThreads; ++i)
|
|
{
|
|
assert(pp_vouts[i]);
|
|
[vouts addObject:[NSValue valueWithPointer:pp_vouts[i]]];
|
|
}
|
|
|
|
free(pp_vouts);
|
|
return vouts;
|
|
}
|
|
|
|
- (void)displayOSDMessage:(NSString *)message
|
|
{
|
|
vlc_player_osd_Message(_p_player, [message UTF8String]);
|
|
}
|
|
|
|
- (void)setAspectRatioIsLocked:(BOOL)b_value
|
|
{
|
|
config_PutInt("macosx-lock-aspect-ratio", b_value);
|
|
}
|
|
|
|
- (BOOL)aspectRatioIsLocked
|
|
{
|
|
return config_GetInt("macosx-lock-aspect-ratio");
|
|
}
|
|
|
|
#pragma mark - audio specific delegation
|
|
|
|
- (void)volumeChanged:(float)volume
|
|
{
|
|
_volume = volume;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerVolumeChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setVolume:(float)volume
|
|
{
|
|
vlc_player_aout_SetVolume(_p_player, volume);
|
|
}
|
|
|
|
- (void)incrementVolume
|
|
{
|
|
vlc_player_aout_IncrementVolume(_p_player, 1, NULL);
|
|
}
|
|
|
|
- (void)decrementVolume
|
|
{
|
|
vlc_player_aout_DecrementVolume(_p_player, 1, NULL);
|
|
}
|
|
|
|
- (void)muteChanged:(BOOL)mute
|
|
{
|
|
_mute = mute;
|
|
[_defaultNotificationCenter postNotificationName:VLCPlayerMuteChanged
|
|
object:self];
|
|
}
|
|
|
|
- (void)setMute:(BOOL)mute
|
|
{
|
|
vlc_player_aout_Mute(_p_player, mute);
|
|
}
|
|
|
|
- (void)toggleMute
|
|
{
|
|
vlc_player_aout_Mute(_p_player, !_mute);
|
|
}
|
|
|
|
- (audio_output_t *)mainAudioOutput
|
|
{
|
|
return vlc_player_aout_Hold(_p_player);
|
|
}
|
|
|
|
- (int)enableAudioFilterWithName:(NSString *)name state:(BOOL)state
|
|
{
|
|
if (name == nil || name.length == 0) {
|
|
return VLC_EINVAL;
|
|
}
|
|
|
|
return vlc_player_aout_EnableFilter(_p_player, [name UTF8String], state);
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation VLCInputStats
|
|
|
|
- (instancetype)initWithStatsStructure:(const struct input_stats_t *)p_stats
|
|
{
|
|
self = [super init];
|
|
if (self && p_stats != NULL) {
|
|
_inputReadPackets = p_stats->i_read_packets;
|
|
_inputReadBytes = p_stats->i_read_bytes;
|
|
_inputBitrate = p_stats->f_input_bitrate;
|
|
|
|
_demuxReadPackets = p_stats->i_demux_read_packets;
|
|
_demuxReadBytes = p_stats->i_demux_read_bytes;
|
|
_demuxBitrate = p_stats->f_demux_bitrate;
|
|
_demuxCorrupted = p_stats->i_demux_corrupted;
|
|
_demuxDiscontinuity = p_stats->i_demux_discontinuity;
|
|
|
|
_decodedAudio = p_stats->i_decoded_audio;
|
|
_decodedVideo = p_stats->i_decoded_video;
|
|
|
|
_displayedPictures = p_stats->i_displayed_pictures;
|
|
_latePictures = p_stats->i_late_pictures;
|
|
_lostPictures = p_stats->i_lost_pictures;
|
|
|
|
_playedAudioBuffers = p_stats->i_played_abuffers;
|
|
_lostAudioBuffers = p_stats->i_lost_abuffers;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation VLCTrackMetaData
|
|
|
|
- (instancetype)initWithTrackStructure:(const struct vlc_player_track *)p_track
|
|
{
|
|
self = [super init];
|
|
if (self && p_track != NULL) {
|
|
_esID = p_track->es_id;
|
|
_name = toNSStr(p_track->name);
|
|
_selected = p_track->selected;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)description
|
|
{
|
|
return [NSString stringWithFormat:@"%@: name: %@, selected: %i", [VLCTrackMetaData className], self.name, self.selected];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation VLCProgramMetaData
|
|
|
|
- (instancetype)initWithProgramStructure:(const struct vlc_player_program *)p_program
|
|
{
|
|
self = [super init];
|
|
if (self && p_program != NULL) {
|
|
_group_id = p_program->group_id;
|
|
_name = toNSStr(p_program->name);
|
|
_selected = p_program->selected;
|
|
_scrambled = p_program->scrambled;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)description
|
|
{
|
|
return [NSString stringWithFormat:@"%@: name: %@", [VLCProgramMetaData className], self.name];
|
|
}
|
|
|
|
@end
|
|
|