mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-02-16 10:30:47 +00:00
Add video decoder using OpenMax LI on Raspberry Pi
This commit is contained in:
5
jni/nv_omx_dec/build.sh
Executable file
5
jni/nv_omx_dec/build.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
rm *.o libnv_omx_dec.so
|
||||
gcc -I /opt/vc/src/hello_pi/libs/ilclient -I /usr/lib/jvm/java-7-openjdk/include -I /usr/lib/jvm/java-7-openjdk/include/linux -I /opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux -I ./inc -fPIC -L. -c *.c
|
||||
gcc -shared -Wl,-soname,libnv_omx_dec.so -Wl,--no-undefined -o libnv_omx_dec.so *.o -L. -L /opt/vc/lib/ -lbcm_host -lopenmaxil -lvcos -lvchiq_arm -lpthread -lrt -L /opt/vc/src/hello_pi/libs/ilclient -lilclient
|
||||
rm *.o
|
||||
|
||||
253
jni/nv_omx_dec/nv_omx_dec.c
Normal file
253
jni/nv_omx_dec/nv_omx_dec.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
Copyright (c) 2012, Broadcom Europe Ltd
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Video deocode demo using OpenMAX IL though the ilcient helper library
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bcm_host.h"
|
||||
#include "ilclient.h"
|
||||
|
||||
TUNNEL_T tunnel[2];
|
||||
COMPONENT_T *list[3];
|
||||
ILCLIENT_T *client;
|
||||
|
||||
COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL;
|
||||
|
||||
OMX_BUFFERHEADERTYPE *buf;
|
||||
unsigned char *dest;
|
||||
|
||||
int port_settings_changed;
|
||||
int first_packet;
|
||||
int last_packet;
|
||||
|
||||
int nv_omx_init(void) {
|
||||
bcm_host_init();
|
||||
|
||||
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
|
||||
OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
|
||||
COMPONENT_T *clock = NULL;
|
||||
int status = 0;
|
||||
unsigned int data_len = 0;
|
||||
int packet_size = 80<<10;
|
||||
|
||||
memset(list, 0, sizeof(list));
|
||||
memset(tunnel, 0, sizeof(tunnel));
|
||||
|
||||
if((client = ilclient_init()) == NULL)
|
||||
return -3;
|
||||
|
||||
if(OMX_Init() != OMX_ErrorNone) {
|
||||
ilclient_destroy(client);
|
||||
return -4;
|
||||
}
|
||||
|
||||
// create video_decode
|
||||
if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0)
|
||||
status = -14;
|
||||
list[0] = video_decode;
|
||||
|
||||
// create video_render
|
||||
if(status == 0 && ilclient_create_component(client, &video_render, "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0)
|
||||
status = -14;
|
||||
list[1] = video_render;
|
||||
|
||||
/*
|
||||
// create clock
|
||||
if(status == 0 && ilclient_create_component(client, &clock, "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0)
|
||||
status = -14;
|
||||
list[2] = clock;
|
||||
*/
|
||||
|
||||
// memset(&cstate, 0, sizeof(cstate));
|
||||
// cstate.nSize = sizeof(cstate);
|
||||
// cstate.nVersion.nVersion = OMX_VERSION;
|
||||
// cstate.eState = OMX_TIME_ClockStateWaitingForStartTime;
|
||||
// cstate.nWaitMask = 1;
|
||||
// if(clock != NULL && OMX_SetParameter(ILC_GET_HANDLE(clock), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone)
|
||||
// status = -13;
|
||||
|
||||
// create video_scheduler
|
||||
// if(status == 0 && ilclient_create_component(client, &video_scheduler, "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0)
|
||||
// status = -14;
|
||||
// list[3] = video_scheduler;
|
||||
|
||||
// set_tunnel(tunnel, video_decode, 131, video_scheduler, 10);
|
||||
// set_tunnel(tunnel+1, video_scheduler, 11, video_render, 90);
|
||||
// set_tunnel(tunnel+2, clock, 80, video_scheduler, 12);
|
||||
set_tunnel(tunnel, video_decode, 131, video_render, 90);
|
||||
|
||||
// setup clock tunnel first
|
||||
/*
|
||||
if(status == 0 && ilclient_setup_tunnel(tunnel+2, 0, 0) != 0)
|
||||
status = -15;
|
||||
else
|
||||
ilclient_change_component_state(clock, OMX_StateExecuting);
|
||||
*/
|
||||
|
||||
if(status == 0)
|
||||
ilclient_change_component_state(video_decode, OMX_StateIdle);
|
||||
|
||||
memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE));
|
||||
format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE);
|
||||
format.nVersion.nVersion = OMX_VERSION;
|
||||
format.nPortIndex = 130;
|
||||
format.eCompressionFormat = OMX_VIDEO_CodingAVC;
|
||||
|
||||
OMX_PARAM_DATAUNITTYPE unit;
|
||||
|
||||
memset(&unit, 0, sizeof(OMX_PARAM_DATAUNITTYPE));
|
||||
unit.nSize = sizeof(OMX_PARAM_DATAUNITTYPE);
|
||||
unit.nVersion.nVersion = OMX_VERSION;
|
||||
unit.nPortIndex = 130;
|
||||
unit.eUnitType = OMX_DataUnitCodedPicture;
|
||||
unit.eEncapsulationType = OMX_DataEncapsulationElementaryStream;
|
||||
|
||||
if(status == 0 &&
|
||||
OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone &&
|
||||
OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmDataUnit, &unit) == OMX_ErrorNone &&
|
||||
/* OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmExtraBuffers, 2) == OMX_ErrorNone && */
|
||||
ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) {
|
||||
|
||||
port_settings_changed = 0;
|
||||
first_packet = 1;
|
||||
last_packet = 1;
|
||||
|
||||
ilclient_change_component_state(video_decode, OMX_StateExecuting);
|
||||
} else
|
||||
status = -14;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int nv_omx_decode(const unsigned char* indata, int data_len, int last) {
|
||||
if (last_packet) {
|
||||
last_packet = 0;
|
||||
|
||||
if((buf = ilclient_get_input_buffer(video_decode, 130, 1)) == NULL)
|
||||
return -22;
|
||||
|
||||
// feed data and wait until we get port settings changed
|
||||
dest = buf->pBuffer;
|
||||
|
||||
buf->nFilledLen = 0;
|
||||
|
||||
buf->nOffset = 0;
|
||||
|
||||
buf->nFlags = OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS;
|
||||
|
||||
if(first_packet) {
|
||||
buf->nFlags = OMX_BUFFERFLAG_STARTTIME;
|
||||
first_packet = 0;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(dest, indata, data_len);
|
||||
buf->nFilledLen += data_len;
|
||||
dest += data_len;
|
||||
|
||||
if (last) {
|
||||
if (buf->nFilledLen == 26) {
|
||||
buf->nFilledLen += 2;
|
||||
buf->pBuffer[24] = 0x11;
|
||||
buf->pBuffer[25] = 0xe3;
|
||||
buf->pBuffer[26] = 0x06;
|
||||
buf->pBuffer[27] = 0x50;
|
||||
}
|
||||
|
||||
if (buf->nFilledLen <=28) {
|
||||
int i;
|
||||
for (i=0; i<buf->nFilledLen; i++) {
|
||||
printf("%02x ", buf->pBuffer[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if(port_settings_changed == 0 &&
|
||||
((data_len > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) ||
|
||||
(data_len == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1,
|
||||
ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) {
|
||||
port_settings_changed = 1;
|
||||
|
||||
//OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmDataUnit, OMX_DataUnitCodedPicture);
|
||||
|
||||
if(ilclient_setup_tunnel(tunnel, 0, 0) != 0)
|
||||
return -7;
|
||||
|
||||
//ilclient_change_component_state(video_scheduler, OMX_StateExecuting);
|
||||
|
||||
// now setup tunnel to video_render
|
||||
//if(ilclient_setup_tunnel(tunnel+1, 0, 1000) != 0)
|
||||
// return -12;
|
||||
|
||||
ilclient_change_component_state(video_render, OMX_StateExecuting);
|
||||
printf("Port changed\n");
|
||||
}
|
||||
|
||||
last_packet = last;
|
||||
|
||||
if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone)
|
||||
return -6;
|
||||
}
|
||||
}
|
||||
|
||||
void nv_omx_stop(void) {
|
||||
int status = 0;
|
||||
|
||||
buf->nFilledLen = 0;
|
||||
buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS;
|
||||
|
||||
if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(list[0]), buf) != OMX_ErrorNone)
|
||||
status = -20;
|
||||
|
||||
// wait for EOS from render
|
||||
// ilclient_wait_for_event(list[1], OMX_EventBufferFlag, 90, 0, OMX_BUFFERFLAG_EOS, 0,
|
||||
// ILCLIENT_BUFFER_FLAG_EOS, 10000);
|
||||
|
||||
// need to flush the renderer to allow video_decode to disable its input port
|
||||
ilclient_flush_tunnels(tunnel, 0);
|
||||
|
||||
ilclient_disable_port_buffers(list[0], 130, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void nv_omx_destroy(void) {
|
||||
ilclient_disable_tunnel(tunnel);
|
||||
// ilclient_disable_tunnel(tunnel+1);
|
||||
// ilclient_disable_tunnel(tunnel+2);
|
||||
ilclient_teardown_tunnels(tunnel);
|
||||
|
||||
ilclient_state_transition(list, OMX_StateIdle);
|
||||
ilclient_state_transition(list, OMX_StateLoaded);
|
||||
|
||||
ilclient_cleanup_components(list);
|
||||
|
||||
OMX_Deinit();
|
||||
|
||||
ilclient_destroy(client);
|
||||
}
|
||||
8
jni/nv_omx_dec/nv_omx_dec.h
Normal file
8
jni/nv_omx_dec/nv_omx_dec.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <jni.h>
|
||||
|
||||
int nv_omx_init(void);
|
||||
|
||||
int nv_omx_decode(unsigned char* indata, int inlen, int last);
|
||||
|
||||
void nv_omx_stop(void);
|
||||
void nv_omx_destroy(void);
|
||||
47
jni/nv_omx_dec/nv_omx_dec_jni.c
Executable file
47
jni/nv_omx_dec/nv_omx_dec_jni.c
Executable file
@@ -0,0 +1,47 @@
|
||||
#include "nv_omx_dec.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
|
||||
// This function must be called before
|
||||
// any other decoding functions
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_limelight_binding_video_OmxDecoder_init(JNIEnv *env, jobject this)
|
||||
{
|
||||
return nv_omx_init();
|
||||
}
|
||||
|
||||
// This function must be called after
|
||||
// decoding is finished
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_limelight_binding_video_OmxDecoder_stop(JNIEnv *env, jobject this) {
|
||||
nv_omx_stop();
|
||||
}
|
||||
|
||||
// This function must be called after
|
||||
// decoding is finished
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_limelight_binding_video_OmxDecoder_destroy(JNIEnv *env, jobject this) {
|
||||
nv_omx_destroy();
|
||||
}
|
||||
|
||||
// packets must be decoded in order
|
||||
// the input buffer must have proper padding
|
||||
// returns 0 on success, < 0 on error
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_limelight_binding_video_OmxDecoder_decode(
|
||||
JNIEnv *env, jobject this, // JNI parameters
|
||||
jbyteArray indata, jint inoff, jint inlen, jboolean last)
|
||||
{
|
||||
jint ret;
|
||||
jbyte* jni_input_data;
|
||||
|
||||
jni_input_data = (*env)->GetByteArrayElements(env, indata, 0);
|
||||
|
||||
ret = nv_omx_decode(&jni_input_data[inoff], inlen, last);
|
||||
|
||||
// The input data isn't changed so it can be safely aborted
|
||||
(*env)->ReleaseByteArrayElements(env, indata, jni_input_data, JNI_ABORT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
17
src/com/limelight/binding/video/OmxDecoder.java
Normal file
17
src/com/limelight/binding/video/OmxDecoder.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.limelight.binding.video;
|
||||
|
||||
import com.limelight.binding.LibraryHelper;
|
||||
|
||||
public class OmxDecoder {
|
||||
static {
|
||||
LibraryHelper.loadNativeLibrary("nv_omx_dec");
|
||||
}
|
||||
|
||||
public static native int init();
|
||||
|
||||
public static native void stop();
|
||||
|
||||
public static native void destroy();
|
||||
|
||||
public static native int decode(byte[] indata, int inoff, int inlen, boolean last);
|
||||
}
|
||||
54
src/com/limelight/binding/video/OmxDecoderRenderer.java
Normal file
54
src/com/limelight/binding/video/OmxDecoderRenderer.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package com.limelight.binding.video;
|
||||
|
||||
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
||||
import com.limelight.nvstream.av.DecodeUnit;
|
||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of a video decoder and renderer.
|
||||
* @author Iwan Timmer
|
||||
*/
|
||||
public class OmxDecoderRenderer implements VideoDecoderRenderer {
|
||||
|
||||
@Override
|
||||
public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags) {
|
||||
int err = OmxDecoder.init();
|
||||
if (err != 0) {
|
||||
throw new IllegalStateException("AVC decoder initialization failure: "+err);
|
||||
}
|
||||
|
||||
System.out.println("Using omx decoding");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
System.out.println("Start omx rendering");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
OmxDecoder.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
OmxDecoder.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean submitDecodeUnit(DecodeUnit decodeUnit) {
|
||||
boolean ok = true;
|
||||
List<ByteBufferDescriptor> units = decodeUnit.getBufferList();
|
||||
for (int i=0;i<units.size();i++) {
|
||||
ByteBufferDescriptor bbd = units.get(i);
|
||||
if (ok)
|
||||
ok = (OmxDecoder.decode(bbd.data, bbd.offset, bbd.length, i == (units.size()-1)) == 0);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user