Dans cette rubrique, vous trouverez une collection d'extraits de code qui peuvent vous être utiles comme référence lors du développement avec le SDK. Pour des solutions plus détaillées, consultez les exemples de lecteur iOS.
Si vous utilisez les commandes Brightcove PlayerUI, tout est prêt. La fonctionnalité AirPlay est prête à l'emploi, permettant aux utilisateurs de diffuser des vidéos sur des écrans haute définition avec Apple TV.
Si vous utilisez des contrôles personnalisés, vous pouvez suivre ces étapes :
Remplacez vos valeurs pour la clé de stratégie, l'identifiant de compte et l'identifiant vidéo. Sélectionnez une vidéo de votre compte qui contient des pistes de texte.
Configurez la vue du lecteur avec une disposition VOD standard :
// Set up our player view. Create with a standard VOD layout.
BCOVPUIPlayerView *playerView = [[BCOVPUIPlayerView alloc]
initWithPlaybackController:self.playbackController options:nil controlsView:
[BCOVPUIBasicControlView basicControlViewWithVODLayout] ];
// Set up our player view. Create with a standard VOD layout.
guard let playerView = BCOVPUIPlayerView(playbackController:
self.playbackController, options: options, controlsView:
BCOVPUIBasicControlView.withVODLayout()) else {
return
}
Le closedCaptionButton est déclaré comme a BCOVPUIButton, qui est une sous-classe de UIButton et ajoute trois méthodes supplémentaires de personnalisation. Chaque fois que vous personnalisez les contrôles BCOVPlayerUI, vous devez utiliser les API Native Player partout où elles sont disponibles. Votre code personnalisé devrait ressembler à ceci :
// Customize the CC button.
BCOVPUIButton *ccButton = playerView.controlsView.closedCaptionButton;
ccButton.titleLabel.font = [UIFont systemFontOfSize:14.];
ccButton.primaryTitle = @"CC";
[ccButton showPrimaryTitle:YES];
if let ccButton = playerView.controlsView.closedCaptionButton {
ccButton.titleLabel?.font = UIFont.systemFont(ofSize: 14)
ccButton.primaryTitle = "CC"
ccButton.showPrimaryTitle(true)}
}
Affichage du contenu FairPlay sur un écran externe
Lorsqu'un écran externe est connecté à un appareil iOS à l'aide d'un adaptateur AV et d'un câble HDMI, le comportement par défaut est de refléter l'écran iOS. L'exception à cette règle est lorsque vous utilisez une vidéo protégée par FairPlay, qu'Apple empêche de mettre en miroir (WWDC 2015, Session 502).
Pour afficher des vidéos protégées par FairPlay, définissez les propriétés AVPlayer exposées via le contrôleur de lecture Brightcove pour permettre la lecture de la vidéo FairPlay sur un écran externe. La vidéo est lue en mode plein écran. Voici un exemple de définition de ces propriétés :
Si vous utilisez le lecteur Brightcove et la classe de catalogue, les analyses vidéo seront automatiquement collectées et apparaîtront dans votre module Video Cloud Analytics. Pour des métriques supplémentaires, vous pouvez ajouter Google Analytics à votre application.
Pour intégrer Google Analytics à votre application, procédez comme suit :
Voici une façon d'utiliser Google Analytics pour suivre la lecture de vidéos à l'aide du SDK Firebase de Google :
// This snippet shows one way to track video playback
// using the Firebase SDK from Google Analytics with
// the lifecycle event playback controller delegate method.
- (void)playbackController:(id<BCOVPlaybackController>)controller playbackSession:(id<BCOVPlaybackSession>)session didReceiveLifecycleEvent:(BCOVPlaybackSessionLifecycleEvent *)lifecycleEvent
{
// Common parameters
NSString *video_name = session.video.properties[kBCOVVideoPropertyKeyName];
NSString *video_ID = session.video.properties[kBCOVVideoPropertyKeyId];
// Session is ready to play
if ([kBCOVPlaybackSessionLifecycleEventReady isEqualToString:lifecycleEvent.eventType])
{
[FIRAnalytics logEventWithName:@"bcov_video_ready"
parameters:@{
@"bcov_video_name": video_name,
@"bcov_video_id": video_ID
}];
}
// Session encountered an error
if ([kBCOVPlaybackSessionLifecycleEventError isEqualToString:lifecycleEvent.eventType])
{
NSError *error = lifecycleEvent.properties[kBCOVPlaybackSessionEventKeyError];
int error_code = error.code;
[FIRAnalytics logEventWithName:@"bcov_video_playback_error"
parameters:@{
@"bcov_video_name": video_name,
@"bcov_video_id": video_ID,
@"bcov_video_error_code": @(error_code)
}];
}
// Session has completed
if ([kBCOVPlaybackSessionLifecycleEventTerminate isEqualToString:lifecycleEvent.eventType])
{
[FIRAnalytics logEventWithName:@"bcov_video_terminate"
parameters:@{
@"bcov_video_name": video_name,
@"bcov_video_id": video_ID
}];
}
}
// This snippet shows one way to track video playback
// using the Firebase SDK from Google Analytics with
// the lifecycle event playback controller delegate method.
func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didReceive lifecycleEvent: BCOVPlaybackSessionLifecycleEvent!) {
// Common parameters
let video_name = session.video.properties[kBCOVVideoPropertyKeyName]
let video_id = session.video.properties[kBCOVVideoPropertyKeyId]
// Session is ready to play
if (lifecycleEvent.eventType == kBCOVPlaybackSessionLifecycleEventReady) {
if let video_name = video_name as? String, let video_id = video_id as? String {
Analytics.logEvent("bcov_video_ready", parameters: [
"bcov_video_name" : video_name,
"bcov_video_id" : video_id
])
}
}
// Session encountered an error
if (lifecycleEvent.eventType == kBCOVPlaybackSessionLifecycleEventError) {
if let error = lifecycleEvent.properties[kBCOVPlaybackSessionEventKeyError] as? NSError {
let error_code = error.code
if let video_name = video_name as? String, let video_id = video_id as? String {
Analytics.logEvent("bcov_video_playback_error", parameters: [
"bcov_video_name" : video_name,
"bcov_video_id" : video_id,
"bcov_video_error_code" : error_code
])
}
}
}
// Session has completed
if (lifecycleEvent.eventType == kBCOVPlaybackSessionLifecycleEventTerminate) {
if let video_name = video_name as? String, let video_id = video_id as? String {
Analytics.logEvent("bcov_video_terminate", parameters: [
"bcov_video_name" : video_name,
"bcov_video_id" : video_id
])
}
}
}
Limiter le débit
Vous ne pouvez pas contrôler quelle source (rendu) du manifeste HLS est sélectionnée par le AVPlayer , mais vous pouvez limiter le débit de lecture. Cela empêche le lecteur d'utiliser des sources (rendus) avec un débit supérieur au débit spécifié.
Met le preferredPeakBitRate à la limite souhaitée, en bits par seconde, de la consommation de bande passante du réseau pour le AVPlayerItem .
Dans certains cas, vous souhaiterez peut-être qu'une vidéo soit rejouée automatiquement. Pour ce faire, vous pouvez obtenir l'événement de cycle de vie "fin de la vidéo", rechercher au début et rejouer.
Ce code suppose que vous avez défini le délégué du playbackController sur l'objet avec cette méthode :
func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didReceive lifecycleEvent: BCOVPlaybackSessionLifecycleEvent!) {
if (lifecycleEvent.eventType == kBCOVPlaybackSessionLifecycleEventEnd) {
controller.seek(to: CMTime.zero) { (finished: Bool) in
if (finished) {
controller.play()
}
}
}
}
Gérer les vidéos dans une playlist
Une façon de gérer une liste de lecture de vidéos consiste à stocker les objets vidéo dans une table. Lorsque l'utilisateur sélectionne une vidéo dans le tableau, la ligne du tableau contiendra l'objet vidéo.
func retrievePlaylist() {
refreshControl.beginRefreshing()
let playbackServiceRequestFactory = BCOVPlaybackServiceRequestFactory(accountId: kDynamicDeliveryAccountID, policyKey: kDynamicDeliveryPolicyKey)
let playbackService = BCOVPlaybackService(requestFactory: playbackServiceRequestFactory)
playbackService?.findPlaylist(withPlaylistID: kDynamicDeliveryPlaylistRefID, parameters: nil, completion: { [weak self] (playlist: BCOVPlaylist?, jsonResponse: [AnyHashable:Any]?, error: Error?) in
guard let strongSelf = self else {
return
}
strongSelf.refreshControl.endRefreshing()
if let playlist = playlist {
strongSelf.currentVideos = playlist.videos
strongSelf.currentPlaylistTitle = playlist.properties["name"] as? String ?? ""
strongSelf.currentPlaylistDescription = playlist.properties["description"] as? String ?? ""
print("Retrieved playlist containing \(playlist.videos.count) videos")
strongSelf.usePlaylist(playlist)
} else {
print("No playlist for ID \(kDynamicDeliveryPlaylistRefID) was found.");
}
})
}
Réinitialisez les conteneurs qui stockent les informations relatives aux vidéos de la liste de lecture actuelle.
Lorsque la vue tabulaire est sélectionnée, l'index de la ligne est utilisé pour créer un nouveau videoDictionary. Ensuite, demandez au dictionnaire pour la vidéo. Si la vidéo n'est pas nulle, chargez la vidéo dans le playbackController.
// Play the video in this row when selected
- (IBAction)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
NSDictionary *videoDictionary = self.videosTableViewData[ (int)indexPath.row ];
BCOVVideo *video = videoDictionary[@"video"];
if (video != nil)
{
[self.playbackController setVideos:@[ video ]];
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let videosTableViewData = videosTableViewData,
let videoDictionary = videosTableViewData[indexPath.row] as?
[AnyHashable:Any], let video = videoDictionary["video"] as? BCOVVideo else {
return
}
playbackController.setVideos([video] as NSFastEnumeration)
}
Pour travailler avec des listes de lecture, vous pouvez stocker la liste de lecture dans un autre objet tel qu'un tableau. En fonction de l'interaction de l'utilisateur, vous pouvez parcourir les indices de l'objet et sélectionner la vidéo appropriée.
Valeurs de progression des médias
Pendant la lecture multimédia, les valeurs signalées à la méthode de délégué de progression du SDK du lecteur peuvent inclure une valeur initiale d'infini négatif et une valeur finale d'infini positif. Ces valeurs sont utilisées lors du traitement des annonces pré-roll et post-roll.
Si ces valeurs ne sont pas importantes pour vous ou interfèrent avec votre propre suivi de progression, elles peuvent être facilement ignorées avec une instruction conditionnelle comme celle-ci :
- (void)playbackController:(id<BCOVPlaybackController>)controller playbackSession:(id<BCOVPlaybackSession>)session didProgressTo:(NSTimeInterval)progress
{
if (progress < 0.0 || progress >= INFINITY)
{
return;
}
// Your code here
}
func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didProgressTo progress: TimeInterval) {
if (progress < 0.0 || progress >= Double.infinity) {
return;
}
// Your code here
}
Vous pouvez définir les sous-titres à tout moment pendant la lecture, après le Ready l'événement a été reçu. Pour ce faire, vous pouvez utiliser le BCOVPlaybackControllerDélégué.
Voici un exemple de définition de la langue des sous-titres en espagnol :
let params = [
"limit": 6,
"offset": 9
]
playbackService?.findPlaylist(withPlaylistID: playlistRefID, parameters: params, completion: { (playlist: BCOVPlaylist?, jsonResponse: [AnyHashable:Any]?, error: Error?) in
// Your code here
})
Ajout de points de repère par programme
Les clients de Video Cloud peuvent ajouter des points de repère à une vidéo à l'aide de Video Cloud Studio, comme indiqué dans le Ajout de points de repère aux vidéos document.
Vous pouvez également ajouter des points de repère à votre vidéo par programmation. Le code ci-dessous ajoute des points de repère d'intervalle trimestriel à la vidéo renvoyée par l'API de lecture :
// programmatically add cue points to a video
- (void)requestContentFromPlaybackService
{
[self.service findVideoWithVideoID:kViewControllerVideoID parameters:nil completion:^(BCOVVideo *video, NSDictionary *jsonResponse, NSError *error) {
if (video)
{
// Get the video duration from the properties dictionary
NSNumber *durationNumber = video.properties[@"duration"]; // milliseconds
float duration = durationNumber.floatValue / 1000.0; // convert to seconds
video = [video update:^(id<BCOVMutableVideo> mutableVideo)
{
// Add quarterly interval cue points of your own type
BCOVCuePoint *cp1 = [[BCOVCuePoint alloc] initWithType:@"your cue point type" position:CMTimeMake(duration * 250, 1000)];
BCOVCuePoint *cp2 = [[BCOVCuePoint alloc] initWithType:@"your cue point type" position:CMTimeMake(duration * 500, 1000)];
BCOVCuePoint *cp3 = [[BCOVCuePoint alloc] initWithType:@"your cue point type" position:CMTimeMake(duration * 750, 1000)];
BCOVCuePoint *cp4 = [[BCOVCuePoint alloc] initWithType:@"your cue point type" position:CMTimeMake(duration * 1000, 1000)];
// Create new cue point collection using existing cue points and new cue points
NSMutableArray *newCuePoints = [[NSMutableArray alloc] initWithArray:mutableVideo.cuePoints.array];
[newCuePoints addObject:cp1];
[newCuePoints addObject:cp2];
[newCuePoints addObject:cp3];
[newCuePoints addObject:cp4];
mutableVideo.cuePoints = [[BCOVCuePointCollection alloc] initWithArray:newCuePoints];
}];
[self.playbackController setVideos:@[ video ]];
}
else
{
NSLog(@"ViewController Debug - Error retrieving video: `%@`", error);
}
}];
}
// programmatically add cue points to a video
func requestContentFromPlaybackService() {
playbackService?.findVideo(withVideoID: kViewControllerVideoID, parameters: nil)
{ [weak self] (video: BCOVVideo?, jsonResponse: [AnyHashable: Any]?, error: Error?) -> Void in
if let error = error {
print("ViewController Debug - Error retrieving video: `\(error.localizedDescription)`")
}
if let video = video {
// Get the video duration from the properties dictionary
guard let durationNumber = video.properties["duration"] as? NSNumber else {
return
}
let duration = durationNumber.floatValue / 1000.0; // convert to seconds
let updatedVideo = video.update({ (mutableVideo: BCOVMutableVideo?) in
guard let mutableVideo = mutableVideo else {
return
}
// Add quarterly interval cue points of your own type
let cp1Position = CMTimeMake(value: Int64(duration * 250), timescale: 1000)
let cp1 = BCOVCuePoint(type: "your cue point type", position: cp1Position)!
let cp2Position = CMTimeMake(value: Int64(duration * 500), timescale: 1000)
let cp2 = BCOVCuePoint(type: "your cue point type", position: cp2Position)!
let cp3Position = CMTimeMake(value: Int64(duration * 750), timescale: 1000)
let cp3 = BCOVCuePoint(type: "your cue point type", position: cp3Position)!
let cp4Position = CMTimeMake(value: Int64(duration * 1000), timescale: 1000)
let cp4 = BCOVCuePoint(type: "your cue point type", position: cp4Position)!
// Create new cue point collection using existing cue points and new cue points
var newCuePoints = [BCOVCuePoint]()
newCuePoints.append(cp1)
newCuePoints.append(cp2)
newCuePoints.append(cp3)
newCuePoints.append(cp4)
mutableVideo.cuePoints = BCOVCuePointCollection(array: newCuePoints)
})
self?.playbackController.setVideos([updatedVideo] as NSFastEnumeration)
}
}
}
Notez que la valeur de your cue point type peut être n'importe quelle valeur de chaîne que vous voulez, tant que vous n'utilisez aucun des Plugins iOS. Pour plus de détails, consultez le Référence du protocole BCOVCuePoint document.
Le code ci-dessous écoute vos points de repère et affiche un message :
// listen for cue points and display them
-(void)playbackController:(id<BCOVPlaybackController>)controller playbackSession:(id<BCOVPlaybackSession>)session didPassCuePoints:(NSDictionary *)cuePointInfo
{
BCOVCuePointCollection *cpc = cuePointInfo[@"kBCOVPlaybackSessionEventKeyCuePoints"];
for (BCOVCuePoint *cp in cpc.array)
{
if ([cp.type isEqualToString:@"your cue point type"])
{
NSLog(@"Found your cue point at %f", CMTimeGetSeconds(cp.position));
}
}
}
// listen for cue points and display them
func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didPassCuePoints cuePointInfo: [AnyHashable : Any]!) {
if let cpc = cuePointInfo[kBCOVPlaybackSessionEventKeyCuePoints] as? BCOVCuePointCollection {
for cp in cpc.array() {
if let cp = cp as? BCOVCuePoint {
if (cp.type == "your cue point type") {
print("Found your cue point at \(CMTimeGetSeconds(cp.position))")
}
}
}
}
}
Il peut arriver que vous souhaitiez supprimer le lecteur et la vue.
Désallouer le contrôleur de vue qui a la propriété d'un BCOVPlaybackController libérera également le contrôleur de lecture. Pour ce faire, supprimez la vue du lecteur de sa vue et définissez le pointeur de son contrôleur de lecture sur nil.
La session audio gère le comportement audio au niveau de l'application. Vous pouvez choisir parmi plusieurs catégories et paramètres de session audio pour personnaliser le comportement audio de votre application.
Choisissez la meilleure catégorie de session audio pour votre application. Pour plus de détails, consultez la documentation d'Apple :
Pour notre échantillon de base, nous utilisons AVAudioSessionCategoryPlayback. Cela joue le son même lorsque l'écran est verrouillé et avec le commutateur Sonnerie/Silence réglé sur silencieux. Pour rester simple, nous mettons le code pour cela dans l'App Delegate.
var categoryError :NSError?
var success: Bool
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
success = true
} catch let error as NSError {
categoryError = error
success = false
}
if !success {
// Handle error
}
Mixer avec d'autres sons
Vous souhaiterez peut-être autoriser l'écoute du son d'autres applications lorsque le son de votre application est coupé. Pour ce faire, vous pouvez configurer le AVAudioSession dans le contrôleur de vue qui a accès à votre AVPlayer.
Pour plus de détails, consultez le mélangerAvecAutres option de catégorie.
- (void)setUpAudioSession
{
NSError *categoryError = nil;
BOOL success;
// If the player is muted, then allow mixing.
// Ensure other apps can have their background audio active when this app is in foreground
if (self.currentPlayer.isMuted)
{
success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&categoryError];
}
else
{
success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:0 error:&categoryError];
}
if (!success)
{
NSLog(@"AppDelegate Debug - Error setting AVAudioSession category. Because of this, there may be no sound. `%@`", categoryError);
}
}
func setUpAudioSession() {
var categoryError :NSError?
var success: Bool
do {
if let currentPlayer = currentPlayer {
// If the player is muted, then allow mixing.
// Ensure other apps can have their background audio active when this app is in foreground
if currentPlayer.isMuted {
try AVAudioSession.sharedInstance().setCategory(.playback, options: .mixWithOthers)
} else {
try AVAudioSession.sharedInstance().setCategory(.playback, options: AVAudioSession.CategoryOptions(rawValue: 0))
}
} else {
try AVAudioSession.sharedInstance().setCategory(.playback, options: AVAudioSession.CategoryOptions(rawValue: 0))
}
success = true
} catch let error as NSError {
categoryError = error
success = false
}
if !success {
print("AppDelegate Debug - Error setting AVAudioSession category. Because of this, there may be no sound. \(categoryError!)")
}
}
Réglage de la vitesse de lecture
Pour contrôler la vitesse de lecture, vous pouvez régler le rate propriété sur le AVPlayer classe exposée sur la session.
Par défaut, la vitesse de lecture ne peut être définie qu'à intervalles réguliers (0,50, 0,67, 1,0, 1,25, 1,50 et 2,0). En définissant l'algorithme AudioTimePitchAlgorithm, vous pouvez utiliser des valeurs de débit plus précises (comme 1,7). Pour plus de détails, voir ceci discussion sur le débordement de pile.
func playbackController(_ controller: BCOVPlaybackController!, playbackSession session: BCOVPlaybackSession!, didReceive lifecycleEvent: BCOVPlaybackSessionLifecycleEvent!) {
if (lifecycleEvent.eventType == kBCOVPlaybackSessionLifecycleEventReady) {
let seconds = CMTimeGetSeconds(session.player.currentTime())
print("kBCOVPlaybackSessionLifecycleEventReady at time \(seconds)")
let avPlayerItem = session.player.currentItem
avPlayerItem?.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithm.varispeed
session.player.rate = 1.7
}
}
Paramétrer le mode lunettes VR pour les vidéos 360°
Lors de la lecture d'une vidéo à 360°, les utilisateurs peuvent sélectionner le bouton Vidéo 360 sur la barre de contrôle pour passer en mode Lunettes VR. Vous pouvez également le faire par programmation, avant le début de la lecture. Vous pouvez le faire en mettant à jour le BCOVPlaybackController protocole viewProjection propriété comme suit :
Lors de la lecture d'une vidéo en mode portrait, vous remarquerez peut-être une bordure noire en haut et en dessous du lecteur. La vue du lecteur correspond à la taille de l'écran, mais la vidéo n'occupe qu'une petite partie du centre de la vue du lecteur. Les parties visibles autour de la vidéo constituent l'arrière-plan du calque du lecteur.
C'est normal AVPlayer comportement. Il réduit votre vidéo pour qu'elle s'adapte à l'intérieur du calque du lecteur, et le reste est l'arrière-plan du calque du lecteur.
Vous pouvez modifier l'arrière-plan du calque du lecteur avec le code suivant :