Image of phone with a video preview opened while the user is on a different app.

Como ativar o recurso “Picture in Picture” do iOS nas chamadas de Video do Vonage

Publicado em August 27, 2024

Tempo de leitura: 4 minutos

Com o lançamento do iOS 15, a Apple introduziu o recurso Picture in Picture, uma melhoria significativa para a multitarefa em iPhones e iPads. O Picture in Picture permite que você continue suas videochamadas enquanto usa outros aplicativos, oferecendo grande flexibilidade e praticidade. Isso aumenta sua produtividade e agilidade, demonstrando a importância desse recurso para a comunicação moderna.

Neste artigo, vamos explorar como os desenvolvedores podem usar o recurso Picture in Picture do iOS para integrar chamadas de vídeo da Vonage aos seus aplicativos de maneira eficaz.

Pré-requisitos

Renderização de vídeo personalizada da Vonage

Antes de integrar as chamadas de vídeo da Vonage ao recurso “Picture in Picture” do iOS, os desenvolvedores precisarão obter e processar os dados brutos de vídeo do renderizador de vídeo da Vonage. Esses dados são essenciais para acessar o fluxo de vídeo diretamente dentro do seu aplicativo. Vamos começar a construir a base do nosso aplicativo!

Siga o tutorial para criar um aplicativo básico de chat por vídeo (tempo estimado de conclusão: 25 minutos). Se você preferir não começar do zero, pode clonar nosso repositório de aplicativos de exemplo em Swift no GitHub.

Se você clonou o repositório, certifique-se de substituir os valores de App ID, ID de sessão e token por os seus próprios:

  • O ID do aplicativo é a sua chave da API da Vonage, que pode ser encontrada no seu painel de controle no momento em que você criou o aplicativo.

  • O ID da sessão é criado quando você seleciona “Criar uma sessão de Video” no seu painel.

  • Um token pode ser gerado usando um dos SDKs do servidor da Vonage , selecionando uma linguagem e localizando o trecho de código na seção “Geração de tokens”), com seus próprios valores.

Supondo que você tenha o CocoaPods instalado, abra o terminal, navegue até o diretório do seu projeto e digite pod install. Reabra seu projeto no Xcode usando o arquivo .xcworkspace.

No arquivo ViewController.swift, substitua as seguintes sequências vazias pelos valores correspondentes da chave de API, do ID de sessão e do token:

// *** Fill the following variables using your own Project info  ***

//  ***            https://tokbox.com/account/#/                  ***

// Replace with your OpenTok API key

let kApiKey = ""

// Replace with your generated session ID

let kSessionId = ""

// Replace with your generated token

let kToken = ""

Compile e execute o aplicativo.

No arquivo ViewController.swift do aplicativo de exemplo, você já criou um assinante usando OTSubscriber:

  subscriber = OTSubscriber(stream: stream, delegate: self)

O assinante possui uma propriedade chamada videoRender, à qual você pode atribuir um renderizador personalizado para o fluxo assinado. Agora você precisa atribuir o propriedade videoRender do assinante ao ExampleVideoRender , uma classe personalizada que implementa a OTVideoRender (definido no SDK do iOS). Esse protocolo permite definir um renderizador de vídeo personalizado a ser usado por um editor ou assinante do Vonage Video.

   let videoRender = ExampleVideoRender()
   subscriber?.videoRender = videoRender

Para a transmissão contínua de Video, você precisará remover o willResignActiveNotification do assinante. Isso permitirá que o aplicativo mantenha as operações de transmissão de vídeo mesmo quando estiver em segundo plano.

NotificationCenter.default.removeObserver(subscriber, name: UIApplication.willResignActiveNotification, object: nil)

O classe ExampleVideoRender lida com os dados do fluxo de vídeo recebidos e os processa, garantindo que os quadros de vídeo do assinante possam ser acessados e ajustados dentro do seu aplicativo. A método renderVideoFrame é chamado quando o assinante renderiza um quadro de vídeo para o renderizador de vídeo. O quadro é um OTVideoFrame (definido pelo SDK do iOS), que contém informações sobre o quadro de vídeo, como metadados e dados de plano.

Desde o OTVideoFrame fornece apenas dados do plano YUV, precisamos convertê-los para CMSampleBuffer antes que possam ser exibidos no UIView e no Picture in Picture. Abordaremos o processo de conversão mais adiante neste artigo. 

Com o CMSampleBuffer pronto, você pode recuperá-lo e exibi-lo em um UIView designado.

let bufferDisplayLayer = videoRender.bufferDisplayLayer

bufferDisplayLayer.frame = frame

videoContainerView.layer.addSublayer(bufferDisplayLayer)

Conversão de dados YUV para o buffer de amostras

YUV (YCbCr) é um espaço de cor comum para Video, que representa a luminância (Y) e a crominância (UV) separadamente. Ao extrair os dados do plano YUV, os desenvolvedores podem manipular e converter esses componentes para um formato compatível com o buffer de amostras do iOS. Isso é necessário para exibir quadros de Video no modo Picture in Picture do iOS.

Conforme mencionado acima, você pode obter o OTVideoFrame na seção método renderVideoFrame , que é chamado quando o assinante renderiza um quadro de Video. Esse quadro inclui dados do plano YUV, que os desenvolvedores podem usar para criar um CVPixelBuffer. Essa etapa organiza e mapeia os componentes YUV no buffer de pixels corretamente. 

let pixelAttributes: NSDictionary = [kCVPixelBufferIOSurfacePropertiesKey as String: [:]]

var pixelBuffer: CVPixelBuffer?

let result = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA, pixelAttributes as CFDictionary, &pixelBuffer)
         _ = accel.convertFrameVImageYUV(frame, to: pixelBuffer)

Em seguida, crie um CMSampleBuffer a partir do CVPixelBuffer, incluindo os metadados necessários, como carimbo de data/hora e descrição do formato. Essas etapas são cruciais para preparar os quadros de Video para exibição no modo Picture in Picture do iOS, garantindo uma reprodução suave e a conformidade com os padrões do iOS.

func createSampleBufferFrom(pixelBuffer: CVPixelBuffer) -> CMSampleBuffer? {
        CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)
        
        var sampleBuffer: CMSampleBuffer?
        
        
        let now = CMTimeMakeWithSeconds(CACurrentMediaTime(), preferredTimescale: 1000)
        var timingInfo = CMSampleTimingInfo(duration: CMTimeMakeWithSeconds(1, preferredTimescale: 1000), presentationTimeStamp: now, decodeTimeStamp: now)
        var formatDescription: CMFormatDescription? = nil
        CMVideoFormatDescriptionCreateForImageBuffer(allocator: kCFAllocatorDefault, imageBuffer: pixelBuffer, formatDescriptionOut: &formatDescription)
        
        let osStatus = CMSampleBufferCreateReadyWithImageBuffer(
            allocator: kCFAllocatorDefault,
            imageBuffer: pixelBuffer,
            formatDescription: formatDescription!,
            sampleTiming: &timingInfo,
            sampleBufferOut: &sampleBuffer
        )
        
        if osStatus != noErr {
            let errorMessage = osStatusToString(status: osStatus)
            print("osStatus error: \(errorMessage)")
        }
        
        guard let buffer = sampleBuffer else {
            print("Cannot create sample buffer")
            return nil
        }
        
        CVPixelBufferUnlockBaseAddress(pixelBuffer, [])
        
        return buffer
    }

Implementação do componente “Picture in Picture”

Agora que o CMSampleBuffer está pronto, siga estas etapas para adicionar o recurso Picture in Picture às chamadas de Video

1. Crie uma visualização de origem para exibir dentro do controlador de visualização da videochamada.

class SampleBufferVideoCallView: UIView {

    override class var layerClass: AnyClass {

        AVSampleBufferDisplayLayer.self

    }

    var sampleBufferDisplayLayer: AVSampleBufferDisplayLayer {

        layer as! AVSampleBufferDisplayLayer

    }

}

2. Crie um AVPictureInPictureVideoCallViewController e adicione a fonte como uma subvisualização para exibir a visualização da fonte.

let pipVideoCallViewController = AVPictureInPictureVideoCallViewController()

pipVideoCallViewController.preferredContentSize = CGSize(width: 640, height: 480)  

pipVideoCallViewController.view.addSubview(sampleBufferVideoCallView)

3. Crie um AVPictureInPictureController.ContentSource que represente a fonte do conteúdo que o sistema exibe no arquivo ViewController.swift.

let contentSource = AVPictureInPictureController.ContentSource(

            activeVideoCallSourceView: videoContainerView,

            contentViewController: pipVideoCallViewController)

4. Inicializar o AVPictureInPictureController e defina canStartPictureInPictureAutomaticallyFromInline como true.

pipController = AVPictureInPictureController(contentSource: contentSource)   

pipController.canStartPictureInPictureAutomaticallyFromInline = true

Com a configuração acima, seu aplicativo deve estar pronto para o modo Picture in Picture. Você poderá ver a videochamada iniciar quando um usuário passar para o segundo plano. Caso isso não ocorra, verifique se o modo Picture in Picture está ativado nas configurações do seu iPhone/iPad.

Screen recording of opening the video app, minimizing it and still being able to see a window while opening other applications.iOS Demo

Resumo do processo de integração

A integração das chamadas de vídeo da Vonage com o recurso “Picture in Picture” do iOS aprimora a multitarefa, permitindo que os usuários continuem suas chamadas de vídeo sem interrupções enquanto utilizam outros aplicativos. Esse recurso aumenta a produtividade, especialmente em reuniões profissionais e no trabalho colaborativo.

Mais detalhes e o código completo do aplicativo de exemplo “Picture in Picture” estão disponíveis em nosso repositório do GitHub.

Se você tiver alguma dúvida, participe do nosso Comunidade no Slack ou envie uma mensagem para nós no X, antes conhecido como Twitter.

Recursos adicionais

Compartilhar:

https://a.storyblok.com/f/270183/400x351/0c294bb1fc/iu-jie-lim.png
Iu Jie Lim

Iu Jie is a Software Engineer who is constantly seeking innovative ways to solve a problem. She is passionate about new technology, especially relating to cloud and AI. Out of work, she likes to spend her time hunting for tasty food with family.