mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 16:11:20 +02:00
Add experimental MPV player backend (#161)
Experimental MPV player backend Related to #14, #22, & #85 You can switch to MPV in advanced settings and toggle using hardware decoding or not. This uses code and buildscripts from https://github.com/mpv-android/mpv-android, plus other third party libraries to build MPV
This commit is contained in:
parent
d7b333e519
commit
6be2662d4e
84 changed files with 3987 additions and 289 deletions
133
app/src/main/jni/thumbnail.cpp
Normal file
133
app/src/main/jni/thumbnail.cpp
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#include <stdlib.h>
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
#include <android/bitmap.h>
|
||||
#include <mpv/client.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libswscale/swscale.h>
|
||||
};
|
||||
|
||||
#include "jni_utils.h"
|
||||
#include "globals.h"
|
||||
#include "log.h"
|
||||
|
||||
extern "C" {
|
||||
jni_func(jobject, grabThumbnail, jint dimension);
|
||||
};
|
||||
|
||||
static inline mpv_node make_node_str(const char *s)
|
||||
{
|
||||
mpv_node r{};
|
||||
r.format = MPV_FORMAT_STRING;
|
||||
r.u.string = const_cast<char*>(s);
|
||||
return r;
|
||||
}
|
||||
|
||||
jni_func(jobject, grabThumbnail, jint dimension) {
|
||||
CHECK_MPV_INIT();
|
||||
|
||||
mpv_node result{};
|
||||
{
|
||||
mpv_node c{}, c_args[2];
|
||||
mpv_node_list c_array{};
|
||||
c_args[0] = make_node_str("screenshot-raw");
|
||||
c_args[1] = make_node_str("video");
|
||||
c_array.num = 2;
|
||||
c_array.values = c_args;
|
||||
c.format = MPV_FORMAT_NODE_ARRAY;
|
||||
c.u.list = &c_array;
|
||||
if (mpv_command_node(g_mpv, &c, &result) < 0) {
|
||||
ALOGE("screenshot-raw command failed");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// extract relevant property data from the node map mpv returns
|
||||
int w = 0, h = 0, stride = 0;
|
||||
bool format_ok = false;
|
||||
struct mpv_byte_array *data = NULL;
|
||||
do {
|
||||
if (result.format != MPV_FORMAT_NODE_MAP)
|
||||
break;
|
||||
for (int i = 0; i < result.u.list->num; i++) {
|
||||
std::string key(result.u.list->keys[i]);
|
||||
const mpv_node *val = &result.u.list->values[i];
|
||||
if (key == "w" || key == "h" || key == "stride") {
|
||||
if (val->format != MPV_FORMAT_INT64)
|
||||
break;
|
||||
if (key == "w")
|
||||
w = val->u.int64;
|
||||
else if (key == "h")
|
||||
h = val->u.int64;
|
||||
else
|
||||
stride = val->u.int64;
|
||||
} else if (key == "format") {
|
||||
if (val->format != MPV_FORMAT_STRING)
|
||||
break;
|
||||
format_ok = !strcmp(val->u.string, "bgr0");
|
||||
} else if (key == "data") {
|
||||
if (val->format != MPV_FORMAT_BYTE_ARRAY)
|
||||
break;
|
||||
data = val->u.ba;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
if (!w || !h || !stride || !format_ok || !data) {
|
||||
ALOGE("extracting data failed");
|
||||
mpv_free_node_contents(&result);
|
||||
return NULL;
|
||||
}
|
||||
ALOGV("screenshot w:%d h:%d stride:%d", w, h, stride);
|
||||
|
||||
// crop to square
|
||||
int crop_left = 0, crop_top = 0;
|
||||
int new_w = w, new_h = h;
|
||||
if (w > h) {
|
||||
crop_left = (w - h) / 2;
|
||||
new_w = h;
|
||||
} else {
|
||||
crop_top = (h - w) / 2;
|
||||
new_h = w;
|
||||
}
|
||||
ALOGV("cropped w:%u h:%u", new_w, new_h);
|
||||
|
||||
uint8_t *new_data = reinterpret_cast<uint8_t*>(data->data);
|
||||
new_data += crop_left * sizeof(uint32_t); // move begin rightwards
|
||||
new_data += stride * crop_top; // move begin downwards
|
||||
|
||||
// convert & scale to appropriate size
|
||||
struct SwsContext *ctx = sws_getContext(
|
||||
new_w, new_h, AV_PIX_FMT_BGR0,
|
||||
dimension, dimension, AV_PIX_FMT_RGB32,
|
||||
SWS_BICUBIC, NULL, NULL, NULL);
|
||||
if (!ctx) {
|
||||
mpv_free_node_contents(&result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jintArray arr = env->NewIntArray(dimension * dimension);
|
||||
jint *scaled = env->GetIntArrayElements(arr, NULL);
|
||||
|
||||
uint8_t *src_p[4] = { new_data }, *dst_p[4] = { (uint8_t*) scaled };
|
||||
int src_stride[4] = { stride },
|
||||
dst_stride[4] = { (int) sizeof(jint) * dimension };
|
||||
sws_scale(ctx, src_p, src_stride, 0, new_h, dst_p, dst_stride);
|
||||
sws_freeContext(ctx);
|
||||
|
||||
mpv_free_node_contents(&result); // frees data->data
|
||||
|
||||
// create android.graphics.Bitmap
|
||||
env->ReleaseIntArrayElements(arr, scaled, 0);
|
||||
|
||||
jobject bitmap_config =
|
||||
env->GetStaticObjectField(android_graphics_Bitmap_Config, android_graphics_Bitmap_Config_ARGB_8888);
|
||||
jobject bitmap =
|
||||
env->CallStaticObjectMethod(android_graphics_Bitmap, android_graphics_Bitmap_createBitmap,
|
||||
arr, dimension, dimension, bitmap_config);
|
||||
env->DeleteLocalRef(arr);
|
||||
env->DeleteLocalRef(bitmap_config);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue