Compare commits

...

3 Commits

Author SHA1 Message Date
Taner Sener ae769f8cfd connect ffplay to sdl for android 2020-07-17 19:06:32 +01:00
Taner Sener 1da0c9d8d7 implement ffplay for android 2020-02-09 22:38:08 +00:00
Taner Sener 1883d97999 fix ios/tvos sdl flags 2020-01-30 22:03:15 +00:00
34 changed files with 8862 additions and 81 deletions
+9
View File
@@ -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
+39
View File
@@ -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);
}
}
+32
View File
@@ -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;
}
}
}
@@ -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));
}
}
}
@@ -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)
);
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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);
}
}
@@ -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
View File
@@ -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)
+10
View File
@@ -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)
+2 -2
View File
@@ -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>
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)