Crear un marco de chat de vídeo móvil mediante la API de Nex Gen Media Server (NGMS)

ANDROID Blog

Introducción

Nex Gen Media Server es mejor conocido como un servidor de transmisión de medios multipropósita para transmitir video en vivo y grabado a diferentes dispositivos. El mismo servidor de medios se puede incrustar en la aplicación móvil para facilitar la comunicación de vídeo en directo. Aquí usamos la API de NGMS para facilitar la creación de un cliente de chat de video para dispositivos Android utilizando Android Software Developer Kit (SDK) y Google Android Native Developer Kit (NDK).

Utilizamos NGMS para proporcionar todos los servicios de RTP en línea, compresión de fotogramas de vídeo y descompresión. NGMS funciona como un objeto compartido compilado localmente utilizado en nuestra aplicación para Android. Utilizamos la capa de puente JNI (Java Native Interface) para confiar en métodos locales de código de aplicación java.

Solicitudes previas

En este artículo se supone que tiene un conocimiento básico y al menos una experiencia intermedia en la creación de aplicaciones de Android.This article assumes that you have a basic understanding and at least an intermediate experience in creating Android applications. No voy a repasar los detalles de la configuración y el proyecto de Android en un iDE como Eclipse. Si previamente ha creado aplicaciones de Android o al menos ha pasado algunos tutoriales de ejemplo, debería poder usar este tutorial para crear un cliente de transmisión de video en vivo.

Si aún no lo has hecho, tendrás que descargar Google Android SDK (Software Developer Kit). En este ejemplo, utilicé la revisión 15 en una computadora Linux de 32 bits. La capa de aplicación Java se utiliza para crear una aplicación Android real. El código de la aplicación interactúa con la capa JNI para ejecutar rutinas de API de NGMS.

También es necesario descargar Google Android NDK (Native Developer Kit). En este ejemplo, utilicé la revisión 6b en una computadora Linux de 32 bits. NDK se utiliza para crear una capa de interfaz local que actúa como adhesivo entre el código local y el código de la aplicación Java. También debe tener un conocimiento básico del lenguaje de programación C.

En primer lugar, debe crear una aplicación de Android esqueleto utilizando un IDE como Eclipse. En este ejemplo, la aplicación se denomina ngmsclient. El nombre del paquete java se llama com.example.ngmsclient y está dirigido al sistema operativo Android 2.3 o posterior. El directorio raíz del proyecto debe contener una carpeta denominada “jni” que contenga las fuentes locales utilizadas para interactuar con el código de la aplicación Java. En el ejemplo siguiente se supone que está ejecutando una aplicación androide esqueleto y le mostrará cómo integrar la clase ExampleChat en su proyecto Android.

Capa local

La biblioteca integrada de núcleo NGMS se escribe en C y se empaqueta como un archivo de objeto compartido. Debido a que el sistema operativo Android se basa en Linux, la biblioteca principal de NGMS funciona en su espacio de aplicaciones Android. Debe obtener la biblioteca principal de NGMS del sistema operativo Android desde el sitio web de Ngmsvid.com. Los componentes importantes son los archivos de biblioteca libngms.so, libxcode.so y el archivo de encabezado ngmslib.h. Ngms bundled.so archivos se empaquetan con su ngmsclient.apk proporcionar servicios de chat de video.

Capa JNI

Estamos construyendo una biblioteca compartida llamada ngmsglue.so que es el pegamento entre la API ngms y nuestro código de aplicación Java. La estructura del directorio JNI (Java Native Interface) en el directorio del proyecto ngmsclient debería tener este aspecto.


jni/Android.mk
jni/ngmsglue.c
jni/ngms/include/ngmslib.h
jni/ngms/lib/libngms.so
jni/ngms/lib/libxcode.so

ngmsglue.c contiene código para controlar las funciones de salida y entrada de NGMS. El código almacena dos contextos de API de NGMS independientes, uno para los parámetros de salida de flujo y otro para los parámetros de entrada de flujo. Debe tener en cuenta que la convención de nomenclatura para cada función debe coincidir con el paquete Java y el nombre de clase del que se basa en el código.

ngmsStartReceiver se llama para ejecutar la API de entrada de captura NGMS para escuchar el puerto 5004 secuencia de vídeo en red encapsulado a través de MPEG-2 TS. Cada fotograma de vídeo posterior se deskumatiza automáticamente y se decodifica en formato de píxel RGB565. A continuación, puede leer el fotograma completo llamando a ngmsReceiveFrame.

ngmsStartSender se llama para reajustar la API de salida de la secuencia NGMS para las tramas de vídeo codificadas de la salida al host remoto vía el puerto 5004. ngmsTransmitFrame se llama para codificar y transmitir una trama sin formato en formato de píxel NV21.


/*
* ngmsglue.c
*
* JNI layer to access the NGMS streaming and capture API
*
*/

#include
#include
#include
#include
#include "ngmslib.h"

/**
* Holds the configuration for the NGMS RTP receiver and decoder
*/
static NGMSLIB_STREAM_PARAMS_T ngmsReceiver;

/**
* Holds the configuration for the NGMS RTP sender and encoder
*/
static NGMSLIB_STREAM_PARAMS_T ngmsSender;

/**
* Starts the NGMS capture service
*/
jint Java_com_example_ngmsclient_ExampleChat_ngmsStartReceiver (JNIEnv* _env, jobject _thiz)
{

char xcoderConfig[512];
char sdpConfig[512];
int i;

/*
* Before doing anything call ngmslib_open to open the NGMS service
*/
i = ngmslib_open(&ngmsReceiver);
if (i!= NGMS_RC_OK)
{
__android_log_print(ANDROID_LOG_ERROR, "ngms", "ngmslib_open failed with error %d", i);
return -1;
}

/*
* Construct an xcoder configuration key value string
*/
sprintf(xcoderConfig, "vc=rgb565,vx=320,vy=240");

/*
* Construct an SDP configuration for the RTP receiver
* Here we setup NGMS to read data over a MPEG-2 Transport Stream.
*/
sprintf(sdpConfig, "sdp://"
"m=video 5004 RTP/AVP 33n"
"a=rtpmap:33 MP2T/90000n"
"a=fmtp:33n");

/*
* Customize the capture configuration parameters
*/
ngmsReceiver.inputs[0] = sdpConfig;
ngmsReceiver.strxcode = xcoderConfig;
ngmsReceiver.islive = 1;
ngmsReceiver.noaud = 1;

/*
* Start the RTP receiver
*/
i = ngmslib_stream(&ngmsReceiver);

if (i!= NGMS_RC_OK)
{
__android_log_print(ANDROID_LOG_ERROR, "ngms", "ngmslib_stream failed with error %d", i);
ngmslib_close(&ngmsReceiver);
return -1;
}

return 0;
}

/**
* Stops the NGMS RTP capture service
*/
jint Java_com_example_ngmsclient_ExampleChat_ngmsStopReceiver (JNIEnv* _env, jobject _thiz)
{

/*
* Stop the RTP receiver
*/
ngmslib_close(&ngmsReceiver);

return 0;
}

/**
* NGMS Callback operation which blocks until a complete video frame has been received and decoded
*/
jint Java_com_example_ngmsclient_ExampleChat_ngmsReceiveFrame (JNIEnv* _env, jobject _thiz, jbyteArray frameBytes)
{
jstring str;

jboolean copy;
jbyte* buffer = (*_env)->GetByteArrayElements(_env, frameBytes, ©);
jsize max = (*_env)->GetArrayLength(_env, frameBytes);
int frameSize;

frameSize = ngmslib_readVidFrame(&ngmsReceiver, buffer, max, NULL);

(*_env)->ReleaseByteArrayElements(_env, frameBytes, buffer, JNI_ABORT);

return frameSize;

}

/**
* Starts the NGMS streaming service
*/
jint Java_com_example_ngmsclient_ExampleChat_ngmsStartSender (JNIEnv* _env, jobject _thiz, jstring remoteAddress)
{

char xcoderConfig[512];
char filter[512];
char destination[512];
const char *p;
int i;

/*
* Before doing anything call ngmslib_open to open the NGMS service
*/
i = ngmslib_open(&ngmsSender);

if (i!= NGMS_RC_OK)
{
__android_log_print(ANDROID_LOG_ERROR, "ngms", "ngmslib_open failed with error %d", i);
return -1;
}

/*
* Get the destination host string
*/
p = (*_env)->GetStringUTFChars(_env, remoteAddress, 0);

sprintf(destination, "rtp://%s:5004", p);

(*_env)->ReleaseStringUTFChars(_env, remoteAddress, p);

/*
* Customize the stream configuration parameters
*/
ngmsSender.output = destination;
sprintf(xcoderConfig, "vc=h264,vp=66,vb=250,vx=320,vy=240,vgmax=2000,vgmin=1500,vfr=15,vcfrout=-1,vth=1,vl=1,vsc=1,vf=2");
ngmsSender.strxcode = xcoderConfig;
sprintf(filter, "type=yuv420sp, ngmsSender.strfilters[0] = filter;
ngmsSender.inputs[0]= "/dev/dummyvideo";
ngmsSender.noaud = 1;

/*
* Start the RTP sender
*/
i = ngmslib_stream(&ngmsSender);

if (i!= NGMS_RC_OK)
{
__android_log_print(ANDROID_LOG_ERROR, "ngms", "ngmslib_stream failed with error %d", i);
ngmslib_close(&ngmsSender);
return -1;
}

return 0;
}

/**
* Stops the NGMS streaming service
*/
jint Java_com_example_ngmsclient_ExampleChat_ngmsStopSender (JNIEnv* _env, jobject _thiz)
{

/*
* Stop the RTP sender
*/
ngmslib_close(&ngmsSender);

return 0;
}

/**
* Encodes and transmits a single video frame using local RTP transport
*/
jint Java_com_example_ngmsclient_ExampleChat_ngmsTransmitFrame (JNIEnv* _env, jobject _thiz, jbyteArray frameBytes)
{

jboolean copy;
jbyte* buffer = (*_env)->GetByteArrayElements(_env, frameBytes, ©);
jsize size = (*_env)->GetArrayLength(_env, frameBytes);
int i;

i = ngmslib_onVidFrame(&ngmsSender, buffer, size);

(*_env)->ReleaseByteArrayElements(_env, frameBytes, buffer, JNI_ABORT);

return i;
}

Creamos makefile Android.mk que Android NDK usa para construir nuestra biblioteca ngmsglue.


#Example Android.mk Makefile

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE:= ngmsglue
LOCAL_SRC_FILES:= ngmsglue.c
LOCAL_C_INCLUDES:= ngms/include
LOCAL_LDFLAGS:= -Lngms/lib
LOCAL_LDLIBS:= -llog -lm -lngms -lxcode

include $(BUILD_SHARED_LIBRARY)

Para crear una biblioteca compartida ngmsglue.so, simplemente haga lo siguiente:


cd [YOUR NGMSCLIENT PROJECT DIR]/jni
${ANDROID_NDK_HOME}/ndk-build

Esto debería proporcionar un archivo de salida [YOUR NGMSCLIENT PROJECT DIR]/libs/armeabi/ibngmsglue.so

Es posible que deba copiar manualmente las bibliotecas ngms/lib/libngms.so y ngms/lib/libxcode.so en el directorio de salida libs/armeabi porque el contenido de esta carpeta se empaqueta automáticamente en la aplicación .apk proyecto.

Capa de aplicación Java

La capa de aplicación Java consiste en la lógica de nivel de aplicación de la aplicación androide que estamos construyendo. Para usar el código local en la sección anterior, necesitamos crear una aplicación simple usando android SDK. En este ejemplo se muestra cómo usar la clase ExampleChat, que tu propia aplicación puede usar para proporcionar transmisión de vídeo interactiva en directo. No muestra todopasos necesarios para crear una aplicación completa de transmisión de video de Android. Un desarrollador de Android de nivel medio debe poder completar la tarea basada en la clase ExampleChat e incluirla en su aplicación inflado.

La clase Samplechat a continuación se utiliza para controlar el transmisor de flujo NGMS y el receptor de flujo NGMS. Se puede llamar al método startSender para iniciar la transmisión de vídeo desde una cámara local mediante el mecanismo de vista previa de la cámara. La salida de fotograma de vídeo de cada cámara se da a la API de NGMS, donde se codifica y se transmite al lado remoto utilizando la tapa RTP local. Se puede llamar al método starReceiver para empezar a recibir un vídeo de una instancia remota de la misma aplicación. startReceiver inicia un subproceso que realiza una encuesta de bloqueo para el siguiente fotograma de vídeo recibido y descodificado disponible. Juntos, estos dos métodos se pueden utilizar para crear una secuencia de vídeo bidireccional entre dos copias remotas de la misma aplicación en una red.


/*
* ExampleChat.java
*
*/

package com.example.ngmsclient;

import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.graphics.ImageFormat;

public class ExampleChat
{
private boolean myReceiverRunning;
private Camera myCamera;
private PreviewCallback myCameraCallback;

/**
* Change this value to the destination where the video will be streamed
*/
private static final DESTINATION = "10.0.0.1";

/**
* method to initialize and start sending video from the local camera
*/
public void startSender () throws Exception
{

myCamera = Camera.open();

Parameters parameters = myCamera.getParameters();
parameters.setPreviewFormat(ImageFormat.NV21);
parameters.setPreviewFrameRate(15);
parameters.setPreviewSize(320, 240);

myCamera.setParameters(parameters);

/**
* Call the NGMS native method to perform streaming initialization
*/
ngmsStartSender(DESTINATION);

myCameraCallback = new PreviewCallback()
{
public void onPreviewFrame(byte[] frameBytes, Camera camera)
{
/**
* Call the NGMS native routine to encode and transmit the video frame
* to the remote destination using RTP transport.
*/
ngmsTransmitFrame(frameBytes);
}
}

myCamera.startPreview();
myCamera.setPreviewCallback(myCameraCallback);

}

/**
* method to stop sending video
*/
public void stopSender () throws Exception
{
ngmsStopSender();
}

/**
* method to start receiving remote video
*/
public void startReceiver () throws Exception
{

/**
* Call the NGMS native method to perform input initialization
*/
ngmsStartReceiver();

/**
* Start the thread which will poll for received video frames
*/
new Thread(myVideoFrameReceiver).start();

}

/**
* method to stop receiving remote video
*/
public void stopReceiver () throws Exception
{
myReceiverRunning = false;
ngmsStopReceiver();
}

/**
* Thread used to check for decoded video frames
* received by the NGMS native layer
*/
private Runnable myVideoFrameReceiver = new Runnable()
{
public void run()
{
/**
* Allocate a buffer to hold a video frame 320 x 240 pixels
* in RGB565 format, taking 16 bits (2 bytes) per pixel.
*/
byte[] frameBytes = new byte[ 320 * 240 * 2 ];
int size;
myReceiverRunning = true;

while(myReceiverRunning)
{
/**
* Block until a frame has been received and decoded by the native layer
*/
size = ngmsReceiveFrame(frameBytes);
if(size > 0)
{
/**
* The frameBytes array represents a video frame in RGB565 format.
* These bytes can be converted into a Bitmap object for display
* on the screen.
*/
}
}
}
}

/**
* Static initialization to load native libraries into address space
*/
static
{
System.loadLibrary("xcode");
System.loadLibrary("ngms");
System.loadLibrary("ngmsglue");

}

/**
* Define native method prototypes
*/
public native int ngmsStartReceiver ();
public native int ngmsStopReceiver ();
public native int ngmsReceiveFrame (byte[] frameBytes);

public native int ngmsStartSender (String remoteAddress);
public native int ngmsStopSender ();
public native int ngmsTransmitFrame (byte[] frameBytes);

}

Una vez que haya integrado la clase ExampleChat en su proyecto de Android, asegúrese de que las tres bibliotecas locales estén presentes en el directorio project/libs para que se incluyan en su.apk salida.


libs/armeabi/libngms.so
libs/armeabi/libxcode.so
libs/armeabi/libngmsglue.so

Este ejemplo proporciona una salida de vídeo a la dirección IP de destino 10.0.0.1. Para que dos clientes transmitan correctamente vídeo en dos direcciones, debe cambiar esta dirección IP para que coincida con su cliente remoto.

conclusión

El ejemplo anterior muestra cómo transmitir video en vivo desde un objeto de cámara en tiempo real y cómo obtener una transmisión remota y extraer cada fotograma de video decodificado para mostrar. Puede aplicar la misma secuencia de proceso para agregar transmisión de audio para crear una aplicación de chat de video completa. ​

Para ver la guía de API integrada para Nex Gen Media Server (NGMS) http://ngmsvid.com/develop.php

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *