Fragmentos de código usando el SDK nativo para iOS
En este tema, encontrará una colección de fragmentos de código que puede resultarle útil como referencia mientras desarrolla con el SDK. Para obtener soluciones más detalladas, consulte las muestras del reproductor de iOS .
Si usa el Controles de Brightcove PlayerUI , estas listo. La funcionalidad AirPlay funciona de inmediato, lo que permite a los usuarios transmitir video a pantallas de alta definición con Apple TV.
Si está utilizando controles personalizados, puede seguir estos pasos:
Reemplace sus valores por la clave de política, la identificación de la cuenta y la identificación del video. Seleccione un video de su cuenta que tenga pistas de texto.
Configure la vista del reproductor con un diseño VOD estándar:
// 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
}
La closedCaptionButton se declara como un BCOVPUIButton , que es una subclase de UIButton y agrega tres métodos adicionales de personalización. Siempre que personalice los controles de BCOVPlayerUI, debe utilizar las API de Native Player siempre que estén disponibles. Su código personalizado debería verse similar a esto:
// 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)}
}
Visualización de contenido FairPlay en una pantalla externa
Cuando se conecta una pantalla externa a un dispositivo iOS mediante un adaptador AV y un cable HDMI, el comportamiento predeterminado es reflejar la pantalla de iOS. La excepción a esto es cuando usa video protegido con FairPlay, que Apple evita que se refleje (WWDC 2015, Sesión 502).
Para mostrar videos protegidos con FairPlay, configure las propiedades de AVPlayer expuestas a través del controlador de reproducción Brightcove para permitir que el video FairPlay se reproduzca en una pantalla externa. El video se reproduce en modo de pantalla completa. A continuación, se muestra un ejemplo de cómo configurar estas propiedades:
Si utiliza el reproductor Brightcove y la clase de catálogo, los análisis de video se recopilarán automáticamente y aparecerán en su módulo de análisis de Video Cloud. Para obtener métricas adicionales, puede agregar Google Analytics a su aplicación.
Para integrar Google Analytics con su aplicación, siga estos pasos:
Aquí hay una forma en que puede usar Google Analytics para rastrear la reproducción de video usando el SDK de 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
])
}
}
}
Limitando la tasa de bits
No puede controlar qué fuente (interpretación) en el manifiesto de HLS es seleccionada por el AVPlayer , pero puede poner un límite a la tasa de bits en la reproducción. Esto evita que el reproductor utilice fuentes (representaciones) con una tasa de bits superior a la tasa de bits especificada.
Selecciona el preferredPeakBitRate al límite deseado, en bits por segundo, del consumo de ancho de banda de la red para el AVPlayerItem .
En algunos casos, es posible que desee que un video se reproduzca automáticamente. Para hacer esto, puede obtener el evento del ciclo de vida "fin del video", buscar el principio y volver a reproducir.
Este código asume que ha configurado el delegado del control de reproducción en el objeto con este método:
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()
}
}
}
}
Administrar videos en una lista de reproducción
Una forma de administrar una lista de reproducción de videos es almacenar los objetos de video en una tabla. Cuando el usuario selecciona un video de la tabla, la fila de la tabla contendrá el objeto de video.
Aquí hay una descripción general de cómo funciona:
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.");
}
})
}
Reinicialice los contenedores que almacenan información relacionada con los videos en la lista de reproducción actual.
Cuando se selecciona la vista de tabla, el índice de la fila se usa para crear una nueva videoDictionary. A continuación, solicite el video al diccionario. Si el video no es nulo, cargue el video en el 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)
}
Para trabajar con listas de reproducción, puede almacenar la lista de reproducción en otro objeto, como una mesa. Según la interacción del usuario, puede navegar por los índices del objeto y seleccionar el video apropiado.
Valores de progreso de los medios
Durante la reproducción de medios, los valores informados al método de delegado de progreso del Player SDK pueden incluir un valor inicial de infinito negativo y un valor final de infinito positivo. Estos valores se utilizan al procesar anuncios pre-roll y post-roll.
Si estos valores no son importantes para usted o interfieren con su propio seguimiento de progreso, pueden ignorarse fácilmente con una declaración condicional como esta:
- (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
}
Puede configurar los subtítulos en cualquier momento durante la reproducción, después de Ready Se ha recibido el evento. Para hacer esto, puede usar el BCOVPlaybackControllerDelegate.
A continuación, se muestra un ejemplo de cómo configurar el idioma de los subtítulos en español:
let params = [
"limit": 6,
"offset": 9
]
playbackService?.findPlaylist(withPlaylistID: playlistRefID, parameters: params, completion: { (playlist: BCOVPlaylist?, jsonResponse: [AnyHashable:Any]?, error: Error?) in
// Your code here
})
Agregar puntos de referencia mediante programación
Los clientes de Video Cloud pueden agregar puntos de referencia a un video usando Video Cloud Studio como se muestra en la Agregar puntos de referencia a videos documento.
También puede agregar puntos de referencia a su video mediante programación. El siguiente código agrega puntos de referencia de intervalo trimestral al video devuelto desde la API de reproducción:
// 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)
}
}
}
Tenga en cuenta que el valor de your cue point type puede ser cualquier valor de cadena que desee, siempre que no esté utilizando ninguno de los Complementos de iOS. Para obtener más detalles, consulte la Referencia del protocolo BCOVCuePoint documento.
El siguiente código escucha sus puntos de referencia y muestra un mensaje:
// 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))")
}
}
}
}
}
Referencias
Para obtener más información, consulte lo siguiente:
Puede haber casos en los que desee eliminar el reproductor y la vista.
Desasignar el controlador de vista que tiene la propiedad de un BCOVPlaybackController también desasignará el controlador de reproducción. Para hacer esto, elimine la vista del reproductor de su supervista y configure el puntero del controlador de reproducción en nil.
La sesión de audio maneja el comportamiento del audio a nivel de la aplicación. Puede elegir entre varias categorías y configuraciones de sesiones de audio para personalizar el comportamiento de audio de su aplicación.
Elija la mejor categoría de sesión de audio para tu aplicación. Para obtener más información, consulte la documentación de Apple:
Para nuestra muestra básica, utilizamos AVAudioSessionCategoryPlayback. Esto reproduce audio incluso cuando la pantalla está bloqueada y con el interruptor de timbre / silencio en silencio. Para simplificar, colocamos el código para esto en el Delegado de la aplicación.
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
}
Mezclar con otro audio
Es posible que desee permitir que se escuche el audio de otras aplicaciones cuando el audio de su aplicación esté silenciado. Para hacer esto, puede configurar el AVAudioSession en el controlador de vista que tiene acceso a su actual AVPlayer.
Para obtener más detalles, consulte la mixWithOthers opción de categoría.
- (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!)")
}
}
Configuración de la velocidad de reproducción
Para controlar la velocidad de reproducción, puede configurar el rate propiedad en el AVPlayer clase expuesta en la sesión.
De forma predeterminada, la velocidad de reproducción solo se puede establecer en intervalos regulares (0,50, 0,67, 1,0, 1,25, 1,50 y 2,0). Al establecer el audioTimePitchAlgorithm , puede usar valores de tasa más granulares (como 1.7). Para obtener más detalles, consulte este discusión de stackoverflow.
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
}
}
Configuración del modo VR Goggles para videos de 360 °
Al reproducir un video de 360 °, los usuarios pueden seleccionar el botón Video 360 en la barra de control para cambiar al modo VR Goggles. Es posible que también desee hacer esto mediante programación, antes de que comience la reproducción. Puede hacer esto actualizando el BCOVPlaybackController protocolo viewProjection propiedad de la siguiente manera:
Al reproducir un video en modo vertical, es posible que observe un borde negro en la parte superior e inferior del reproductor. La vista del reproductor es del tamaño de la pantalla, pero el video solo ocupa una pequeña parte del centro de la vista del reproductor. Las partes visibles alrededor del video son el fondo de la capa del reproductor.
Esto es normal AVPlayer comportamiento. Reduce el vídeo para que quepa dentro de la capa del reproductor, y el resto es el fondo de la capa del reproductor.
Puede cambiar el fondo de la capa del reproductor con el siguiente código: