Compare commits
3 Commits
v4.4.LTS
...
sdl-player
| Author | SHA1 | Date | |
|---|---|---|---|
| ae769f8cfd | |||
| 1da0c9d8d7 | |||
| 1883d97999 |
@@ -780,6 +780,7 @@ done
|
||||
|
||||
export API=${ORIGINAL_API}
|
||||
|
||||
# DEFINE ANDROID ARCHITECTURES
|
||||
rm -f ${BASEDIR}/android/build/.neon 1>>${BASEDIR}/build.log 2>&1
|
||||
ANDROID_ARCHITECTURES=""
|
||||
if [[ ${ENABLED_ARCHITECTURES[1]} -eq 1 ]]; then
|
||||
@@ -800,6 +801,14 @@ if [[ ${ENABLED_ARCHITECTURES[4]} -eq 1 ]]; then
|
||||
ANDROID_ARCHITECTURES+="$(get_android_arch 4) "
|
||||
fi
|
||||
|
||||
# DEFINE BUILD FFPLAY FLAG
|
||||
rm -f ${BASEDIR}/android/build/.ffplay 1>>${BASEDIR}/build.log 2>&1
|
||||
if [[ ${ENABLED_LIBRARIES[LIBRARY_SDL]} -eq 1 ]]; then
|
||||
mkdir -p ${BASEDIR}/android/build 1>>${BASEDIR}/build.log 2>&1
|
||||
cat > "${BASEDIR}/android/build/.ffplay" << EOF
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [[ ! -z ${ANDROID_ARCHITECTURES} ]]; then
|
||||
|
||||
echo -n -e "\nmobile-ffmpeg: "
|
||||
|
||||
@@ -77,6 +77,7 @@ extern __thread AVDictionary *swr_opts;
|
||||
extern __thread AVDictionary *format_opts, *codec_opts, *resample_opts;
|
||||
extern __thread int hide_banner;
|
||||
extern __thread int find_stream_info;
|
||||
extern __thread int filter_nbthreads;
|
||||
|
||||
/**
|
||||
* Register a program-specific cleanup routine.
|
||||
|
||||
@@ -627,7 +627,6 @@ extern __thread AVIOContext *progress_avio;
|
||||
extern __thread float max_error_rate;
|
||||
extern __thread char *videotoolbox_pixfmt;
|
||||
|
||||
extern __thread int filter_nbthreads;
|
||||
extern __thread int filter_complex_nbthreads;
|
||||
extern __thread int vstats_version;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Taner Sener
|
||||
*
|
||||
* This file is part of MobileFFmpeg.
|
||||
*
|
||||
* MobileFFmpeg is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MobileFFmpeg 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include "libavcodec/jni.h"
|
||||
|
||||
/** Forward declaration for function defined in fftools_ffplay.c */
|
||||
int ffplay_execute(int argc, char **argv);
|
||||
|
||||
/** Forward declaration for functions defined in SDl_android.c */
|
||||
void set_mobile_ffmpeg_ffplay_execute(int (*ffplay_execute_function)(int argc, char **argv));
|
||||
jint SDL_Android_Initialize(JavaVM* vm, void* reserved);
|
||||
|
||||
/**
|
||||
* Initializes SDL for FFplay. It must be called before other SDL functions.
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_arthenica_mobileffmpeg_FFplay_nativeSDLInit(JNIEnv *env, jclass object) {
|
||||
set_mobile_ffmpeg_ffplay_execute(ffplay_execute);
|
||||
JavaVM *globalVm = av_jni_get_java_vm(NULL);
|
||||
if (globalVm) {
|
||||
SDL_Android_Initialize(globalVm, NULL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Taner Sener
|
||||
*
|
||||
* This file is part of MobileFFmpeg.
|
||||
*
|
||||
* MobileFFmpeg is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MobileFFmpeg 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MOBILE_FFPLAY_H
|
||||
#define MOBILE_FFPLAY_H
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
/*
|
||||
* Class: com_arthenica_mobileffmpeg_Config
|
||||
* Method: nativeFFplayExecute
|
||||
* Signature: ([Ljava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_arthenica_mobileffmpeg_Config_nativeFFplayExecute(JNIEnv *, jclass, jobjectArray);
|
||||
|
||||
#endif /* MOBILE_FFPLAY_H */
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Taner Sener
|
||||
* Copyright (c) 2018-2020 Taner Sener
|
||||
*
|
||||
* This file is part of MobileFFmpeg.
|
||||
*
|
||||
@@ -103,6 +103,7 @@ public class Config {
|
||||
Abi.class.getName();
|
||||
FFmpeg.class.getName();
|
||||
FFprobe.class.getName();
|
||||
FFplay.class.getName();
|
||||
|
||||
/*
|
||||
* NEON supported arm-v7a library has a different name
|
||||
@@ -598,26 +599,26 @@ public class Config {
|
||||
/**
|
||||
* <p>Enables native redirection. Necessary for log and statistics callback functions.
|
||||
*/
|
||||
private static native void enableNativeRedirection();
|
||||
private native static void enableNativeRedirection();
|
||||
|
||||
/**
|
||||
* <p>Disables native redirection
|
||||
*/
|
||||
private static native void disableNativeRedirection();
|
||||
private native static void disableNativeRedirection();
|
||||
|
||||
/**
|
||||
* Sets native log level
|
||||
*
|
||||
* @param level log level
|
||||
*/
|
||||
private static native void setNativeLogLevel(int level);
|
||||
private native static void setNativeLogLevel(int level);
|
||||
|
||||
/**
|
||||
* Returns native log level.
|
||||
*
|
||||
* @return log level
|
||||
*/
|
||||
private static native int getNativeLogLevel();
|
||||
private native static int getNativeLogLevel();
|
||||
|
||||
/**
|
||||
* <p>Returns FFmpeg version bundled within the library natively.
|
||||
|
||||
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Taner Sener
|
||||
*
|
||||
* This file is part of MobileFFmpeg.
|
||||
*
|
||||
* MobileFFmpeg is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MobileFFmpeg 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.arthenica.mobileffmpeg.player.AudioHandler;
|
||||
import com.arthenica.mobileffmpeg.player.ControllerHandler;
|
||||
import com.arthenica.mobileffmpeg.player.PlayerManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static com.arthenica.mobileffmpeg.Config.TAG;
|
||||
|
||||
public class FFplay {
|
||||
|
||||
private static final boolean enabled;
|
||||
|
||||
private static boolean separateMouseAndTouch;
|
||||
|
||||
private static AudioHandler audioHandler;
|
||||
|
||||
private static ControllerHandler controllerHandler;
|
||||
|
||||
private static PlayerManager playerManager;
|
||||
|
||||
static {
|
||||
|
||||
/* MOUSE AND TOUCH IS THE SAME DEVICE BY DEFAULT */
|
||||
separateMouseAndTouch = false;
|
||||
|
||||
/* FFPLAY DEPENDS ON SDL. CHECK WHETHER IT IS ENABLED OR NOT */
|
||||
if (Config.getExternalLibraries().contains("sdl2")) {
|
||||
init();
|
||||
|
||||
/* FFPLAY METHODS ARE ENABLED ONLY IF INITIALIZATION COMPLETES SUCCESSFULLY */
|
||||
enabled = true;
|
||||
} else {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void init() {
|
||||
|
||||
// ENABLE SDL FIRST
|
||||
nativeSDLInit();
|
||||
|
||||
// COMPLETE OTHER COMPONENTS AFTER
|
||||
nativePlayerInit();
|
||||
nativeAudioInit();
|
||||
nativeControllerInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Synchronously executes FFplay with arguments provided.
|
||||
*
|
||||
* @param arguments FFplay command options/arguments as string array
|
||||
* @return zero on successful execution, 255 on user cancel and non-zero on error
|
||||
*/
|
||||
public static int execute(final String[] arguments) {
|
||||
if (enabled) {
|
||||
return nativePlayerRun(arguments);
|
||||
} else {
|
||||
throw new RuntimeException("sdl not found. FFplay requires sdl to run.");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSeparateMouseAndTouch() {
|
||||
return separateMouseAndTouch;
|
||||
}
|
||||
|
||||
public static void setSeparateMouseAndTouch(boolean separateMouseAndTouch) {
|
||||
FFplay.separateMouseAndTouch = separateMouseAndTouch;
|
||||
}
|
||||
|
||||
public static AudioHandler getAudioHandler() {
|
||||
return audioHandler;
|
||||
}
|
||||
|
||||
public static ControllerHandler getControllerHandler() {
|
||||
return controllerHandler;
|
||||
}
|
||||
|
||||
public static PlayerManager getPlayerManager() {
|
||||
return playerManager;
|
||||
}
|
||||
|
||||
public static void setAudioHandler(final AudioHandler newAudioHandler) {
|
||||
audioHandler = newAudioHandler;
|
||||
}
|
||||
|
||||
public static void setControllerManager(final ControllerHandler newControllerHandler) {
|
||||
controllerHandler = newControllerHandler;
|
||||
}
|
||||
|
||||
public static void setPlayerManager(PlayerManager newPlayerManager) {
|
||||
playerManager = newPlayerManager;
|
||||
}
|
||||
|
||||
public static String playerNativeGetHint(final String name) {
|
||||
return nativePlayerNativeGetHint(name);
|
||||
}
|
||||
|
||||
public static void inputSetComposingText(final String text, final int newCursorPosition) {
|
||||
nativeInputSetComposingText(text, newCursorPosition);
|
||||
}
|
||||
|
||||
public static void inputGenerateScancodeForUnichar(final char c) {
|
||||
nativeInputGenerateScancodeForUnichar(c);
|
||||
}
|
||||
|
||||
public static void inputCommitText(String text, int newCursorPosition) {
|
||||
nativeInputCommitText(text, newCursorPosition);
|
||||
}
|
||||
|
||||
public static void playerNativeSetenv(final String name, final String value) {
|
||||
//@TODO We already have this method under Config
|
||||
nativePlayerNativeSetenv(name, value);
|
||||
}
|
||||
|
||||
public static void playerOnDropFile(final String filename) {
|
||||
nativePlayerOnDropFile(filename);
|
||||
}
|
||||
|
||||
public static void playerNativeLowMemory() {
|
||||
nativePlayerNativeLowMemory();
|
||||
}
|
||||
|
||||
public static void playerNativeQuit() {
|
||||
nativePlayerNativeQuit();
|
||||
}
|
||||
|
||||
public static void playerNativePause() {
|
||||
nativePlayerNativePause();
|
||||
}
|
||||
|
||||
public static void playerNativeResume() {
|
||||
nativePlayerNativeResume();
|
||||
}
|
||||
|
||||
public static void playerOnKeyDown(final int keyCode) {
|
||||
nativePlayerOnKeyDown(keyCode);
|
||||
}
|
||||
|
||||
public static void playerOnKeyUp(final int keyCode) {
|
||||
nativePlayerOnKeyUp(keyCode);
|
||||
}
|
||||
|
||||
public static void playerOnKeyboardFocusLost() {
|
||||
nativePlayerOnKeyboardFocusLost();
|
||||
}
|
||||
|
||||
public static void playerOnClipboardChanged() {
|
||||
nativePlayerOnClipboardChanged();
|
||||
}
|
||||
|
||||
public static void playerOnSurfaceChanged() {
|
||||
nativePlayerOnSurfaceChanged();
|
||||
}
|
||||
|
||||
public static void playerOnSurfaceDestroyed() {
|
||||
nativePlayerOnSurfaceDestroyed();
|
||||
}
|
||||
|
||||
public static void playerOnMouse(final int button, final int action, final float x, final float y) {
|
||||
nativePlayerOnMouse(button, action, x, y);
|
||||
}
|
||||
|
||||
public static int controllerAddHaptic(final int deviceId, final String name) {
|
||||
return nativeControllerAddHaptic(deviceId, name);
|
||||
}
|
||||
|
||||
public static int controllerRemoveHaptic(final int deviceId) {
|
||||
return nativeControllerRemoveHaptic(deviceId);
|
||||
}
|
||||
|
||||
public static int controllerAddJoystick(final int deviceId, final String name, final String desc, final int isAccelerometer, final int nButtons, final int nAxes, final int nHats, final int nBalls) {
|
||||
return nativeControllerAddJoystick(deviceId, name, desc, isAccelerometer, nButtons, nAxes, nHats, nBalls);
|
||||
}
|
||||
|
||||
public static int controllerRemoveJoystick(final int deviceId) {
|
||||
return nativeControllerRemoveJoystick(deviceId);
|
||||
}
|
||||
|
||||
public static void controllerOnJoy(final int deviceId, final int axis, final float value) {
|
||||
nativeControllerOnJoy(deviceId, axis, value);
|
||||
}
|
||||
|
||||
public static void controllerOnHat(final int deviceId, final int hatId, final int x, final int y) {
|
||||
nativeControllerOnHat(deviceId, hatId, x, y);
|
||||
}
|
||||
|
||||
public static void playerOnResize(final int x, final int y, final int format, final float rate) {
|
||||
nativePlayerOnResize(x, y, format, rate);
|
||||
}
|
||||
|
||||
public static int controllerOnPadDown(final int deviceId, final int keycode) {
|
||||
return nativeControllerOnPadDown(deviceId, keycode);
|
||||
}
|
||||
|
||||
public static int controllerOnPadUp(final int deviceId, final int keycode) {
|
||||
return nativeControllerOnPadUp(deviceId, keycode);
|
||||
}
|
||||
|
||||
public static void playerOnTouch(final int touchDevId, final int pointerFingerId, final int action, final float x, final float y, final float p) {
|
||||
nativePlayerOnTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
}
|
||||
|
||||
public static void playerOnAccel(final float x, final float y, final float z) {
|
||||
nativePlayerOnAccel(x, y, z);
|
||||
}
|
||||
|
||||
/* AUDIO FUNCTIONS CALLED BY NATIVE THREADS */
|
||||
|
||||
static int audioOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, final int desiredFrames) {
|
||||
if (audioHandler != null) {
|
||||
return audioHandler.audioOpen(sampleRate, is16Bit, isStereo, desiredFrames);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void audioWriteShortBuffer(final short[] buffer) {
|
||||
if (audioHandler != null) {
|
||||
audioHandler.audioWriteShortBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void audioWriteByteBuffer(final byte[] buffer) {
|
||||
if (audioHandler != null) {
|
||||
audioHandler.audioWriteByteBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static int captureOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, final int desiredFrames) {
|
||||
if (audioHandler != null) {
|
||||
return audioHandler.captureOpen(sampleRate, is16Bit, isStereo, desiredFrames);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int captureReadShortBuffer(final short[] buffer, final boolean blocking) {
|
||||
if (audioHandler != null) {
|
||||
return audioHandler.captureReadShortBuffer(buffer, blocking);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int captureReadByteBuffer(final byte[] buffer, final boolean blocking) {
|
||||
if (audioHandler != null) {
|
||||
return audioHandler.captureReadByteBuffer(buffer, blocking);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void audioClose() {
|
||||
if (audioHandler != null) {
|
||||
audioHandler.audioClose();
|
||||
}
|
||||
}
|
||||
|
||||
static void captureClose() {
|
||||
if (audioHandler != null) {
|
||||
audioHandler.captureClose();
|
||||
}
|
||||
}
|
||||
|
||||
/* CONTROLLER FUNCTIONS CALLED BY NATIVE THREADS */
|
||||
|
||||
static void pollInputDevices() {
|
||||
if (controllerHandler != null) {
|
||||
controllerHandler.pollInputDevices();
|
||||
}
|
||||
}
|
||||
|
||||
static void pollHapticDevices() {
|
||||
if (controllerHandler != null) {
|
||||
controllerHandler.pollHapticDevices();
|
||||
}
|
||||
}
|
||||
|
||||
static void hapticRun(final int deviceId, final int length) {
|
||||
if (controllerHandler != null) {
|
||||
controllerHandler.hapticRun(deviceId, length);
|
||||
}
|
||||
}
|
||||
|
||||
/* PLAYER FUNCTIONS CALLED BY NATIVE THREADS */
|
||||
|
||||
static boolean setActivityTitle(final String title) {
|
||||
if (playerManager != null) {
|
||||
return playerManager.setActivityTitle(title);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void setWindowStyle(final boolean fullScreen) {
|
||||
if (playerManager != null) {
|
||||
playerManager.setWindowStyle(fullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
static void setOrientation(final int w, final int h, final boolean resizable, final String hint) {
|
||||
if (playerManager != null) {
|
||||
playerManager.setOrientation(w, h, resizable, hint);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isScreenKeyboardShown() {
|
||||
if (playerManager != null) {
|
||||
return playerManager.isScreenKeyboardShown();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean sendMessage(final int command, final int param) {
|
||||
if (playerManager != null) {
|
||||
return playerManager.sendMessage(command, param);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Context getContext() {
|
||||
if (playerManager != null) {
|
||||
return playerManager.getContext();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isAndroidTV() {
|
||||
if (playerManager != null) {
|
||||
return playerManager.isAndroidTV();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static DisplayMetrics getDisplayDPI() {
|
||||
if (playerManager != null) {
|
||||
return playerManager.getDisplayDPI();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean getManifestEnvironmentVariables() {
|
||||
if (playerManager != null) {
|
||||
return playerManager.getManifestEnvironmentVariables();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean showTextInput(final int x, final int y, final int w, final int h) {
|
||||
if (playerManager != null) {
|
||||
return playerManager.showTextInput(x, y, w, h);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Surface getNativeSurface() {
|
||||
if (playerManager != null) {
|
||||
return playerManager.getNativeSurface();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static int[] inputGetInputDeviceIds(final int sources) {
|
||||
if (playerManager != null) {
|
||||
return playerManager.inputGetInputDeviceIds(sources);
|
||||
} else {
|
||||
return new int[0];
|
||||
}
|
||||
}
|
||||
|
||||
static boolean clipboardHasText() {
|
||||
if (playerManager != null) {
|
||||
return playerManager.clipboardHasText();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static String clipboardGetText() {
|
||||
if (playerManager != null) {
|
||||
return playerManager.clipboardGetText();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static void clipboardSetText(final String string) {
|
||||
if (playerManager != null) {
|
||||
playerManager.clipboardSetText(string);
|
||||
}
|
||||
}
|
||||
|
||||
static InputStream openAPKExpansionInputStream(final String fileName) throws IOException {
|
||||
Log.e(TAG, "Opening APK Expansion is not supported.");
|
||||
return null;
|
||||
}
|
||||
|
||||
static int messageBoxShowMessageBox(final int flags, final String title, final String message, final int[] buttonFlags, final int[] buttonIds, final String[] buttonTexts, final int[] colors) {
|
||||
if (playerManager != null) {
|
||||
return playerManager.showMessageBox(flags, title, message, buttonFlags, buttonIds, buttonTexts, colors);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* NATIVE SDL FUNCTIONS */
|
||||
|
||||
/**
|
||||
* <p>Initializes SDL for FFplay. It must be called before other SDL functions.
|
||||
*/
|
||||
native static void nativeSDLInit();
|
||||
|
||||
/* NATIVE PLAYER FUNCTIONS */
|
||||
|
||||
native static int nativePlayerInit();
|
||||
|
||||
/**
|
||||
* <p>Synchronously executes FFplay natively with arguments provided.
|
||||
*
|
||||
* @param arguments FFplay command options/arguments as string array
|
||||
* @return zero on successful execution, 255 on user cancel and non-zero on error
|
||||
*/
|
||||
native static int nativePlayerRun(final String[] arguments);
|
||||
|
||||
native static void nativePlayerNativeLowMemory();
|
||||
|
||||
native static void nativePlayerNativeQuit();
|
||||
|
||||
native static void nativePlayerNativePause();
|
||||
|
||||
native static void nativePlayerNativeResume();
|
||||
|
||||
native static void nativePlayerOnDropFile(final String filename);
|
||||
|
||||
native static void nativePlayerOnResize(final int x, final int y, final int format, final float rate);
|
||||
|
||||
native static void nativePlayerOnKeyDown(final int keyCode);
|
||||
|
||||
native static void nativePlayerOnKeyUp(final int keyCode);
|
||||
|
||||
native static void nativePlayerOnKeyboardFocusLost();
|
||||
|
||||
native static void nativePlayerOnMouse(final int button, final int action, final float x, final float y);
|
||||
|
||||
native static void nativePlayerOnTouch(final int touchDevId, final int pointerFingerId, final int action, final float x, final float y, final float p);
|
||||
|
||||
native static void nativePlayerOnAccel(final float x, final float y, final float z);
|
||||
|
||||
native static void nativePlayerOnClipboardChanged();
|
||||
|
||||
native static void nativePlayerOnSurfaceChanged();
|
||||
|
||||
native static void nativePlayerOnSurfaceDestroyed();
|
||||
|
||||
native static String nativePlayerNativeGetHint(final String name);
|
||||
|
||||
native static void nativePlayerNativeSetenv(final String name, final String value);
|
||||
|
||||
/* NATIVE AUDIO FUNCTIONS */
|
||||
|
||||
native static int nativeAudioInit();
|
||||
|
||||
/* NATIVE CONTROLLER FUNCTIONS */
|
||||
|
||||
native static int nativeControllerInit();
|
||||
|
||||
native static int nativeControllerAddJoystick(final int deviceId, final String name, final String desc, final int isAccelerometer, final int nButtons, final int nAxes, final int nHats, final int nBalls);
|
||||
|
||||
native static int nativeControllerRemoveJoystick(final int deviceId);
|
||||
|
||||
native static int nativeControllerAddHaptic(final int deviceId, final String name);
|
||||
|
||||
native static int nativeControllerRemoveHaptic(final int deviceId);
|
||||
|
||||
native static int nativeControllerOnPadDown(final int deviceId, final int keycode);
|
||||
|
||||
native static int nativeControllerOnPadUp(final int deviceId, final int keycode);
|
||||
|
||||
native static void nativeControllerOnJoy(final int deviceId, final int axis, final float value);
|
||||
|
||||
native static void nativeControllerOnHat(final int deviceId, final int hatId, final int x, final int y);
|
||||
|
||||
/* NATIVE INPUT FUNCTIONS */
|
||||
|
||||
native static void nativeInputCommitText(final String text, final int newCursorPosition);
|
||||
|
||||
native static void nativeInputGenerateScancodeForUnichar(final char c);
|
||||
|
||||
native static void nativeInputSetComposingText(final String text, final int newCursorPosition);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Taner Sener
|
||||
*
|
||||
* This file is part of MobileFFmpeg.
|
||||
*
|
||||
* MobileFFmpeg is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MobileFFmpeg 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
public interface AudioHandler {
|
||||
|
||||
void initialize();
|
||||
|
||||
int audioOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, final int desiredFrames);
|
||||
|
||||
void audioWriteShortBuffer(final short[] buffer);
|
||||
|
||||
void audioWriteByteBuffer(final byte[] buffer);
|
||||
|
||||
int captureOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, final int desiredFrames);
|
||||
|
||||
int captureReadShortBuffer(final short[] buffer, final boolean blocking);
|
||||
|
||||
int captureReadByteBuffer(final byte[] buffer, final boolean blocking);
|
||||
|
||||
void audioClose();
|
||||
|
||||
void captureClose();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Taner Sener
|
||||
*
|
||||
* This file is part of MobileFFmpeg.
|
||||
*
|
||||
* MobileFFmpeg is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MobileFFmpeg 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public interface ControllerHandler {
|
||||
|
||||
void initialize(final Context context);
|
||||
|
||||
boolean handleJoystickMotionEvent(final MotionEvent event);
|
||||
|
||||
void pollInputDevices();
|
||||
|
||||
void pollHapticDevices();
|
||||
|
||||
void hapticRun(final int deviceId, final int length);
|
||||
|
||||
boolean isDeviceSDLJoystick(final int deviceId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Simple DirectMedia Layer
|
||||
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CHANGES 07.2020
|
||||
* - SDLActivity renamed as FullScreenActivity
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import com.arthenica.mobileffmpeg.FFplay;
|
||||
|
||||
import static com.arthenica.mobileffmpeg.Config.TAG;
|
||||
import static com.arthenica.mobileffmpeg.player.PlayerSession.FFPLAY_COMMAND;
|
||||
import static com.arthenica.mobileffmpeg.player.PlayerSession.NativeState;
|
||||
|
||||
public class FullScreenActivity extends Activity {
|
||||
protected PlayerSurface playerSurface;
|
||||
protected ViewGroup viewLayout;
|
||||
protected PlayerSession playerSession;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
String ffplayCommand = intent.getStringExtra(FFPLAY_COMMAND);
|
||||
|
||||
if (ffplayCommand == null) {
|
||||
Log.i(TAG, "FullScreenActivity created with empty ffplay command.");
|
||||
} else {
|
||||
Log.v(TAG, "FullScreenActivity created.");
|
||||
}
|
||||
|
||||
playerSession = new PlayerSession(getRequestedOrientation(), this, ffplayCommand);
|
||||
playerSurface = new PlayerSurface(this);
|
||||
playerSurface.init(this, new GenericMotionListener(), playerSession);
|
||||
|
||||
viewLayout = new RelativeLayout(this);
|
||||
viewLayout.addView(playerSurface);
|
||||
|
||||
setContentView(viewLayout);
|
||||
setWindowStyle(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Log.v(TAG, "FullScreenActivity paused.");
|
||||
setNextNativeState(NativeState.PAUSED);
|
||||
setResumedCalled(false);
|
||||
handleNativeState();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
Log.v(TAG, "FullScreenActivity resumed.");
|
||||
setNextNativeState(NativeState.RESUMED);
|
||||
setResumedCalled(true);
|
||||
handleNativeState();
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
Log.v(TAG, String.format("FullScreenActivity window focus changed, hasFocus: %s.", hasFocus));
|
||||
setHasFocus(hasFocus);
|
||||
if (hasFocus) {
|
||||
setNextNativeState(NativeState.RESUMED);
|
||||
} else {
|
||||
setNextNativeState(NativeState.PAUSED);
|
||||
}
|
||||
handleNativeState();
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
Log.v(TAG, "FullScreenActivity is on low memory.");
|
||||
FFplay.playerNativeLowMemory();
|
||||
super.onLowMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Log.v(TAG, "FullScreenActivity destroyed.");
|
||||
setNextNativeState(NativeState.PAUSED);
|
||||
handleNativeState();
|
||||
|
||||
// Send a quit message to the application
|
||||
FFplay.playerNativeQuit();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(final KeyEvent event) {
|
||||
int keyCode = event.getKeyCode();
|
||||
|
||||
// Ignore certain special keys so they're handled by Android
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
|
||||
keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
|
||||
keyCode == KeyEvent.KEYCODE_CAMERA ||
|
||||
keyCode == KeyEvent.KEYCODE_ZOOM_IN ||
|
||||
keyCode == KeyEvent.KEYCODE_ZOOM_OUT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
protected void setNextNativeState(final NativeState nextNativeState) {
|
||||
PlayerSession playerSession = this.playerSession;
|
||||
if (playerSession != null) {
|
||||
playerSession.setNextNativeState(nextNativeState);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleNativeState() {
|
||||
PlayerSurface playerSurface = this.playerSurface;
|
||||
if (playerSurface != null) {
|
||||
playerSurface.handleNativeState();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setHasFocus(final boolean hasFocus) {
|
||||
PlayerSurface playerSurface = this.playerSurface;
|
||||
if (playerSurface != null) {
|
||||
playerSurface.setHasFocus(hasFocus);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setResumedCalled(final boolean resumedCalled) {
|
||||
PlayerSurface playerSurface = this.playerSurface;
|
||||
if (playerSurface != null) {
|
||||
playerSurface.setResumedCalled(resumedCalled);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setWindowStyle(final boolean fullScreen) {
|
||||
PlayerSurface playerSurface = this.playerSurface;
|
||||
if (playerSurface != null) {
|
||||
playerSurface.setWindowStyle(fullScreen);
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerSurface getPlayerSurface() {
|
||||
return playerSurface;
|
||||
}
|
||||
|
||||
public PlayerSession getPlayerSession() {
|
||||
return playerSession;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Simple DirectMedia Layer
|
||||
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CHANGES 07.2020
|
||||
* - SDLAudioHandler renamed as GenericAudioHandler
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.AudioTrack;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import static com.arthenica.mobileffmpeg.Config.TAG;
|
||||
|
||||
public class GenericAudioHandler implements AudioHandler {
|
||||
protected AudioTrack audioTrack;
|
||||
protected AudioRecord audioRecord;
|
||||
|
||||
public void initialize() {
|
||||
audioTrack = null;
|
||||
audioRecord = null;
|
||||
}
|
||||
|
||||
public int audioOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, int desiredFrames) {
|
||||
int channelConfig = isStereo ? AudioFormat.CHANNEL_OUT_STEREO : AudioFormat.CHANNEL_OUT_MONO;
|
||||
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
|
||||
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
|
||||
|
||||
Log.v(TAG, String.format("AudioHandler audio: wanted %s %s %skHz, %d frames buffer.", isStereo ? "stereo" : "mono", is16Bit ? "16-bit" : "8-bit", sampleRate / 1000f, desiredFrames));
|
||||
|
||||
// Let the user pick a larger buffer if they really want -- but ye
|
||||
// gods they probably shouldn't, the minimums are horrifyingly high
|
||||
// latency already
|
||||
desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
|
||||
|
||||
if (audioTrack == null) {
|
||||
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
|
||||
channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
|
||||
|
||||
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
|
||||
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
|
||||
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
|
||||
|
||||
if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
|
||||
Log.e(TAG, "AudioHandler failed during initialization of AudioTrack.");
|
||||
audioTrack = null;
|
||||
return -1;
|
||||
}
|
||||
|
||||
audioTrack.play();
|
||||
}
|
||||
|
||||
Log.v(TAG, String.format("AudioHandler audio: got %s %s %skHz, %d frames buffer.", (audioTrack.getChannelCount() >= 2) ? "stereo" : "mono", (audioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit", audioTrack.getSampleRate() / 1000f, desiredFrames));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void audioWriteShortBuffer(final short[] buffer) {
|
||||
if (audioTrack == null) {
|
||||
Log.e(TAG, "AudioHandler attempted to make audio call with uninitialized audio!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < buffer.length; ) {
|
||||
int result = audioTrack.write(buffer, i, buffer.length - i);
|
||||
if (result > 0) {
|
||||
i += result;
|
||||
} else if (result == 0) {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
// Nom nom
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "AudioHandler audio: error return from write(short).");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void audioWriteByteBuffer(final byte[] buffer) {
|
||||
if (audioTrack == null) {
|
||||
Log.e(TAG, "AudioHandler attempted to make audio call with uninitialized audio!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < buffer.length; ) {
|
||||
int result = audioTrack.write(buffer, i, buffer.length - i);
|
||||
if (result > 0) {
|
||||
i += result;
|
||||
} else if (result == 0) {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
// Nom nom
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "AudioHandler audio: error return from write(byte).");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int captureOpen(final int sampleRate, final boolean is16Bit, final boolean isStereo, int desiredFrames) {
|
||||
int channelConfig = isStereo ? AudioFormat.CHANNEL_IN_STEREO : AudioFormat.CHANNEL_IN_MONO;
|
||||
int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
|
||||
int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
|
||||
|
||||
Log.v(TAG, String.format("AudioHandler capture: wanted %s %s %skHz, %d frames buffer.", isStereo ? "stereo" : "mono", is16Bit ? "16-bit" : "8-bit", sampleRate / 1000f, desiredFrames));
|
||||
|
||||
// Let the user pick a larger buffer if they really want -- but ye
|
||||
// gods they probably shouldn't, the minimums are horrifyingly high
|
||||
// latency already
|
||||
desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
|
||||
|
||||
if (audioRecord == null) {
|
||||
audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize);
|
||||
|
||||
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
|
||||
if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
|
||||
Log.e(TAG, "AudioHandler failed during initialization of AudioRecord.");
|
||||
audioRecord.release();
|
||||
audioRecord = null;
|
||||
return -1;
|
||||
}
|
||||
|
||||
audioRecord.startRecording();
|
||||
}
|
||||
|
||||
Log.v(TAG, String.format("AudioHandler capture: got %s %s %skHz, %d frames buffer.", (audioRecord.getChannelCount() >= 2) ? "stereo" : "mono", (audioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit", audioRecord.getSampleRate() / 1000f, desiredFrames));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int captureReadShortBuffer(final short[] buffer, final boolean blocking) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return audioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||
} else {
|
||||
return audioRecord.read(buffer, 0, buffer.length);
|
||||
}
|
||||
}
|
||||
|
||||
public int captureReadByteBuffer(final byte[] buffer, final boolean blocking) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return audioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||
} else {
|
||||
return audioRecord.read(buffer, 0, buffer.length);
|
||||
}
|
||||
}
|
||||
|
||||
public void audioClose() {
|
||||
if (audioTrack != null) {
|
||||
audioTrack.stop();
|
||||
audioTrack.release();
|
||||
audioTrack = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void captureClose() {
|
||||
if (audioRecord != null) {
|
||||
audioRecord.stop();
|
||||
audioRecord.release();
|
||||
audioRecord = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Simple DirectMedia Layer
|
||||
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import static com.arthenica.mobileffmpeg.Config.TAG;
|
||||
|
||||
/**
|
||||
* A Handler class for Messages from native SDL applications.
|
||||
* It uses current Activities as target (e.g. for the title).
|
||||
* static to prevent implicit references to enclosing object.
|
||||
*/
|
||||
public class GenericCommandHandler extends Handler {
|
||||
|
||||
public static final int COMMAND_CHANGE_TITLE = 1;
|
||||
public static final int COMMAND_CHANGE_WINDOW_STYLE = 2;
|
||||
public static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
|
||||
|
||||
protected final Context context;
|
||||
|
||||
public GenericCommandHandler(final Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(final Message message) {
|
||||
switch (message.arg1) {
|
||||
case COMMAND_CHANGE_TITLE:
|
||||
if (context instanceof Activity) {
|
||||
((Activity) context).setTitle((String) message.obj);
|
||||
} else {
|
||||
Log.e(TAG, "CommandHandler error handling message, getContext() returned no Activity.");
|
||||
}
|
||||
break;
|
||||
case COMMAND_CHANGE_WINDOW_STYLE:
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
// This version of Android doesn't support the immersive fullscreen mode
|
||||
break;
|
||||
}
|
||||
/* This needs more testing, per bug 4096 - Enabling fullscreen on Android causes the app to toggle fullscreen mode continuously in a loop
|
||||
***
|
||||
if (context instanceof Activity) {
|
||||
Window window = ((Activity) context).getWindow();
|
||||
if (window != null) {
|
||||
if ((message.obj instanceof Integer) && (((Integer) message.obj).intValue() != 0)) {
|
||||
int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
window.getDecorView().setSystemUiVisibility(flags);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
} else {
|
||||
int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||
window.getDecorView().setSystemUiVisibility(flags);
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "error handling message, getContext() returned no Activity");
|
||||
}
|
||||
***/
|
||||
break;
|
||||
case COMMAND_SET_KEEP_SCREEN_ON: {
|
||||
if (context instanceof Activity) {
|
||||
Window window = ((Activity) context).getWindow();
|
||||
if (window != null) {
|
||||
if ((message.obj instanceof Integer) && (((Integer) message.obj).intValue() != 0)) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Log.e(TAG, String.format("CommandHandler error handling message, command is %d.", message.arg1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Simple DirectMedia Layer
|
||||
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CHANGES 07.2020
|
||||
* - SDLControllerHandler renamed as GenericControllerHandler
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class GenericControllerHandler implements ControllerHandler {
|
||||
protected GenericJoystickHandler genericJoystickHandler;
|
||||
protected GenericHapticHandler genericHapticHandler;
|
||||
|
||||
public void initialize(final Context context) {
|
||||
genericJoystickHandler = new GenericJoystickHandler(this);
|
||||
genericHapticHandler = new GenericHapticHandler(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Joystick glue code, just a series of stubs that redirect to the JoystickHandler instance.
|
||||
*/
|
||||
public boolean handleJoystickMotionEvent(final MotionEvent event) {
|
||||
return genericJoystickHandler.handleMotionEvent(event);
|
||||
}
|
||||
|
||||
public void pollInputDevices() {
|
||||
genericJoystickHandler.pollInputDevices();
|
||||
}
|
||||
|
||||
public void pollHapticDevices() {
|
||||
genericHapticHandler.pollHapticDevices();
|
||||
}
|
||||
|
||||
public void hapticRun(final int deviceId, final int length) {
|
||||
genericHapticHandler.run(deviceId, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given device is considered a possible SDL joystick.
|
||||
*
|
||||
* @param deviceId device identifier
|
||||
* @return true if device is a joystick, false otherwise
|
||||
*/
|
||||
public boolean isDeviceSDLJoystick(final int deviceId) {
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if ((device == null) || device.isVirtual() || (deviceId < 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int sources = device.getSources();
|
||||
return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
|
||||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
||||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Simple DirectMedia Layer
|
||||
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CHANGES 07.2020
|
||||
* - SDLHapticHandler renamed as GenericHapticHandler
|
||||
* - SDLHaptic class renamed as Haptic
|
||||
* - Haptic class refactored
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Vibrator;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import com.arthenica.mobileffmpeg.FFplay;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <p>Generic haptic handler for FFplay Controller.
|
||||
*/
|
||||
public class GenericHapticHandler {
|
||||
|
||||
public static class Haptic {
|
||||
public final int deviceId;
|
||||
public final String name;
|
||||
public final Vibrator vibrator;
|
||||
|
||||
public Haptic(final int deviceId, final String name, final Vibrator vibrator) {
|
||||
this.deviceId = deviceId;
|
||||
this.name = name;
|
||||
this.vibrator = vibrator;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected final ArrayList<Haptic> hapticList;
|
||||
protected final Vibrator vibratorService;
|
||||
|
||||
public GenericHapticHandler(final Context context) {
|
||||
hapticList = new ArrayList<>();
|
||||
vibratorService = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
public void run(final int deviceId, final int length) {
|
||||
Haptic haptic = getHaptic(deviceId);
|
||||
if (haptic != null) {
|
||||
haptic.vibrator.vibrate(length);
|
||||
}
|
||||
}
|
||||
|
||||
public void pollHapticDevices() {
|
||||
|
||||
final int DEVICE_ID_VIBRATOR_SERVICE = 999999;
|
||||
boolean hasVibratorService = false;
|
||||
|
||||
final int[] deviceIdArray = InputDevice.getDeviceIds();
|
||||
// It helps processing the device ids in reverse order
|
||||
// For example, in the case of the XBox 360 wireless dongle,
|
||||
// so the first controller seen by SDL matches what the receiver
|
||||
// considers to be the first controller
|
||||
for (int i = deviceIdArray.length - 1; i > -1; i--) {
|
||||
Haptic haptic = getHaptic(deviceIdArray[i]);
|
||||
if (haptic == null) {
|
||||
InputDevice device = InputDevice.getDevice(deviceIdArray[i]);
|
||||
Vibrator vib = device.getVibrator();
|
||||
if (vib.hasVibrator()) {
|
||||
haptic = new Haptic(deviceIdArray[i], device.getName(), vib);
|
||||
hapticList.add(haptic);
|
||||
FFplay.controllerAddHaptic(haptic.deviceId, haptic.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check VIBRATOR_SERVICE */
|
||||
if (vibratorService != null) {
|
||||
hasVibratorService = vibratorService.hasVibrator();
|
||||
|
||||
if (hasVibratorService) {
|
||||
Haptic haptic = getHaptic(DEVICE_ID_VIBRATOR_SERVICE);
|
||||
if (haptic == null) {
|
||||
haptic = new Haptic(DEVICE_ID_VIBRATOR_SERVICE, "VIBRATOR_SERVICE", vibratorService);
|
||||
hapticList.add(haptic);
|
||||
FFplay.controllerAddHaptic(haptic.deviceId, haptic.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check removed devices */
|
||||
ArrayList<Integer> removedDevices = new ArrayList<>();
|
||||
for (int i = 0; i < hapticList.size(); i++) {
|
||||
int deviceId = hapticList.get(i).deviceId;
|
||||
int j;
|
||||
for (j = 0; j < deviceIdArray.length; j++) {
|
||||
if (deviceId == deviceIdArray[j]) break;
|
||||
}
|
||||
|
||||
if (deviceId == DEVICE_ID_VIBRATOR_SERVICE && hasVibratorService) {
|
||||
// don't remove the vibrator if it is still present
|
||||
} else if (j == deviceIdArray.length) {
|
||||
removedDevices.add(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < removedDevices.size(); i++) {
|
||||
int deviceId = removedDevices.get(i);
|
||||
FFplay.controllerRemoveHaptic(deviceId);
|
||||
for (int j = 0; j < hapticList.size(); j++) {
|
||||
if (hapticList.get(j).deviceId == deviceId) {
|
||||
hapticList.remove(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Haptic getHaptic(final int deviceId) {
|
||||
for (int i = 0; i < hapticList.size(); i++) {
|
||||
if (hapticList.get(i).deviceId == deviceId) {
|
||||
return hapticList.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Simple DirectMedia Layer
|
||||
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CHANGES 07.2020
|
||||
* - SDLJoystickHandler renamed as GenericJoystickHandler
|
||||
* - SDLJoystick class renamed as Joystick
|
||||
* - Joystick class refactored
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.arthenica.mobileffmpeg.FFplay;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Generic joystick handler for FFplay Controller.
|
||||
*/
|
||||
public class GenericJoystickHandler {
|
||||
|
||||
public static class Joystick {
|
||||
public final int deviceId;
|
||||
public final String name;
|
||||
public final String desc;
|
||||
public final ArrayList<InputDevice.MotionRange> axes;
|
||||
public final ArrayList<InputDevice.MotionRange> hats;
|
||||
|
||||
public Joystick(final int deviceId, final String name, final String desc, final ArrayList<InputDevice.MotionRange> axes, final ArrayList<InputDevice.MotionRange> hats) {
|
||||
this.deviceId = deviceId;
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
this.axes = axes;
|
||||
this.hats = hats;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
||||
@Override
|
||||
public int compare(final InputDevice.MotionRange arg0, final InputDevice.MotionRange arg1) {
|
||||
return arg0.getAxis() - arg1.getAxis();
|
||||
}
|
||||
}
|
||||
|
||||
protected final ArrayList<Joystick> joystickList;
|
||||
protected final ControllerHandler controllerHandler;
|
||||
|
||||
public GenericJoystickHandler(final ControllerHandler controllerHandler) {
|
||||
this.joystickList = new ArrayList<>();
|
||||
this.controllerHandler = controllerHandler;
|
||||
}
|
||||
|
||||
public void pollInputDevices() {
|
||||
int[] deviceIds = InputDevice.getDeviceIds();
|
||||
// It helps processing the device ids in reverse order
|
||||
// For example, in the case of the XBox 360 wireless dongle,
|
||||
// so the first controller seen by SDL matches what the receiver
|
||||
// considers to be the first controller
|
||||
|
||||
for (int i = deviceIds.length - 1; i > -1; i--) {
|
||||
Joystick joystick = getJoystick(deviceIds[i]);
|
||||
if (joystick == null) {
|
||||
InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
|
||||
if (controllerHandler.isDeviceSDLJoystick(deviceIds[i])) {
|
||||
joystick = new Joystick(deviceIds[i], joystickDevice.getName(), getJoystickDescriptor(joystickDevice), new ArrayList<InputDevice.MotionRange>(), new ArrayList<InputDevice.MotionRange>());
|
||||
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
|
||||
Collections.sort(ranges, new RangeComparator());
|
||||
|
||||
for (InputDevice.MotionRange range : ranges) {
|
||||
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
|
||||
joystick.hats.add(range);
|
||||
} else {
|
||||
joystick.axes.add(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
joystickList.add(joystick);
|
||||
FFplay.controllerAddJoystick(joystick.deviceId, joystick.name, joystick.desc, 0, -1, joystick.axes.size(), joystick.hats.size() / 2, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check removed devices */
|
||||
ArrayList<Integer> removedDevices = new ArrayList<>();
|
||||
for (int i = 0; i < joystickList.size(); i++) {
|
||||
int deviceId = joystickList.get(i).deviceId;
|
||||
int j;
|
||||
for (j = 0; j < deviceIds.length; j++) {
|
||||
if (deviceId == deviceIds[j]) break;
|
||||
}
|
||||
if (j == deviceIds.length) {
|
||||
removedDevices.add(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < removedDevices.size(); i++) {
|
||||
int deviceId = removedDevices.get(i);
|
||||
FFplay.controllerRemoveJoystick(deviceId);
|
||||
for (int j = 0; j < joystickList.size(); j++) {
|
||||
if (joystickList.get(j).deviceId == deviceId) {
|
||||
joystickList.remove(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Joystick getJoystick(int deviceId) {
|
||||
for (int i = 0; i < joystickList.size(); i++) {
|
||||
if (joystickList.get(i).deviceId == deviceId) {
|
||||
return joystickList.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean handleMotionEvent(final MotionEvent event) {
|
||||
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
|
||||
int actionPointerIndex = event.getActionIndex();
|
||||
int action = event.getActionMasked();
|
||||
|
||||
if (action == MotionEvent.ACTION_MOVE) {
|
||||
Joystick joystick = getJoystick(event.getDeviceId());
|
||||
if (joystick != null) {
|
||||
for (int i = 0; i < joystick.axes.size(); i++) {
|
||||
InputDevice.MotionRange range = joystick.axes.get(i);
|
||||
/* Normalize the value to -1...1 */
|
||||
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
|
||||
FFplay.controllerOnJoy(joystick.deviceId, i, value);
|
||||
}
|
||||
for (int i = 0; i < joystick.hats.size(); i += 2) {
|
||||
int hatX = Math.round(event.getAxisValue(joystick.hats.get(i).getAxis(), actionPointerIndex));
|
||||
int hatY = Math.round(event.getAxisValue(joystick.hats.get(i + 1).getAxis(), actionPointerIndex));
|
||||
FFplay.controllerOnHat(joystick.deviceId, i / 2, hatX, hatY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getJoystickDescriptor(final InputDevice joystickDevice) {
|
||||
String desc = joystickDevice.getDescriptor();
|
||||
|
||||
if (desc != null && !desc.isEmpty()) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
return joystickDevice.getName();
|
||||
}
|
||||
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Simple DirectMedia Layer
|
||||
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CHANGES 07.2020
|
||||
* - SDLGenericMotionListener renamed as GenericMotionListener
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.arthenica.mobileffmpeg.FFplay;
|
||||
|
||||
/**
|
||||
* <p>Generic motion listener for FFplay Player.
|
||||
*/
|
||||
public class GenericMotionListener implements View.OnGenericMotionListener {
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotion(final View view, final MotionEvent event) {
|
||||
ControllerHandler controllerHandler = FFplay.getControllerHandler();
|
||||
float x, y;
|
||||
int action;
|
||||
|
||||
switch (event.getSource()) {
|
||||
case InputDevice.SOURCE_JOYSTICK:
|
||||
case InputDevice.SOURCE_GAMEPAD:
|
||||
case InputDevice.SOURCE_DPAD: {
|
||||
if (controllerHandler != null) {
|
||||
return controllerHandler.handleJoystickMotionEvent(event);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case InputDevice.SOURCE_MOUSE: {
|
||||
if (!FFplay.isSeparateMouseAndTouch()) {
|
||||
break;
|
||||
}
|
||||
action = event.getActionMasked();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL: {
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||
|
||||
FFplay.playerOnMouse(0, action, x, y);
|
||||
return true;
|
||||
}
|
||||
case MotionEvent.ACTION_HOVER_MOVE: {
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
|
||||
FFplay.playerOnMouse(0, action, x, y);
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Simple DirectMedia Layer
|
||||
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CHANGES 07.2020
|
||||
* - SDLClipboardHandler renamed as PlayerClipboard
|
||||
* - Added null checks for clipboardManager field
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
|
||||
import com.arthenica.mobileffmpeg.FFplay;
|
||||
|
||||
/**
|
||||
* <p>Clipboard for FFplay Player.
|
||||
*/
|
||||
public class PlayerClipboard implements ClipboardManager.OnPrimaryClipChangedListener {
|
||||
|
||||
protected final Context context;
|
||||
protected final ClipboardManager clipboardManager;
|
||||
|
||||
public PlayerClipboard(final Context context) {
|
||||
this.context = context;
|
||||
this.clipboardManager = (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
||||
if (this.clipboardManager != null) {
|
||||
this.clipboardManager.addPrimaryClipChangedListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean clipboardHasText() {
|
||||
if (clipboardManager != null) {
|
||||
final ClipData clip = clipboardManager.getPrimaryClip();
|
||||
return (clip != null) && (clip.getItemCount() > 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String clipboardGetText() {
|
||||
CharSequence text = null;
|
||||
|
||||
if (clipboardManager != null) {
|
||||
ClipData clip = clipboardManager.getPrimaryClip();
|
||||
if (clip != null && clip.getItemCount() > 0) {
|
||||
text = clip.getItemAt(0).coerceToText(context);
|
||||
}
|
||||
}
|
||||
|
||||
if (text != null) {
|
||||
return text.toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void clipboardSetText(final String string) {
|
||||
if (clipboardManager != null) {
|
||||
clipboardManager.removePrimaryClipChangedListener(this);
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, string));
|
||||
clipboardManager.addPrimaryClipChangedListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrimaryClipChanged() {
|
||||
FFplay.playerOnClipboardChanged();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Taner Sener
|
||||
*
|
||||
* This file is part of MobileFFmpeg.
|
||||
*
|
||||
* MobileFFmpeg is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MobileFFmpeg 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Surface;
|
||||
|
||||
public interface PlayerManager {
|
||||
|
||||
void initialize();
|
||||
|
||||
boolean setActivityTitle(final String title);
|
||||
|
||||
void setWindowStyle(final boolean fullScreen);
|
||||
|
||||
void setOrientation(final int w, final int h, final boolean resizable, final String hint);
|
||||
|
||||
boolean isScreenKeyboardShown();
|
||||
|
||||
boolean sendMessage(final int command, final int param);
|
||||
|
||||
Context getContext();
|
||||
|
||||
boolean isAndroidTV();
|
||||
|
||||
DisplayMetrics getDisplayDPI();
|
||||
|
||||
boolean getManifestEnvironmentVariables();
|
||||
|
||||
boolean showTextInput(final int x, final int y, final int w, final int h);
|
||||
|
||||
Surface getNativeSurface();
|
||||
|
||||
int[] inputGetInputDeviceIds(final int sources);
|
||||
|
||||
boolean clipboardHasText();
|
||||
|
||||
String clipboardGetText();
|
||||
|
||||
void clipboardSetText(final String string);
|
||||
|
||||
int showMessageBox(final int flags, final String title, final String message, final int[] buttonFlags, final int[] buttonIds, final String[] buttonTexts, final int[] colors);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Taner Sener
|
||||
*
|
||||
* This file is part of MobileFFmpeg.
|
||||
*
|
||||
* MobileFFmpeg is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MobileFFmpeg 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.arthenica.mobileffmpeg.FFplay;
|
||||
import com.arthenica.mobileffmpeg.util.AsyncSingleFFplayExecuteTask;
|
||||
|
||||
public class PlayerSession {
|
||||
|
||||
public static final String FFPLAY_COMMAND = "ffplayCommand";
|
||||
|
||||
public enum NativeState {
|
||||
INIT, RESUMED, PAUSED
|
||||
}
|
||||
|
||||
protected int requestedOrientation;
|
||||
protected Context context;
|
||||
protected final String command;
|
||||
protected NativeState nextNativeState;
|
||||
protected NativeState currentNativeState;
|
||||
|
||||
public PlayerSession(final int requestedOrientation, final Context context, final String command) {
|
||||
this.requestedOrientation = requestedOrientation;
|
||||
this.context = context;
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public NativeState getNextNativeState() {
|
||||
return nextNativeState;
|
||||
}
|
||||
|
||||
public void setNextNativeState(final NativeState nextNativeState) {
|
||||
this.nextNativeState = nextNativeState;
|
||||
}
|
||||
|
||||
public NativeState getCurrentNativeState() {
|
||||
return currentNativeState;
|
||||
}
|
||||
|
||||
public void setCurrentNativeState(final NativeState currentNativeState) {
|
||||
this.currentNativeState = currentNativeState;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public void setContext(final Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public int getRequestedOrientation() {
|
||||
return requestedOrientation;
|
||||
}
|
||||
|
||||
public AsyncTask<String, Integer, Integer> execute() {
|
||||
FFplay.setAudioHandler(new GenericAudioHandler());
|
||||
FFplay.setControllerManager(new GenericControllerHandler());
|
||||
|
||||
AudioHandler audioHandler = FFplay.getAudioHandler();
|
||||
if (audioHandler != null) {
|
||||
audioHandler.initialize();
|
||||
}
|
||||
ControllerHandler controllerHandler = FFplay.getControllerHandler();
|
||||
if (controllerHandler != null) {
|
||||
controllerHandler.initialize(context);
|
||||
}
|
||||
|
||||
AsyncSingleFFplayExecuteTask task = new AsyncSingleFFplayExecuteTask(command);
|
||||
return task.execute("");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* Simple DirectMedia Layer
|
||||
* Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CHANGES 07.2020
|
||||
* - SDLSurface renamed as PlayerSurface
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.player;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.UiModeManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.arthenica.mobileffmpeg.FFplay;
|
||||
import com.arthenica.mobileffmpeg.player.PlayerSession.NativeState;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static android.content.Context.UI_MODE_SERVICE;
|
||||
import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
|
||||
import static com.arthenica.mobileffmpeg.Config.TAG;
|
||||
import static com.arthenica.mobileffmpeg.player.GenericCommandHandler.COMMAND_CHANGE_TITLE;
|
||||
import static com.arthenica.mobileffmpeg.player.GenericCommandHandler.COMMAND_CHANGE_WINDOW_STYLE;
|
||||
|
||||
/**
|
||||
* PlayerSurface. This is what we draw on, so we need to know when it's created in order to do
|
||||
* anything useful.
|
||||
* <p>
|
||||
* Because of this, that's where we set up the SDL thread
|
||||
*/
|
||||
public class PlayerSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
View.OnKeyListener, View.OnTouchListener, SensorEventListener, PlayerManager {
|
||||
|
||||
protected Activity activity;
|
||||
protected Handler commandHandler;
|
||||
protected PlayerClipboard playerClipboard;
|
||||
protected SensorManager sensorManager;
|
||||
protected Display display;
|
||||
protected float width, height;
|
||||
protected PlayerSession playerSession;
|
||||
protected boolean ready;
|
||||
protected boolean resumedCalled;
|
||||
protected boolean hasFocus;
|
||||
|
||||
public PlayerSurface(final Context context) {
|
||||
super(context);
|
||||
|
||||
onCreated(context);
|
||||
}
|
||||
|
||||
public PlayerSurface(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
onCreated(context);
|
||||
}
|
||||
|
||||
public PlayerSurface(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
onCreated(context);
|
||||
}
|
||||
|
||||
public PlayerSurface(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
onCreated(context);
|
||||
}
|
||||
|
||||
protected void onCreated(final Context context) {
|
||||
Log.v(TAG, String.format("PlayerSurface created on device: %s and model: %s.", android.os.Build.DEVICE, android.os.Build.MODEL));
|
||||
|
||||
getHolder().addCallback(this);
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
|
||||
commandHandler = new GenericCommandHandler(context);
|
||||
playerClipboard = new PlayerClipboard(context);
|
||||
display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
// Some arbitrary defaults to avoid a potential division by zero
|
||||
width = 1.0f;
|
||||
height = 1.0f;
|
||||
|
||||
ready = false;
|
||||
}
|
||||
|
||||
public void init(final Activity activity, final View.OnGenericMotionListener motionListener, final PlayerSession playerSession) {
|
||||
this.activity = activity;
|
||||
setOnGenericMotionListener(motionListener);
|
||||
|
||||
FFplay.setPlayerManager(this);
|
||||
|
||||
this.playerSession = playerSession;
|
||||
}
|
||||
|
||||
public void handlePause() {
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||
}
|
||||
|
||||
public void handleResume() {
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||
}
|
||||
|
||||
public Surface getNativeSurface() {
|
||||
return getHolder().getSurface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(final SurfaceHolder ignored) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(final SurfaceHolder holder) {
|
||||
|
||||
// Transition to pause, if needed
|
||||
setNextNativeState(NativeState.PAUSED);
|
||||
handleNativeState();
|
||||
|
||||
ready = false;
|
||||
FFplay.playerOnSurfaceDestroyed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
|
||||
int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
|
||||
switch (format) {
|
||||
case PixelFormat.A_8:
|
||||
Log.v(TAG, "PlayerSurface using pixel format A_8");
|
||||
break;
|
||||
case PixelFormat.LA_88:
|
||||
Log.v(TAG, "PlayerSurface using pixel format LA_88");
|
||||
break;
|
||||
case PixelFormat.L_8:
|
||||
Log.v(TAG, "PlayerSurface using pixel format L_8");
|
||||
break;
|
||||
case PixelFormat.RGBA_4444:
|
||||
Log.v(TAG, "PlayerSurface using pixel format RGBA_4444");
|
||||
sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
|
||||
break;
|
||||
case PixelFormat.RGBA_5551:
|
||||
Log.v(TAG, "PlayerSurface using pixel format RGBA_5551");
|
||||
sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
|
||||
break;
|
||||
case PixelFormat.RGBA_8888:
|
||||
Log.v(TAG, "PlayerSurface using pixel format RGBA_8888");
|
||||
sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
|
||||
break;
|
||||
case PixelFormat.RGBX_8888:
|
||||
Log.v(TAG, "PlayerSurface using pixel format RGBX_8888");
|
||||
sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
|
||||
break;
|
||||
case PixelFormat.RGB_332:
|
||||
Log.v(TAG, "PlayerSurface using pixel format RGB_332");
|
||||
sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
|
||||
break;
|
||||
case PixelFormat.RGB_565:
|
||||
Log.v(TAG, "PlayerSurface using pixel format RGB_565");
|
||||
sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
|
||||
break;
|
||||
case PixelFormat.RGB_888:
|
||||
Log.v(TAG, "PlayerSurface using pixel format RGB_888");
|
||||
// Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
|
||||
sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
|
||||
break;
|
||||
default:
|
||||
Log.v(TAG, String.format("PlayerSurface using pixel format unknown %d", format));
|
||||
break;
|
||||
}
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
FFplay.playerOnResize(width, height, sdlFormat, display.getRefreshRate());
|
||||
|
||||
Log.v(TAG, String.format("PlayerSurface window size: %dx%d.", width, height));
|
||||
|
||||
boolean skip = false;
|
||||
int requestedOrientation = getRequestedOrientation();
|
||||
|
||||
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
|
||||
// Accept any
|
||||
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||
if (this.width > this.height) {
|
||||
skip = true;
|
||||
}
|
||||
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||
if (this.width < this.height) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special Patch for Square Resolution: Black Berry Passport
|
||||
if (skip) {
|
||||
double min = Math.min(this.width, this.height);
|
||||
double max = Math.max(this.width, this.height);
|
||||
|
||||
if (max / min < 1.20) {
|
||||
Log.v(TAG, "PlayerSurface don't skip on such aspect-ratio. Could be a square resolution.");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
Log.v(TAG, "PlayerSurface skip .. Surface is not ready.");
|
||||
ready = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Surface is ready */
|
||||
ready = true;
|
||||
|
||||
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||
FFplay.playerOnSurfaceChanged();
|
||||
|
||||
handleNativeState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKey(final View v, final int keyCode, final KeyEvent event) {
|
||||
final ControllerHandler controllerHandler = FFplay.getControllerHandler();
|
||||
// Dispatch the different events depending on where they come from
|
||||
// Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
|
||||
// So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
|
||||
//
|
||||
// Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
|
||||
// SOURCE_JOYSTICK, while its key events arrive from the keyboard source
|
||||
// So, retrieve the device itself and check all of its sources
|
||||
if (controllerHandler != null && controllerHandler.isDeviceSDLJoystick(event.getDeviceId())) {
|
||||
// Note that we process events with specific key codes here
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (FFplay.controllerOnPadDown(event.getDeviceId(), keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
if (FFplay.controllerOnPadUp(event.getDeviceId(), keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (isTextInputEvent(event)) {
|
||||
FFplay.inputCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
}
|
||||
FFplay.playerOnKeyDown(keyCode);
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
FFplay.playerOnKeyUp(keyCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((event.getSource() & InputDevice.SOURCE_MOUSE) != 0) {
|
||||
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
|
||||
// they are ignored here because sending them as mouse input to SDL is messy
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
|
||||
switch (event.getAction()) {
|
||||
case KeyEvent.ACTION_DOWN:
|
||||
case KeyEvent.ACTION_UP:
|
||||
// mark the event as handled or it will be handled by system
|
||||
// handling KEYCODE_BACK by system will call onBackPressed()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(final View v, final MotionEvent event) {
|
||||
final int touchDevId = event.getDeviceId();
|
||||
final int pointerCount = event.getPointerCount();
|
||||
int action = event.getActionMasked();
|
||||
int pointerFingerId;
|
||||
int mouseButton;
|
||||
int i = -1;
|
||||
float x, y, p;
|
||||
|
||||
if (event.getSource() == InputDevice.SOURCE_MOUSE && FFplay.isSeparateMouseAndTouch()) {
|
||||
try {
|
||||
mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event);
|
||||
} catch (Exception e) {
|
||||
mouseButton = 1; // oh well.
|
||||
}
|
||||
FFplay.playerOnMouse(mouseButton, action, event.getX(0), event.getY(0));
|
||||
} else {
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / width;
|
||||
y = event.getY(i) / height;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
FFplay.playerOnTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Primary pointer up/down, the index is always zero
|
||||
i = 0;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
// Non primary pointer up/down
|
||||
if (i == -1) {
|
||||
i = event.getActionIndex();
|
||||
}
|
||||
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / width;
|
||||
y = event.getY(i) / height;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
FFplay.playerOnTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / width;
|
||||
y = event.getY(i) / height;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
FFplay.playerOnTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(final SensorEvent event) {
|
||||
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
float x, y;
|
||||
switch (display.getRotation()) {
|
||||
case Surface.ROTATION_90:
|
||||
x = -event.values[1];
|
||||
y = event.values[0];
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
x = event.values[1];
|
||||
y = -event.values[0];
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
x = -event.values[1];
|
||||
y = -event.values[0];
|
||||
break;
|
||||
default:
|
||||
x = event.values[0];
|
||||
y = event.values[1];
|
||||
break;
|
||||
}
|
||||
FFplay.playerOnAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||
y / SensorManager.GRAVITY_EARTH,
|
||||
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||
}
|
||||
}
|
||||
|
||||
public void enableSensor(final int sensorType, final boolean enabled) {
|
||||
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||
if (enabled) {
|
||||
sensorManager.registerListener(this,
|
||||
sensorManager.getDefaultSensor(sensorType),
|
||||
SensorManager.SENSOR_DELAY_GAME, null);
|
||||
} else {
|
||||
sensorManager.unregisterListener(this, sensorManager.getDefaultSensor(sensorType));
|
||||
}
|
||||
}
|
||||
|
||||
int getRequestedOrientation() {
|
||||
final PlayerSession playerSession = this.playerSession;
|
||||
if (playerSession == null) {
|
||||
return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
} else {
|
||||
return playerSession.getRequestedOrientation();
|
||||
}
|
||||
}
|
||||
|
||||
public void setNextNativeState(final NativeState nextNativeState) {
|
||||
final PlayerSession playerSession = this.playerSession;
|
||||
if (playerSession != null) {
|
||||
playerSession.setNextNativeState(nextNativeState);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentNativeState(final NativeState currentNativeState) {
|
||||
final PlayerSession playerSession = this.playerSession;
|
||||
if (playerSession != null) {
|
||||
playerSession.setCurrentNativeState(currentNativeState);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isTextInputEvent(final KeyEvent event) {
|
||||
if (event.isCtrlPressed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
|
||||
}
|
||||
|
||||
public void handleNativeState() {
|
||||
Log.v(TAG, String.format("PlayerSurface handling nativeState with ready: %s, hasFocus: %s, resumed: %s.", ready, hasFocus, resumedCalled));
|
||||
|
||||
NativeState currentNativeState = null;
|
||||
NativeState nextNativeState = null;
|
||||
if (playerSession != null) {
|
||||
currentNativeState = playerSession.getCurrentNativeState();
|
||||
nextNativeState = playerSession.getNextNativeState();
|
||||
}
|
||||
|
||||
Log.v(TAG, String.format("PlayerSurface handling nativeState with current:%s, next: %s.", currentNativeState, nextNativeState));
|
||||
|
||||
if (nextNativeState == currentNativeState) {
|
||||
// Already in same state, discard.
|
||||
return;
|
||||
}
|
||||
|
||||
// Try a transition to init state
|
||||
if (nextNativeState == NativeState.INIT) {
|
||||
|
||||
setCurrentNativeState(nextNativeState);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try a transition to paused state
|
||||
if (nextNativeState == NativeState.PAUSED) {
|
||||
FFplay.playerNativePause();
|
||||
handlePause();
|
||||
setCurrentNativeState(nextNativeState);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try a transition to resumed state
|
||||
if (nextNativeState == NativeState.RESUMED) {
|
||||
play();
|
||||
}
|
||||
}
|
||||
|
||||
public void play() {
|
||||
if (ready && resumedCalled) {
|
||||
initialize();
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||
if (playerSession != null) {
|
||||
playerSession.execute();
|
||||
}
|
||||
FFplay.playerNativeResume();
|
||||
handleResume();
|
||||
setCurrentNativeState(NativeState.RESUMED);
|
||||
} else {
|
||||
Log.v(TAG, String.format("PlayerSurface play failed for ready:%s, hasFocus: %s, resumed: %s.", ready, hasFocus, resumedCalled));
|
||||
}
|
||||
}
|
||||
|
||||
public void setHasFocus(final boolean hasFocus) {
|
||||
this.hasFocus = hasFocus;
|
||||
}
|
||||
|
||||
public void setResumedCalled(final boolean resumedCalled) {
|
||||
this.resumedCalled = resumedCalled;
|
||||
}
|
||||
|
||||
public boolean sendCommand(final int command, final Object data) {
|
||||
Message message = commandHandler.obtainMessage();
|
||||
message.arg1 = command;
|
||||
message.obj = data;
|
||||
return commandHandler.sendMessage(message);
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
setHasFocus(true);
|
||||
setNextNativeState(NativeState.INIT);
|
||||
setCurrentNativeState(NativeState.INIT);
|
||||
}
|
||||
|
||||
public boolean setActivityTitle(final String title) {
|
||||
return sendCommand(COMMAND_CHANGE_TITLE, title);
|
||||
}
|
||||
|
||||
public void setWindowStyle(final boolean fullScreen) {
|
||||
sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullScreen ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setOrientation(final int w, final int h, final boolean resizable, final String hint) {
|
||||
int orientation = -1;
|
||||
|
||||
if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) {
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
|
||||
} else if (hint.contains("LandscapeRight")) {
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
} else if (hint.contains("LandscapeLeft")) {
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
|
||||
} else if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) {
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
|
||||
} else if (hint.contains("Portrait")) {
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
} else if (hint.contains("PortraitUpsideDown")) {
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
|
||||
}
|
||||
|
||||
/* no valid hint */
|
||||
if (orientation == -1) {
|
||||
if (resizable) {
|
||||
/* no fixed orientation */
|
||||
} else {
|
||||
if (w > h) {
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
|
||||
} else {
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.v(TAG, String.format("PlayerSurface set orientation:%d, width:%d, height:%d, resizable:%s and hint:%s.", orientation, w, h, resizable, hint));
|
||||
|
||||
if (orientation != -1) {
|
||||
activity.setRequestedOrientation(orientation);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isScreenKeyboardShown() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean sendMessage(final int command, final int param) {
|
||||
return sendCommand(command, param);
|
||||
}
|
||||
|
||||
public boolean isAndroidTV() {
|
||||
UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(UI_MODE_SERVICE);
|
||||
return (uiModeManager.getCurrentModeType() == UI_MODE_TYPE_TELEVISION);
|
||||
}
|
||||
|
||||
public DisplayMetrics getDisplayDPI() {
|
||||
return getContext().getResources().getDisplayMetrics();
|
||||
}
|
||||
|
||||
public boolean getManifestEnvironmentVariables() {
|
||||
try {
|
||||
ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
|
||||
Bundle bundle = applicationInfo.metaData;
|
||||
if (bundle == null) {
|
||||
return false;
|
||||
}
|
||||
String prefix = "SDL_ENV.";
|
||||
final int trimLength = prefix.length();
|
||||
for (String key : bundle.keySet()) {
|
||||
if (key.startsWith(prefix)) {
|
||||
String name = key.substring(trimLength);
|
||||
String value = bundle.get(key).toString();
|
||||
FFplay.playerNativeSetenv(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (final Exception e) {
|
||||
Log.i(TAG, "PlayerSurface failed to set environment variables. " + e.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean showTextInput(final int x, final int y, final int w, final int h) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int[] inputGetInputDeviceIds(final int sources) {
|
||||
int[] ids = InputDevice.getDeviceIds();
|
||||
int[] filtered = new int[ids.length];
|
||||
int used = 0;
|
||||
|
||||
for (int id : ids) {
|
||||
InputDevice device = InputDevice.getDevice(id);
|
||||
if ((device != null) && ((device.getSources() & sources) != 0)) {
|
||||
filtered[used++] = device.getId();
|
||||
}
|
||||
}
|
||||
|
||||
return Arrays.copyOf(filtered, used);
|
||||
}
|
||||
|
||||
public int showMessageBox(
|
||||
final int flags,
|
||||
final String title,
|
||||
final String message,
|
||||
final int[] buttonFlags,
|
||||
final int[] buttonIds,
|
||||
final String[] buttonTexts,
|
||||
final int[] colors) {
|
||||
|
||||
Log.i(TAG, String.format("PlayerSurface was asked to showMessageBox for title: %s, message: %s and %d buttons: %s.", title, message, (buttonIds != null) ? buttonIds.length : 0, Arrays.toString(buttonTexts)));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean clipboardHasText() {
|
||||
return playerClipboard.clipboardHasText();
|
||||
}
|
||||
|
||||
public String clipboardGetText() {
|
||||
return playerClipboard.clipboardGetText();
|
||||
}
|
||||
|
||||
public void clipboardSetText(final String string) {
|
||||
playerClipboard.clipboardSetText(string);
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 Taner Sener
|
||||
*
|
||||
* This file is part of MobileFFmpeg.
|
||||
*
|
||||
* MobileFFmpeg is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MobileFFmpeg 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MobileFFmpeg. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.arthenica.mobileffmpeg.util;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import com.arthenica.mobileffmpeg.FFplay;
|
||||
|
||||
import static com.arthenica.mobileffmpeg.Config.TAG;
|
||||
|
||||
public class AsyncSingleFFplayExecuteTask extends AsyncTask<String, Integer, Integer> {
|
||||
private final String command;
|
||||
|
||||
public AsyncSingleFFplayExecuteTask(final String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(final String... ignored) {
|
||||
|
||||
Log.v(TAG, String.format("Running FFplay for %s.", command));
|
||||
|
||||
int rc = FFplay.execute(command.split(" "));
|
||||
|
||||
Log.v(TAG, String.format("Finished running FFplay for %s.", command));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final Integer ignored) {
|
||||
}
|
||||
|
||||
}
|
||||
+25
-7
@@ -4,6 +4,20 @@ $(call import-add-path, $(LOCAL_PATH))
|
||||
MY_ARM_MODE := arm
|
||||
MY_ARM_NEON := false
|
||||
MY_PATH := ../app/src/main/cpp
|
||||
MY_LIBRARY_LTS_SOURCE := $(MY_PATH)/android_lts_support.c
|
||||
MY_LIBRARY_SOURCE := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
MY_LIBRARY_SHARED_LIBRARIES := libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
|
||||
MY_LIBRARY_LDLIBS := -llog -lz -landroid
|
||||
MY_LIBRARY_INCLUDES :=
|
||||
MY_LIBRARY_WHOLE_STATIC_LIBRARIES :=
|
||||
|
||||
# ENABLE FFPLAY
|
||||
ifeq ("$(shell test -e $(LOCAL_PATH)/../build/.ffplay && echo ffplay)","ffplay")
|
||||
MY_LIBRARY_SOURCE += $(MY_PATH)/fftools_ffplay.c $(MY_PATH)/mobileffplay.c
|
||||
MY_LIBRARY_WHOLE_STATIC_LIBRARIES += sdl
|
||||
MY_LIBRARY_LDLIBS += -lGLESv1_CM -lGLESv2
|
||||
MY_LIBRARY_INCLUDES := -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/sdl/include/SDL2 -I${LOCAL_PATH}/../../src/sdl/src/core/android
|
||||
endif
|
||||
|
||||
# DEFINE ARCH FLAGS
|
||||
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
|
||||
@@ -44,17 +58,21 @@ include $(BUILD_SHARED_LIBRARY)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_ARM_MODE := $(MY_ARM_MODE)
|
||||
LOCAL_MODULE := mobileffmpeg
|
||||
LOCAL_SRC_FILES := $(MY_LIBRARY_SOURCE)
|
||||
ifeq ($(TARGET_PLATFORM),android-16)
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/android_lts_support.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
|
||||
else ifeq ($(TARGET_PLATFORM),android-17)
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/android_lts_support.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
else
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
|
||||
endif
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include
|
||||
LOCAL_LDLIBS := -llog -lz -landroid
|
||||
LOCAL_SHARED_LIBRARIES := c++_shared libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include ${MY_LIBRARY_INCLUDES}
|
||||
LOCAL_LDLIBS := ${MY_LIBRARY_LDLIBS}
|
||||
LOCAL_SHARED_LIBRARIES := ${MY_LIBRARY_SHARED_LIBRARIES}
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := ${MY_LIBRARY_WHOLE_STATIC_LIBRARIES}
|
||||
LOCAL_ARM_NEON := ${MY_ARM_NEON}
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
ifeq ("$(shell test -e $(LOCAL_PATH)/../build/.ffplay && echo ffplay)","ffplay")
|
||||
$(call import-module, sdl)
|
||||
endif
|
||||
|
||||
$(call import-module, ffmpeg)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
MY_ARM_MODE := arm
|
||||
MY_SDL_LIB := ../../../prebuilt/android-$(TARGET_ARCH)/sdl/lib
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_ARM_MODE := $(MY_ARM_MODE)
|
||||
LOCAL_MODULE := sdl
|
||||
LOCAL_SRC_FILES := $(MY_SDL_LIB)/libSDL2.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
@@ -51,8 +51,8 @@ android.applicationVariants.all { variant ->
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// implementation project(':app')
|
||||
implementation 'com.arthenica:mobile-ffmpeg-full:4.3.1'
|
||||
implementation project(':app')
|
||||
// implementation 'com.arthenica:mobile-ffmpeg-full:4.3.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
}
|
||||
|
||||
@@ -26,6 +26,14 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.arthenica.mobileffmpeg.player.FullScreenActivity"
|
||||
android:label="play"
|
||||
android:configChanges="keyboardHidden|orientation"
|
||||
android:screenOrientation="landscape"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
+58
-16
@@ -20,18 +20,15 @@
|
||||
package com.arthenica.mobileffmpeg.test;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.Bundle;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.MediaController;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.VideoView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -39,10 +36,14 @@ import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.arthenica.mobileffmpeg.Config;
|
||||
import com.arthenica.mobileffmpeg.FFmpeg;
|
||||
import com.arthenica.mobileffmpeg.FFplay;
|
||||
import com.arthenica.mobileffmpeg.LogCallback;
|
||||
import com.arthenica.mobileffmpeg.LogMessage;
|
||||
import com.arthenica.mobileffmpeg.Statistics;
|
||||
import com.arthenica.mobileffmpeg.StatisticsCallback;
|
||||
import com.arthenica.mobileffmpeg.player.GenericMotionListener;
|
||||
import com.arthenica.mobileffmpeg.player.PlayerSession;
|
||||
import com.arthenica.mobileffmpeg.player.PlayerSurface;
|
||||
import com.arthenica.mobileffmpeg.util.DialogUtil;
|
||||
import com.arthenica.mobileffmpeg.util.ResourcesUtil;
|
||||
import com.arthenica.mobileffmpeg.util.SingleExecuteCallback;
|
||||
@@ -57,11 +58,13 @@ import static com.arthenica.mobileffmpeg.test.MainActivity.TAG;
|
||||
|
||||
public class VideoTabFragment extends Fragment implements AdapterView.OnItemSelectedListener {
|
||||
|
||||
private VideoView videoView;
|
||||
private AlertDialog progressDialog;
|
||||
private String selectedCodec;
|
||||
private Statistics statistics;
|
||||
|
||||
private PlayerSession playerSession;
|
||||
private PlayerSurface playerSurface;
|
||||
|
||||
public VideoTabFragment() {
|
||||
super(R.layout.fragment_video_tab);
|
||||
}
|
||||
@@ -83,23 +86,20 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
encodeVideo();
|
||||
playVideo();
|
||||
// encodeVideo();
|
||||
// encodeWebp();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
videoView = view.findViewById(R.id.videoPlayerFrame);
|
||||
|
||||
progressDialog = DialogUtil.createProgressDialog(requireContext(), "Encoding video");
|
||||
|
||||
selectedCodec = getResources().getStringArray(R.array.video_codec)[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setActive();
|
||||
playerSession = new PlayerSession(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, getContext(), getVideoFile().getAbsolutePath());
|
||||
playerSurface = view.findViewById(R.id.videoPlayerFrame);
|
||||
playerSurface.init(getActivity(), new GenericMotionListener(), playerSession);
|
||||
}
|
||||
|
||||
public static VideoTabFragment newInstance() {
|
||||
@@ -155,7 +155,7 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
|
||||
try {
|
||||
|
||||
// IF VIDEO IS PLAYING STOP PLAYBACK
|
||||
videoView.stopPlayback();
|
||||
// videoView.stopPlayback();
|
||||
|
||||
if (videoFile.exists()) {
|
||||
videoFile.delete();
|
||||
@@ -234,7 +234,7 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
|
||||
}
|
||||
|
||||
protected void playVideo() {
|
||||
MediaController mediaController = new MediaController(requireContext());
|
||||
/* MediaController mediaController = new MediaController(requireContext());
|
||||
mediaController.setAnchorView(videoView);
|
||||
videoView.setVideoURI(Uri.parse("file://" + getVideoFile().getAbsolutePath()));
|
||||
videoView.setMediaController(mediaController);
|
||||
@@ -254,7 +254,12 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
|
||||
return false;
|
||||
}
|
||||
});
|
||||
videoView.start();
|
||||
videoView.start();*/
|
||||
|
||||
// Intent intent = new Intent(getContext(), FullScreenActivity.class);
|
||||
// intent.putExtra(PlayerSession.FFPLAY_COMMAND, getVideoFile().getAbsolutePath());
|
||||
// startActivity(intent);
|
||||
playerSurface.play();
|
||||
}
|
||||
|
||||
public String getSelectedVideoCodec() {
|
||||
@@ -393,4 +398,41 @@ public class VideoTabFragment extends Fragment implements AdapterView.OnItemSele
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
Log.v(Config.TAG, "VideoTabFragment paused.");
|
||||
playerSurface.setNextNativeState(PlayerSession.NativeState.PAUSED);
|
||||
playerSurface.setResumedCalled(false);
|
||||
playerSurface.handleNativeState();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
Log.v(Config.TAG, "VideoTabFragment resumed.");
|
||||
setActive();
|
||||
playerSurface.setNextNativeState(PlayerSession.NativeState.RESUMED);
|
||||
playerSurface.setResumedCalled(true);
|
||||
playerSurface.handleNativeState();
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
Log.v(Config.TAG, "VideoTabFragment is on low memory.");
|
||||
FFplay.playerNativeLowMemory();
|
||||
super.onLowMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.v(Config.TAG, "VideoTabFragment destroyed.");
|
||||
playerSurface.setNextNativeState(PlayerSession.NativeState.PAUSED);
|
||||
playerSurface.handleNativeState();
|
||||
|
||||
// Send a quit message to the application
|
||||
FFplay.playerNativeQuit();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<VideoView
|
||||
<com.arthenica.mobileffmpeg.player.PlayerSurface
|
||||
android:id="@+id/videoPlayerFrame"
|
||||
android:layout_width="wrap_content"
|
||||
android:background="@drawable/rounded_video_frame"
|
||||
|
||||
+15
-12
@@ -250,27 +250,27 @@ get_size_optimization_cflags() {
|
||||
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -O2 -ffunction-sections -fdata-sections"
|
||||
;;
|
||||
*)
|
||||
ARCH_OPTIMIZATION="-Os -ffunction-sections -fdata-sections"
|
||||
ARCH_OPTIMIZATION="-O2 -ffunction-sections -fdata-sections"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
arm64-v8a)
|
||||
case $1 in
|
||||
ffmpeg)
|
||||
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -fuse-ld=gold -O2 -ffunction-sections -fdata-sections"
|
||||
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -fuse-ld=bfd -O2 -ffunction-sections -fdata-sections"
|
||||
;;
|
||||
*)
|
||||
ARCH_OPTIMIZATION="-Os -ffunction-sections -fdata-sections"
|
||||
ARCH_OPTIMIZATION="-O2 -ffunction-sections -fdata-sections"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
x86 | x86-64)
|
||||
case $1 in
|
||||
ffmpeg)
|
||||
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -Os -ffunction-sections -fdata-sections"
|
||||
ARCH_OPTIMIZATION="${LINK_TIME_OPTIMIZATION_FLAGS} -O2 -ffunction-sections -fdata-sections"
|
||||
;;
|
||||
*)
|
||||
ARCH_OPTIMIZATION="-Os -ffunction-sections -fdata-sections"
|
||||
ARCH_OPTIMIZATION="-O2 -ffunction-sections -fdata-sections"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
@@ -291,15 +291,18 @@ get_app_specific_cflags() {
|
||||
ffmpeg)
|
||||
APP_FLAGS="-Wno-unused-function -DBIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD"
|
||||
;;
|
||||
kvazaar)
|
||||
APP_FLAGS="-std=gnu99 -Wno-unused-function"
|
||||
;;
|
||||
shine)
|
||||
APP_FLAGS="-Wno-unused-function"
|
||||
;;
|
||||
sdl)
|
||||
APP_FLAGS="-std=c11 -Wno-unused-function"
|
||||
;;
|
||||
soxr | snappy | libwebp)
|
||||
APP_FLAGS="-std=gnu99 -Wno-unused-function -DPIC"
|
||||
;;
|
||||
kvazaar)
|
||||
APP_FLAGS="-std=gnu99 -Wno-unused-function"
|
||||
;;
|
||||
*)
|
||||
APP_FLAGS="-std=c99 -Wno-unused-function"
|
||||
;;
|
||||
@@ -330,7 +333,7 @@ get_cxxflags() {
|
||||
fi
|
||||
|
||||
if [[ -z ${MOBILE_FFMPEG_DEBUG} ]]; then
|
||||
local OPTIMIZATION_FLAGS="-Os -ffunction-sections -fdata-sections"
|
||||
local OPTIMIZATION_FLAGS="-O2 -ffunction-sections -fdata-sections"
|
||||
else
|
||||
local OPTIMIZATION_FLAGS="${MOBILE_FFMPEG_DEBUG}"
|
||||
fi
|
||||
@@ -392,10 +395,10 @@ get_size_optimization_ldflags() {
|
||||
arm64-v8a)
|
||||
case $1 in
|
||||
ffmpeg)
|
||||
echo "-Wl,--gc-sections ${LINK_TIME_OPTIMIZATION_FLAGS} -fuse-ld=gold -O2 -ffunction-sections -fdata-sections -finline-functions"
|
||||
echo "-Wl,--gc-sections ${LINK_TIME_OPTIMIZATION_FLAGS} -fuse-ld=bfd -O2 -ffunction-sections -fdata-sections -finline-functions"
|
||||
;;
|
||||
*)
|
||||
echo "-Wl,--gc-sections -Os -ffunction-sections -fdata-sections"
|
||||
echo "-Wl,--gc-sections -O2 -ffunction-sections -fdata-sections"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
@@ -405,7 +408,7 @@ get_size_optimization_ldflags() {
|
||||
echo "-Wl,--gc-sections,--icf=safe ${LINK_TIME_OPTIMIZATION_FLAGS} -O2 -ffunction-sections -fdata-sections -finline-functions"
|
||||
;;
|
||||
*)
|
||||
echo "-Wl,--gc-sections,--icf=safe -Os -ffunction-sections -fdata-sections"
|
||||
echo "-Wl,--gc-sections,--icf=safe -O2 -ffunction-sections -fdata-sections"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
+57
-15
@@ -34,28 +34,70 @@ export CXXFLAGS=$(get_cxxflags ${LIB_NAME})
|
||||
export LDFLAGS=$(get_ldflags ${LIB_NAME})
|
||||
export PKG_CONFIG_LIBDIR="${INSTALL_PKG_CONFIG_DIR}"
|
||||
|
||||
SIMD_OPTIONS=""
|
||||
case ${ARCH} in
|
||||
arm-v7a)
|
||||
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a -DCMAKE_ANDROID_ARM_MODE=ON -DCMAKE_ANDROID_ARM_NEON=OFF -DCMAKE_SYSTEM_PROCESSOR=armv7-a"
|
||||
;;
|
||||
arm-v7a-neon)
|
||||
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a -DCMAKE_ANDROID_ARM_MODE=ON -DCMAKE_ANDROID_ARM_NEON=ON -DCMAKE_SYSTEM_PROCESSOR=armv7-a"
|
||||
;;
|
||||
arm64-v8a)
|
||||
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=$(get_android_arch 2) -DCMAKE_SYSTEM_PROCESSOR=$(get_cmake_target_processor)"
|
||||
;;
|
||||
x86)
|
||||
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=$(get_android_arch 3) -DCMAKE_SYSTEM_PROCESSOR=i686"
|
||||
;;
|
||||
x86-64)
|
||||
SIMD_OPTIONS="-DCMAKE_ANDROID_ARCH_ABI=$(get_android_arch 4) -DCMAKE_SYSTEM_PROCESSOR=$(get_cmake_target_processor)"
|
||||
;;
|
||||
esac
|
||||
|
||||
cd ${BASEDIR}/src/${LIB_NAME} || exit 1
|
||||
|
||||
make distclean 2>/dev/null 1>/dev/null
|
||||
|
||||
# RECONFIGURING IF REQUESTED
|
||||
if [[ ${RECONF_sdl} -eq 1 ]]; then
|
||||
autoreconf_library ${LIB_NAME}
|
||||
if [ -d "build" ]; then
|
||||
rm -rf build
|
||||
fi
|
||||
|
||||
./configure \
|
||||
--prefix=${BASEDIR}/prebuilt/android-$(get_target_build)/${LIB_NAME} \
|
||||
--with-pic \
|
||||
--without-x \
|
||||
--with-sysroot=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--disable-fast-install \
|
||||
--host=${TARGET_HOST} || exit 1
|
||||
mkdir build;
|
||||
cd build
|
||||
|
||||
# USE OUR OWN IMPLEMENTATION
|
||||
rm -f ${BASEDIR}/src/${LIB_NAME}/src/core/android/SDL_android.c
|
||||
cp ${BASEDIR}/tools/make/sdl/SDL_android.c ${BASEDIR}/src/${LIB_NAME}/src/core/android/SDL_android.c
|
||||
|
||||
cmake -Wno-dev \
|
||||
-DCMAKE_VERBOSE_MAKEFILE=0 \
|
||||
-DCMAKE_C_FLAGS="${CFLAGS}" \
|
||||
-DCMAKE_CXX_FLAGS="${CXXFLAGS}" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" \
|
||||
-DCMAKE_SYSROOT="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot" \
|
||||
-DCMAKE_FIND_ROOT_PATH="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/${TARGET_HOST}/lib;${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot/usr/lib/${TARGET_HOST}/${API};${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/lib" \
|
||||
-DCMAKE_ANDROID_NDK=${ANDROID_NDK_ROOT} \
|
||||
-DCMAKE_SYSTEM_LIBRARY_PATH="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/${TARGET_HOST}/lib;${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot/usr/lib/${TARGET_HOST}/${API};${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/lib" \
|
||||
-DCMAKE_SYSTEM_NAME=Android \
|
||||
-DCMAKE_ANDROID_API="${API}" \
|
||||
-DCMAKE_ANDROID_NDK="${ANDROID_NDK_ROOT}" \
|
||||
${SIMD_OPTIONS} \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${BASEDIR}/prebuilt/android-$(get_target_build)/${LIB_NAME}" \
|
||||
-DCMAKE_LINKER="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/bin/$LD" \
|
||||
-DCMAKE_AR="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/bin/$AR" \
|
||||
-DCMAKE_AS="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/bin/$AS" \
|
||||
-DCMAKE_RANLIB="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${TOOLCHAIN}/bin/$RANLIB" \
|
||||
-DSDL_SHARED=0 \
|
||||
-DSDL_STATIC=1 \
|
||||
-DSDL_STATIC_PIC=1 .. || exit 1
|
||||
|
||||
make -j$(get_cpu_count) || exit 1
|
||||
|
||||
# WORKAROUND FOR LINKERS THAT CAN NOT USE FULL PATHS i.e. arm64-v8a LINKER
|
||||
${SED_INLINE} "s/\.so / /g" sdl2.pc
|
||||
${SED_INLINE} "s/${ANDROID_NDK_ROOT//\//\\/}\/toolchains\/llvm\/prebuilt\/${TOOLCHAIN//\//\\/}\/${TARGET_HOST//\//\\/}\/lib\/lib//g" sdl2.pc
|
||||
${SED_INLINE} "s/${ANDROID_NDK_ROOT//\//\\/}\/toolchains\/llvm\/prebuilt\/${TOOLCHAIN//\//\\/}\/sysroot\/usr\/lib\/${TARGET_HOST//\//\\/}\/${API}\/lib//g" sdl2.pc
|
||||
${SED_INLINE} "s/${ANDROID_NDK_ROOT//\//\\/}\/toolchains\/llvm\/prebuilt\/${TOOLCHAIN//\//\\/}\/lib\/lib//g" sdl2.pc
|
||||
|
||||
# MANUALLY COPY PKG-CONFIG FILES
|
||||
cp ./*.pc ${INSTALL_PKG_CONFIG_DIR} || exit 1
|
||||
cp *.pc ${INSTALL_PKG_CONFIG_DIR} || exit 1
|
||||
|
||||
make install || exit 1
|
||||
|
||||
+1
-1
@@ -321,7 +321,7 @@ get_app_specific_cflags() {
|
||||
mobile-ffmpeg)
|
||||
APP_FLAGS="-std=c99 -Wno-unused-function -Wall -Wno-deprecated-declarations -Wno-pointer-sign -Wno-switch -Wno-unused-result -Wno-unused-variable -DPIC -fobjc-arc"
|
||||
;;
|
||||
sdl2)
|
||||
sdl)
|
||||
APP_FLAGS="-DPIC -Wno-unused-function -D__IPHONEOS__"
|
||||
;;
|
||||
shine)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+32
-14
@@ -4,6 +4,20 @@ $(call import-add-path, $(LOCAL_PATH))
|
||||
MY_ARM_MODE := arm
|
||||
MY_ARM_NEON := false
|
||||
MY_PATH := ../app/src/main/cpp
|
||||
MY_LIBRARY_LTS_SOURCE := $(MY_PATH)/android_lts_support.c
|
||||
MY_LIBRARY_SOURCE := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
MY_LIBRARY_SHARED_LIBRARIES := c++_shared libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
|
||||
MY_LIBRARY_LDLIBS := -llog -lz -landroid
|
||||
MY_LIBRARY_INCLUDES :=
|
||||
MY_LIBRARY_WHOLE_STATIC_LIBRARIES :=
|
||||
|
||||
# ENABLE FFPLAY
|
||||
ifeq ("$(shell test -e $(LOCAL_PATH)/../build/.ffplay && echo ffplay)","ffplay")
|
||||
MY_LIBRARY_SOURCE += $(MY_PATH)/fftools_ffplay.c $(MY_PATH)/mobileffplay.c
|
||||
MY_LIBRARY_WHOLE_STATIC_LIBRARIES += sdl
|
||||
MY_LIBRARY_LDLIBS += -lGLESv1_CM -lGLESv2
|
||||
MY_LIBRARY_INCLUDES := -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/sdl/include/SDL2 -I${LOCAL_PATH}/../../src/sdl/src/core/android
|
||||
endif
|
||||
|
||||
# DEFINE ARCH FLAGS
|
||||
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
|
||||
@@ -44,35 +58,39 @@ include $(BUILD_SHARED_LIBRARY)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_ARM_MODE := $(MY_ARM_MODE)
|
||||
LOCAL_MODULE := mobileffmpeg
|
||||
LOCAL_SRC_FILES := $(MY_LIBRARY_SOURCE)
|
||||
ifeq ($(TARGET_PLATFORM),android-16)
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/android_lts_support.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
|
||||
else ifeq ($(TARGET_PLATFORM),android-17)
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/android_lts_support.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
else
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
|
||||
endif
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include
|
||||
LOCAL_LDLIBS := -llog -lz -landroid
|
||||
LOCAL_SHARED_LIBRARIES := c++_shared libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include ${MY_LIBRARY_INCLUDES}
|
||||
LOCAL_LDLIBS := ${MY_LIBRARY_LDLIBS}
|
||||
LOCAL_SHARED_LIBRARIES := ${MY_LIBRARY_SHARED_LIBRARIES}
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := ${MY_LIBRARY_WHOLE_STATIC_LIBRARIES}
|
||||
LOCAL_ARM_NEON := ${MY_ARM_NEON}
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
ifeq ("$(shell test -e $(LOCAL_PATH)/../build/.ffplay && echo ffplay)","ffplay")
|
||||
$(call import-module, sdl)
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
|
||||
ifeq ("$(shell test -e $(LOCAL_PATH)/../build/.neon && echo neon)","neon")
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_ARM_MODE := $(MY_ARM_MODE)
|
||||
LOCAL_MODULE := mobileffmpeg_armv7a_neon
|
||||
LOCAL_SRC_FILES := $(MY_LIBRARY_SOURCE)
|
||||
ifeq ($(TARGET_PLATFORM),android-16)
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/android_lts_support.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
|
||||
else ifeq ($(TARGET_PLATFORM),android-17)
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/android_lts_support.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
else
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
|
||||
endif
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include
|
||||
LOCAL_LDLIBS := -llog -lz -landroid
|
||||
LOCAL_SHARED_LIBRARIES := c++_shared libavcodec_neon libavfilter_neon libswscale_neon libavformat libavutil libswresample libavdevice
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include ${MY_LIBRARY_INCLUDES}
|
||||
LOCAL_LDLIBS := ${MY_LIBRARY_LDLIBS}
|
||||
LOCAL_SHARED_LIBRARIES := ${MY_LIBRARY_SHARED_LIBRARIES}
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := ${MY_LIBRARY_WHOLE_STATIC_LIBRARIES}
|
||||
LOCAL_ARM_NEON := true
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
|
||||
+25
-7
@@ -4,6 +4,20 @@ $(call import-add-path, $(LOCAL_PATH))
|
||||
MY_ARM_MODE := arm
|
||||
MY_ARM_NEON := false
|
||||
MY_PATH := ../app/src/main/cpp
|
||||
MY_LIBRARY_LTS_SOURCE := $(MY_PATH)/android_lts_support.c
|
||||
MY_LIBRARY_SOURCE := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
MY_LIBRARY_SHARED_LIBRARIES := c++_shared libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
|
||||
MY_LIBRARY_LDLIBS := -llog -lz -landroid
|
||||
MY_LIBRARY_INCLUDES :=
|
||||
MY_LIBRARY_WHOLE_STATIC_LIBRARIES :=
|
||||
|
||||
# ENABLE FFPLAY
|
||||
ifeq ("$(shell test -e $(LOCAL_PATH)/../build/.ffplay && echo ffplay)","ffplay")
|
||||
MY_LIBRARY_SOURCE += $(MY_PATH)/fftools_ffplay.c $(MY_PATH)/mobileffplay.c
|
||||
MY_LIBRARY_WHOLE_STATIC_LIBRARIES += sdl
|
||||
MY_LIBRARY_LDLIBS += -lGLESv1_CM -lGLESv2
|
||||
MY_LIBRARY_INCLUDES := -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/sdl/include/SDL2 -I${LOCAL_PATH}/../../src/sdl/src/core/android
|
||||
endif
|
||||
|
||||
# DEFINE ARCH FLAGS
|
||||
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
|
||||
@@ -44,17 +58,21 @@ include $(BUILD_SHARED_LIBRARY)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_ARM_MODE := $(MY_ARM_MODE)
|
||||
LOCAL_MODULE := mobileffmpeg
|
||||
LOCAL_SRC_FILES := $(MY_LIBRARY_SOURCE)
|
||||
ifeq ($(TARGET_PLATFORM),android-16)
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/android_lts_support.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
|
||||
else ifeq ($(TARGET_PLATFORM),android-17)
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/android_lts_support.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
else
|
||||
LOCAL_SRC_FILES := $(MY_PATH)/mobileffmpeg.c $(MY_PATH)/mobileffprobe.c $(MY_PATH)/mobileffmpeg_exception.c $(MY_PATH)/fftools_cmdutils.c $(MY_PATH)/fftools_ffmpeg.c $(MY_PATH)/fftools_ffprobe.c $(MY_PATH)/fftools_ffmpeg_opt.c $(MY_PATH)/fftools_ffmpeg_hw.c $(MY_PATH)/fftools_ffmpeg_filter.c
|
||||
LOCAL_SRC_FILES += $(MY_LIBRARY_LTS_SOURCE)
|
||||
endif
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include
|
||||
LOCAL_LDLIBS := -llog -lz -landroid
|
||||
LOCAL_SHARED_LIBRARIES := c++_shared libavfilter libavformat libavcodec libavutil libswresample libavdevice libswscale
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -Wno-switch -Wno-sign-compare -I${LOCAL_PATH}/../../prebuilt/android-$(TARGET_ARCH)/ffmpeg/include ${MY_LIBRARY_INCLUDES}
|
||||
LOCAL_LDLIBS := ${MY_LIBRARY_LDLIBS}
|
||||
LOCAL_SHARED_LIBRARIES := ${MY_LIBRARY_SHARED_LIBRARIES}
|
||||
LOCAL_WHOLE_STATIC_LIBRARIES := ${MY_LIBRARY_WHOLE_STATIC_LIBRARIES}
|
||||
LOCAL_ARM_NEON := ${MY_ARM_NEON}
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
ifeq ("$(shell test -e $(LOCAL_PATH)/../build/.ffplay && echo ffplay)","ffplay")
|
||||
$(call import-module, sdl)
|
||||
endif
|
||||
|
||||
$(call import-module, ffmpeg)
|
||||
|
||||
Reference in New Issue
Block a user