Commit 95bd4770 authored by Ludovic Fauvet's avatar Ludovic Fauvet
Browse files

Replace the current vout by a native surface vout

This commit merges the vout module made by Ming Hu in his github project
and removes the current vmem/opengl code.

This new vout is much faster than the previous: It is able to draw and
scale a 720p-h264 video with only few frame drops on a Nexus One.
The counterpart is that it uses some non-public and non-documented APIs.

Tested on a Nexus One running Android 2.3.4 and a Xoom tablet running
Android 3.0 (Honeycomb).
parent 309bcfcf
......@@ -51,7 +51,7 @@ $(APK_MK):
printf 'LOCAL_PATH := $$(call my-dir)\n' > $(APK_MK); \
printf "include \$$(CLEAR_VARS)\n" >> $(APK_MK); \
printf "LOCAL_MODULE := libvlcjni\n" >> $(APK_MK); \
printf "LOCAL_SRC_FILES := libvlcjni.c vout.c aout.c thumbnailer.c\n" >> $(APK_MK); \
printf "LOCAL_SRC_FILES := libvlcjni.c aout.c thumbnailer.c\n" >> $(APK_MK); \
printf "LOCAL_C_INCLUDES := \$$(LOCAL_PATH)/../../../../../include\n" >> $(APK_MK); \
printf "LOCAL_LDLIBS := -L$$vlc_contrib/lib \\\\\n" >> $(APK_MK); \
printf "\t-L$$ANDROID_NDK/platforms/android-8/arch-arm/usr/lib \\\\\n" >> $(APK_MK); \
......@@ -59,7 +59,7 @@ $(APK_MK):
printf "\t$$prefix$$VLC_BUILD_DIR/compat/.libs/libcompat.a \\\\\n" >> $(APK_MK); \
printf "\t$$prefix$$VLC_BUILD_DIR/src/.libs/libvlc.a \\\\\n" >> $(APK_MK); \
printf "\t$$prefix$$VLC_BUILD_DIR/src/.libs/libvlccore.a \\\\\n" >> $(APK_MK); \
printf "\t-ldl -lz -lm -logg -lvorbisenc -lvorbis -lFLAC -lspeex -ltheora -lavformat -lavcodec -lavcore -lavutil -lpostproc -lswscale -lmpeg2 -lgcc -lpng -ldca -ldvbpsi -ltwolame -lkate -llog -la52 -lliveMedia -lUsageEnvironment -lBasicUsageEnvironment -lgroupsock\n" >> $(APK_MK); \
printf "\t-ldl -lz -lm -logg -lvorbisenc -lvorbis -lFLAC -lspeex -ltheora -lavformat -lavcodec -lavcore -lavutil -lpostproc -lswscale -lmpeg2 -lgcc -lpng -ldca -ldvbpsi -ltwolame -lkate -llog -la52 -lliveMedia -lUsageEnvironment -lBasicUsageEnvironment -lgroupsock -lpixman-1\n" >> $(APK_MK); \
printf "include \$$(BUILD_SHARED_LIBRARY)\n" >> $(APK_MK)
$(LIBVLCJNI): $(JNI_SOURCES) $(APK_MK)
......
......@@ -39,6 +39,7 @@ sh ../configure --host=arm-eabi-linux --build=x86_64-unknown-linux \
--enable-swscale \
--enable-avcodec \
--enable-avformat \
--enable-android-vout \
--disable-vlc \
--enable-live555 --enable-realrtsp \
--disable-xcb --disable-dbus --disable-vcd --disable-v4l2 --disable-atmo --disable-qt4 --disable-skins2 --disable-mad --disable-mkv --disable-live555 --disable-libgcrypt --disable-lua --disable-mtp --disable-dvdread --disable-alsa --disable-sdl --disable-sdl-image --disable-taglib --disable-notify --disable-freetype --disable-sqlite --disable-udev --disable-caca --disable-glx --disable-egl --disable-gl --disable-libxml2 --disable-svg
......@@ -2,17 +2,28 @@
#include <string.h>
#include <assert.h>
#include <jni.h>
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <pthread.h>
#include <vlc/vlc.h>
#include <jni.h>
#include "libvlcjni.h"
#include "aout.h"
#include "vout.h"
#define LOG_TAG "VLC/JNI/main"
#include "log.h"
#define NAME1(CLZ, FUN) Java_##CLZ##_##FUN
#define NAME2(CLZ, FUN) NAME1(CLZ, FUN)
#define NAME(FUN) NAME2(CLASS, FUN)
jint getMediaPlayer(JNIEnv *env, jobject thiz)
{
jclass clazz = (*env)->GetObjectClass(env, thiz);
......@@ -43,16 +54,64 @@ jboolean releaseMediaPlayer(JNIEnv *env, jobject thiz)
*/
JavaVM *myVm;
static pthread_mutex_t vout_android_lock;
static void *vout_android_surf = NULL;
void LockSurface() {
pthread_mutex_lock(&vout_android_lock);
}
void UnlockSurface() {
pthread_mutex_unlock(&vout_android_lock);
}
void *GetSurface() {
return vout_android_surf;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
// Keep a reference on the Java VM.
myVm = vm;
pthread_mutex_init(&vout_android_lock, NULL);
LOGD("JNI interface loaded.");
return JNI_VERSION_1_2;
}
void JNI_OnUnload(JavaVM* vm, void* reserved) {
pthread_mutex_destroy(&vout_android_lock);
}
void Java_vlc_android_LibVLC_attachSurface(JNIEnv *env, jobject thiz, jobject surf, jint width, jint height) {
jclass clz;
jfieldID fid;
jthrowable exp;
pthread_mutex_lock(&vout_android_lock);
//vout_android_ref = (*env)->NewGlobalRef(env, surf);
clz = (*env)->GetObjectClass(env, surf);
fid = (*env)->GetFieldID(env, clz, "mSurface", "I");
if (fid == NULL) {
exp = (*env)->ExceptionOccurred(env);
if (exp) {
(*env)->DeleteLocalRef(env, exp);
(*env)->ExceptionClear(env);
}
fid = (*env)->GetFieldID(env, clz, "mNativeSurface", "I");
}
vout_android_surf = (void*)(*env)->GetIntField(env, surf, fid);
(*env)->DeleteLocalRef(env, clz);
pthread_mutex_unlock(&vout_android_lock);
}
void Java_vlc_android_LibVLC_detachSurface(JNIEnv *env, jobject thiz) {
pthread_mutex_lock(&vout_android_lock);
//(*env)->DeleteGlobalRef(env, vout_android_ref);
vout_android_surf = NULL;
pthread_mutex_unlock(&vout_android_lock);
}
void Java_vlc_android_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
{
......@@ -114,12 +173,6 @@ void Java_vlc_android_LibVLC_readMedia(JNIEnv *env, jobject thiz,
jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz);
libvlc_media_player_set_media(mp, m);
libvlc_video_set_format_callbacks(mp, vout_format, vout_cleanup);
libvlc_video_set_callbacks(mp, vout_lock, vout_unlock, vout_display,
(void*) myJavaLibVLC);
libvlc_audio_set_callbacks(mp, aout_open, aout_play, aout_close,
(void*) myJavaLibVLC);
/* No need to keep the media now */
libvlc_media_release(m);
......
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <jni.h>
#include <vlc/vlc.h>
#include "vout.h"
#define LOG_TAG "LibVLC/JNI/vout"
#include "log.h"
/** Unique Java VM instance, as defined in libvlcjni.c */
extern JavaVM *myVm;
unsigned vout_format(void **opaque, char *chroma,
unsigned *width, unsigned *height,
unsigned *pitches,
unsigned *lines)
{
/* So far, *opaque is a pointer to the libvlc instance */
jobject libVlc = (jobject) *opaque;
assert (libVlc != NULL);
/* Let's replace opaque by p_sys and store the pointer to libVlc */
vout_sys_t **pp_sys = (vout_sys_t **) opaque;
*pp_sys = calloc(1, sizeof(vout_sys_t));
if (*pp_sys == NULL)
return 0;
vout_sys_t *p_sys = *pp_sys;
p_sys->j_libVlc = libVlc;
p_sys->i_frameWidth = *width;
p_sys->i_frameHeight = *height;
p_sys->i_frameSize = p_sys->i_frameWidth * p_sys->i_frameHeight * 2;
p_sys->p_frameData = calloc(1, p_sys->i_frameSize);
if (p_sys->p_frameData == NULL)
return 0;
strcpy(chroma, "RV16");
*pitches = p_sys->i_frameWidth * 2;
*lines = p_sys->i_frameHeight;
return 1;
}
void vout_cleanup(void *opaque)
{
vout_sys_t *p_sys = opaque;
if (p_sys->byteArray != NULL)
(*p_sys->p_env)->DeleteLocalRef(p_sys->p_env, p_sys->byteArray);
if (p_sys->b_attached)
{
if ((*myVm)->DetachCurrentThread(myVm) != 0)
{
LOGE("Couldn't detach the display thread from the JVM!");
return;
}
}
free(p_sys);
}
void *vout_lock(void *opaque, void **pixels)
{
vout_sys_t *p_sys = (vout_sys_t *)opaque;
*pixels = p_sys->p_frameData;
return NULL;
}
void vout_unlock(void *opaque, void *picture, void *const *p_pixels)
{
// Nothing to do here until now.
}
void vout_display(void *opaque, void *picture)
{
vout_sys_t *p_sys = (vout_sys_t *)opaque;
JNIEnv *p_env = p_sys->p_env;
if (!p_sys->b_attached)
{
if ((*myVm)->AttachCurrentThread(myVm, &p_env, NULL) != 0)
{
LOGE("Couldn't attach the display thread to the JVM !");
return;
}
p_sys->b_attached = 1;
// Save the environment refernce.
p_sys->p_env = p_env;
jclass cls = (*p_env)->GetObjectClass(p_env, p_sys->j_libVlc);
jmethodID methodIdSetVoutSize =
(*p_env)->GetMethodID(p_env, cls, "setVoutSize", "(II)V");
if(methodIdSetVoutSize == 0)
{
LOGE("Method setVoutParams not found !");
return;
}
// Transmit to Java the vout size.
(*p_env)->CallVoidMethod(p_env, p_sys->j_libVlc, methodIdSetVoutSize,
p_sys->i_frameWidth, p_sys->i_frameHeight);
p_sys->methodIdDisplay = (*p_env)->GetMethodID(p_env, cls,
"displayCallback",
"([B)V");
if (!p_sys->methodIdDisplay)
{
LOGE("Method displayCallback not found !");
return;
}
/* Create a new byte array to store the frame data. */
jbyteArray byteArray = (*p_env)->NewByteArray(p_env, p_sys->i_frameSize);
if (byteArray == NULL)
{
LOGE("Couldn't allocate the Java byte array to store the frame !");
return;
}
/* Use a global reference to not reallocate memory each time we run
the display function. */
p_sys->byteArray = (*p_env)->NewGlobalRef(p_env, byteArray);
if (byteArray == NULL)
{
LOGE("Couldn't create the global reference !");
return;
}
/* The local reference is no longer useful. */
(*p_env)->DeleteLocalRef(p_env, byteArray);
}
// Fill the image buffer for debug purpose.
//memset(p_sys->p_imageData, 255, p_sys->i_texSize / 2);
(*p_env)->SetByteArrayRegion(p_env, p_sys->byteArray, 0,
p_sys->i_frameSize,
(jbyte *)p_sys->p_frameData);
(*p_env)->CallVoidMethod(p_env, p_sys->j_libVlc,
p_sys->methodIdDisplay, p_sys->byteArray);
}
#ifndef LIBVLCJNI_VOUT_H
#define LIBVLCJNI_VOUT_H
typedef struct vout_sys_t
{
unsigned i_frameWidth;
unsigned i_frameHeight;
unsigned i_frameSize;
char *p_frameData;
jbyteArray byteArray;
jobject j_libVlc; // Pointer to the LibVLC Java object.
JNIEnv *p_env;
unsigned char b_attached; // Thread is attached to Java VM
jmethodID methodIdDisplay;
}vout_sys_t;
unsigned getAlignedSize(unsigned size);
unsigned vout_format(void **opaque, char *chroma,
unsigned *width, unsigned *height,
unsigned *pitches,
unsigned *lines);
void vout_cleanup(void *opaque);
void *vout_lock(void *opaque, void **pixels);
void vout_unlock(void *opaque, void *picture, void *const *p_pixels);
void vout_display(void *opaque, void *picture);
#endif // LIBVLCJNI_VOUT_H
......@@ -4,11 +4,9 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<android.opengl.GLSurfaceView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/surface_view"/>
<SurfaceView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/surface_view"
/>
</LinearLayout>
......@@ -13,6 +13,10 @@ public class Aout {
* TODO Use MODE_STATIC instead of MODE_STREAM with a MemoryFile (ashmem)
*/
public Aout()
{
}
private AudioTrack mAudioTrack;
private static final String TAG = "LibVLC/aout";
......
package vlc.android;
import java.util.concurrent.BrokenBarrierException;
import android.opengl.GLSurfaceView;
import android.util.Log;
import android.view.Surface;
......@@ -15,11 +12,11 @@ public class LibVLC {
private int mLibVlcInstance = 0; // Read-only, reserved for JNI
private int mMediaPlayerInstance = 0; // Read-only, reserved for JNI
private GLSurfaceView mSurfaceView;
private Vout mVout;
private Aout mAout;
public native void attachSurface(Surface surface, int width, int height);
public native void detachSurface();
/* Load library before object instantiation */
static {
try {
......@@ -38,27 +35,20 @@ public class LibVLC {
/**
* Singleton constructors
*/
public static LibVLC getInstance(GLSurfaceView s, Vout v)
public static LibVLC getInstance()
{
if (instance == null) {
/* First call */
instance = new LibVLC(s, v);
instance = new LibVLC();
}
return instance;
}
public static LibVLC getInstance()
{
return instance;
}
/**
* Constructor
* It is private because this class is a singleton.
*/
private LibVLC(GLSurfaceView s, Vout v) {
mSurfaceView = s;
mVout = v;
private LibVLC() {
mAout = new Aout();
}
......@@ -100,36 +90,6 @@ public class LibVLC {
nativeDestroy();
}
/**
* Transmit to the renderer the size of the video.
* This function is called by the native code.
* @param frameWidth
* @param frameHeight
*/
public void setVoutSize(int frameWidth, int frameHeight)
{
mVout.frameWidth = frameWidth;
mVout.frameHeight = frameHeight;
mVout.mustInit = true;
}
/**
* Transmit the image given by VLC to the renderer.
* This function is called by the native code.
* @param image the image data.
*/
public void displayCallback(byte[] image)
{
mVout.image = image;
mVout.hasReceivedFrame = true;
mSurfaceView.requestRender();
try {
mVout.mBarrier.await();
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
}
/**
* Open the Java audio output.
* This function is called by the native code
......
......@@ -7,7 +7,6 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.widget.Button;
......@@ -16,6 +15,8 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
......@@ -25,7 +26,8 @@ public class VLC extends Activity {
private static VLC sInstance;
private LibVLC mLibVlc;
private Vout mVout;
private SurfaceView mSurfaceViewVideo;
private SurfaceHolder mSurfaceHolderVideo;
/** Called when the activity is first created. */
@Override
......@@ -46,17 +48,29 @@ public class VLC extends Activity {
/* ... and then load the layout */
setContentView(R.layout.main);
final GLSurfaceView surfaceView = (GLSurfaceView) findViewById(R.id.surface_view);
mSurfaceViewVideo = (SurfaceView) findViewById(R.id.surface_view);
mSurfaceHolderVideo = mSurfaceViewVideo.getHolder();
mVout = new Vout(this);
mLibVlc = LibVLC.getInstance();
// For debug purpose.
/* surfaceView.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR
| GLSurfaceView.DEBUG_LOG_GL_CALLS);*/
surfaceView.setRenderer(mVout);
surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
mSurfaceHolderVideo.addCallback(new SurfaceHolder.Callback() {
mLibVlc = LibVLC.getInstance(surfaceView, mVout);
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mLibVlc.attachSurface(holder.getSurface(), width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mLibVlc.detachSurface();
}
});
try {
mLibVlc.init();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment