Commit 94e1b3ef authored by Geoffrey Métais's avatar Geoffrey Métais

Medialibrary integration

Written with Bastion Penavayre and Thomas Guillem for making medialibrary build on Android

medialibrary: use clang

MediaWrapper class is now integrated into medialibrary module
parent 7a4f2e60
......@@ -3,7 +3,13 @@
/toolchains/
#medialibrary
medialibrary/
medialibrary/medialibrary
medialibrary/sqlite*
medialibrary/libvlcpp
medialibrary/build
medialibrary/jni/obj
medialibrary/jni/libs
medialibrary/.libs
# Android .so
/android-libs/**/*.so
......
......@@ -732,6 +732,158 @@ $ANDROID_NDK/ndk-build$OSCMD -C libvlc \
checkfail "ndk-build failed for private libs"
################
# MEDIALIBRARY #
################
if [ ! -d "${SRC_DIR}/medialibrary" ]; then
mkdir "${SRC_DIR}/medialibrary"
fi
##########
# SQLITE #
##########
MEDIALIBRARY_MODULE_DIR=${SRC_DIR}/medialibrary
MEDIALIBRARY_BUILD_DIR=${MEDIALIBRARY_MODULE_DIR}/medialibrary
OUT_LIB_DIR=$MEDIALIBRARY_MODULE_DIR/jni/libs/${ANDROID_ABI}
SQLITE_RELEASE="sqlite-autoconf-3120200"
if [ ! -d "${MEDIALIBRARY_MODULE_DIR}/${SQLITE_RELEASE}" ]; then
echo -e "\e[1m\e[32msqlite source not found, downloading\e[0m"
cd ${MEDIALIBRARY_MODULE_DIR}
wget https://www.sqlite.org/2016/${SQLITE_RELEASE}.tar.gz
tar -xzf ${SQLITE_RELEASE}.tar.gz
fi
cd ${MEDIALIBRARY_MODULE_DIR}/${SQLITE_RELEASE}
if [ ! -d build ]; then
mkdir build;
fi;
cd build;
if [ ! -e ./config.h -o "$RELEASE" = 1 ]; then
../bootstrap
../configure \
--host=$TARGET_TUPLE \
--disable-shared \
CFLAGS="${VLC_CFLAGS} ${EXTRA_CFLAGS}" \
CXXFLAGS="${VLC_CXXFLAGS} ${EXTRA_CFLAGS} ${EXTRA_CXXFLAGS}" \
CC="clang" \
CXX="clang++"
fi
make $MAKEFLAGS
cd ${SRC_DIR}
checkfail "sqlite build failed"
##############################
# FETCH MEDIALIBRARY SOURCES #
##############################
if [ ! -d "${MEDIALIBRARY_MODULE_DIR}/medialibrary" ]; then
echo -e "\e[1m\e[32mmedialibrary source not found, cloning\e[0m"
git clone http://code.videolan.org/videolan/medialibrary.git "${SRC_DIR}/medialibrary/medialibrary"
checkfail "medialibrary source: git clone failed"
fi
if [ ! -d "${MEDIALIBRARY_MODULE_DIR}/libvlcpp" ]; then
echo -e "\e[1m\e[32mlibvlcpp source not found, cloning\e[0m"
git clone http://code.videolan.org/videolan/libvlcpp.git "${MEDIALIBRARY_MODULE_DIR}/libvlcpp"
checkfail "libvlcpp source: git clone failed"
fi
echo -e "\e[1m\e[36mCFLAGS: ${CFLAGS}\e[0m"
echo -e "\e[1m\e[36mEXTRA_CFLAGS: ${EXTRA_CFLAGS}\e[0m"
#################
# Setup folders #
#################
#############
# CONFIGURE #
#############
cd ${MEDIALIBRARY_BUILD_DIR}
sed "s#@prefix@#${MEDIALIBRARY_MODULE_DIR}/libvlcpp#g" $SRC_DIR/pkgs/libvlcpp.pc.in > \
$SRC_DIR/pkgs/libvlcpp.pc;
sed "s#@libdir@#$SRC_DIR/libvlc/jni/libs/$ANDROID_ABI#g" $SRC_DIR/pkgs/libvlc.pc.in > \
$SRC_DIR/pkgs/libvlc.pc;
sed -i "s#@includedirs@#-I${SRC_DIR}/vlc/include \
-I${SRC_DIR}/vlc/build-android-$TARGET_TUPLE/include#g" $SRC_DIR/pkgs/libvlc.pc;
if [ ! -d build-android ]; then
mkdir build-android;
fi;
cd build-android;
if [ ! -e ./config.h -o "$RELEASE" = 1 ]; then
../bootstrap
../configure \
--host=$TARGET_TUPLE \
--disable-shared \
CFLAGS="${VLC_CFLAGS} ${EXTRA_CFLAGS}" \
CXXFLAGS="${VLC_CXXFLAGS} ${EXTRA_CFLAGS} ${EXTRA_CXXFLAGS}" \
CC="clang" \
CXX="clang++" \
NM="${CROSS_TOOLS}nm" \
STRIP="${CROSS_TOOLS}strip" \
RANLIB="${CROSS_TOOLS}ranlib" \
PKG_CONFIG_LIBDIR="$SRC_DIR/pkgs/" \
LIBJPEG_LIBS="-L$SRC_DIR/vlc/contrib/contrib-android-$TARGET_TUPLE/jpeg/.libs -ljpeg" \
LIBJPEG_CFLAGS="-I$SRC_DIR/vlc/contrib/$TARGET_TUPLE/include/" \
SQLITE_LIBS="-L$MEDIALIBRARY_MODULE_DIR/$SQLITE_RELEASE/build/.libs -lsqlite3" \
SQLITE_CFLAGS="-I$MEDIALIBRARY_MODULE_DIR/$SQLITE_RELEASE" \
AR="${CROSS_TOOLS}ar"
checkfail "medialibrary: autoconf failed"
fi
############
# BUILDING #
############
echo -e "\e[1m\e[32mBuilding medialibrary\e[0m"
make $MAKEFLAGS
checkfail "medialibrary: make failed"
cd ${SRC_DIR}
echo -e "ndk-build medialibrary"
$ANDROID_NDK/ndk-build -C medialibrary \
APP_STL="c++_shared" \
ANDROID_SYS_HEADERS="$ANDROID_SYS_HEADERS" \
APP_BUILD_SCRIPT=jni/Android.mk \
APP_PLATFORM=android-${ANDROID_API} \
APP_ABI=${ANDROID_ABI} \
LOCAL_CPP_FEATURES="exceptions" \
TARGET_TUPLE=$TARGET_TUPLE \
NDK_PROJECT_PATH=jni \
NDK_TOOLCHAIN_VERSION=clang \
NDK_DEBUG=${NDK_DEBUG} \
VLC_LIBS="-L$SRC_DIR/libvlc/jni/libs/$ANDROID_ABI -lvlc" \
MEDIALIBRARY_LIBS="-L${MEDIALIBRARY_BUILD_DIR}/build-android/.libs -lmedialibrary" \
LIBJPEG_LIBS="-L$SRC_DIR/vlc/contrib/contrib-android-$TARGET_TUPLE/jpeg/.libs -ljpeg" \
SQLITE_LIBS="-L$MEDIALIBRARY_MODULE_DIR/$SQLITE_RELEASE/build/.libs -lsqlite3" \
MEDIALIBRARY_INCLUDE_DIR=${MEDIALIBRARY_BUILD_DIR}/include \
SQLITE3_DIR=${MEDIALIBRARY_MODULE_DIR}/${SQLITE_RELEASE}/build/.libs
checkfail "ndk-build failed"
cd ${SRC_DIR}
if [ ! -d $OUT_LIB_DIR ]; then
mkdir -p $OUT_LIB_DIR
fi
if [ "$RELEASE" = 1 ]; then
echo -e "\e[1m\e[32mStripping\e[0m"
${CROSS_TOOLS}strip ${OUT_LIB_DIR}/*.so
checkfail "stripping"
fi
VERSION=$(grep "android:versionName" vlc-android/AndroidManifest.xml|cut -d\" -f 2)
OUT_DBG_DIR=.dbg/${ANDROID_ABI}/$VERSION
......@@ -740,3 +892,4 @@ echo "Dumping dbg symbols info ${OUT_DBG_DIR}"
mkdir -p $OUT_DBG_DIR
cp -a libvlc/jni/obj/local/${ANDROID_ABI}/*.so ${OUT_DBG_DIR}
cp -a libvlc/private_libs/obj/local/${ANDROID_ABI}/*.so ${OUT_DBG_DIR}
cp -a medialibrary/jni/obj/local/${ANDROID_ABI}/*.so ${OUT_DBG_DIR}
......@@ -37,6 +37,9 @@ mkdir -p "$TMP_PATH"/jni
cp -r "$SCRIPT_PATH"/libvlc/jni/libs "$TMP_PATH"
cp -r "$SCRIPT_PATH"/libvlc/jni/obj "$TMP_PATH"
cp -r "$SCRIPT_PATH"/medialibrary/jni/libs "$TMP_PATH"
cp -r "$SCRIPT_PATH"/medialibrary/jni/obj "$TMP_PATH"
ln -s "$TMP_PATH"/obj "$TMP_PATH"/jni
cp "$SCRIPT_PATH"/libvlc/jni/Android.mk "$TMP_PATH"/jni
......
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.videolan.medialibrary">
<uses-sdk android:minSdkVersion="9" />
<application/>
</manifest>
/*
* *************************************************************************
* build.gradle.java
* **************************************************************************
* Copyright © 2015 VLC authors and VideoLAN
* Author: Geoffrey Métais
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
* ***************************************************************************
*/
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
sourceSets {
main {
jni.srcDirs = []
// Prevent gradle from building native code with ndk; we have our own Makefile for it.
jniLibs.srcDir 'jni/libs' // Where generated .so files are placed.
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets', 'libcompat/libs/armeabi']
}
}
buildTypes {
release {
minifyEnabled false
proguardFile 'proguard.cfg'
}
}
}
dependencies {
compile project(':libvlc')
compile 'com.android.support:support-compat:24.2.1'
compile 'com.android.support:support-fragment:24.2.1'
}
\ No newline at end of file
LOCAL_PATH := $(call my-dir)
LOCAL_SRC_FILES := medialibrary.cpp AndroidMediaLibrary.cpp AndroidDeviceLister.cpp utils.cpp
LOCAL_MODULE := mla
LOCAL_MODULE_FILENAME := libmla
LOCAL_LDLIBS += $(MEDIALIBRARY_LIBS) $(VLC_LIBS) $(LIBJPEG_LIBS) $(SQLITE_LIBS) -llog
LOCAL_C_INCLUDES := $(MEDIALIBRARY_INCLUDE_DIR)
include $(BUILD_SHARED_LIBRARY)
#include "AndroidDeviceLister.h"
#include <mutex>
#define LOG_TAG "VLC/JNI/AndroidDeviceLister"
#include "log.h"
static std::mutex m_mutex;
AndroidDeviceLister::AndroidDeviceLister()
{
}
std::vector<std::tuple<std::string, std::string, bool>>
AndroidDeviceLister::devices() const
{
std::lock_guard<std::mutex> guard(m_mutex);
std::vector<std::tuple<std::string, std::string, bool>> devices(m_devices.size());
for(auto kv : m_devices)
devices.push_back(kv.second);
return devices;
}
void
AndroidDeviceLister::addDevice(std::string uuid, std::string path, bool removable)
{
std::lock_guard<std::mutex> guard(m_mutex);
m_devices.insert(std::make_pair(uuid, std::make_tuple(uuid, path, removable)));
}
bool
AndroidDeviceLister::removeDevice(std::string uuidToRemove)
{
std::lock_guard<std::mutex> guard(m_mutex);
return m_devices.erase(uuidToRemove);
}
#ifndef ANDROIDDEVICELISTER_H
#define ANDROIDDEVICELISTER_H
#include <medialibrary/IDeviceLister.h>
#include <string>
#include <vector>
#include <unordered_map>
class AndroidDeviceLister : public medialibrary::IDeviceLister
{
public:
AndroidDeviceLister();
std::vector<std::tuple<std::string, std::string, bool>> devices() const;
void addDevice(std::string, std::string, bool);
bool removeDevice(std::string uuidToRemove);
private:
std::unordered_map<std::string, std::tuple<std::string, std::string, bool>> m_devices;
};
#endif // ANDROIDDEVICELISTER_H
#include "AndroidMediaLibrary.h"
#define LOG_TAG "VLC/JNI/AndroidMediaLibrary"
#include "log.h"
#define FLAG_MEDIA_UPDATED_AUDIO 1 << 0
#define FLAG_MEDIA_UPDATED_AUDIO_EMPTY 1 << 1
#define FLAG_MEDIA_UPDATED_VIDEO 1 << 2
#define FLAG_MEDIA_ADDED_AUDIO 1 << 3
#define FLAG_MEDIA_ADDED_AUDIO_EMPTY 1 << 4
#define FLAG_MEDIA_ADDED_VIDEO 1 << 5
static pthread_key_t jni_env_key;
static JavaVM *myVm;
static void jni_detach_thread(void *data)
{
myVm->DetachCurrentThread();
}
static void key_init(void)
{
pthread_key_create(&jni_env_key, jni_detach_thread);
}
AndroidMediaLibrary::AndroidMediaLibrary(JavaVM *vm, fields *ref_fields, jobject thiz)
: p_ml( NewMediaLibrary() ), p_fields ( ref_fields )
{
myVm = vm;
p_lister = std::make_shared<AndroidDeviceLister>();
p_ml->setLogger( new AndroidMediaLibraryLogger );
p_ml->setVerbosity(medialibrary::LogLevel::Info);
p_DeviceListerCb = p_ml->setDeviceLister(p_lister);
pthread_once(&key_once, key_init);
JNIEnv *env = getEnv();
if (env == NULL)
return;
if (p_fields->MediaLibrary.getWeakReferenceID)
{
weak_thiz = nullptr;
jobject weak_compat = env->CallObjectMethod(thiz, p_fields->MediaLibrary.getWeakReferenceID);
if (weak_compat)
this->weak_compat = env->NewGlobalRef(weak_compat);
env->DeleteLocalRef(weak_compat);
} else
{
weak_thiz = p_fields->MediaLibrary.getWeakReferenceID ? nullptr : env->NewWeakGlobalRef(thiz);
weak_compat = nullptr;
}
}
AndroidMediaLibrary::~AndroidMediaLibrary()
{
LOGD("AndroidMediaLibrary delete");
pthread_key_delete(jni_env_key);
delete p_ml;
}
void
AndroidMediaLibrary::initML(const std::string& dbPath, const std::string& thumbsPath)
{
p_ml->initialize(dbPath, thumbsPath, this);
}
void
AndroidMediaLibrary::addDevice(std::string uuid, std::string path, bool removable)
{
p_lister->addDevice(uuid, path, removable);
p_DeviceListerCb->onDevicePlugged(uuid, path);
}
bool
AndroidMediaLibrary::removeDevice(std::string uuid)
{
bool removed = p_lister->removeDevice(uuid);
if (removed)
p_DeviceListerCb->onDeviceUnplugged(uuid);
return removed;
}
void
AndroidMediaLibrary::banFolder(const std::string& path)
{
p_ml->banFolder(path);
}
void
AndroidMediaLibrary::discover(const std::string& libraryPath)
{
p_ml->discover(libraryPath);
}
bool
AndroidMediaLibrary::isWorking()
{
return m_nbDiscovery > 0 || m_progress > 0 && m_progress < 100;
}
void
AndroidMediaLibrary::pauseBackgroundOperations()
{
p_ml->pauseBackgroundOperations();
}
void
AndroidMediaLibrary::setMediaUpdatedCbFlag(int flags)
{
m_mediaUpdatedType = flags;
}
void
AndroidMediaLibrary::setMediaAddedCbFlag(int flags)
{
m_mediaAddedType = flags;
}
void
AndroidMediaLibrary::resumeBackgroundOperations()
{
p_ml->resumeBackgroundOperations();
}
void
AndroidMediaLibrary::reload()
{
p_ml->reload();
}
void
AndroidMediaLibrary::reload( const std::string& entryPoint )
{
p_ml->reload(entryPoint);
}
bool
AndroidMediaLibrary::increasePlayCount(int64_t mediaId)
{
return p_ml->media(mediaId)->increasePlayCount();
}
bool
AndroidMediaLibrary::updateProgress(int64_t mediaId, int64_t time)
{
medialibrary::MediaPtr media = p_ml->media(mediaId);
if (media->duration() == 0)
return false;
float progress = time/(double)media->duration();
if (progress > 0.95)
progress = 0.0;
LOGD("update progress %f", progress);
return media->setProgress(progress);
}
std::vector<medialibrary::MediaPtr>
AndroidMediaLibrary::lastMediaPlayed()
{
return p_ml->lastMediaPlayed();
}
medialibrary::SearchAggregate
AndroidMediaLibrary::search(const std::string& query)
{
return p_ml->search(query);
}
medialibrary::MediaPtr
AndroidMediaLibrary::media(long id)
{
return p_ml->media(id);
}
medialibrary::MediaPtr
AndroidMediaLibrary::media(const std::string& mrl)
{
return p_ml->media(mrl);
}
std::vector<medialibrary::MediaPtr>
AndroidMediaLibrary::videoFiles( medialibrary::SortingCriteria sort, bool desc )
{
return p_ml->videoFiles(sort, desc);
}
std::vector<medialibrary::MediaPtr>
AndroidMediaLibrary::audioFiles( medialibrary::SortingCriteria sort, bool desc )
{
return p_ml->audioFiles(sort, desc);
}
std::vector<medialibrary::AlbumPtr>
AndroidMediaLibrary::albums()
{
return p_ml->albums();
}
std::vector<medialibrary::ArtistPtr>
AndroidMediaLibrary::artists()
{
return p_ml->artists();
}
std::vector<medialibrary::GenrePtr>
AndroidMediaLibrary::genres()
{
return p_ml->genres();
}
std::vector<medialibrary::PlaylistPtr>
AndroidMediaLibrary::playlists()
{
return p_ml->playlists();
}
medialibrary::PlaylistPtr
AndroidMediaLibrary::playlist( int64_t playlistId )
{
return p_ml->playlist(playlistId);
}
medialibrary::PlaylistPtr
AndroidMediaLibrary::PlaylistCreate( const std::string &name )
{
return p_ml->createPlaylist(name);
}
std::vector<medialibrary::MediaPtr>
AndroidMediaLibrary::tracksFromAlbum( int64_t albumId )
{
return p_ml->album(albumId)->tracks();
}
std::vector<medialibrary::MediaPtr>
AndroidMediaLibrary::mediaFromArtist( int64_t artistId )
{
return p_ml->artist(artistId)->media();
}
std::vector<medialibrary::AlbumPtr>
AndroidMediaLibrary::albumsFromArtist( int64_t artistId )
{
return p_ml->artist(artistId)->albums();
}
std::vector<medialibrary::MediaPtr>
AndroidMediaLibrary::mediaFromGenre( int64_t genreId )
{
return p_ml->genre(genreId)->tracks();
}
std::vector<medialibrary::AlbumPtr>
AndroidMediaLibrary::albumsFromGenre( int64_t genreId )
{
return p_ml->genre(genreId)->albums();
}
std::vector<medialibrary::ArtistPtr>
AndroidMediaLibrary::artistsFromGenre( int64_t genreId )
{
return p_ml->genre(genreId)->artists();
}
std::vector<medialibrary::MediaPtr>
AndroidMediaLibrary::mediaFromPlaylist( int64_t playlistId )
{
return p_ml->playlist(playlistId)->media();
}
bool
AndroidMediaLibrary::playlistAppend(int64_t playlistId, int64_t mediaId) {
medialibrary::PlaylistPtr playlist = p_ml->playlist(playlistId);
if (playlist == nullptr)
return false;
return playlist->append(mediaId);
}
bool
AndroidMediaLibrary::playlistAdd(int64_t playlistId, int64_t mediaId, unsigned int position) {
medialibrary::PlaylistPtr playlist = p_ml->playlist(playlistId);
if (playlist == nullptr)
return false;
return playlist->add(mediaId, position);
}
bool
AndroidMediaLibrary::playlistMove(int64_t playlistId, int64_t mediaId, unsigned int position) {
medialibrary::PlaylistPtr playlist = p_ml->playlist(playlistId);
if (playlist == nullptr)
return false;
return playlist->move(mediaId, position);
}
bool
AndroidMediaLibrary::playlistRemove(int64_t playlistId, int64_t mediaId) {
medialibrary::PlaylistPtr playlist = p_ml->playlist(playlistId);
if (playlist == nullptr)
return false;
return playlist->remove(mediaId);
}
bool
AndroidMediaLibrary::PlaylistDelete( int64_t playlistId )
{
return p_ml->deletePlaylist(playlistId);
}
void
AndroidMediaLibrary::onMediaAdded( std::vector<medialibrary::MediaPtr> mediaList )
{
if (m_mediaAddedType & FLAG_MEDIA_ADDED_AUDIO || m_mediaAddedType & FLAG_MEDIA_ADDED_VIDEO
|| m_mediaAddedType & FLAG_MEDIA_ADDED_AUDIO_EMPTY) {
JNIEnv *env = getEnv();
if (env == NULL /*|| env->IsSameObject(weak_thiz, NULL)*/)
return;
jobjectArray mediaRefs;
int index;
if (m_mediaAddedType & FLAG_MEDIA_ADDED_AUDIO_EMPTY)
{
index = 0;
mediaRefs = (jobjectArray) env->NewObjectArray(0, p_fields->MediaWrapper.clazz, NULL);
} else
{
mediaRefs = (jobjectArray) env->NewObjectArray(mediaList.size(), p_fields->MediaWrapper.clazz, NULL);
index = -1;
for (medialibrary::MediaPtr const& media : mediaList) {
medialibrary::IMedia::Type type = media->type();
if (!((type == medialibrary::IMedia::Type::AudioType && m_mediaAddedType & FLAG_MEDIA_ADDED_AUDIO) ||
(type == medialibrary::IMedia::Type::VideoType && m_mediaAddedType & FLAG_MEDIA_ADDED_VIDEO)))
continue;
jobject item = mediaToMediaWrapper(env, p_fields, media);
env->SetObjectArrayElement(mediaRefs, ++index, item);
env->DeleteLocalRef(item);
}
}
if (index > -1)
{
jobject thiz = getWeakReference(env);
if (thiz)
{
env->CallVoidMethod(thiz, p_fields->MediaLibrary.onMediaAddedId, mediaRefs);
if (weak_compat)
env->DeleteLocalRef(thiz);
}
}
env->DeleteLocalRef(mediaRefs);
}