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 video 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 puede utilizar las mismas técnicas para modificar el renderizador de vídeo utilizado por un Publisher (aunque este ejemplo sólo 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-samples repo. Si aún no lo has hecho, tendrás que clonar el repositorio en un directorio local. En la línea de comandos, ejecute:

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

Abra el Basic-Video-Renderer-Java en Android Studio.

Importante: Tenga en cuenta que su 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 amplía la clase 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();

En InvertedColorsVideoRenderer() 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 renderizador de este objeto GLSurfaceView se establece en 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);
}

En onFrame() del renderizador de vídeo se hereda del método BaseVideoRenderer clase. En BaseVideoRenderer.onFrame() es llamado cuando el editor (o suscriptor) renderiza un fotograma de vídeo al renderizador de vídeo.

En InvertedColorsVideoRenderer de este método, toma el búfer de imagen del fotograma (YUV representación del marco), lo pasa al displayFrame método del MyRenderer y llama al objeto 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 (aunque este ejemplo sólo ilustra un renderizador personalizado para un editor).

El código de esta sección se encuentra en Renderizador de vídeo básico proyecto del 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 el proceso.

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 te permite definir un renderizador de video personalizado para ser utilizado por un editor o suscriptor de Vonage Video.

En [OTKBasicVideoRender init:] establece un _renderView a un objeto UIView. Este es el objeto UIView que contendrá la vista a renderizar (por el editor o el suscriptor). En este ejemplo, el objeto UIView está definido por la clase personalizada OTKCustomRenderView, que extiende UIView:

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

La clase OTKCustomRenderView incluye métodos (discutidos más adelante) que convierten un cuadro de video en una representación en blanco y negro.

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

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

En [OTKCustomRenderView renderVideoFrame] itera a través de los píxeles en el plano, ajusta cada píxel a un valor en blanco y negro, añade el 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];
        });
    });
}

Ver también