您的位置:首页 > 移动开发 > Android开发

VLC Media Player for Android

2011-03-05 21:23 621 查看
VLC.java

package vlc.android;
import android.app.Activity;
import android.content.Context;
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;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
public class VLC extends Activity {
private static final String TAG = "VLC_Activity";
private static VLC sInstance;
private LibVLC mLibVlc;
private Vout mVout;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sInstance = this;
Log.v(TAG, "Starting VLC media player...");
/* Force orientation... */
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getBoolean(VlcPreferences.ORIENTATION_MODE, true)) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
/* ... and then load the layout */
setContentView(R.layout.main);
final GLSurfaceView surfaceView = (GLSurfaceView) findViewById(R.id.surface_view);
mVout = new Vout(this);
// For debug purpose.
/* surfaceView.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR
| GLSurfaceView.DEBUG_LOG_GL_CALLS);*/
surfaceView.setRenderer(mVout);
surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
mLibVlc = new LibVLC(surfaceView, mVout);

try {
mLibVlc.init();
} catch (LibVlcException lve) {
Log.e(TAG, "Could not load underlying libvlc library: " + lve);
mLibVlc = null;
/// FIXME Abort cleanly, alert user
System.exit(1);
}
}
/** Resume the application */
public void onResume() {
super.onResume();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
// FIXME Make sure we have the requested orientation
boolean preferredMode  = !prefs.getBoolean(VlcPreferences.ORIENTATION_MODE, true);
boolean currentMode    = this.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
if (currentMode != preferredMode) {
// This will probably recreate the activity
if (preferredMode) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}
// FIXME
// if (prefs.getBoolean("hud lock", false))
}
/** Called when the activity is finally destroyed */
@Override
public void onDestroy() {
Log.v (TAG, "VLC is exiting");
if (mLibVlc != null) {
mLibVlc.destroy();
}
super.onDestroy();
}
/** Handle menu key
* Note: this is called only once
* Implement onPrepareOptionsMenu() to recreate the menu every time it is
* displayed.
*/
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.stop_menu, menu);
return true;
}
/** Handle menu item selection */
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Main menu entries
case R.id.menuOpen:
// FIXME showAboutBox
Intent i = new Intent(this, SimpleFileBrowser.class);
startActivityForResult(i, 0);
return true;

case R.id.menuAbout:
// FIXME showAboutBox
return true;
case R.id.menuQuit:
finish();
return true;
// Options menu
case R.id.menuOptions:
startActivity(new Intent(this, VlcPreferences.class));
return true;
default:
// Handle submenus...
return super.onOptionsItemSelected(item);
}
}
/** Get the global Activity context for use elsewhere */
public static Context getActivityContext() {
// The Activity is a context itself
return sInstance;
}

/** Activity result callback */
protected void onActivityResult (int requestCode, int resultCode, Intent data)
{
Bundle extras = data.getExtras();
if (requestCode == 0 && resultCode == RESULT_OK) {
String filePath = extras.getString("filePath");
mLibVlc.readMedia(filePath);
}
}
}


LibVLC.java

package vlc.android;
import android.opengl.GLSurfaceView;
import android.util.Log;
import android.view.Surface;
public class LibVLC {
private static final String TAG = "LibVLC";
/** libVLC instance C pointer */
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;
/* Load library before object instantiation */
static {
try {
System.loadLibrary("vlcjni");
} catch (UnsatisfiedLinkError ule) {
Log.e(TAG, "Can't load vlcjni library: " + ule);
/// FIXME Alert user
System.exit(1);
} catch (SecurityException se) {
Log.e(TAG, "Encountered a security issue when loading vlcjni library: " + se);
/// FIXME Alert user
System.exit(1);
}
}
/**
* Constructor
*/
public LibVLC(GLSurfaceView s, Vout v)
{
mSurfaceView = s;
mVout = v;
mAout = new Aout();
};
/**
* Destructor:
* It is bad practice to rely on them, so please don't forget to call
* destroy() before exiting.
*/
public void finalize()
{
if (mLibVlcInstance != 0) {
Log.d(TAG, "LibVLC is was destroyed yet before finalize()");
destroy();
}
}
/**
* Give to LibVLC the surface to draw the video.
* @param f the surface to draw
*/
public native void setSurface(Surface f);
/**
* Initialize the libVLC class
*/
public void init() throws LibVlcException
{
Log.v(TAG, "Initializing LibVLC");
nativeInit();
}
/**
* Destroy this libVLC instance
* @note You must call it before exiting
*/
public void destroy()
{
Log.v(TAG, "Destroying LibVLC instance");
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();
}
/**
* Open the Java audio output.
* This function is called by the native code
*/
public void initAout(int sampleRateInHz, int channels, int samples)
{
Log.d(TAG, "Opening the java audio output");
mAout.init(sampleRateInHz, channels, samples);
}
/**
* Play an audio buffer taken from the native code
* This function is called by the native code
*/
public void playAudio(byte[] audioData, int bufferSize, int nbSamples)
{
Log.d(TAG, "Playing an audio buffer in Java");
mAout.playBuffer(audioData, bufferSize, nbSamples);
}
/**
* Close the Java audio output
* This function is called by the native code
*/
public void closeAout()
{
Log.d(TAG, "Closing the java audio output");
mAout.release();
}
/**
* Read a media.
*/
public void readMedia(String mrl)
{
Log.v(TAG, "Reading " + mrl);
readMedia(mLibVlcInstance, mrl);
}
/**
* Initialize the libvlc C library
* @return a pointer to the libvlc instance
*/
private native void nativeInit() throws LibVlcException;
/**
* Close the libvlc C library
* @note mLibVlcInstance should be 0 after a call to destroy()
*/
private native void nativeDestroy();
/**
* Read a media
* @param instance: the instance of libVLC
* @param mrl: the media mrl
*/
private native void readMedia(int instance, String mrl);
/**
* Get the libVLC version
* @return the libVLC version string
*/
public native String version();
/**
* Get the libVLC compiler
* @return the libVLC compiler string
*/
public native String compiler();
/**
* Get the libVLC changeset
* @return the libVLC changeset string
*/
public native String changeset();
}


Aout.java

package vlc.android;
import android.util.Log;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
public class Aout {
/**
* Java side of the audio output module for Android.
* Uses an AudioTrack to play decoded audio buffers.
*
* TODO Use MODE_STATIC instead of MODE_STREAM with a MemoryFile (ashmem)
*/
private AudioTrack mAudioTrack;
private static final String TAG = "LibVLC/aout";
public void init(int sampleRateInHz, int channels, int samples) {
Log.d(TAG, sampleRateInHz + ", " + channels + ", " + samples + "=>" + channels*samples);
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRateInHz,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
channels * samples * 2,
AudioTrack.MODE_STREAM);
}
public void release() {
Log.d(TAG, "Stopping audio playback");
// mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
public void playBuffer(byte[] audioData, int bufferSize, int nbSamples) {
Log.d(TAG, "Buffer size: " + bufferSize + " nb samples: " + nbSamples);
if (mAudioTrack.write(audioData, 0, bufferSize) != bufferSize)
{
Log.w(TAG, "Could not write all the samples to the audio device");
}
mAudioTrack.play();
}
}


Vout.java

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0 *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package vlc.android;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.util.Log;

public class Vout implements GLSurfaceView.Renderer{
private static final String TAG = "LibVLC/vout";
/* Video orientation parameters */
public enum Orientation {
HORIZONTAL,
VERTICAL
}
public Vout(Context context) {
mContext = context;
mQuad = new Quad();
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
/*
* By default, OpenGL enables features that improve quality
* but reduce performance. One might want to tweak that
* especially on software renderer.
*/
gl.glDisable(GL10.GL_DITHER);
/*
* Some one-time OpenGL initialization can be made here
* probably based on features of this particular context
*/
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
GL10.GL_FASTEST);
gl.glClearColor(.5f, .5f, .5f, 1);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glEnable(GL10.GL_TEXTURE_2D);
/*
* Create our texture. This has to be done each time the
* surface is created.
*/
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
mTextureID = textures[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE);
InputStream is = mContext.getResources()
.openRawResource(R.raw.cone);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch(IOException e) {
// Ignore.
}
}
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
public void onDrawFrame(GL10 gl) {
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_MODULATE);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

if (mustInit)
{
texWidth = getAlignedSize(frameWidth);
texHeight = getAlignedSize(frameHeight);
byte[] texData = new byte[texWidth * texHeight * 2];
ByteBuffer byteBufferInit = ByteBuffer.wrap(texData);

gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGB, texWidth,
texHeight, 0, GL10.GL_RGB, GL10.GL_UNSIGNED_SHORT_5_6_5,
byteBufferInit);

mQuad.computeTexCoord(texWidth, texHeight, frameWidth, frameHeight);
onSurfaceChanged(gl, surfaceWidth, surfaceHeight); // Compute AspectRatio
/* Wrap the image buffer to the byte buffer. */
byteBuffer = ByteBuffer.wrap(image);
mustInit = false;
}

if (hasReceivedFrame)
{
gl.glTexSubImage2D(GL10.GL_TEXTURE_2D, 0, 0, 0, frameWidth,
frameHeight, GL10.GL_RGB, GL10.GL_UNSIGNED_SHORT_5_6_5,
byteBuffer);
}
mQuad.draw(gl);
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
gl.glViewport(0, 0, w, h);
/*
* Retain surface size, to be able to correct the AspectRatio
* on demand when a new video starts
*/
surfaceWidth = w;
surfaceHeight = h;
/*
* Set our projection matrix. This doesn't have to be done
* each time we draw, but usually a new projection needs to
* be set when the viewport is resized.
*/
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
/*
* glFrustumf expand the clipping volume vertically or horizontally : it adds bars.
* how big those bars should be depends on both the video ratio and the surface ratio
*/
float vRatio = frameHeight > 0 ? (float) frameWidth / frameHeight : 1f;
float sRatio = (float) surfaceWidth / surfaceHeight;
if (sRatio > vRatio) {
float ratio = sRatio / vRatio;
gl.glFrustumf(-ratio, ratio, -1f, 1f, 1f, 2f);
}
else
{
float ratio = vRatio / sRatio;
gl.glFrustumf(-1f, 1f, -ratio, ratio, 1f, 2f);
}
}

private int getAlignedSize(int i_size)
{
/* Return the nearest power of 2 */
int i_result = 1;
while(i_result < i_size)
i_result *= 2;
return i_result;
}
/* Manually change video orientation */
public void setOrientation (Orientation orient) {
// FIXME
// preferences.set() ...
Util.toaster("Orientation:/nNot implemented yet");
}
private Context mContext;
private Quad mQuad;
private int mTextureID;
public boolean mustInit = false;
public boolean hasReceivedFrame = false;
private int surfaceWidth;
private int surfaceHeight;
public int frameWidth;
public int frameHeight;
private int texWidth;
private int texHeight;
public byte[] image;
private ByteBuffer byteBuffer;
}
class Quad {
public Quad() {
computeTexCoord(1, 1, 1, 1);
}

public void computeTexCoord(int texWidth, int texHeight,
int frameWidth, int frameHeight) {
// Buffers to be passed to gl*Pointer() functions
// must be direct, i.e., they must be placed on the
// native heap where the garbage collector cannot
// move them.
//
// Buffers with multi-byte datatypes (e.g., short, int, float)
// must have their byte order set to native order
ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);
vbb.order(ByteOrder.nativeOrder());
mFVertexBuffer = vbb.asFloatBuffer();
ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);
tbb.order(ByteOrder.nativeOrder());
mTexBuffer = tbb.asFloatBuffer();
ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);
ibb.order(ByteOrder.nativeOrder());
mIndexBuffer = ibb.asShortBuffer();
float[] coords = {
// X, Y, Z
-1f, -1f, -1f,
1f, -1f, -1f,
-1f, 1f, -1f,
1f, 1f, -1f
};

float f_width = (float)frameWidth / texWidth;
float f_height = (float)frameHeight / texHeight;

float[] tex_coords = {
// X, Y, Z
0f,      f_height, -1f,
f_width, f_height, -1f,
0f,      0f,       -1f,
f_width, 0f,       -1f
};
for (int i = 0; i < VERTS; i++) {
for(int j = 0; j < 3; j++) {
mFVertexBuffer.put(coords[i*3+j]);
}
}
for (int i = 0; i < VERTS; i++) {
for(int j = 0; j < 2; j++) {
mTexBuffer.put(tex_coords[i*3+j]);
}
}
for(int i = 0; i < VERTS; i++) {
mIndexBuffer.put((short) i);
}
mFVertexBuffer.position(0);
mTexBuffer.position(0);
mIndexBuffer.position(0);
}
public void draw(GL10 gl) {
gl.glFrontFace(GL10.GL_CCW);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexBuffer);
gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, VERTS,
GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
}
private final static int VERTS = 4;
private FloatBuffer mFVertexBuffer;
private FloatBuffer mTexBuffer;
private ShortBuffer mIndexBuffer;
}


libvlcjni.c

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <jni.h>
#include <vlc/vlc.h>
#include "libvlcjni.h"
#include "aout.h"
#include "vout.h"
#define LOG_TAG "VLC/JNI/main"
#include "log.h"
/* Pointer to the Java virtual machine
* Note: It's okay to use a static variable for the VM pointer since there
* can only be one instance of this shared library in a single VM
*/
JavaVM *myVm;

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
// Keep a reference on the Java VM.
myVm = vm;
LOGD("JNI interface loaded.");
return JNI_VERSION_1_2;
}

void Java_vlc_android_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
{
const char *argv[] = {"-I", "dummy", "-vvv", "--no-plugins-cache",
"--no-drop-late-frames",
"--aout", "amem"};
libvlc_instance_t *instance =
libvlc_new_with_builtins(sizeof(argv) / sizeof(*argv),
argv, vlc_builtins_modules);
jclass clazz = (*env)->GetObjectClass(env, thiz);
jfieldID field = (*env)->GetFieldID(env, clazz,
"mLibVlcInstance", "I");
(*env)->SetIntField(env, thiz, field, (jint) instance);
if (!instance)
{
jclass exc = (*env)->FindClass(env, "vlc/android/LibVlcException");
(*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
}
LOGI("LibVLC initialized: %p", instance);
return;
}

void Java_vlc_android_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
{
jclass clazz = (*env)->GetObjectClass(env, thiz);
jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
"mMediaPlayerInstance", "I");
jint mediaPlayer = (*env)->GetIntField(env, thiz, fieldMP);
if (mediaPlayer != 0)
{
libvlc_media_player_t *mp = (libvlc_media_player_t*) mediaPlayer;
libvlc_media_player_stop(mp);
libvlc_media_player_release(mp);
(*env)->SetIntField(env, thiz, fieldMP, 0);
}
jfieldID field = (*env)->GetFieldID(env, clazz, "mLibVlcInstance", "I");
jint libVlcInstance = (*env)->GetIntField(env, thiz, field);
if (!libVlcInstance)
return; // Already destroyed
libvlc_instance_t *instance = (libvlc_instance_t*) libVlcInstance;
libvlc_release(instance);
(*env)->SetIntField(env, thiz, field, 0);
}

void Java_vlc_android_LibVLC_readMedia(JNIEnv *env, jobject thiz,
jint instance, jstring mrl)
{
jboolean isCopy;
const char *psz_mrl = (*env)->GetStringUTFChars(env, mrl, &isCopy);
/* Create a new item */
libvlc_media_t *m = libvlc_media_new_path((libvlc_instance_t*)instance,
psz_mrl);
/* Create a media player playing environment */
libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)instance);
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);
/* Keep a pointer to this media player */
jclass clazz = (*env)->GetObjectClass(env, thiz);
jfieldID field = (*env)->GetFieldID(env, clazz,
"mMediaPlayerInstance", "I");
(*env)->SetIntField(env, thiz, field, (jint) mp);
/* Play the media. */
libvlc_media_player_play(mp);
//libvlc_media_player_release(mp);
(*env)->ReleaseStringUTFChars(env, mrl, psz_mrl);
}

jstring Java_vlc_android_LibVLC_version(JNIEnv* env, jobject thiz)
{
return (*env)->NewStringUTF(env, libvlc_get_version());
}
jstring Java_vlc_android_LibVLC_compiler(JNIEnv* env, jobject thiz)
{
return (*env)->NewStringUTF(env, libvlc_get_compiler());
}
jstring Java_vlc_android_LibVLC_changeset(JNIEnv* env, jobject thiz)
{
return (*env)->NewStringUTF(env, libvlc_get_changeset());
}


aout.c

#include <stdio.h>
#include <assert.h>
#include <vlc/vlc.h>
#include "aout.h"
#define LOG_TAG "VLC/JNI/aout"
#include "log.h"
// An audio frame will contain FRAME_SIZE samples
#define FRAME_SIZE (4096*2)
/** Unique Java VM instance, as defined in libvlcjni.c */
extern JavaVM *myVm;
void aout_open (void **opaque, unsigned int *rate, unsigned int *nb_channels,
unsigned int *fourCCFormat, unsigned int *nb_samples)
{
LOGV("Opening the JNI audio output");
aout_sys_t *p_sys;
JNIEnv *p_env;
// Replace opaque by p_sys and keep the libvlc pointe
p_sys = calloc (1, sizeof (*p_sys));
if (!p_sys)
{
*opaque = NULL;
return; // OOM
}
// Keep a reference to our Java object and return aout_sys_t
p_sys->j_libVlc = (jobject) *opaque;
*opaque         = (void*) p_sys;
LOGV ("Parameters: %u channels, %u samples/frame, FOURCC '%4.4s', "
"sample rate: %uHz",
*nb_channels, *nb_samples, (const char *) fourCCFormat, *rate);
// Attach the thread to the VM. Keep this JNIEnv for aout_close()
if ((*myVm)->AttachCurrentThread (myVm, &p_env, NULL) != 0)
{
LOGE("Couldn't attach the display thread to the JVM !");
return;
}
p_sys->p_env = p_env;
// Call the init function.
jclass cls = (*p_env)->GetObjectClass (p_env, p_sys->j_libVlc);
jmethodID methodIdInitAout = (*p_env)->GetMethodID (p_env, cls,
"initAout", "(III)V");
if (!methodIdInitAout)
{
LOGE ("Method initAout() could not be found!");
(*myVm)->DetachCurrentThread (myVm);
*opaque = NULL;
free (p_sys);
return;
}
LOGV ("Fixed number of channels to 2, number of samples to %d",
FRAME_SIZE);
*nb_channels = 2;
*nb_samples  = FRAME_SIZE;
(*p_env)->CallVoidMethod (p_env, p_sys->j_libVlc, methodIdInitAout,
*rate, *nb_channels, *nb_samples);
if ((*p_env)->ExceptionCheck (p_env))
{
LOGE ("Unable to create audio player!");
#ifndef NDEBUG
(*p_env)->ExceptionDescribe (p_env);
#endif
(*p_env)->ExceptionClear (p_env);
*opaque = NULL;
free (p_sys);
return;
}
/* Create a new byte array to store the audio data. */
jbyteArray byteArray = (*p_env)->NewByteArray (p_env,
*nb_channels *
*nb_samples *
sizeof (uint16_t) /* =2 */);
if (byteArray == NULL)
{
LOGE("Couldn't allocate the Java byte array to store the audio data!");
(*myVm)->DetachCurrentThread (myVm);
*opaque = NULL;
free (p_sys);
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 (p_sys->byteArray == NULL)
{
LOGE ("Couldn't create the global reference!");
(*myVm)->DetachCurrentThread (myVm);
*opaque = NULL;
free (p_sys);
return;
}
/* The local reference is no longer useful. */
(*p_env)->DeleteLocalRef (p_env, byteArray);
// Get the play methodId
p_sys->play = (*p_env)->GetMethodID (p_env, cls, "playAudio", "([BII)V");
assert (p_sys->play != NULL);
}
/**
* Play an audio sample
**/
void aout_play (void *opaque, unsigned char *buffer,
size_t bufferSize, unsigned int nb_samples)
{
aout_sys_t *p_sys = (aout_sys_t*) opaque;
JNIEnv *p_env;
/* How ugly: we constantly attach/detach this thread to/from the JVM
* because it will be killed before aout_close is called.
* aout_close will actually be called in an different thread!
*/
(*myVm)->AttachCurrentThread (myVm, &p_env, NULL);
(*p_env)->SetByteArrayRegion (p_env, p_sys->byteArray, 0,
bufferSize, (jbyte*) buffer);
if ((*p_env)->ExceptionCheck (p_env))
{
// This can happen if for some reason the size of the input buffer
// is larger than the size of the output buffer
LOGE ("An exception occurred while calling SetByteArrayRegion");
(*p_env)->ExceptionDescribe (p_env);
(*p_env)->ExceptionClear (p_env);
return;
}
(*p_env)->CallVoidMethod (p_env, p_sys->j_libVlc, p_sys->play,
p_sys->byteArray, bufferSize, nb_samples);
// FIXME: check for errors
(*myVm)->DetachCurrentThread (myVm);
}
void aout_close(void *opaque)
{
LOGI ("Closing audio output");
aout_sys_t *p_sys = (aout_sys_t*) opaque;
if (!p_sys)
return;
if (p_sys->byteArray)
{
// Want a crash? Call this function! But whyyyyy???
// Anyway, one more good reason to create the buffer in pure Java
//(*p_sys->p_env)->DeleteGlobalRef (p_sys->p_env, p_sys->byteArray);
}
(*myVm)->DetachCurrentThread (myVm);
free (p_sys);
}


vout.c

#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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: