mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-14 05:12:09 +00:00
playback/player: Add start of an Android player
This commit is contained in:
parent
10eabda41b
commit
be4f88ef41
6 changed files with 1109 additions and 0 deletions
79
playback/player/android/AndroidManifest.xml
Normal file
79
playback/player/android/AndroidManifest.xml
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.freedesktop.gstreamer.play"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="14"/>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
<uses-feature android:glEsVersion="0x00020000"/>
|
||||||
|
<application android:label="@string/app_name">
|
||||||
|
<activity android:name=".Play"
|
||||||
|
android:label="@string/app_name">
|
||||||
|
<!-- <intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>-->
|
||||||
|
|
||||||
|
<!-- Local files whose MIME type is known to Android -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:mimeType="audio/*" />
|
||||||
|
<data android:mimeType="video/*" />
|
||||||
|
<data android:mimeType="image/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Local files with unknown MIME type.
|
||||||
|
The list of extensions and supported protocols can certainly be extended. -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="file" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
<data android:pathPattern=".*\\.avi" />
|
||||||
|
<data android:pathPattern=".*\\.AVI" />
|
||||||
|
<data android:pathPattern=".*\\.mkv" />
|
||||||
|
<data android:pathPattern=".*\\.MKV" />
|
||||||
|
<data android:pathPattern=".*\\.webm" />
|
||||||
|
<data android:pathPattern=".*\\.WEBM" />
|
||||||
|
<data android:pathPattern=".*\\.ogv" />
|
||||||
|
<data android:pathPattern=".*\\.OGV" />
|
||||||
|
<data android:pathPattern=".*\\.mp4" />
|
||||||
|
<data android:pathPattern=".*\\.MP4" />
|
||||||
|
<data android:pathPattern=".*\\.mov" />
|
||||||
|
<data android:pathPattern=".*\\.MOV" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Remote files. These typically have unknown MIME type.
|
||||||
|
The list of extensions and supported protocols can certainly be extended. -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="http" />
|
||||||
|
<data android:pathPattern=".*\\.avi" />
|
||||||
|
<data android:pathPattern=".*\\.AVI" />
|
||||||
|
<data android:pathPattern=".*\\.mkv" />
|
||||||
|
<data android:pathPattern=".*\\.MKV" />
|
||||||
|
<data android:pathPattern=".*\\.webm" />
|
||||||
|
<data android:pathPattern=".*\\.WEBM" />
|
||||||
|
<data android:pathPattern=".*\\.ogv" />
|
||||||
|
<data android:pathPattern=".*\\.OGV" />
|
||||||
|
<data android:pathPattern=".*\\.mp4" />
|
||||||
|
<data android:pathPattern=".*\\.MP4" />
|
||||||
|
<data android:pathPattern=".*\\.mov" />
|
||||||
|
<data android:pathPattern=".*\\.MOV" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
24
playback/player/android/jni/Android.mk
Normal file
24
playback/player/android/jni/Android.mk
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE := gstplayer
|
||||||
|
LOCAL_SRC_FILES := player.c ../../lib/gstplayer.c
|
||||||
|
LOCAL_SHARED_LIBRARIES := gstreamer_android
|
||||||
|
LOCAL_LDLIBS := -llog -landroid
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
|
ifndef GSTREAMER_ROOT
|
||||||
|
ifndef GSTREAMER_ROOT_ANDROID
|
||||||
|
$(error GSTREAMER_ROOT_ANDROID is not defined!)
|
||||||
|
endif
|
||||||
|
GSTREAMER_ROOT := $(GSTREAMER_ROOT_ANDROID)
|
||||||
|
endif
|
||||||
|
|
||||||
|
GSTREAMER_NDK_BUILD_PATH := $(GSTREAMER_ROOT)/share/gst-android/ndk-build/
|
||||||
|
|
||||||
|
include $(GSTREAMER_NDK_BUILD_PATH)/plugins.mk
|
||||||
|
GSTREAMER_PLUGINS := $(GSTREAMER_PLUGINS_CORE) $(GSTREAMER_PLUGINS_PLAYBACK) $(GSTREAMER_PLUGINS_CODECS) $(GSTREAMER_PLUGINS_NET) $(GSTREAMER_PLUGINS_SYS) $(GSTREAMER_CODECS_RESTRICTED) $(GSTREAMER_CODECS_GPL) $(GSTREAMER_PLUGINS_ENCODING) $(GSTREAMER_PLUGINS_VIS) $(GSTREAMER_PLUGINS_EFFECTS) $(GSTREAMER_PLUGINS_NET_RESTRICTED)
|
||||||
|
GSTREAMER_EXTRA_DEPS := gstreamer-video-1.0
|
||||||
|
|
||||||
|
include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer-1.0.mk
|
489
playback/player/android/jni/player.c
Normal file
489
playback/player/android/jni/player.c
Normal file
|
@ -0,0 +1,489 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <android/native_window.h>
|
||||||
|
#include <android/native_window_jni.h>
|
||||||
|
|
||||||
|
#include "../../lib/gstplayer.h"
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (debug_category);
|
||||||
|
#define GST_CAT_DEFAULT debug_category
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These macros provide a way to store the native pointer to Player, which might be 32 or 64 bits, into
|
||||||
|
* a jlong, which is always 64 bits, without warnings.
|
||||||
|
*/
|
||||||
|
#if GLIB_SIZEOF_VOID_P == 8
|
||||||
|
# define GET_CUSTOM_DATA(env, thiz, fieldID) (Player *)(*env)->GetLongField (env, thiz, fieldID)
|
||||||
|
# define SET_CUSTOM_DATA(env, thiz, fieldID, data) (*env)->SetLongField (env, thiz, fieldID, (jlong)data)
|
||||||
|
#else
|
||||||
|
# define GET_CUSTOM_DATA(env, thiz, fieldID) (Player *)(jint)(*env)->GetLongField (env, thiz, fieldID)
|
||||||
|
# define SET_CUSTOM_DATA(env, thiz, fieldID, data) (*env)->SetLongField (env, thiz, fieldID, (jlong)(jint)data)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _Player
|
||||||
|
{
|
||||||
|
jobject java_player;
|
||||||
|
GstPlayer *player;
|
||||||
|
ANativeWindow *native_window;
|
||||||
|
} Player;
|
||||||
|
|
||||||
|
static pthread_t gst_app_thread;
|
||||||
|
static pthread_key_t current_jni_env;
|
||||||
|
static JavaVM *java_vm;
|
||||||
|
static jfieldID native_player_field_id;
|
||||||
|
static jmethodID on_position_updated_method_id;
|
||||||
|
static jmethodID on_duration_changed_method_id;
|
||||||
|
static jmethodID on_end_of_stream_method_id;
|
||||||
|
static jmethodID on_seek_finished_method_id;
|
||||||
|
static jmethodID on_error_method_id;
|
||||||
|
static jmethodID on_video_dimensions_changed_method_id;
|
||||||
|
|
||||||
|
/* Register this thread with the VM */
|
||||||
|
static JNIEnv *
|
||||||
|
attach_current_thread (void)
|
||||||
|
{
|
||||||
|
JNIEnv *env;
|
||||||
|
JavaVMAttachArgs args;
|
||||||
|
|
||||||
|
GST_DEBUG ("Attaching thread %p", g_thread_self ());
|
||||||
|
args.version = JNI_VERSION_1_4;
|
||||||
|
args.name = NULL;
|
||||||
|
args.group = NULL;
|
||||||
|
|
||||||
|
if ((*java_vm)->AttachCurrentThread (java_vm, &env, &args) < 0) {
|
||||||
|
GST_ERROR ("Failed to attach current thread");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unregister this thread from the VM */
|
||||||
|
static void
|
||||||
|
detach_current_thread (void *env)
|
||||||
|
{
|
||||||
|
GST_DEBUG ("Detaching thread %p", g_thread_self ());
|
||||||
|
(*java_vm)->DetachCurrentThread (java_vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve the JNI environment for this thread */
|
||||||
|
static JNIEnv *
|
||||||
|
get_jni_env (void)
|
||||||
|
{
|
||||||
|
JNIEnv *env;
|
||||||
|
|
||||||
|
if ((env = pthread_getspecific (current_jni_env)) == NULL) {
|
||||||
|
env = attach_current_thread ();
|
||||||
|
pthread_setspecific (current_jni_env, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Java Bindings
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
on_position_updated (GstPlayer * unused, GstClockTime position, Player * player)
|
||||||
|
{
|
||||||
|
JNIEnv *env = get_jni_env ();
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod (env, player->java_player,
|
||||||
|
on_position_updated_method_id, position);
|
||||||
|
if ((*env)->ExceptionCheck (env)) {
|
||||||
|
(*env)->ExceptionDescribe (env);
|
||||||
|
(*env)->ExceptionClear (env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_duration_changed (GstPlayer * unused, GstClockTime duration, Player * player)
|
||||||
|
{
|
||||||
|
JNIEnv *env = get_jni_env ();
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod (env, player->java_player,
|
||||||
|
on_duration_changed_method_id, duration);
|
||||||
|
if ((*env)->ExceptionCheck (env)) {
|
||||||
|
(*env)->ExceptionDescribe (env);
|
||||||
|
(*env)->ExceptionClear (env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_end_of_stream (GstPlayer * unused, Player * player)
|
||||||
|
{
|
||||||
|
JNIEnv *env = get_jni_env ();
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod (env, player->java_player, on_end_of_stream_method_id);
|
||||||
|
if ((*env)->ExceptionCheck (env)) {
|
||||||
|
(*env)->ExceptionDescribe (env);
|
||||||
|
(*env)->ExceptionClear (env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_seek_finished (GstPlayer * unused, Player * player)
|
||||||
|
{
|
||||||
|
JNIEnv *env = get_jni_env ();
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod (env, player->java_player, on_seek_finished_method_id);
|
||||||
|
if ((*env)->ExceptionCheck (env)) {
|
||||||
|
(*env)->ExceptionDescribe (env);
|
||||||
|
(*env)->ExceptionClear (env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_error (GstPlayer * unused, GError * err, Player * player)
|
||||||
|
{
|
||||||
|
JNIEnv *env = get_jni_env ();
|
||||||
|
jstring error_msg;
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
error_msg = err ? (*env)->NewStringUTF (env, err->message) : NULL;
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod (env, player->java_player, on_error_method_id,
|
||||||
|
error_msg);
|
||||||
|
if ((*env)->ExceptionCheck (env)) {
|
||||||
|
(*env)->ExceptionDescribe (env);
|
||||||
|
(*env)->ExceptionClear (env);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->DeleteLocalRef (env, error_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_video_dimensions_changed (GstPlayer * unused, gint width, gint height,
|
||||||
|
Player * player)
|
||||||
|
{
|
||||||
|
JNIEnv *env = get_jni_env ();
|
||||||
|
|
||||||
|
(*env)->CallVoidMethod (env, player->java_player,
|
||||||
|
on_video_dimensions_changed_method_id, width, height);
|
||||||
|
if ((*env)->ExceptionCheck (env)) {
|
||||||
|
(*env)->ExceptionDescribe (env);
|
||||||
|
(*env)->ExceptionClear (env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_new (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = g_slice_new0 (Player);
|
||||||
|
|
||||||
|
player->player = gst_player_new (FALSE);
|
||||||
|
SET_CUSTOM_DATA (env, thiz, native_player_field_id, player);
|
||||||
|
player->java_player = (*env)->NewGlobalRef (env, thiz);
|
||||||
|
|
||||||
|
g_signal_connect (player->player, "position-updated",
|
||||||
|
G_CALLBACK (on_position_updated), player);
|
||||||
|
g_signal_connect (player->player, "duration-changed",
|
||||||
|
G_CALLBACK (on_duration_changed), player);
|
||||||
|
g_signal_connect (player->player, "end-of-stream",
|
||||||
|
G_CALLBACK (on_end_of_stream), player);
|
||||||
|
g_signal_connect (player->player, "seek-finished",
|
||||||
|
G_CALLBACK (on_seek_finished), player);
|
||||||
|
g_signal_connect (player->player, "error", G_CALLBACK (on_error), player);
|
||||||
|
g_signal_connect (player->player, "video-dimensions-changed",
|
||||||
|
G_CALLBACK (on_video_dimensions_changed), player);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_free (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
|
||||||
|
(*env)->DeleteGlobalRef (env, player->java_player);
|
||||||
|
g_slice_free (Player, player);
|
||||||
|
SET_CUSTOM_DATA (env, thiz, native_player_field_id, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_play (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gst_player_play (player->player);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_pause (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gst_player_pause (player->player);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_stop (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gst_player_stop (player->player);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_seek (JNIEnv * env, jobject thiz, jlong position)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gst_player_seek (player->player, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_set_uri (JNIEnv * env, jobject thiz, jobject uri)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
const gchar *uri_str;
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uri_str = (*env)->GetStringUTFChars (env, uri, NULL);
|
||||||
|
g_object_set (player->player, "uri", uri_str, NULL);
|
||||||
|
(*env)->ReleaseStringUTFChars (env, uri, uri_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject
|
||||||
|
native_get_uri (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
jobject uri;
|
||||||
|
gchar *uri_str;
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
g_object_get (player->player, "uri", &uri_str, NULL);
|
||||||
|
|
||||||
|
uri = (*env)->NewStringUTF (env, uri_str);
|
||||||
|
g_free (uri_str);
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jboolean
|
||||||
|
native_is_playing (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
jboolean is_playing;
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_object_get (player->player, "is-playing", &is_playing, NULL);
|
||||||
|
|
||||||
|
return is_playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jlong
|
||||||
|
native_get_position (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
jdouble position;
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
g_object_get (player->player, "position", &position, NULL);
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jlong
|
||||||
|
native_get_duration (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
jlong duration;
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
g_object_get (player->player, "duration", &duration, NULL);
|
||||||
|
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jdouble
|
||||||
|
native_get_volume (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
jdouble volume;
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return 1.0;
|
||||||
|
|
||||||
|
g_object_get (player->player, "volume", &volume, NULL);
|
||||||
|
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_set_volume (JNIEnv * env, jobject thiz, jdouble volume)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_object_set (player->player, "volume", volume, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jboolean
|
||||||
|
native_get_mute (JNIEnv * env, jobject thiz)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
jboolean mute;
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_object_get (player->player, "mute", &mute, NULL);
|
||||||
|
|
||||||
|
return mute;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_set_mute (JNIEnv * env, jobject thiz, jboolean mute)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_object_set (player->player, "mute", mute, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_set_surface (JNIEnv * env, jobject thiz, jobject surface)
|
||||||
|
{
|
||||||
|
Player *player = GET_CUSTOM_DATA (env, thiz, native_player_field_id);
|
||||||
|
ANativeWindow *new_native_window;
|
||||||
|
|
||||||
|
if (!player)
|
||||||
|
return;
|
||||||
|
|
||||||
|
new_native_window = surface ? ANativeWindow_fromSurface (env, surface) : NULL;
|
||||||
|
GST_DEBUG ("Received surface %p (native window %p)", surface,
|
||||||
|
new_native_window);
|
||||||
|
|
||||||
|
if (player->native_window) {
|
||||||
|
ANativeWindow_release (player->native_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
player->native_window = new_native_window;
|
||||||
|
g_object_set (player->player, "window-handle", (gpointer) new_native_window,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
native_class_init (JNIEnv * env, jclass klass)
|
||||||
|
{
|
||||||
|
native_player_field_id =
|
||||||
|
(*env)->GetFieldID (env, klass, "native_player", "J");
|
||||||
|
on_position_updated_method_id =
|
||||||
|
(*env)->GetMethodID (env, klass, "onPositionUpdated", "(J)V");
|
||||||
|
on_duration_changed_method_id =
|
||||||
|
(*env)->GetMethodID (env, klass, "onDurationChanged", "(J)V");
|
||||||
|
on_end_of_stream_method_id =
|
||||||
|
(*env)->GetMethodID (env, klass, "onEndOfStream", "()V");
|
||||||
|
on_seek_finished_method_id =
|
||||||
|
(*env)->GetMethodID (env, klass, "onSeekFinished", "()V");
|
||||||
|
on_error_method_id =
|
||||||
|
(*env)->GetMethodID (env, klass, "onError", "(Ljava/lang/String;)V");
|
||||||
|
on_video_dimensions_changed_method_id =
|
||||||
|
(*env)->GetMethodID (env, klass, "onVideoDimensionsChanged", "(II)V");
|
||||||
|
|
||||||
|
if (!native_player_field_id ||
|
||||||
|
!on_position_updated_method_id || !on_duration_changed_method_id ||
|
||||||
|
!on_end_of_stream_method_id || !on_seek_finished_method_id ||
|
||||||
|
!on_error_method_id || !on_video_dimensions_changed_method_id) {
|
||||||
|
static const gchar *message =
|
||||||
|
"The calling class does not implement all necessary interface methods";
|
||||||
|
jclass exception_class = (*env)->FindClass (env, "java/lang/Exception");
|
||||||
|
__android_log_print (ANDROID_LOG_ERROR, "GstPlayer", "%s", message);
|
||||||
|
(*env)->ThrowNew (env, exception_class, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_debug_set_threshold_for_name ("gst-player", GST_LEVEL_TRACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* List of implemented native methods */
|
||||||
|
static JNINativeMethod native_methods[] = {
|
||||||
|
{"nativeClassInit", "()V", (void *) native_class_init},
|
||||||
|
{"nativeNew", "()V", (void *) native_new},
|
||||||
|
{"nativePlay", "()V", (void *) native_play},
|
||||||
|
{"nativePause", "()V", (void *) native_pause},
|
||||||
|
{"nativeSeek", "(J)V", (void *) native_seek},
|
||||||
|
{"nativeFree", "()V", (void *) native_free},
|
||||||
|
{"nativeGetUri", "()Ljava/lang/String;", (void *) native_get_uri},
|
||||||
|
{"nativeSetUri", "(Ljava/lang/String;)V", (void *) native_set_uri},
|
||||||
|
{"nativeIsPlaying", "()Z", (void *) native_is_playing},
|
||||||
|
{"nativeGetPosition", "()J", (void *) native_get_position},
|
||||||
|
{"nativeGetDuration", "()J", (void *) native_get_duration},
|
||||||
|
{"nativeGetVolume", "()D", (void *) native_get_volume},
|
||||||
|
{"nativeSetVolume", "(D)V", (void *) native_set_volume},
|
||||||
|
{"nativeGetMute", "()Z", (void *) native_get_mute},
|
||||||
|
{"nativeSetMute", "(Z)V", (void *) native_set_mute},
|
||||||
|
{"nativeSetSurface", "(Landroid/view/Surface;)V",
|
||||||
|
(void *) native_set_surface}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Library initializer */
|
||||||
|
jint
|
||||||
|
JNI_OnLoad (JavaVM * vm, void *reserved)
|
||||||
|
{
|
||||||
|
JNIEnv *env = NULL;
|
||||||
|
|
||||||
|
java_vm = vm;
|
||||||
|
|
||||||
|
if ((*vm)->GetEnv (vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
|
||||||
|
__android_log_print (ANDROID_LOG_ERROR, "GstPlayer",
|
||||||
|
"Could not retrieve JNIEnv");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
jclass klass = (*env)->FindClass (env, "org/freedesktop/gstreamer/Player");
|
||||||
|
(*env)->RegisterNatives (env, klass, native_methods,
|
||||||
|
G_N_ELEMENTS (native_methods));
|
||||||
|
|
||||||
|
pthread_key_create (¤t_jni_env, detach_current_thread);
|
||||||
|
|
||||||
|
return JNI_VERSION_1_4;
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.freedesktop.gstreamer;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.content.Context;
|
||||||
|
import org.freedesktop.gstreamer.GStreamer;
|
||||||
|
|
||||||
|
public class Player implements Closeable {
|
||||||
|
private static native void nativeClassInit();
|
||||||
|
public static void init(Context context) throws Exception {
|
||||||
|
System.loadLibrary("gstreamer_android");
|
||||||
|
GStreamer.init(context);
|
||||||
|
|
||||||
|
System.loadLibrary("gstplayer");
|
||||||
|
nativeClassInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long native_player;
|
||||||
|
private native void nativeNew();
|
||||||
|
public Player() {
|
||||||
|
nativeNew();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativeFree();
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
nativeFree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativePlay();
|
||||||
|
public void play() {
|
||||||
|
nativePlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativePause();
|
||||||
|
public void pause() {
|
||||||
|
nativePause();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativeStop();
|
||||||
|
public void stop() {
|
||||||
|
nativeStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativeSeek(long position);
|
||||||
|
public void seek(long position) {
|
||||||
|
nativeSeek(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native String nativeGetUri();
|
||||||
|
public String getUri() {
|
||||||
|
return nativeGetUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativeSetUri(String uri);
|
||||||
|
public void setUri(String uri) {
|
||||||
|
nativeSetUri(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native boolean nativeIsPlaying();
|
||||||
|
public boolean isPlaying() {
|
||||||
|
return nativeIsPlaying();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native long nativeGetPosition();
|
||||||
|
public long getPosition() {
|
||||||
|
return nativeGetPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native long nativeGetDuration();
|
||||||
|
public long getDuration() {
|
||||||
|
return nativeGetDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native double nativeGetVolume();
|
||||||
|
public double getVolume() {
|
||||||
|
return nativeGetVolume();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativeSetVolume(double volume);
|
||||||
|
public void setVolume(double volume) {
|
||||||
|
nativeSetVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native boolean nativeGetMute();
|
||||||
|
public boolean getMute() {
|
||||||
|
return nativeGetMute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private native void nativeSetMute(boolean mute);
|
||||||
|
public void setMute(boolean mute) {
|
||||||
|
nativeSetMute(mute);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Surface surface;
|
||||||
|
private native void nativeSetSurface(Surface surface);
|
||||||
|
public void setSurface(Surface surface) {
|
||||||
|
this.surface = surface;
|
||||||
|
nativeSetSurface(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Surface getSurface() {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface PositionUpdatedListener {
|
||||||
|
abstract void positionUpdated(Player player, long position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PositionUpdatedListener positionUpdatedListener;
|
||||||
|
public void setPositionUpdatedListener(PositionUpdatedListener listener) {
|
||||||
|
positionUpdatedListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPositionUpdated(long position) {
|
||||||
|
if (positionUpdatedListener != null) {
|
||||||
|
positionUpdatedListener.positionUpdated(this, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface DurationChangedListener {
|
||||||
|
abstract void durationChanged(Player player, long duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DurationChangedListener durationChangedListener;
|
||||||
|
public void setDurationChangedListener(DurationChangedListener listener) {
|
||||||
|
durationChangedListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDurationChanged(long duration) {
|
||||||
|
if (durationChangedListener != null) {
|
||||||
|
durationChangedListener.durationChanged(this, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface EndOfStreamListener {
|
||||||
|
abstract void endOfStream(Player player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EndOfStreamListener endOfStreamListener;
|
||||||
|
public void setEndOfStreamListener(EndOfStreamListener listener) {
|
||||||
|
endOfStreamListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onEndOfStream() {
|
||||||
|
if (endOfStreamListener != null) {
|
||||||
|
endOfStreamListener.endOfStream(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface SeekFinishedListener {
|
||||||
|
abstract void seekFinished(Player player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeekFinishedListener seekFinishedListener;
|
||||||
|
public void setSeekFinishedListener(SeekFinishedListener listener) {
|
||||||
|
seekFinishedListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSeekFinished() {
|
||||||
|
if (seekFinishedListener != null) {
|
||||||
|
seekFinishedListener.seekFinished(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface ErrorListener {
|
||||||
|
// FIXME: enums
|
||||||
|
abstract void error(Player player, String errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ErrorListener errorListener;
|
||||||
|
public void setErrorListener(ErrorListener listener) {
|
||||||
|
errorListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError(String errorMessage) {
|
||||||
|
if (errorListener != null) {
|
||||||
|
errorListener.error(this, errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface VideoDimensionsChangedListener {
|
||||||
|
abstract void videoDimensionsChanged(Player player, int width, int height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VideoDimensionsChangedListener videoDimensionsChangedListener;
|
||||||
|
public void setVideoDimensionsChangedListener(VideoDimensionsChangedListener listener) {
|
||||||
|
videoDimensionsChangedListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onVideoDimensionsChanged(int width, int height) {
|
||||||
|
if (videoDimensionsChangedListener != null) {
|
||||||
|
videoDimensionsChangedListener.videoDimensionsChanged(this, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.freedesktop.gstreamer.play;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
// A simple SurfaceView whose width and height can be set from the outside
|
||||||
|
public class GStreamerSurfaceView extends SurfaceView {
|
||||||
|
public int media_width = 320;
|
||||||
|
public int media_height = 240;
|
||||||
|
|
||||||
|
// Mandatory constructors, they do not do much
|
||||||
|
public GStreamerSurfaceView(Context context, AttributeSet attrs,
|
||||||
|
int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GStreamerSurfaceView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GStreamerSurfaceView (Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by the layout manager to find out our size and give us some rules.
|
||||||
|
// We will try to maximize our size, and preserve the media's aspect ratio if
|
||||||
|
// we are given the freedom to do so.
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
int width = 0, height = 0;
|
||||||
|
int wmode = View.MeasureSpec.getMode(widthMeasureSpec);
|
||||||
|
int hmode = View.MeasureSpec.getMode(heightMeasureSpec);
|
||||||
|
int wsize = View.MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
int hsize = View.MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
|
||||||
|
Log.i ("GStreamer", "onMeasure called with " + media_width + "x" + media_height);
|
||||||
|
// Obey width rules
|
||||||
|
switch (wmode) {
|
||||||
|
case View.MeasureSpec.AT_MOST:
|
||||||
|
if (hmode == View.MeasureSpec.EXACTLY) {
|
||||||
|
width = Math.min(hsize * media_width / media_height, wsize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case View.MeasureSpec.EXACTLY:
|
||||||
|
width = wsize;
|
||||||
|
break;
|
||||||
|
case View.MeasureSpec.UNSPECIFIED:
|
||||||
|
width = media_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obey height rules
|
||||||
|
switch (hmode) {
|
||||||
|
case View.MeasureSpec.AT_MOST:
|
||||||
|
if (wmode == View.MeasureSpec.EXACTLY) {
|
||||||
|
height = Math.min(wsize * media_height / media_width, hsize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case View.MeasureSpec.EXACTLY:
|
||||||
|
height = hsize;
|
||||||
|
break;
|
||||||
|
case View.MeasureSpec.UNSPECIFIED:
|
||||||
|
height = media_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, calculate best size when both axis are free
|
||||||
|
if (hmode == View.MeasureSpec.AT_MOST && wmode == View.MeasureSpec.AT_MOST) {
|
||||||
|
int correct_height = width * media_height / media_width;
|
||||||
|
int correct_width = height * media_width / media_height;
|
||||||
|
|
||||||
|
if (correct_height < height)
|
||||||
|
height = correct_height;
|
||||||
|
else
|
||||||
|
width = correct_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obey minimum size
|
||||||
|
width = Math.max (getSuggestedMinimumWidth(), width);
|
||||||
|
height = Math.max (getSuggestedMinimumHeight(), height);
|
||||||
|
setMeasuredDimension(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.freedesktop.gstreamer.play;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.freedesktop.gstreamer.Player;
|
||||||
|
|
||||||
|
public class Play extends Activity implements SurfaceHolder.Callback, OnSeekBarChangeListener {
|
||||||
|
private PowerManager.WakeLock wake_lock;
|
||||||
|
private Player player;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Player.init(this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentView(R.layout.main);
|
||||||
|
|
||||||
|
player = new Player();
|
||||||
|
|
||||||
|
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||||
|
wake_lock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GStreamer Play");
|
||||||
|
wake_lock.setReferenceCounted(false);
|
||||||
|
|
||||||
|
ImageButton play = (ImageButton) this.findViewById(R.id.button_play);
|
||||||
|
play.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
player.play();
|
||||||
|
wake_lock.acquire();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ImageButton pause = (ImageButton) this.findViewById(R.id.button_pause);
|
||||||
|
pause.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
player.pause();
|
||||||
|
wake_lock.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final SeekBar sb = (SeekBar) this.findViewById(R.id.seek_bar);
|
||||||
|
sb.setOnSeekBarChangeListener(this);
|
||||||
|
|
||||||
|
player.setPositionUpdatedListener(new Player.PositionUpdatedListener() {
|
||||||
|
public void positionUpdated(Player player, final long position) {
|
||||||
|
runOnUiThread (new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
sb.setProgress((int) (position / 1000000));
|
||||||
|
updateTimeWidget();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
player.setDurationChangedListener(new Player.DurationChangedListener() {
|
||||||
|
public void durationChanged(Player player, final long duration) {
|
||||||
|
runOnUiThread (new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
sb.setMax((int) (duration / 1000000));
|
||||||
|
updateTimeWidget();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final GStreamerSurfaceView gsv = (GStreamerSurfaceView) this.findViewById(R.id.surface_video);
|
||||||
|
player.setVideoDimensionsChangedListener(new Player.VideoDimensionsChangedListener() {
|
||||||
|
public void videoDimensionsChanged(Player player, final int width, final int height) {
|
||||||
|
runOnUiThread (new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
Log.i ("GStreamer", "Media size changed to " + width + "x" + height);
|
||||||
|
gsv.media_width = width;
|
||||||
|
gsv.media_height = height;
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
gsv.requestLayout();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
SurfaceView sv = (SurfaceView) this.findViewById(R.id.surface_video);
|
||||||
|
SurfaceHolder sh = sv.getHolder();
|
||||||
|
sh.addCallback(this);
|
||||||
|
|
||||||
|
String mediaUri = null;
|
||||||
|
Intent intent = getIntent();
|
||||||
|
android.net.Uri uri = intent.getData();
|
||||||
|
Log.i ("GStreamer", "Received URI: " + uri);
|
||||||
|
if (uri.getScheme().equals("content")) {
|
||||||
|
android.database.Cursor cursor = getContentResolver().query(uri, null, null, null, null);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
mediaUri = "file://" + cursor.getString(cursor.getColumnIndex(android.provider.MediaStore.Video.Media.DATA));
|
||||||
|
cursor.close();
|
||||||
|
} else {
|
||||||
|
mediaUri = uri.toString();
|
||||||
|
}
|
||||||
|
player.setUri(mediaUri);
|
||||||
|
|
||||||
|
updateTimeWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onDestroy() {
|
||||||
|
player.close();
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTimeWidget () {
|
||||||
|
final TextView tv = (TextView) this.findViewById(R.id.textview_time);
|
||||||
|
final SeekBar sb = (SeekBar) this.findViewById(R.id.seek_bar);
|
||||||
|
final int pos = sb.getProgress();
|
||||||
|
final int max = sb.getMax();
|
||||||
|
|
||||||
|
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
|
||||||
|
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
final String message = df.format(new Date (pos)) + " / " + df.format(new Date (max));
|
||||||
|
tv.setText(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void surfaceChanged(SurfaceHolder holder, int format, int width,
|
||||||
|
int height) {
|
||||||
|
Log.d("GStreamer", "Surface changed to format " + format + " width "
|
||||||
|
+ width + " height " + height);
|
||||||
|
player.setSurface(holder.getSurface());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
|
Log.d("GStreamer", "Surface created: " + holder.getSurface());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
|
Log.d("GStreamer", "Surface destroyed");
|
||||||
|
player.setSurface(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onProgressChanged(SeekBar sb, int progress, boolean fromUser) {
|
||||||
|
if (!fromUser) return;
|
||||||
|
|
||||||
|
updateTimeWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartTrackingTouch(SeekBar sb) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStopTrackingTouch(SeekBar sb) {
|
||||||
|
Log.d("GStreamer", "Seek to " + sb.getProgress());
|
||||||
|
player.seek(((long) sb.getProgress()) * 1000000);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue