Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/vlc
  • chouquette/vlc
  • bakiewicz.marek122/vlc
  • devnexen/vlc
  • rohanrajpal/vlc
  • blurrrb/vlc
  • gsoc/gsoc2019/darkapex/vlc
  • b1ue/vlc
  • fkuehne/vlc
  • magsoft/vlc
  • chub/vlc
  • cramiro9/vlc
  • robUx4/vlc
  • rom1v/vlc
  • akshayaky/vlc
  • tmk907/vlc
  • akymaster/vlc
  • govind.sharma/vlc
  • psilokos/vlc
  • xjbeta/vlc
  • jahan/vlc
  • 1480c1/vlc
  • amanchande/vlc
  • aaqib/vlc
  • rist/vlc
  • apol/vlc
  • mindfreeze/vlc
  • alexandre-janniaux/vlc
  • sandsmark/vlc
  • jagannatharjun/vlc
  • gsoc/gsoc2020/matiaslgonzalez/vlc
  • gsoc/gsoc2020/jagannatharjun/vlc
  • mstorsjo/vlc
  • gsoc/gsoc2020/vedenta/vlc
  • gsoc/gsoc2020/arnav-ishaan/vlc
  • gsoc/gsoc2020/andreduong/vlc
  • fuzun/vlc
  • gsoc/gsoc2020/vatsin/vlc
  • gsoc/gsoc2020/sagid/vlc
  • yaron/vlc
  • Phoenix/vlc
  • Garf/vlc
  • ePiratWorkarounds/vlc
  • tguillem/vlc
  • jnqnfe/vlc
  • mdc/vlc
  • Vedaa/vlc
  • rasa/vlc
  • quink/vlc
  • yealo/vlc
  • aleksey_ak/vlc
  • ePirat/vlc
  • ilya.yanok/vlc
  • asenat/vlc
  • m/vlc
  • bunjee/vlc
  • BLumia/vlc
  • sagudev/vlc
  • hamedmonji30/vlc
  • nullgemm/vlc
  • DivyamAhuja/vlc
  • thesamesam/vlc
  • dag7/vlc
  • snehil101/vlc
  • haasn/vlc
  • jbk/vlc
  • ValZapod/vlc
  • mfkl/vlc
  • WangChuan/vlc
  • core1024/vlc
  • GhostVaibhav/vlc
  • dfuhrmann/vlc
  • davide.prade/vlc
  • tmatth/vlc
  • Courmisch/vlc
  • zouya/vlc
  • hpi/vlc
  • EwoutH/vlc
  • aleung27/vlc
  • hengwu0/vlc
  • saladin/vlc
  • ashuio/vlc
  • richselwood/vlc
  • verma16Ayush/vlc
  • chemicalflash/vlc
  • PoignardAzur/vlc
  • huangjieNT/vlc
  • Blake-Haydon/vlc
  • AnuthaDev/vlc
  • gsoc/gsoc2021/mpd/vlc
  • nicolas_lequec/vlc
  • sambassaly/vlc
  • thresh/vlc
  • bonniegong/vlc
  • myaashish/vlc
  • stavros.vagionitis/vlc
  • ileoo/vlc
  • louis-santucci/vlc
  • cchristiansen/vlc
  • sabyasachi07/vlc
  • AbduAmeen/vlc
  • ashishb0410/vlc
  • urbanhusky/vlc
  • davidepietrasanta/vlc
  • riksleutelstad/vlc
  • jeremyVignelles/vlc
  • komh/vlc
  • iamjithinjohn/vlc
  • JohannesKauffmann/vlc2
  • kunglao/vlc
  • natzberg/vlc
  • jill/vlc
  • cwendling/vlc
  • adufou/vlc
  • ErwanAirone/vlc
  • HasinduDilshan10/vlc
  • vagrantc/vlc
  • rafiv/macos-bigsur-icon
  • Aymeriic/vlc
  • saranshg20/vlc
  • metzlove24/vlc
  • linkfanel/vlc
  • Ds886/vlc
  • metehan-arslan/vlc
  • Skantes/vlc
  • kgsandundananjaya96/vlc
  • mitchcapper/vlc
  • advaitgupta/vlc
  • StefanBruens/vlc
  • ratajs/vlc
  • T.M.F.B.3761/vlc
  • m222059/vlc
  • casemerrick/vlc
  • joshuaword2alt/vlc
  • sjwaddy/vlc
  • dima/vlc
  • Ybalrid/vlc
  • umxprime/vlc
  • eschmidt/vlc
  • vannieuwenhuysenmichelle/vlc
  • badcf00d/vlc
  • wesinator/vlc
  • louis/vlc
  • xqq/vlc
  • EmperorYP7/vlc
  • NicoLiam/vlc
  • loveleen/vlc
  • rofferom/vlc
  • rbultje/vlc
  • TheUnamed/vlc
  • pratiksharma341/vlc
  • Saurab17/vlc
  • purist.coder/vlc
  • Shuicheng/vlc
  • mdrrubel292/vlc
  • silverbleu00/vlc
  • metif12/vlc
  • asher-m/vlc
  • jeffk/vlc
  • Brandonbr1/vlc
  • beautyyuyanli/vlc
  • rego21/vlc
  • muyangren907/vlc
  • collectionbylawrencejason/vlc
  • evelez/vlc
  • GSMgeeth/vlc
  • Oneric/vlc
  • TJ5/vlc
  • XuanTung95/vlc
  • darrenjenny21/vlc
  • Trenly/vlc
  • RockyTDR/vlc
  • mjakubowski/vlc
  • caprica/vlc
  • ForteFrankie/vlc
  • seannamiller19/vlc
  • junlon2006/vlc
  • kiwiren6666/vlc
  • iuseiphonexs/vlc
  • fenngtun/vlc
  • Rajdutt999/vlc
  • typx/vlc
  • leon.vitanos/vlc
  • robertogarci0938/vlc
  • gsoc/gsoc2022/luc65r/vlc-mpd
  • skeller/vlc
  • MCJack123/vlc
  • luc65r/vlc-mpd
  • popov895/vlc
  • claucambra/vlc
  • brad/vlc
  • matthewmurua88/vlc
  • Tomas8874/vlc
  • philenotfound/vlc
  • makita-do3/vlc
  • LZXCorp/vlc
  • mar0x/vlc
  • senojetkennedy0102/vlc
  • shaneb243/vlc
  • ahmadbader/vlc
  • rajduttcse26/vlc-audio-filters
  • Juniorzito8415/vlc
  • achernyakov/vlc
  • lucasjetgroup/vlc
  • pupdoggy666/vlc
  • gmde9363/vlc
  • alexnwayne/vlc
  • bahareebrahimi781/vlc
  • hamad633666/vlc
  • umghof3112/vlc
  • joe0199771874/vlc
  • Octocats66666666/vlc
  • jjm_223/vlc
  • btech10110.19/vlc
  • sunnykfc028/vlc-audio-filters
  • loic/vlc
  • nguyenminhducmx1/vlc
  • JanekKrueger/vlc
  • bstubbington2/vlc
  • rcombs/vlc
  • Ordissimo/vlc
  • king7532/vlc
  • noobsauce101/vlc
  • schong0525/vlc
  • myQwil/vlc
  • apisbg91/vlc
  • geeboy0101017/vlc
  • kim.faughey/vlc
  • nurupo/vlc
  • yyusea/vlc
  • 0711235879.khco/vlc
  • ialo/vlc
  • iloveyeye2/vlc
  • gdtdftdqtd/vlc
  • leandroconsiglio/vlc
  • AndyHTML2012/vlc
  • ncz/vlc
  • lucenticus/vlc
  • knr1931/vlc
  • kjoonlee/vlc
  • chandrakant100/vlc-qt
  • johge42/vlc
  • polter/vlc
  • hexchain/vlc
  • Tushwrld/vlc
  • mztea928/vlc
  • jbelloncastro/vlc
  • alvinhochun/vlc
  • ghostpiratecrow/vlc
  • ujjwaltwitx/vlc
  • alexsonarin06/vlc
  • adrianbon76/vlc
  • altsod/vlc
  • damien.lucas44/vlc
  • dmytrivtaisa/vlc
  • utk202/vlc
  • aaxhrj/vlc
  • thomas.hermes/vlc
  • structurenewworldorder/vlc
  • slomo/vlc
  • wantlamy/vlc
  • musc.o3cminc/vlc
  • thebarshablog/vlc
  • kerrick/vlc
  • kratos142518/vlc
  • leogps/vlc
  • vacantron/vlc
  • luna_koly/vlc
  • Ratio2/vlc
  • anuoshemohammad/vlc
  • apsun/vlc
  • aaa1115910/vlc
  • alimotmoyo/vlc
  • Ambossmann/vlc
  • Sam-LearnsToCode/vlc
  • Chilledheart/vlc
  • Labnann/vlc
  • ktcoooot1/vlc
  • mohit-marathe/vlc
  • johnddx/vlc
  • manstabuk/vlc
  • Omar-ahmed314/vlc
  • vineethkm/vlc
  • 9Enemi86/vlc
  • radoslav.m.panteleev/vlc
  • ashishami2002/vlc
  • Corbax/vlc
  • firnasahmed/vlc
  • pelayarmalam4/vlc
  • c0ff330k/vlc
  • shikhindahikar/vlc
  • l342723951/vlc
  • christianschwandner/vlc
  • douniwan5788/vlc
  • 7damian7/vlc
  • ferdnyc/vlc
  • f.ales1/vlc
  • pandagby/vlc
  • BaaBaa/vlc
  • jewe37/vlc
  • w00drow/vlc
  • russelltg/vlc
  • ironicallygod/vlc
  • soumyaDghosh/vlc
  • linzihao1999/vlc
  • deyayush6/vlc
  • mibi88/vlc
  • newabdallah10/vlc
  • jhorbincolombia/vlc
  • rimvihaqueshupto/vlc
  • andrewkhon98/vlc
  • fab78/vlc
  • lapaz17/vlc
  • amanna13/vlc
  • mdakram28/vlc
  • 07jw1980/vlc
  • sohamgupta/vlc
  • Eson-Jia1/vlc
  • Sumou/vlc
  • vikram-kangotra/vlc
  • chalice191/vlc
  • olivercalder/vlc
  • aaasg4001/vlc
  • zipdox/vlc
  • kwizart/vlc
  • Dragon-S/vlc
  • jdemeule/vlc
  • gabriel_lt/vlc
  • locutusofborg/vlc
  • sammirata/vlc-librist
  • another/vlc
  • Benjamin_Loison/vlc
  • ahmedmoselhi/vlc
  • petergaal/vlc
  • huynhsontung/vlc
  • dariusmihut/vlc
  • tvermaashutosh/vlc
  • buti/vlc
  • Niram7777/vlc
  • rohan-here/vlc
  • balaji-sivasakthi/vlc
  • rlindner81/vlc
  • Kakadus/vlc
  • djain/vlc
  • ABBurmeister/vlc
  • craighuggins/vlc
  • orbea/vlc
  • maxos/vlc
  • aakarshmj/vlc
  • kblaschke/vlc
  • ankitm/vlc
  • advait-0/vlc
  • mohak2003/vlc
  • yselkowitz/vlc
  • AZM999/vlc-azm
  • andrey.turkin/vlc
  • Disha-Baghel/vlc
  • nowrep/vlc
  • Apeng/vlc
  • Choucroute_melba/vlc
  • autra/vlc
  • eclipseo/vlc
  • fhuber/vlc
  • olafhering/vlc
  • sdasda7777/vlc
  • 1div0/vlc
  • skosnits/vlc-extended-playlist-support
  • dnicolson/vlc
  • Timshel/vlc
  • octopols/vlc
  • MangalK/vlc
  • nima64/vlc
  • misawai/vlc
  • Alexander-Wilms/vlc
  • Maxime2/vlc-fork-for-visualizer
  • ww/vlc
  • jeske/vlc
  • sgross-emlix/vlc
  • morenonatural/vlc
  • freakingLovesVLC/vlc
  • borisgolovnev/vlc
  • mpromonet/vlc
  • diogo.simao-marques/vlc
  • masstock/vlc
  • pratikpatel8982/vlc
  • hugok79/vlc
  • longervision/vlc
  • abhiudaysurya/vlc
  • rishabhgarg/vlc
  • tumic/vlc
  • cart/vlc
  • shubham442/vlc
  • Aditya692005/vlc
  • sammirata/vlc4
  • syrykh/vlc
  • Vvorcun/macos-new-icon
  • AyaanshC/vlc
  • nasso/vlc
  • Quark/vlc
  • sebastinas/vlc
  • rhstone/vlc
  • talregev/vlc
  • Managor/vlc
403 results
Show changes
Commits on Source (15)
......@@ -117,20 +117,35 @@ if HAVE_WIN32_DESKTOP
aout_LTLIBRARIES += libwaveout_plugin.la
endif
libauhal_plugin_la_SOURCES = audio_output/auhal.c \
audio_output/coreaudio_common.c audio_output/coreaudio_common.h
libavsamplebuffer_plugin_la_SOURCES = audio_output/apple/avsamplebuffer.m \
audio_output/apple/channel_layout.c
if HAVE_IOS
libavsamplebuffer_plugin_la_SOURCES += audio_output/apple/avaudiosession_common.m
endif
if HAVE_TVOS
libavsamplebuffer_plugin_la_SOURCES += audio_output/apple/avaudiosession_common.m
endif
libavsamplebuffer_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(aoutdir)' \
-Wl,-framework,CoreMedia,-framework,Foundation,-framework,AVFoundation
libavsamplebuffer_plugin_la_OBJCFLAGS = $(AM_OBJCFLAGS) -fobjc-arc
libauhal_plugin_la_SOURCES = audio_output/apple/auhal.c \
audio_output/apple/coreaudio_common.c audio_output/apple/coreaudio_common.h \
audio_output/apple/channel_layout.c
libauhal_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(aoutdir)' \
-Wl,-framework,CoreAudio,-framework,AudioUnit,-framework,AudioToolbox,-framework,CoreServices
if HAVE_OSX
aout_LTLIBRARIES += libauhal_plugin.la
aout_LTLIBRARIES += libauhal_plugin.la libavsamplebuffer_plugin.la
endif
libaudiounit_ios_plugin_la_SOURCES = audio_output/audiounit_ios.m \
audio_output/coreaudio_common.c audio_output/coreaudio_common.h
libaudiounit_ios_plugin_la_SOURCES = audio_output/apple/audiounit_ios.m \
audio_output/apple/coreaudio_common.c audio_output/apple/coreaudio_common.h \
audio_output/apple/channel_layout.c audio_output/apple/avaudiosession_common.m
libaudiounit_ios_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(aoutdir)' \
-Wl,-framework,Foundation,-framework,CoreAudio,-framework,AudioToolbox,-framework,UIKit,-framework,AVFoundation
if HAVE_IOS_OR_TVOS
aout_LTLIBRARIES += libaudiounit_ios_plugin.la
aout_LTLIBRARIES += libaudiounit_ios_plugin.la libavsamplebuffer_plugin.la
endif
if HAVE_XROS
aout_LTLIBRARIES += libaudiounit_ios_plugin.la
aout_LTLIBRARIES += libaudiounit_ios_plugin.la libavsamplebuffer_plugin.la
endif
......@@ -30,6 +30,7 @@
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <mach/mach_time.h>
#import "avaudiosession_common.h"
#pragma mark -
#pragma mark private declarations
......@@ -56,68 +57,6 @@ static const struct {
AU_DEV_ENCODED }, /* This can also be forced with the --spdif option */
};
#if ((__IPHONE_OS_VERSION_MAX_ALLOWED && __IPHONE_OS_VERSION_MAX_ALLOWED < 150000) || (__TV_OS_MAX_VERSION_ALLOWED && __TV_OS_MAX_VERSION_ALLOWED < 150000))
NSString *const AVAudioSessionSpatialAudioEnabledKey = @"AVAudioSessionSpatializationEnabledKey";
NSString *const AVAudioSessionSpatialPlaybackCapabilitiesChangedNotification = @"AVAudioSessionSpatialPlaybackCapabilitiesChangedNotification";
@interface AVAudioSession (iOS15RoutingConfiguration)
- (BOOL)setSupportsMultichannelContent:(BOOL)inValue error:(NSError **)outError;
@end
@interface AVAudioSessionPortDescription (iOS15RoutingConfiguration)
@property (readonly, getter=isSpatialAudioEnabled) BOOL spatialAudioEnabled;
@end
#endif
@interface SessionManager : NSObject
{
NSMutableSet *_registeredInstances;
}
+ (SessionManager *)sharedInstance;
- (void)addAoutInstance:(AoutWrapper *)wrapperInstance;
- (NSInteger)removeAoutInstance:(AoutWrapper *)wrapperInstance;
@end
@implementation SessionManager
+ (SessionManager *)sharedInstance
{
static SessionManager *sharedInstance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance = [SessionManager new];
});
return sharedInstance;
}
- (instancetype)init
{
self = [super init];
if (self) {
_registeredInstances = [[NSMutableSet alloc] init];
}
return self;
}
- (void)addAoutInstance:(AoutWrapper *)wrapperInstance
{
@synchronized(_registeredInstances) {
[_registeredInstances addObject:wrapperInstance];
}
}
- (NSInteger)removeAoutInstance:(AoutWrapper *)wrapperInstance
{
@synchronized(_registeredInstances) {
[_registeredInstances removeObject:wrapperInstance];
return _registeredInstances.count;
}
}
@end
/*****************************************************************************
* aout_sys_t: private audio output method descriptor
*****************************************************************************
......@@ -134,8 +73,6 @@ typedef struct
AudioUnit au_unit;
bool b_muted;
bool b_stopped;
bool b_preferred_channels_set;
bool b_spatial_audio_supported;
enum au_dev au_dev;
/* For debug purpose, to print when specific latency changed */
......@@ -150,14 +87,6 @@ typedef struct
/* Soft volume helper */
#include "audio_output/volume.h"
enum port_type
{
PORT_TYPE_DEFAULT,
PORT_TYPE_USB,
PORT_TYPE_HDMI,
PORT_TYPE_HEADPHONES
};
static vlc_tick_t
GetLatency(audio_output_t *p_aout)
{
......@@ -240,215 +169,8 @@ GetLatency(audio_output_t *p_aout)
}
}
- (void)handleSpatialCapabilityChange:(NSNotification *)notification
{
if (@available(iOS 15.0, tvOS 15.0, *)) {
audio_output_t *p_aout = [self aout];
struct aout_sys_t *p_sys = p_aout->sys;
NSDictionary *userInfo = notification.userInfo;
BOOL spatialAudioEnabled =
[[userInfo valueForKey:AVAudioSessionSpatialAudioEnabledKey] boolValue];
msg_Dbg(p_aout, "Spatial Audio availability changed: %i", spatialAudioEnabled);
if (spatialAudioEnabled) {
aout_RestartRequest(p_aout, false);
}
}
}
@end
static void
avas_setPreferredNumberOfChannels(audio_output_t *p_aout,
const audio_sample_format_t *fmt)
{
aout_sys_t *p_sys = p_aout->sys;
if (aout_BitsPerSample(fmt->i_format) == 0)
return; /* Don't touch the number of channels for passthrough */
AVAudioSession *instance = p_sys->avInstance;
NSInteger max_channel_count = [instance maximumOutputNumberOfChannels];
unsigned channel_count = aout_FormatNbChannels(fmt);
/* Increase the preferred number of output channels if possible */
if (channel_count > 2 && max_channel_count > 2)
{
channel_count = __MIN(channel_count, max_channel_count);
bool success = [instance setPreferredOutputNumberOfChannels:channel_count
error:nil];
if (success && [instance outputNumberOfChannels] == channel_count)
p_sys->b_preferred_channels_set = true;
else
{
/* Not critical, output channels layout will be Stereo */
msg_Warn(p_aout, "setPreferredOutputNumberOfChannels failed");
}
}
}
static void
avas_resetPreferredNumberOfChannels(audio_output_t *p_aout)
{
aout_sys_t *p_sys = p_aout->sys;
AVAudioSession *instance = p_sys->avInstance;
if (p_sys->b_preferred_channels_set)
{
[instance setPreferredOutputNumberOfChannels:2 error:nil];
p_sys->b_preferred_channels_set = false;
}
}
static int
avas_GetPortType(audio_output_t *p_aout, enum port_type *pport_type)
{
aout_sys_t * p_sys = p_aout->sys;
AVAudioSession *instance = p_sys->avInstance;
*pport_type = PORT_TYPE_DEFAULT;
long last_channel_count = 0;
for (AVAudioSessionPortDescription *out in [[instance currentRoute] outputs])
{
/* Choose the layout with the biggest number of channels or the HDMI
* one */
enum port_type port_type;
if ([out.portType isEqualToString: AVAudioSessionPortUSBAudio])
port_type = PORT_TYPE_USB;
else if ([out.portType isEqualToString: AVAudioSessionPortHDMI])
port_type = PORT_TYPE_HDMI;
else if ([out.portType isEqualToString: AVAudioSessionPortHeadphones])
port_type = PORT_TYPE_HEADPHONES;
else
port_type = PORT_TYPE_DEFAULT;
if (@available(iOS 15.0, tvOS 15.0, *)) {
p_sys->b_spatial_audio_supported = out.spatialAudioEnabled;
}
*pport_type = port_type;
if (port_type == PORT_TYPE_HDMI) /* Prefer HDMI */
break;
}
return VLC_SUCCESS;
}
struct API_AVAILABLE(ios(11.0))
role2policy
{
char role[sizeof("accessibility")];
AVAudioSessionRouteSharingPolicy policy;
};
static int API_AVAILABLE(ios(11.0))
role2policy_cmp(const void *key, const void *val)
{
const struct role2policy *entry = val;
return strcmp(key, entry->role);
}
static AVAudioSessionRouteSharingPolicy API_AVAILABLE(ios(11.0))
GetRouteSharingPolicy(audio_output_t *p_aout)
{
#if __IPHONEOS_VERSION_MAX_ALLOWED < 130000
AVAudioSessionRouteSharingPolicy AVAudioSessionRouteSharingPolicyLongFormAudio =
AVAudioSessionRouteSharingPolicyLongForm;
AVAudioSessionRouteSharingPolicy AVAudioSessionRouteSharingPolicyLongFormVideo =
AVAudioSessionRouteSharingPolicyLongForm;
#endif
/* LongFormAudio by default */
AVAudioSessionRouteSharingPolicy policy = AVAudioSessionRouteSharingPolicyLongFormAudio;
AVAudioSessionRouteSharingPolicy video_policy;
#if !TARGET_OS_TV
if (@available(iOS 13.0, *))
video_policy = AVAudioSessionRouteSharingPolicyLongFormVideo;
else
#endif
video_policy = AVAudioSessionRouteSharingPolicyLongFormAudio;
char *str = var_InheritString(p_aout, "role");
if (str != NULL)
{
const struct role2policy role_list[] =
{
{ "accessibility", AVAudioSessionRouteSharingPolicyDefault },
{ "animation", AVAudioSessionRouteSharingPolicyDefault },
{ "communication", AVAudioSessionRouteSharingPolicyDefault },
{ "game", AVAudioSessionRouteSharingPolicyLongFormAudio },
{ "music", AVAudioSessionRouteSharingPolicyLongFormAudio },
{ "notification", AVAudioSessionRouteSharingPolicyDefault },
{ "production", AVAudioSessionRouteSharingPolicyDefault },
{ "test", AVAudioSessionRouteSharingPolicyDefault },
{ "video", video_policy},
};
const struct role2policy *entry =
bsearch(str, role_list, ARRAY_SIZE(role_list),
sizeof (*role_list), role2policy_cmp);
free(str);
if (entry != NULL)
policy = entry->policy;
}
return policy;
}
static int
avas_SetActive(audio_output_t *p_aout, bool active, NSUInteger options)
{
aout_sys_t * p_sys = p_aout->sys;
AVAudioSession *instance = p_sys->avInstance;
BOOL ret = false;
NSError *error = nil;
if (active)
{
if (@available(iOS 11.0, tvOS 11.0, *))
{
AVAudioSessionRouteSharingPolicy policy = GetRouteSharingPolicy(p_aout);
ret = [instance setCategory:AVAudioSessionCategoryPlayback
mode:AVAudioSessionModeMoviePlayback
routeSharingPolicy:policy
options:0
error:&error];
}
else
{
ret = [instance setCategory:AVAudioSessionCategoryPlayback
error:&error];
ret = ret && [instance setMode:AVAudioSessionModeMoviePlayback
error:&error];
/* Not AVAudioSessionRouteSharingPolicy on older devices */
}
if (@available(iOS 15.0, tvOS 15.0, *)) {
ret = ret && [instance setSupportsMultichannelContent:p_sys->b_spatial_audio_supported error:&error];
}
ret = ret && [instance setActive:YES withOptions:options error:&error];
if (ret)
[[SessionManager sharedInstance] addAoutInstance: p_sys->aoutWrapper];
} else {
NSInteger numberOfRegisteredInstances = [[SessionManager sharedInstance] removeAoutInstance: p_sys->aoutWrapper];
if (numberOfRegisteredInstances == 0) {
ret = [instance setActive:NO withOptions:options error:&error];
} else {
ret = true;
}
}
if (!ret)
{
msg_Err(p_aout, "AVAudioSession playback change failed: %s(%d)",
error.domain.UTF8String, (int)error.code);
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
#pragma mark -
#pragma mark actual playback
......@@ -472,17 +194,17 @@ Pause (audio_output_t *p_aout, bool pause, vlc_tick_t date)
err = AudioOutputUnitStop(p_sys->au_unit);
if (err != noErr)
ca_LogErr("AudioOutputUnitStop failed");
avas_SetActive(p_aout, false, 0);
avas_SetActive(p_aout, p_sys->avInstance, false, 0);
}
else
{
if (avas_SetActive(p_aout, true, 0) == VLC_SUCCESS)
if (avas_SetActive(p_aout, p_sys->avInstance, true, 0) == VLC_SUCCESS)
{
err = AudioOutputUnitStart(p_sys->au_unit);
if (err != noErr)
{
ca_LogErr("AudioOutputUnitStart failed");
avas_SetActive(p_aout, false, 0);
avas_SetActive(p_aout, p_sys->avInstance, false, 0);
/* Do not un-pause, the Render Callback won't run, and next call
* of ca_Play will deadlock */
return;
......@@ -536,9 +258,7 @@ Stop(audio_output_t *p_aout)
if (err != noErr)
ca_LogWarn("AudioComponentInstanceDispose failed");
avas_resetPreferredNumberOfChannels(p_aout);
avas_SetActive(p_aout, false,
avas_SetActive(p_aout, p_sys->avInstance, false,
AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation);
}
......@@ -571,50 +291,26 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
selector:@selector(handleInterruption:)
name:AVAudioSessionInterruptionNotification
object:nil];
if (@available(iOS 15.0, tvOS 15.0, *)) {
[notificationCenter addObserver:p_sys->aoutWrapper
selector:@selector(handleSpatialCapabilityChange:)
name:AVAudioSessionSpatialPlaybackCapabilitiesChangedNotification
object:nil];
}
/* Activate the AVAudioSession */
if (avas_SetActive(p_aout, true, 0) != VLC_SUCCESS)
if (avas_SetActive(p_aout, p_sys->avInstance, true, 0) != VLC_SUCCESS)
{
[[NSNotificationCenter defaultCenter] removeObserver:p_sys->aoutWrapper];
return VLC_EGENERIC;
}
/* Set the preferred number of channels, then fetch the channel layout that
* should correspond to this number */
avas_setPreferredNumberOfChannels(p_aout, fmt);
BOOL success = [p_sys->avInstance setPreferredSampleRate:fmt->i_rate error:nil];
if (!success)
{
/* Not critical, we can use any sample rates */
msg_Dbg(p_aout, "failed to set preferred sample rate");
}
avas_PrepareFormat(p_aout, p_sys->avInstance, fmt, false);
enum port_type port_type;
int ret = avas_GetPortType(p_aout, &port_type);
int ret = avas_GetPortType(p_aout, p_sys->avInstance, &port_type);
if (ret != VLC_SUCCESS)
goto error;
msg_Dbg(p_aout, "Output on %s, channel count: %ld, spatialAudioEnabled %i",
msg_Dbg(p_aout, "Output on %s, channel count: %ld",
port_type == PORT_TYPE_HDMI ? "HDMI" :
port_type == PORT_TYPE_USB ? "USB" :
port_type == PORT_TYPE_HEADPHONES ? "Headphones" : "Default",
(long) [p_sys->avInstance outputNumberOfChannels],
p_sys->b_spatial_audio_supported);
if (!p_sys->b_preferred_channels_set && fmt->i_channels > 2)
{
/* Ask the core to downmix to stereo if the preferred number of
* channels can't be set. */
fmt->i_physical_channels = AOUT_CHANS_STEREO;
aout_FormatPrepare(fmt);
}
(long) [p_sys->avInstance outputNumberOfChannels]);
p_aout->current_sink_info.headphones = port_type == PORT_TYPE_HEADPHONES;
......@@ -658,8 +354,7 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
error:
if (p_sys->au_unit != NULL)
AudioComponentInstanceDispose(p_sys->au_unit);
avas_resetPreferredNumberOfChannels(p_aout);
avas_SetActive(p_aout, false,
avas_SetActive(p_aout, p_sys->avInstance, false,
AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation);
[[NSNotificationCenter defaultCenter] removeObserver:p_sys->aoutWrapper];
msg_Err(p_aout, "opening AudioUnit output failed");
......@@ -730,8 +425,6 @@ Open(vlc_object_t *obj)
}
sys->b_muted = false;
sys->b_preferred_channels_set = false;
sys->b_spatial_audio_supported = false;
sys->au_dev = var_InheritBool(aout, "spdif") ? AU_DEV_ENCODED : AU_DEV_PCM;
aout->start = Start;
aout->stop = Stop;
......@@ -752,7 +445,7 @@ Open(vlc_object_t *obj)
vlc_module_begin ()
set_shortname("audiounit_ios")
set_description("AudioUnit output for iOS")
set_capability("audio output", 101)
set_capability("audio output", 99)
set_subcategory(SUBCAT_AUDIO_AOUT)
add_sw_gain()
set_callbacks(Open, Close)
......
/*****************************************************************************
* avaudiosession_common.h: AVAudioSession common code for iOS aouts
*****************************************************************************
* Copyright (C) 2012 - 2024 VLC authors and VideoLAN
*
* Authors: 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 Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*****************************************************************************/
enum port_type
{
PORT_TYPE_DEFAULT,
PORT_TYPE_USB,
PORT_TYPE_HDMI,
PORT_TYPE_HEADPHONES
};
void
avas_PrepareFormat(audio_output_t *p_aout, AVAudioSession *instance,
audio_sample_format_t *fmt, bool spatial_audio);
int
avas_GetPortType(audio_output_t *p_aout, AVAudioSession *instance,
enum port_type *pport_type);
int
avas_SetActive(audio_output_t *p_aout, AVAudioSession *instance, bool active,
NSUInteger options);
/*****************************************************************************
* avaudiosession_common.m: AVAudioSession common code for iOS aouts
*****************************************************************************
* Copyright (C) 2012 - 2024 VLC authors and VideoLAN
*
* Authors: 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 Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 "config.h"
#import <vlc_common.h>
#import <vlc_aout.h>
#import <vlc_atomic.h>
#import <AVFoundation/AVFoundation.h>
#import "avaudiosession_common.h"
void
avas_PrepareFormat(audio_output_t *p_aout, AVAudioSession *instance,
audio_sample_format_t *fmt, bool spatial_audio)
{
if (aout_BitsPerSample(fmt->i_format) == 0)
return; /* Don't touch the number of channels for passthrough */
NSInteger max_channel_count = [instance maximumOutputNumberOfChannels];
unsigned channel_count = aout_FormatNbChannels(fmt);
/* Increase the preferred number of output channels if possible */
if (channel_count > max_channel_count)
{
msg_Warn(p_aout, "Requested channel count %u not fully supported, "
"downmixing to %ld\n", channel_count, (long)max_channel_count);
channel_count = max_channel_count;
}
NSError *error = nil;
BOOL success = [instance setPreferredOutputNumberOfChannels:channel_count
error:&error];
if (!success || [instance outputNumberOfChannels] != channel_count)
{
/* Not critical, output channels layout will be Stereo */
msg_Warn(p_aout, "setPreferredOutputNumberOfChannels failed %s(%d)",
!success ? error.domain.UTF8String : "",
!success ? (int)error.code : 0);
channel_count = 2;
}
if (spatial_audio)
{
if (@available(iOS 15.0, tvOS 15.0, *))
{
/* Not mandatory, SpatialAudio can work without it. It just signals to
* the user that he is playing spatial content */
[instance setSupportsMultichannelContent:aout_FormatNbChannels(fmt) > 2
error:nil];
}
}
else if (channel_count == 2 && aout_FormatNbChannels(fmt) > 2)
{
/* Ask the core to downmix to stereo if the preferred number of
* channels can't be set. */
fmt->i_physical_channels = AOUT_CHANS_STEREO;
aout_FormatPrepare(fmt);
}
success = [instance setPreferredSampleRate:fmt->i_rate error:&error];
if (!success)
{
/* Not critical, we can use any sample rates */
msg_Dbg(p_aout, "setPreferredSampleRate failed %s(%d)",
error.domain.UTF8String, (int)error.code);
}
}
int
avas_GetPortType(audio_output_t *p_aout, AVAudioSession *instance,
enum port_type *pport_type)
{
(void) p_aout;
*pport_type = PORT_TYPE_DEFAULT;
long last_channel_count = 0;
for (AVAudioSessionPortDescription *out in [[instance currentRoute] outputs])
{
/* Choose the layout with the biggest number of channels or the HDMI
* one */
enum port_type port_type;
if ([out.portType isEqualToString: AVAudioSessionPortUSBAudio])
port_type = PORT_TYPE_USB;
else if ([out.portType isEqualToString: AVAudioSessionPortHDMI])
port_type = PORT_TYPE_HDMI;
else if ([out.portType isEqualToString: AVAudioSessionPortHeadphones])
port_type = PORT_TYPE_HEADPHONES;
else
port_type = PORT_TYPE_DEFAULT;
*pport_type = port_type;
if (port_type == PORT_TYPE_HDMI) /* Prefer HDMI */
break;
}
return VLC_SUCCESS;
}
struct API_AVAILABLE(ios(11.0))
role2policy
{
char role[sizeof("accessibility")];
AVAudioSessionRouteSharingPolicy policy;
};
static int API_AVAILABLE(ios(11.0))
role2policy_cmp(const void *key, const void *val)
{
const struct role2policy *entry = val;
return strcmp(key, entry->role);
}
static AVAudioSessionRouteSharingPolicy API_AVAILABLE(ios(11.0))
GetRouteSharingPolicy(audio_output_t *p_aout)
{
#if __IPHONEOS_VERSION_MAX_ALLOWED < 130000
AVAudioSessionRouteSharingPolicy AVAudioSessionRouteSharingPolicyLongFormAudio =
AVAudioSessionRouteSharingPolicyLongForm;
AVAudioSessionRouteSharingPolicy AVAudioSessionRouteSharingPolicyLongFormVideo =
AVAudioSessionRouteSharingPolicyLongForm;
#endif
/* LongFormAudio by default */
AVAudioSessionRouteSharingPolicy policy = AVAudioSessionRouteSharingPolicyLongFormAudio;
AVAudioSessionRouteSharingPolicy video_policy;
#if !TARGET_OS_TV
if (@available(iOS 13.0, *))
video_policy = AVAudioSessionRouteSharingPolicyLongFormVideo;
else
#endif
video_policy = AVAudioSessionRouteSharingPolicyLongFormAudio;
char *str = var_InheritString(p_aout, "role");
if (str != NULL)
{
const struct role2policy role_list[] =
{
{ "accessibility", AVAudioSessionRouteSharingPolicyDefault },
{ "animation", AVAudioSessionRouteSharingPolicyDefault },
{ "communication", AVAudioSessionRouteSharingPolicyDefault },
{ "game", AVAudioSessionRouteSharingPolicyLongFormAudio },
{ "music", AVAudioSessionRouteSharingPolicyLongFormAudio },
{ "notification", AVAudioSessionRouteSharingPolicyDefault },
{ "production", AVAudioSessionRouteSharingPolicyDefault },
{ "test", AVAudioSessionRouteSharingPolicyDefault },
{ "video", video_policy},
};
const struct role2policy *entry =
bsearch(str, role_list, ARRAY_SIZE(role_list),
sizeof (*role_list), role2policy_cmp);
free(str);
if (entry != NULL)
policy = entry->policy;
}
return policy;
}
int
avas_SetActive(audio_output_t *p_aout, AVAudioSession *instance, bool active,
NSUInteger options)
{
static vlc_atomic_rc_t active_rc = VLC_STATIC_RC;
BOOL ret = false;
NSError *error = nil;
if (active)
{
if (@available(iOS 11.0, tvOS 11.0, *))
{
AVAudioSessionRouteSharingPolicy policy = GetRouteSharingPolicy(p_aout);
ret = [instance setCategory:AVAudioSessionCategoryPlayback
mode:AVAudioSessionModeMoviePlayback
routeSharingPolicy:policy
options:0
error:&error];
}
else
{
ret = [instance setCategory:AVAudioSessionCategoryPlayback
error:&error];
ret = ret && [instance setMode:AVAudioSessionModeMoviePlayback
error:&error];
/* Not AVAudioSessionRouteSharingPolicy on older devices */
}
ret = ret && [instance setActive:YES withOptions:options error:&error];
if (ret)
vlc_atomic_rc_inc(&active_rc);
} else {
if (vlc_atomic_rc_dec(&active_rc))
ret = [instance setActive:NO withOptions:options error:&error];
else
ret = true;
}
if (!ret)
{
msg_Err(p_aout, "AVAudioSession playback change failed: %s(%d)",
error.domain.UTF8String, (int)error.code);
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* avsamplebuffer.m: AVSampleBufferRender plugin for iOS and macOS
*****************************************************************************
* Copyright (C) 2024 VLC authors, VideoLAN and VideoLABS
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 "config.h"
#import <TargetConditionals.h>
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <vlc_common.h>
#import <vlc_plugin.h>
#import <vlc_aout.h>
#if TARGET_OS_IPHONE || TARGET_OS_TV
#define HAS_AVAUDIOSESSION
#import "avaudiosession_common.h"
#endif
#import "channel_layout.h"
// for (void)setRate:(float)rate time:(CMTime)time atHostTime:(CMTime)hostTime
#define MIN_MACOS 11.3
#define MIN_IOS 14.5
#define MIN_TVOS 14.5
#pragma mark Private
API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS))
@interface VLCAVSample : NSObject
{
audio_output_t *_aout;
CMAudioFormatDescriptionRef _fmtDesc;
AVSampleBufferAudioRenderer *_renderer;
AVSampleBufferRenderSynchronizer *_sync;
id _observer;
dispatch_queue_t _dataQueue;
dispatch_queue_t _timeQueue;
size_t _bytesPerFrame;
vlc_mutex_t _bufferLock;
vlc_cond_t _bufferWait;
block_t *_outChain;
block_t **_outChainLast;
int64_t _ptsSamples;
unsigned _sampleRate;
BOOL _stopped;
}
@end
@implementation VLCAVSample
- (id)init:(audio_output_t*)aout
{
_aout = aout;
_dataQueue = dispatch_queue_create("VLC AVSampleBuffer data queue", DISPATCH_QUEUE_SERIAL);
if (_dataQueue == nil)
return nil;
_timeQueue = dispatch_queue_create("VLC AVSampleBuffer time queue", DISPATCH_QUEUE_SERIAL);
if (_timeQueue == nil)
return nil;
vlc_mutex_init(&_bufferLock);
vlc_cond_init(&_bufferWait);
_outChain = NULL;
_outChainLast = &_outChain;
self = [super init];
if (self == nil)
return nil;
return self;
}
- (void)dealloc
{
block_ChainRelease(_outChain);
}
- (void)clearOutChain
{
block_ChainRelease(_outChain);
_outChain = NULL;
_outChainLast = &_outChain;
}
static void
customBlock_Free(void *refcon, void *doomedMemoryBlock, size_t sizeInBytes)
{
block_t *block = refcon;
assert(block->i_buffer == sizeInBytes);
block_Release(block);
(void) doomedMemoryBlock;
(void) sizeInBytes;
}
- (CMSampleBufferRef)wrapBuffer:(block_t **)pblock
{
// This function take the block ownership
block_t *block = *pblock;
*pblock = NULL;
const CMBlockBufferCustomBlockSource blockSource = {
.version = kCMBlockBufferCustomBlockSourceVersion,
.FreeBlock = customBlock_Free,
.refCon = block,
};
OSStatus status;
CMBlockBufferRef blockBuf;
status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
block->p_buffer, // memoryBlock
block->i_buffer, // blockLength
NULL, // blockAllocator
&blockSource, // customBlockSource
0, // offsetToData
block->i_buffer, // dataLength
0, // flags
&blockBuf);
if (status != noErr)
{
msg_Err(_aout, "CMBlockBufferRef creation failure %li", (long)status);
return nil;
}
const CMSampleTimingInfo timeInfo = {
.duration = kCMTimeInvalid,
.presentationTimeStamp = CMTimeMake(_ptsSamples, _sampleRate),
.decodeTimeStamp = kCMTimeInvalid,
};
CMSampleBufferRef sampleBuf;
status = CMSampleBufferCreateReady(kCFAllocatorDefault,
blockBuf, // dataBuffer
_fmtDesc, // formatDescription
block->i_nb_samples, // numSamples
1, // numSampleTimingEntries
&timeInfo, // sampleTimingArray
1, // numSampleSizeEntries
&_bytesPerFrame, // sampleSizeArray
&sampleBuf);
CFRelease(blockBuf);
if (status != noErr)
{
msg_Warn(_aout, "CMSampleBufferRef creation failure %li", (long)status);
return nil;
}
return sampleBuf;
}
- (void)selectDevice:(const char *)name
{
}
- (void)setMute:(BOOL)muted
{
_renderer.muted = muted;
aout_MuteReport(_aout, muted);
}
- (void)setVolume:(float)volume
{
_renderer.volume = volume * volume * volume;
aout_VolumeReport(_aout, volume);
}
+ (vlc_tick_t)CMTimeTotick:(CMTime) timestamp
{
CMTime scaled = CMTimeConvertScale(
timestamp, CLOCK_FREQ,
kCMTimeRoundingMethod_Default);
return scaled.value;
}
- (void)flush
{
if (_ptsSamples >= 0)
[self stopSyncRenderer];
_ptsSamples = -1;
}
- (void)pause:(BOOL)pause date:(vlc_tick_t)date
{
(void) date;
if (_ptsSamples >= 0)
_sync.rate = pause ? 0.0f : 1.0f;
}
- (void)whenTimeObserved:(CMTime) time
{
if (time.value == 0)
return;
vlc_tick_t system_now = vlc_tick_now();
vlc_tick_t pos_ticks = [VLCAVSample CMTimeTotick:time];
aout_TimingReport(_aout, system_now, pos_ticks);
}
- (void)whenDataReady
{
vlc_mutex_lock(&_bufferLock);
while (_renderer.readyForMoreMediaData)
{
while (!_stopped && _outChain == NULL)
vlc_cond_wait(&_bufferWait, &_bufferLock);
if (_stopped)
{
vlc_mutex_unlock(&_bufferLock);
return;
}
block_t *block = _outChain;
_outChain = _outChain->p_next;
if (_outChain == NULL)
_outChainLast = &_outChain;
CMSampleBufferRef buffer = [self wrapBuffer:&block];
_ptsSamples += CMSampleBufferGetNumSamples(buffer);
[_renderer enqueueSampleBuffer:buffer];
CFRelease(buffer);
}
vlc_mutex_unlock(&_bufferLock);
}
- (void)play:(block_t *)block date:(vlc_tick_t)date
{
vlc_mutex_lock(&_bufferLock);
if (_ptsSamples == -1)
{
__weak typeof(self) weakSelf = self;
[_renderer requestMediaDataWhenReadyOnQueue:_dataQueue usingBlock:^{
[weakSelf whenDataReady];
}];
const CMTime interval = CMTimeMake(CLOCK_FREQ, CLOCK_FREQ);
_observer = [_sync addPeriodicTimeObserverForInterval:interval
queue:_timeQueue
usingBlock:^ (CMTime time){
[weakSelf whenTimeObserved:time];
}];
_ptsSamples = 0;
vlc_tick_t delta = date - vlc_tick_now();
CMTime hostTime = CMTimeAdd(CMClockGetTime(CMClockGetHostTimeClock()),
CMTimeMake(delta, CLOCK_FREQ));
CMTime time = CMTimeMake(_ptsSamples, _sampleRate);
_sync.delaysRateChangeUntilHasSufficientMediaData = NO;
[_sync setRate:1.0f time:time atHostTime:hostTime];
}
block_ChainLastAppend(&_outChainLast, block);
vlc_cond_signal(&_bufferWait);
vlc_mutex_unlock(&_bufferLock);
}
- (void)stopSyncRenderer
{
_sync.rate = 0.0f;
[_sync removeTimeObserver:_observer];
[_renderer stopRequestingMediaData];
[_renderer flush];
[self clearOutChain];
}
- (void)stop
{
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
vlc_mutex_lock(&_bufferLock);
_stopped = YES;
vlc_cond_signal(&_bufferWait);
vlc_mutex_unlock(&_bufferLock);
if (_ptsSamples > 0)
[self stopSyncRenderer];
[_sync removeRenderer:_renderer atTime:kCMTimeInvalid completionHandler:nil];
#ifdef HAS_AVAUDIOSESSION
avas_SetActive(_aout, [AVAudioSession sharedInstance], false,
AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation);
#endif
CFRelease(_fmtDesc);
[notifCenter removeObserver:self];
}
- (void)flushedAutomatically:(NSNotification *)notification
{
msg_Warn(_aout, "flushedAutomatically");
aout_RestartRequest(_aout, false);
}
- (void)outputConfigurationChanged:(NSNotification *)notification
{
msg_Warn(_aout, "outputConfigurationChanged");
aout_RestartRequest(_aout, false);
}
- (BOOL)start:(audio_sample_format_t *)fmt
{
if (aout_BitsPerSample(fmt->i_format) == 0)
return NO; /* Can handle PT */
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
fmt->i_format = VLC_CODEC_FL32;
#ifdef HAS_AVAUDIOSESSION
AVAudioSession *instance = [AVAudioSession sharedInstance];
if (avas_SetActive(_aout, instance, true, 0) != VLC_SUCCESS)
return NO;
avas_PrepareFormat(_aout, instance, fmt, true);
enum port_type port_type;
if (avas_GetPortType(_aout, instance, &port_type) == VLC_SUCCESS)
{
msg_Dbg(_aout, "Output on %s, channel count: %u",
port_type == PORT_TYPE_HDMI ? "HDMI" :
port_type == PORT_TYPE_USB ? "USB" :
port_type == PORT_TYPE_HEADPHONES ? "Headphones" : "Default",
aout_FormatNbChannels(fmt));
_aout->current_sink_info.headphones = port_type == PORT_TYPE_HEADPHONES;
}
#endif
AudioChannelLayout *inlayout_buf = NULL;
size_t inlayout_size = 0;
int err = channel_layout_MapFromVLC(_aout, fmt, &inlayout_buf,
&inlayout_size);
if (err != VLC_SUCCESS)
goto error_avas;
AudioStreamBasicDescription desc = {
.mSampleRate = fmt->i_rate,
.mFormatID = kAudioFormatLinearPCM,
.mFormatFlags = kAudioFormatFlagsNativeFloatPacked,
.mChannelsPerFrame = aout_FormatNbChannels(fmt),
.mFramesPerPacket = 1,
.mBitsPerChannel = 32,
};
desc.mBytesPerFrame = desc.mBitsPerChannel * desc.mChannelsPerFrame / 8;
desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket;
OSStatus status =
CMAudioFormatDescriptionCreate(kCFAllocatorDefault,
&desc,
inlayout_size,
inlayout_buf,
0,
nil,
nil,
&_fmtDesc);
free(inlayout_buf);
if (status != noErr)
{
msg_Warn(_aout, "CMAudioFormatDescriptionRef creation failure %li", (long)status);
goto error_avas;
}
_renderer = [[AVSampleBufferAudioRenderer alloc] init];
if (_renderer == nil)
goto error;
_sync = [[AVSampleBufferRenderSynchronizer alloc] init];
if (_sync == nil)
{
_renderer = nil;
goto error;
}
[_sync addRenderer:_renderer];
_stopped = NO;
_ptsSamples = -1;
_sampleRate = fmt->i_rate;
_bytesPerFrame = desc.mBytesPerFrame;
[notifCenter addObserver:self
selector:@selector(flushedAutomatically:)
name:AVSampleBufferAudioRendererWasFlushedAutomaticallyNotification
object:nil];
if (@available(macOS 12.0, iOS 15.0, tvOS 15.0, *))
{
[notifCenter addObserver:self
selector:@selector(outputConfigurationChanged:)
name:AVSampleBufferAudioRendererOutputConfigurationDidChangeNotification
object:nil];
}
return YES;
error:
CFRelease(_fmtDesc);
error_avas:
#ifdef HAS_AVAUDIOSESSION
avas_SetActive(_aout, instance, false,
AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation);
#endif
return NO;
}
@end
static int API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS))
DeviceSelect(audio_output_t *aout, const char *name)
{
VLCAVSample *sys = (__bridge VLCAVSample*)aout->sys;
[sys selectDevice:name];
return VLC_SUCCESS;
}
static int API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS))
MuteSet(audio_output_t *aout, bool mute)
{
VLCAVSample *sys = (__bridge VLCAVSample*)aout->sys;
[sys setMute:mute];
return VLC_SUCCESS;
}
static int API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS))
VolumeSet(audio_output_t *aout, float volume)
{
VLCAVSample *sys = (__bridge VLCAVSample*)aout->sys;
[sys setVolume:volume];
return VLC_SUCCESS;
}
static void API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS))
Flush(audio_output_t *aout)
{
VLCAVSample *sys = (__bridge VLCAVSample*)aout->sys;
[sys flush];
}
static void API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS))
Pause(audio_output_t *aout, bool pause, vlc_tick_t date)
{
VLCAVSample *sys = (__bridge VLCAVSample*)aout->sys;
[sys pause:pause date:date];
}
static void API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS))
Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
{
VLCAVSample *sys = (__bridge VLCAVSample*)aout->sys;
[sys play:block date:date];
}
static void API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS))
Stop(audio_output_t *aout)
{
VLCAVSample *sys = (__bridge VLCAVSample*)aout->sys;
[sys stop];
}
static int API_AVAILABLE(macos(MIN_MACOS), ios(MIN_IOS), tvos(MIN_TVOS))
Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
{
VLCAVSample *sys = (__bridge VLCAVSample*)aout->sys;
return [sys start:fmt] ? VLC_SUCCESS : VLC_EGENERIC;
}
static void
Close(vlc_object_t *obj)
{
if (@available(macOS MIN_MACOS, iOS MIN_IOS, tvOS MIN_TVOS, *))
{
audio_output_t *aout = (audio_output_t *)obj;
/* Transfer ownership back from VLC to ARC so that it can be released. */
VLCAVSample *sys = (__bridge_transfer VLCAVSample*)aout->sys;
(void) sys;
}
}
static int
Open(vlc_object_t *obj)
{
audio_output_t *aout = (audio_output_t *)obj;
if (@available(macOS MIN_MACOS, iOS MIN_IOS, tvOS MIN_TVOS, *))
{
aout->sys = (__bridge_retained void*) [[VLCAVSample alloc] init:aout];
if (aout->sys == nil)
return VLC_EGENERIC;
aout->start = Start;
aout->stop = Stop;
aout->play = Play;
aout->pause = Pause;
aout->flush = Flush;
aout->volume_set = VolumeSet;
aout->mute_set = MuteSet;
aout->device_select = DeviceSelect;
return VLC_SUCCESS;
}
return VLC_EGENERIC;
}
vlc_module_begin ()
set_shortname("avsample")
set_description(N_("AVSampleBufferAudioRenderer output"))
set_capability("audio output", 100)
set_subcategory(SUBCAT_AUDIO_AOUT)
set_callbacks(Open, Close)
vlc_module_end ()
/*****************************************************************************
* channel_layout.c: Common Channel Layout code for iOS and macOS
*****************************************************************************
* Copyright (C) 2024 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_aout.h>
#include "channel_layout.h"
#include <stdckdint.h>
#include <CoreAudio/CoreAudioTypes.h>
static AudioChannelLabel
VlcChanToAudioChannelLabel(unsigned chan, bool swap_rear_surround)
{
switch (chan)
{
case AOUT_CHAN_LEFT:
return kAudioChannelLabel_Left;
case AOUT_CHAN_RIGHT:
return kAudioChannelLabel_Right;
case AOUT_CHAN_CENTER:
return kAudioChannelLabel_Center;
case AOUT_CHAN_LFE:
return kAudioChannelLabel_LFEScreen;
case AOUT_CHAN_REARLEFT:
return swap_rear_surround ? kAudioChannelLabel_RearSurroundLeft
: kAudioChannelLabel_LeftSurround;
case AOUT_CHAN_REARRIGHT:
return swap_rear_surround ? kAudioChannelLabel_RearSurroundRight
: kAudioChannelLabel_RightSurround;
case AOUT_CHAN_MIDDLELEFT:
return swap_rear_surround ? kAudioChannelLabel_LeftSurround
: kAudioChannelLabel_RearSurroundLeft;
case AOUT_CHAN_MIDDLERIGHT:
return swap_rear_surround ? kAudioChannelLabel_RightSurround
: kAudioChannelLabel_RearSurroundRight;
case AOUT_CHAN_REARCENTER:
return kAudioChannelLabel_CenterSurround;
default:
vlc_assert_unreachable();
}
}
int
channel_layout_MapFromVLC(audio_output_t *p_aout, const audio_sample_format_t *fmt,
AudioChannelLayout **inlayoutp, size_t *inlayout_size)
{
unsigned channels = aout_FormatNbChannels(fmt);
size_t size;
if (ckd_mul(&size, channels, sizeof(AudioChannelDescription)) ||
ckd_add(&size, size, sizeof(AudioChannelLayout)))
return VLC_ENOMEM;
AudioChannelLayout *inlayout = malloc(size);
if (inlayout == NULL)
return VLC_ENOMEM;
*inlayoutp = inlayout;
*inlayout_size = size;
inlayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
inlayout->mNumberChannelDescriptions = aout_FormatNbChannels(fmt);
bool swap_rear_surround = (fmt->i_physical_channels & AOUT_CHANS_7_0) == AOUT_CHANS_7_0;
if (swap_rear_surround)
msg_Dbg(p_aout, "swapping Surround and RearSurround channels "
"for 7.1 Rear Surround");
unsigned chan_idx = 0;
for (unsigned i = 0; i < AOUT_CHAN_MAX; ++i)
{
unsigned vlcchan = pi_vlc_chan_order_wg4[i];
if ((vlcchan & fmt->i_physical_channels) == 0)
continue;
inlayout->mChannelDescriptions[chan_idx].mChannelLabel =
VlcChanToAudioChannelLabel(vlcchan, swap_rear_surround);
inlayout->mChannelDescriptions[chan_idx].mChannelFlags =
kAudioChannelFlags_AllOff;
chan_idx++;
}
msg_Dbg(p_aout, "VLC keeping the same input layout");
return VLC_SUCCESS;
}
/*****************************************************************************
* channel_layout.h: Common Channel Layout code for iOS and macOS
*****************************************************************************
* Copyright (C) 2024 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*****************************************************************************/
typedef struct AudioChannelLayout AudioChannelLayout;
int
channel_layout_MapFromVLC(audio_output_t *p_aout, const audio_sample_format_t *fmt,
AudioChannelLayout **inlayoutp, size_t *inlayout_size);
......@@ -22,8 +22,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include <stdckdint.h>
#include "coreaudio_common.h"
#include "channel_layout.h"
#include <CoreAudio/CoreAudioTypes.h>
#define TIMING_REPORT_DELAY_TICKS VLC_TICK_FROM_MS(1000)
......@@ -554,39 +554,6 @@ AudioChannelLabelToVlcChan(AudioChannelLabel chan, bool swap_rear_surround)
}
}
static AudioChannelLabel
VlcChanToAudioChannelLabel(unsigned chan, bool swap_rear_surround)
{
/* maps auhal channels to vlc ones */
switch (chan)
{
case AOUT_CHAN_LEFT:
return kAudioChannelLabel_Left;
case AOUT_CHAN_RIGHT:
return kAudioChannelLabel_Right;
case AOUT_CHAN_CENTER:
return kAudioChannelLabel_Center;
case AOUT_CHAN_LFE:
return kAudioChannelLabel_LFEScreen;
case AOUT_CHAN_REARLEFT:
return swap_rear_surround ? kAudioChannelLabel_RearSurroundLeft
: kAudioChannelLabel_LeftSurround;
case AOUT_CHAN_REARRIGHT:
return swap_rear_surround ? kAudioChannelLabel_RearSurroundRight
: kAudioChannelLabel_RightSurround;
case AOUT_CHAN_MIDDLELEFT:
return swap_rear_surround ? kAudioChannelLabel_LeftSurround
: kAudioChannelLabel_RearSurroundLeft;
case AOUT_CHAN_MIDDLERIGHT:
return swap_rear_surround ? kAudioChannelLabel_RightSurround
: kAudioChannelLabel_RearSurroundRight;
case AOUT_CHAN_REARCENTER:
return kAudioChannelLabel_CenterSurround;
default:
vlc_assert_unreachable();
}
}
static int
MapOutputLayout(audio_output_t *p_aout, audio_sample_format_t *fmt,
const AudioChannelLayout *outlayout, bool *warn_configuration)
......@@ -693,48 +660,6 @@ MapOutputLayout(audio_output_t *p_aout, audio_sample_format_t *fmt,
return VLC_SUCCESS;
}
static int
MapInputLayout(audio_output_t *p_aout, const audio_sample_format_t *fmt,
AudioChannelLayout **inlayoutp, size_t *inlayout_size)
{
unsigned channels = aout_FormatNbChannels(fmt);
size_t size;
if (ckd_mul(&size, channels, sizeof(AudioChannelDescription)) ||
ckd_add(&size, size, sizeof(AudioChannelLayout)))
return VLC_ENOMEM;
AudioChannelLayout *inlayout = malloc(size);
if (inlayout == NULL)
return VLC_ENOMEM;
*inlayoutp = inlayout;
*inlayout_size = size;
inlayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
inlayout->mNumberChannelDescriptions = aout_FormatNbChannels(fmt);
bool swap_rear_surround = (fmt->i_physical_channels & AOUT_CHANS_7_0) == AOUT_CHANS_7_0;
if (swap_rear_surround)
msg_Dbg(p_aout, "swapping Surround and RearSurround channels "
"for 7.1 Rear Surround");
unsigned chan_idx = 0;
for (unsigned i = 0; i < AOUT_CHAN_MAX; ++i)
{
unsigned vlcchan = pi_vlc_chan_order_wg4[i];
if ((vlcchan & fmt->i_physical_channels) == 0)
continue;
inlayout->mChannelDescriptions[chan_idx].mChannelLabel =
VlcChanToAudioChannelLabel(vlcchan, swap_rear_surround);
inlayout->mChannelDescriptions[chan_idx].mChannelFlags =
kAudioChannelFlags_AllOff;
chan_idx++;
}
msg_Dbg(p_aout, "VLC keeping the same input layout");
return VLC_SUCCESS;
}
int
au_Initialize(audio_output_t *p_aout, AudioUnit au, audio_sample_format_t *fmt,
const AudioChannelLayout *outlayout, vlc_tick_t i_dev_latency_ticks,
......@@ -764,7 +689,8 @@ au_Initialize(audio_output_t *p_aout, AudioUnit au, audio_sample_format_t *fmt,
else
{
aout_FormatPrepare(fmt);
ret = MapInputLayout(p_aout, fmt, &inlayout_buf, &inlayout_size);
ret = channel_layout_MapFromVLC(p_aout, fmt, &inlayout_buf,
&inlayout_size);
if (ret != VLC_SUCCESS)
return ret;
inlayout = inlayout_buf;
......
......@@ -50,7 +50,8 @@ endif
if have_osx
vlc_modules += {
'name' : 'auhal',
'sources' : files('auhal.c', 'coreaudio_common.c'),
'sources' : files('apple/auhal.c', 'apple/coreaudio_common.c',
'apple/channel_layout.c'),
'dependencies' : [
frameworks['CoreFoundation'],
frameworks['AudioUnit'],
......@@ -64,7 +65,8 @@ endif
if have_ios or have_tvos
vlc_modules += {
'name' : 'audiounit_ios',
'sources' : files('audiounit_ios.m', 'coreaudio_common.c'),
'sources' : files('apple/audiounit_ios.m', 'apple/coreaudio_common.c',
'apple/channel_layout.c', 'apple/avaudiosession_common.m'),
'dependencies' : [
frameworks['Foundation'],
frameworks['AVFoundation'],
......@@ -73,3 +75,26 @@ if have_ios or have_tvos
],
}
endif
if have_osx or have_ios or have_tvos
avsamplebuffer_sources = files(
'apple/avsamplebuffer.m',
'apple/channel_layout.c',
)
if have_ios or have_tvos
avsamplebuffer_sources += files(
'apple/avaudiosession_common.m',
)
endif
vlc_modules += {
'name' : 'avsamplebuffer',
'sources' : avsamplebuffer_sources,
'dependencies' : [
frameworks['CoreMedia'],
frameworks['Foundation'],
frameworks['AVFoundation'],
]
'objc_args' : ['-fobjc-arc']
}
endif
......@@ -250,9 +250,10 @@ modules/audio_output/alsa.c
modules/audio_output/amem.c
modules/audio_output/android/device.c
modules/audio_output/android/opensles.c
modules/audio_output/audiounit_ios.m
modules/audio_output/auhal.c
modules/audio_output/coreaudio_common.c
modules/audio_output/apple/audiounit_ios.m
modules/audio_output/apple/avsamplebuffer.m
modules/audio_output/apple/auhal.c
modules/audio_output/apple/coreaudio_common.c
modules/audio_output/file.c
modules/audio_output/jack.c
modules/audio_output/kai.c
......