Brightcove обеспечивает активную поддержку последней версии iOS SDK в последней общедоступной версии следующих версий iOS:
Brightcove предоставляет пассивную поддержку для следующих версий iOS:
Core SDK локализован для арабского (ar), английского (en), французского (fr), немецкого (de), японского (ja), корейского (ko), испанского (es), упрощенного китайского (zh-Hans) и традиционного языков. Китайский (чж-хант). Чтобы получить преимущества от локализации, ваше приложение также должно быть локализовано для того же языка и локали. Дополнительную информацию см. в разделе «Пользовательская локализация».
Все компоненты SDK — ядро и платформы плагинов — выпускаются с одним и тем же номером версии. При обновлении любого отдельного компонента обновите все компоненты до одной и той же версии.
Для проектов, использующих Xcode 12 на Apple Silicon M1 и Universal Frameworks (.framework), при сборке проекта для симулятора Arm64 возвращается ошибка сборки.
*ld: building for iOS Simulator, but linking in dylib built for iOS, file for architecture arm64*
Чтобы выполнить сборку для симулятора Arm64, убедитесь, что arm64
добавлен в параметр сборки «Исключенные архитектуры» для Any iOS Simulator SDK
на вкладке «Настройки сборки» целевого приложения.
В выпуске 6.10.0 Brightcove Player SDK добавлены подспецификации для ядра и каждого плагина для поддержки XCFrameworks. Значение по умолчанию для каждой подспецификации — /XCFramework
.
Имя подспецификации | Имена подспецификаций |
---|---|
Brightcove-Player-Core | Brightcove-Player-Core/Framework Brightcove-Player-Core/XCFramework |
Brightcove-Player-DAI | Brightcove-Player-DAI/Framework Brightcove-Player-DAI/XCFramework |
Brightcove-Player-FreeWheel | Brightcove-Player-FreeWheel/Framework Brightcove-Player-FreeWheel/XCFramework |
Brightcove-Player-GoogleCast | Brightcove-Player-GoogleCast/Framework Brightcove-Player-GoogleCast/XCFramework |
Brightcove-Player-IMA | Brightcove-Player-IMA/Framework Brightcove-Player-IMA/XCFramework |
Brightcove-Player-Omniture | Brightcove-Player-Omniture/Framework Brightcove-Player-Omniture/XCFramework |
Brightcove-Player-Pulse | Brightcove-Player-Pulse/Framework Brightcove-Player-Pulse/XCFramework |
Brightcove-Player-SSAI | Brightcove-Player-SSAI/Framework Brightcove-Player-SSAI/XCFramework |
Brightcove-Player-OpenMeasurement | - |
В выпуске 6.12.0 Brightcove Player SDK обновляются подспецификации Brightcove-Player-FreeWheel
и Brightcove-Player-Omniture
для установки динамической версии BrightcovePlayerSDK
.
Имя подспецификации | Тип платформы | Зависимость |
---|---|---|
Brightcove-Player-Core | динамичный | - |
Brightcove-Player-DAI (доступен с версии 6.12.7) | динамичный | Brightcove-Player-Core |
Brightcove-Player-FreeWheel | динамичный | Brightcove-Player-Core для iOS, Brightcove-Player-Core для tvOS |
Brightcove-Player-GoogleCast | статический | Brightcove-Player-Core |
Brightcove-Player-IMA | динамичный | Brightcove-Player-Core |
Brightcove-Player-Omniture | динамичный | Brightcove-Player-Core |
Brightcove-Player-Pulse | динамичный | Brightcove-Player-Core |
Brightcove-Player-SSAI | динамичный | Brightcove-Player-Core, Brightcove-Player-OpenMeasurement (только для открытого измерения) |
Brightcove-Player-OpenMeasurement (доступно с версии 6.10.0) | динамичный | - |
Поддержка видео, защищенных FairPlay, интегрирована в базовую структуру BrightcovePlayerSDK . Обратитесь к руководству FairPlay для получения полной информации об использовании FairPlay с Brightcove Native Player SDK.
Поддержка дополнительных субтитров интегрирована в базовую структуру BrightcovePlayerSDK . Полную информацию об использовании сопроводительных субтитров с пакетом Brightcove Native Player SDK см. в руководстве по сопроводительным субтитрам.
Начиная с версии 6.0.0, Brightcove Native Player SDK позволяет загружать видео HLS, в том числе защищенные шифрованием FairPlay, для последующего воспроизведения онлайн или оффлайн. Подробную информацию можно найти в руководстве разработчика приложения:
Руководство разработчика приложений iOS по загрузке видео и автономному воспроизведению с помощью FairPlay
Brightcove Player SDK предоставляет установочные пакеты для iOS и tvOS в виде динамических библиотек, упакованных как Frameworks и XCFrameworks. Развертывание поддерживается на iOS 12.0 и более поздних версиях.
Вы можете использовать CocoaPods, чтобы добавить Brightcove Player SDK в свой проект. Вы можете найти последнюю версию модуля Brightcove-Player-Core
здесь. Подспецификация поддерживает как iOS, так и tvOS.
При использовании Brightcove CocoaPods в своем проекте добавьте source 'https://github.com/brightcove/BrightcoveSpecs.git'
в начало вашего подфайла.
имя.
source ' https://github.com/CocoaPods/Specs '
source ' https://github.com/brightcove/BrightcoveSpecs.git '
platform :ios, ' 12.0 '
use_frameworks !
target ' MyVideoPlayer ' do
pod ' Brightcove-Player-Core '
end
Framework можно установить, добавив к модулю подспецификацию /Framework
.
source ' https://github.com/CocoaPods/Specs '
source ' https://github.com/brightcove/BrightcoveSpecs.git '
platform :ios, ' 12.0 '
use_frameworks !
target ' MyVideoPlayer ' do
pod ' Brightcove-Player-Core/Framework '
end
При обновлении установки рекомендуется обновить локальную копию репозитория BrightcoveSpecs, чтобы у вас были самые последние версии подспецификаций локально, так же, как вы обновляете главный репозиторий CococaPods. Обычно, если вы запускаете pod update
в Терминале, это происходит автоматически или, альтернативно, вы можете обновить явно с помощью pod repo update
.
Чтобы добавить Brightcove Player SDK в проект вручную:
BrightcovePlayerSDK.framework
или BrightcovePlayerSDK.xcframework
в свой проект. Обязательно используйте версию, соответствующую вашей целевой системе, iOS или tvOS.BrightcovePlayerSDK.framework
/ BrightcovePlayerSDK.xcframework
bash ${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/BrightcovePlayerSDK.framework/strip-frameworks.sh
. Установите флажок «Запускать скрипт только при установке». Это позволит удалить ненужные архитектуры из сборки, что важно для отправки в App Store. Этот шаг больше не требуется при использовании XCFramework.bash ${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/BrightcovePlayerSDK.framework/strip-frameworks.sh
. Установите флажок «Запускать скрипт только при установке». Это позволит удалить ненужные архитектуры из сборки, что важно для отправки в App Store.Any iOS Simulator SDK
добавлен arm64
.Для справки, вот все компоненты SDK и соответствующие URL-адреса, которые помогут вам найти и загрузить последние версии:
Компонент | URL-адрес |
---|---|
Brightcove-Player-Core | https://github.com/brightcove/brightcove-player-sdk-ios/tags |
Brightcove-Player-DAI | https://github.com/brightcove/brightcove-player-sdk-ios-dai/tags |
Brightcove-Player-FreeWheel | https://github.com/brightcove/brightcove-player-sdk-ios-fw/tags |
Brightcove-Player-GoogleCast | https://github.com/brightcove/brightcove-player-sdk-ios-googlecast/tags |
Brightcove-Player-IMA | https://github.com/brightcove/brightcove-player-sdk-ios-ima/tags |
Brightcove-Player-Omniture | https://github.com/brightcove/brightcove-player-sdk-ios-omniture/tags |
Brightcove-Player-Pulse | https://github.com/brightcove/brightcove-player-sdk-ios-pulse/tags |
Brightcove-Player-SSAI | https://github.com/brightcove/brightcove-player-sdk-ios-ssai/tags |
Чтобы добавить Brightcove Player SDK в свой проект с помощью Swift Package Manager:
https://github.com/brightcove/brightcove-player-sdk-ios.git
Примечание. Для Swift Package Manager поддерживается только динамический XCFramework.
Brightcove Player SDK для iOS можно импортировать с помощью:
import BrightcovePlayerSDK;
Воспроизведение видео с помощью Brightcove Player SDK для iOS:
// ** Customize these values with your own account information **
let kAccountId = " ... "
let kPolicyKey = " ... "
let kVideoId = " ... "
let sdkManager = BCOVPlayerSDKManager . sharedManager ( )
let playbackController = sdkManager . createPlaybackController ( )
self . playbackController = playbackController // store this to a strong property
view . addSubview ( playbackController . view )
let playbackService = BCOVPlaybackService ( withAccountId : kAccountId ,
policyKey : kPolicyKey )
let configuration = [
BCOVPlaybackService . ConfigurationKeyAssetID : kVideoId
]
playbackService . findVideo ( withConfiguration : configuration ,
queryParameters : nil ) { ( video : BCOVVideo ? ,
jsonResponse : Any ? ,
error : Error ? ) in
if let video {
self . playbackController ? . setVideos ( [ video ] )
self . playbackController ? . play ( )
}
}
Вам необходимо предотвратить автоматическое освобождение контроллера в конце метода. Распространенный способ сделать это — сохранить ссылку на контроллер в сильной переменной экземпляра.
Начиная с версии 5.1.0, Brightcove PlayerUI полностью интегрирован в структуру Core SDK. PlayerUI предоставляет полнофункциональный набор элементов управления для воспроизведения и рекламы прямо из коробки.
Пользовательский интерфейс PlayerUI быстро настраивается, отображает элементы управления рекламой для SSAI, Pulse и FreeWheel и может быть настроен путем создания собственных макетов.
Следуйте приведенным ниже инструкциям по настройке элементов управления PlayerUI.
Создайте свойство в своем UIViewController, чтобы отслеживать BCOVPUIPlayerView. BCOVPUIPlayerView будет содержать как представление контроллера воспроизведения, так и представление элементов управления.
// PlayerUI's Player View
var playerView : BCOVPUIPlayerView ?
Создайте BCOVPUIBasicControlView, а затем BCOVPUIPlayerView. Здесь мы связываем контроллер воспроизведения (и, следовательно, все видео, которые он воспроизводит) с элементами управления.
// Create and configure Control View.
let controlView = BCOVPUIBasicControlView . withVODLayout ( )
playerView = BCOVPUIPlayerView ( playbackController : self . playbackController , options : nil )
// Add BCOVPUIPlayerView to your video view.
if let playerView {
videoView . addSubview ( playerView )
}
Вам нужно будет настроить макет для представления проигрывателя, вы можете сделать это с помощью Auto Layout или более старого подхода Springs & Struts.
Настройте вид проигрывателя так, чтобы он соответствовал видеоконтейнеру из вашего макета ( videoView
) при изменении его размера.
playerView . frame = videoView . bounds
playerView . autoresizingMask = [ . flexibleHeight , . flexibleWidth ]
Установите translatesAutoresizingMaskIntoConstraints
в BCOVPUIPlayerView значение false
.
playerView . translatesAutoresizingMaskIntoConstraints = false
Затем добавьте ограничения для макета; установка верхних, правых, левых и нижних привязок вашего BCOVPUIPlayerView равными привязкам videoView
NSLayoutConstraint . activate ( [
playerView . topAnchor . constraint ( equalTo : videoView . topAnchor ) ,
playerView . rightAnchor . constraint ( equalTo : videoView . rightAnchor ) ,
playerView . bottomAnchor . constraint ( equalTo : videoView . bottomAnchor ) ,
playerView . leftAnchor . constraint ( equalTo : videoView . leftAnchor )
] )
Класс BCOVPUIPlayerViewOptions
позволяет вам настроить некоторое поведение BCOVPlayerUI при инициализации. Вы можете настроить следующее:
jumpBackInterval
Время в секундах, в течение которого игрок будет возвращаться назад при нажатии кнопки возврата.
hideControlsInterval
Время в секундах после последнего события касания, прежде чем элементы управления будут скрыты.
hideControlsAnimationDuration
Время в секундах, необходимое для анимации элементов управления и их скрытого состояния.
showControlsAnimationDuration
Время в секундах, необходимое для того, чтобы элементы управления анимировались и стали видимыми.
learnMoreButtonBrowserStyle
Параметр, определяющий, будет ли нажатие кнопки «Подробнее» в объявлении отображать ссылку перехода во внешнем браузере (настройка по умолчанию) или во внутреннем браузере.
presentingViewController
Подкласс UIViewController, используемый для представления других контроллеров представления (например, контроллера представления выбора субтитров).
automaticControlTypeSelection
Хотите ли вы, чтобы BCOVPUIPlayerView
автоматически выбирал тип BCOVPUIBasicControlView
на основе типа носителя. Если для этого значения установлено true
свойство BCOVPUIBasicControlView
, переданное в инициализатор BCOVPUIPlayerView
будет игнорироваться.
Видео + аудио потоки
basicControlViewWithVODLayout
basicControlViewWithLiveLayout
basicControlViewWithLiveDVRLayout
Потоки только для аудио
basicControlViewWithAODLayout
basicControlViewWithLiveAudioLayout
basicControlViewWithLiveDVRAudioLayout
ПРИМЕЧАНИЕ. automaticControlTypeSelection
выбирает макеты из тех, которые предоставлены BCOVPlayerUI, поэтому пользовательские элементы управления и макеты будут перезаписаны; automaticControlTypeSelection
и настройка пользовательского интерфейса проигрывателя несовместимы.
Параметры можно установить, используя следующий метод:
let manager = BCOVPlayerSDKManager . sharedManager ( )
let playbackController = manager . createPlaybackController ( )
let options = BCOVPUIPlayerViewOptions ( )
options . jumpBackInterval = 5
let playerView = BCOVPUIPlayerView ( playbackController : playbackController ,
options : options )
Для поддержки различных типов видео предусмотрены три макета:
BCOVPUIControlLayout basicVODControlLayout
— это базовый макет для обычных видеопотоков по требованию.
BCOVPUIControlLayout basicLiveControlLayout
— это макет для живого видео.
BCOVPUIControlLayout basicLiveDVRControlLayout
— это макет для потокового видео в реальном времени с элементами управления DVR.
Обычно вы устанавливаете новый макет сразу после создания BCOVPUIPlayerView
, но вы также можете установить новый макет в любое время. Например, вы можете установить новый макет VOD следующим образом:
playerView ? . controlsView . layout = BCOVPUIControlLayout . basicVOD ( )
В дополнение к макетам по умолчанию вы можете создавать свои собственные макеты с широкими возможностями настройки, создав новый экземпляр BCOVPUIControlLayout
с собственным дизайном. Однако обратите внимание , что automaticControlTypeSelection
выбирает макеты из тех, которые предоставлены BCOVPlayerUI, поэтому пользовательские элементы управления и макеты будут перезаписаны; automaticControlTypeSelection
и настройка пользовательского интерфейса проигрывателя несовместимы.
Сначала создайте элементы управления, которые будут включены в ваш макет, используя BCOVPUIBasicControlView layoutViewWithControlFromTag:width:elasticity:
. Каждый элемент управления упакован в BCOVPUILayoutView
, который определяет расстояние между элементами управления.
Вы можете установить width
каждого вида макета по умолчанию (которая зависит от типа элемента управления) или указать свою собственную ширину.
Используйте аргумент elasticity
, чтобы определить, насколько представление макета, содержащее элемент управления, изменяет свою ширину для заполнения панели управления.
Вот примеры создания различных базовых элементов управления.
// Create various standard layout views
// Standard play/pause button
let playbackLayoutView = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . buttonPlayback ,
width : kBCOVPUILayoutUseDefaultValue ,
elasticity : 0.0 )
// Standard jump back button
let jumpBackButtonLayoutView = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . buttonJumpBack ,
width : kBCOVPUILayoutUseDefaultValue ,
elasticity : 0.0 )
// Current time indicator
let currentTimeLayoutView = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . labelCurrentTime ,
width : kBCOVPUILayoutUseDefaultValue
elasticity : 0.0 )
// Time separator - typically the '/' character
let timeSeparatorLayoutView = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . labelTimeSeparator ,
width : kBCOVPUILayoutUseDefaultValue ,
elasticity : 0.0 )
// Video duration label
let durationLayoutView = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . labelDuration ,
width : kBCOVPUILayoutUseDefaultValue ,
elasticity : 0.0 )
// Slider bar used for seeking through the video
// The elasticity is set to 1 so that it can resize to fill available space
let progressLayoutView = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . sliderProgress ,
width : kBCOVPUILayoutUseDefaultValue ,
elasticity : 1.0 )
// Closed caption button
// This button is initially hidden ('removed'), and will be shown
// if closed captions or audio tracks are available.
let closedCaptionLayoutView = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . buttonClosedCaption ,
width : kBCOVPUILayoutUseDefaultValue ,
elasticity : 0.0 )
closedCaptionLayoutView ? . isRemoved = true
// The full-screen button
let screenModeLayoutView = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . buttonScreenMode ,
width : kBCOVPUILayoutUseDefaultValue ,
elasticity : 0.0 )
// AirPlay button
// This button is initially hidden ('removed'), and will be shown
// if AirPlay devices are available.
let externalRouteLayoutView = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . viewExternalRoute ,
width : kBCOVPUILayoutUseDefaultValue ,
elasticity : 0.0 )
externalRouteLayoutView ? . isRemoved = true
// Empty view - used as a spacer
let spacerLayoutView1 = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . viewEmpty ,
width : 1.0 ,
elasticity : 1.0 )
// Empty view - used as a spacer
let spacerLayoutView2 = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . viewEmpty ,
width : 1.0 ,
elasticity : 1.0 )
// Empty view - will have a custom UIImageView added as a subview
let logoLayoutView1 = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . viewEmpty ,
width : 88.0 ,
elasticity : 1.0 )
// Empty view - will have a custom UIImageView added as a subview
let logoLayoutView2 = BCOVPUIBasicControlView . layoutViewWithControl ( from : BCOVPUIViewTag . viewEmpty ,
width : 36.0 ,
elasticity : 1.0 )
Обратите внимание, что вы также можете создать пустое представление макета, в которое вы можете поместить свое собственное представление (логотип, элемент управления, ничего и т. д.). Этот код показывает, как разместить логотип UIImage
внутри logoLayoutView1, который мы создали выше.
// Create logo image inside an image view for display in control bar.
let logoImage1 = UIImage ( named : " myLogo " )
let logoImageView1 = UIImageView ( image : logoImage1 )
logoImageView1 . autoresizingMask = [ . flexibleWidth , . flexibleHeight ]
logoImageView1 . contentMode = . scaleAspectFit
logoImageView1 . frame = logoLayoutView1 . frame
// Add image view to our empty layout view.
logoLayoutView1 . addSubview ( logoImageView1 )
Теперь, когда в представлениях макета упакованы различные элементы управления, они упорядочены в массивы, каждый из которых представляет одну строку элементов управления, то есть панель управления. Обратите внимание, что у вас могут быть разные макеты для книжной и альбомной ориентации, поэтому обычно вы настраиваете два разных массива панелей управления.
В стандартной компоновке для альбомной ориентации элементы управления располагаются в одном массиве, а затем этот массив сохраняется в другом массиве, представляющем весь набор элементов управления.
let standardLayoutLine1 = [
playbackLayoutView ,
jumpBackButtonLayoutView ,
currentTimeLayoutView ,
timeSeparatorLayoutView ,
durationLayoutView ,
progressLayoutView ,
spacerLayoutView1 ,
logoLayoutView1 ,
spacerLayoutView2 ,
closedCaptionLayoutView ,
screenModeLayoutView ,
externalRouteLayoutView
]
let standardLayoutLines = [ standardLayoutLine1 ]
В компактном макете для книжной ориентации создаются два массива элементов управления, по одному на каждую строку. Эти массивы упаковываются в другой массив, представляющий компактный макет.
Обратите внимание, что для большинства элементов управления в каждом макете используются одни и те же объекты. Когда это будет сделано и вы переключитесь между книжной и альбомной ориентацией, объект будет перемещен в новое положение с помощью плавной анимации.
let compactLayoutLine1 = [
currentTimeLayoutView ,
progressLayoutView ,
durationLayoutView
]
let compactLayoutLine2 = [
playbackLayoutView ,
jumpBackButtonLayoutView ,
spacerLayoutView1 ,
closedCaptionLayoutView ,
screenModeLayoutView ,
externalRouteLayoutView ,
logoLayoutView2
]
let compactLayoutLines = [
compactLayoutLine1 ,
compactLayoutLine2
]
Наконец, теперь, когда есть две конфигурации макета (одна для полной ширины и одна для компактной ширины), вы можете создать новый объект BCOVPUIControlLayout
и установить его в представлении управления проигрывателя.
let customLayout = BCOVPUIControlLayout . init ( standardControls : standardLayoutLines ,
compactControls : compactLayoutLines )
playerView ? . controlsView . layout = customLayout
Если у вас есть элементы управления, которые вам нужно часто отображать или скрывать, вы можете установить removed
свойство в представлении макета этого элемента управления. После изменения элементов управления вызовите setNeedsLayout
в элементе управления playerView:
logoLayoutView1 ? . isRemoved = true
playerView ? . controlsView . setNeedsLayout ( )
Вы также можете настроить несколько общих свойств BCOVPUIControlLayout
:
controlBarHeight
устанавливает размер каждой строки элементов управления.horizontalItemSpacing
устанавливает расстояние между каждым BCOVPUILayoutView
на каждой панели управления.compactLayoutMaximumWidth
определяет, какой набор элементов управления используется. Если представление элемента управления меньше compactLayoutMaximumWidth
, будет использоваться компактный набор элементов управления, в противном случае будут использоваться стандартные элементы управления. Чтобы изменить набор отображаемых элементов управления, необходимо создать и установить новый BCOVPUIControlLayout
. Новые элементы управления могут быть установлены в любое время.
Дополнительные примеры настройки PlayerUI можно посмотреть в образце кода в папке PlayerUI репозитория BrightcoveOS GitHub:
https://github.com/BrightcoveOS/ios-player-samples
Brightcove Native Player SDK включает встроенные элементы управления для воспроизведения в tvOS на Apple TV. Полную информацию об использовании встроенного пользовательского интерфейса ТВ-плеера с пакетом Brightcove Native Player SDK см. в нашем руководстве по ТВ-плееру.
Включите функциональность AirPlay, установив для свойства setAllowsExternalPlayback
вашего BCOVPlaybackController
значение true
. Кнопка AirPlay будет отображаться в элементах управления воспроизведением, если в вашей сети будут обнаружены устройства AirPlay.
В настоящее время IMA является единственным рекламным плагином, который поддерживает AirPlay и только при использовании рекламы в начале и/или конце ролика. Использование AirPlay с рекламными плагинами Pulse, SSAI или FreeWheel может привести к неожиданному поведению.
Если вы также хотите поддерживать AirPlay 2 и разрешить выбор нескольких устройств для вывода звука, вам придется сделать несколько дополнительных действий. Во-первых, вам нужно настроить AVAudioSession, чтобы вы могли установить routeSharingPolicy
. Например:
do {
try AVAudioSession . sharedInstance ( ) . setCategory ( . playback , mode : . moviePlayback , policy : . longFormVideo )
} catch {
print ( " Error setting AVAudioSession category " )
}
Вам также потребуется настроить хотя бы одну команду воспроизведения через MPRemoteCommandCenter
. По крайней мере, вам понадобится настроить и pauseCommand
, и playCommand
. Например:
let center = MPRemoteCommandCenter . shared ( )
center . pauseCommand . addTarget { _ in
playbackController . pause ( )