Renderización de vídeo personalizada

Visión general

La Video API de Vonage permite realizar modificaciones en el renderizador de video para usarlo en aplicaciones de Android e iOS.

Este tutorial repasará:

  • Realiza modificaciones en el renderizador de vídeo de tu aplicación Vonage Video para Android
  • Realiza modificaciones en el renderizador de video de tu aplicación Vonage Video para iOS

Android

Antes de empezar

Este tutorial muestra el código necesario para realizar pequeñas modificaciones en el renderizador de vídeo utilizado por un archivo Subscriber objeto.

También puedes utilizar las mismas técnicas para modificar el renderizador de vídeo que utiliza un Publisher objeto (aunque este ejemplo solo ilustra un renderizador personalizado para un suscriptor).

El código de esta sección está disponible en el proyecto Basic-Video-Renderer-Java del archivo opentok-android-sdk-ejemplos repositorio. Si aún no lo has hecho, tendrás que clonar el repositorio en un directorio local. En la línea de comandos, ejecuta:

git clone git@github.com:opentok/opentok-android-sdk-samples.git

Abra el Basic-Video-Renderer-Java proyecto en Android Studio para seguir el tutorial.

Importante: Ten en cuenta que tu App ID ¿Es tu API key.

Explorar el código

En este ejemplo, la aplicación utiliza un renderizador de vídeo personalizado para mostrar una versión en color invertida del vídeo.

InvertedColorsVideoRenderer es una clase personalizada que hereda de la BaseVideoRenderer (definida en el SDK de Android). La dirección BaseVideoRenderer te permite definir un renderizador de video personalizado para ser utilizado por un editor o suscriptor de Vonage Video:

subscriber = new Subscriber.Builder(this, stream)
    .renderer(new InvertedColorsVideoRenderer(this))
    .build();


publisher = new Publisher.Builder(MainActivity.this)
    .renderer(new InvertedColorsVideoRenderer(MainActivity.this))
    .build();

El InvertedColorsVideoRenderer() El constructor establece un renderer a una propiedad GLSurfaceView objeto. La aplicación utiliza este objeto para mostrar el vídeo mediante OpenGL ES 2.0. El motor de renderizado para este GLSurfaceView el objeto se establece en un MyRenderer objeto. MyRenderer es una clase personalizada que extiende GLSurfaceView.Renderery se utiliza para renderizar el vídeo en el GLSurfaceView objeto:

public InvertedColorsVideoRenderer(Context context) {
    view = new GLSurfaceView(context);
    view.setEGLContextClientVersion(2);

    renderer = new MyRenderer();
    view.setRenderer(renderer);

    view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}

El onFrame() El método del renderizador de vídeo se hereda del BaseVideoRenderer clase. En BaseVideoRenderer.onFrame() El método se invoca cuando el editor (o el suscriptor) envía un fotograma de vídeo al motor de reproducción de vídeo.

El InvertedColorsVideoRenderer de este método, toma el búfer de imagen del fotograma (YUV representación del marco), se la pasa al displayFrame método del MyRenderer objeto y llama a la requestRender() método del GLSurfaceView objeto:

@Override
public void onFrame(Frame frame) {
    renderer.displayFrame(frame);
    view.requestRender();
}

Para renderizar los fotogramas de vídeo, la clase renderer utiliza shaders OpenGL. En este ejemplo el shader produce el efecto de color invertido, más precisamente esto se logra por esta línea que está dentro de la clase fragmentShaderCode cadena:

"y=1.0-1.1643*(y-0.0625);\n"

iOS

Antes de empezar

Este tutorial muestra el código necesario para realizar pequeñas modificaciones en el renderizador de vídeo utilizado por una aplicación OTPublisher objeto.

También puede utilizar las mismas técnicas para modificar el renderizador de vídeo utilizado por un OTSubscriber objeto (aunque este ejemplo solo ilustra un renderizador personalizado para un editor).

El código de esta sección se encuentra en el Renderizador de vídeo básico proyecto de la opentok-ios-sdk-samples por lo que, si aún no lo ha hecho, deberá clonar el repositorio en un directorio local. esto se puede hacer utilizando la línea de comandos:

git clone https://github.com/Vonage/vonage-video-ios-sdk-samples.git

Cambia el directorio al proyecto Basic Video Renderer:

cd vonage-video-ios-sdk-samples/SwiftUI/BasicVideoRenderer

A continuación, instala la dependencia de Vonage Video:

pod install

Abre el proyecto en Xcode para seguir los pasos.

Importante: Tenga en cuenta que su App ID es su clave API.

Explorar el código

En este ejemplo, la aplicación utiliza un renderizador de vídeo personalizado para mostrar una versión en blanco y negro del vídeo del objeto OTPublisher.

En el ViewController principal, después de inicializar el objeto OTPublisher, la función videoRender del objeto OTPublisher es una instancia de OTKBasicVideoRender:

_publisher = [[OTPublisher alloc] initWithDelegate:self settings:settings];
_renderer = [[OTKBasicVideoRender alloc] init];
_publisher.videoRender = _renderer;

OTKBasicVideoRender es una clase personalizada que implementa el protocolo OTVideoRender (definido en el SDK de iOS). Este protocolo permite definir un renderizador de vídeo personalizado para que lo utilicen los editores o suscriptores de Vonage Video.

El [OTKBasicVideoRender init:] El método establece un _renderView propiedad a un objeto UIView. Se trata del objeto UIView que contendrá la vista que se va a renderizar (por parte del editor o del suscriptor). En este ejemplo, el objeto UIView viene definido por la clase personalizada OTKCustomRenderView, que hereda de UIView:

- (id)init
{
    self = [super init];
    if (self) {
        _renderView = [[OTKCustomRenderView alloc] initWithFrame:CGRectZero];
    }
    return self;
}

La clase OTKCustomRenderView incluye métodos (que se explicarán más adelante) que convierten un fotograma de vídeo en una representación en blanco y negro.

El [OTVideoRender renderVideoFrame:] Este método se invoca cuando el editor (o el suscriptor) envía un fotograma de vídeo al renderizador de vídeo. El fotograma es un objeto OTVideoFrame (definido por el SDK de iOS). En la implementación de este método en OTKCustomRenderView, se toma el fotograma y se le pasa al [renderVideoFrame] del objeto OTKCustomRenderView:

- (void)renderVideoFrame:(OTVideoFrame*) frame
{
    [(OTKCustomRenderView*)self.renderView renderVideoFrame:frame];
}

El [OTKCustomRenderView renderVideoFrame] El método recorre los píxeles del plano, ajusta cada píxel a un valor en blanco y negro y añade dicho valor a un búfer. A continuación, escribe el búfer en un CGImageRef que representa la imagen de la vista y llama a [self setNeedsDisplay] para renderizar la vista de la imagen:

- (void)renderVideoFrame:(OTVideoFrame *)frame
{
    __block OTVideoFrame *frameToRender = frame;
    dispatch_sync(self.renderQueue, ^{
        if (_img != NULL) {
            CGImageRelease(_img);
            _img = NULL;
        }

        size_t bufferSize = frameToRender.format.imageHeight
            * frameToRender.format.imageWidth * 3;
        uint8_t *buffer = malloc(bufferSize);

        uint8_t *yplane = [frameToRender.planes pointerAtIndex:0];

        for (int i = 0; i < frameToRender.format.imageHeight; i++) {
            for (int j = 0; j < frameToRender.format.imageWidth; j++) {
                int starting = (i * frameToRender.format.imageWidth * 3) + (j * 3);
                uint8_t yvalue = yplane[(i * frameToRender.format.imageWidth) + j];
                // If in a RGB image we copy the same Y value for R, G and B
                // we will obtain a Black & White image
                buffer[starting] = yvalue;
                buffer[starting+1] = yvalue;
                buffer[starting+2] = yvalue;
            }
        }

        CGDataProviderRef imgProvider = CGDataProviderCreateWithData(NULL,
                                                                        buffer,
                                                                        bufferSize,
                                                                        release_frame);

        _img = CGImageCreate(frameToRender.format.imageWidth,
                                frameToRender.format.imageHeight,
                                8,
                                24,
                                3 * frameToRender.format.imageWidth,
                                CGColorSpaceCreateDeviceRGB(),
                                kCGBitmapByteOrder32Big | kCGImageAlphaNone,
                                imgProvider,
                                NULL,
                                false,
                                kCGRenderingIntentDefault);


        CGDataProviderRelease(imgProvider);
        dispatch_async(dispatch_get_main_queue(), ^{
            [self setNeedsDisplay];
        });
    });
}

Véase también