feat: add Harmony OHCodec hwdec adapter
This commit is contained in:
1 parent
9fdf08073e
commit
3f6132637b
14 files changed
+579
-1
No files matched your search
+50
@@ -225,6 +225,7 @@ sources = files(
|
||||
'video/filter/vf_format.c',
|
||||
'video/filter/vf_sub.c',
|
||||
'video/fmt-conversion.c',
|
||||
'video/harmony_hwdec.c',
|
||||
'video/hwdec.c',
|
||||
'video/image_loader.c',
|
||||
'video/image_writer.c',
|
||||
@@ -931,6 +932,35 @@ if features['harmony-audio']
|
||||
dependencies += harmony_audio
|
||||
endif
|
||||
|
||||
harmony_ohcodec_opt = get_option('harmony-ohcodec').require(
|
||||
ohos,
|
||||
error_message: 'HarmonyOS OHCodec is only available on OHOS targets!',
|
||||
)
|
||||
harmony_ohcodec_libs = [
|
||||
cc.find_library('native_media_codecbase', required: harmony_ohcodec_opt),
|
||||
cc.find_library('native_media_core', required: harmony_ohcodec_opt),
|
||||
cc.find_library('native_media_vdec', required: harmony_ohcodec_opt),
|
||||
cc.find_library('native_window', required: harmony_ohcodec_opt),
|
||||
]
|
||||
harmony_ohcodec_headers = cc.has_header('multimedia/player_framework/native_avcapability.h',
|
||||
required: harmony_ohcodec_opt) and \
|
||||
cc.has_header('multimedia/player_framework/native_avcodec_videodecoder.h',
|
||||
required: harmony_ohcodec_opt) and \
|
||||
cc.has_header('multimedia/player_framework/native_avbuffer.h',
|
||||
required: harmony_ohcodec_opt) and \
|
||||
cc.has_header('native_window/external_window.h',
|
||||
required: harmony_ohcodec_opt)
|
||||
features += {'harmony-ohcodec': harmony_ohcodec_headers and harmony_ohcodec_libs[0].found() and
|
||||
harmony_ohcodec_libs[1].found() and harmony_ohcodec_libs[2].found() and
|
||||
harmony_ohcodec_libs[3].found()}
|
||||
if harmony_ohcodec_opt.enabled() and not features['harmony-ohcodec']
|
||||
error('HarmonyOS OHCodec headers or native media libraries were not found')
|
||||
endif
|
||||
if features['harmony-ohcodec']
|
||||
dependencies += harmony_ohcodec_libs
|
||||
sources += files('video/out/hwdec/hwdec_ohcodec.c')
|
||||
endif
|
||||
|
||||
opensles = cc.find_library('OpenSLES', required: get_option('opensles'))
|
||||
features += {'opensles': opensles.found()}
|
||||
if features['opensles']
|
||||
@@ -1333,6 +1363,26 @@ if plain_gl.allowed()
|
||||
features += {'gl': true}
|
||||
endif
|
||||
|
||||
harmony_ohcodec_gl_opt = get_option('harmony-ohcodec-gl').require(
|
||||
features['harmony-ohcodec'] and features['gl'] and ohos,
|
||||
error_message: 'HarmonyOS OHCodec GPU interop requires OHCodec and OpenGL!',
|
||||
)
|
||||
harmony_ohcodec_gl_egl = cc.find_library('EGL', required: harmony_ohcodec_gl_opt)
|
||||
harmony_ohcodec_gl_lib = cc.find_library('native_buffer', required: harmony_ohcodec_gl_opt)
|
||||
harmony_ohcodec_gl_headers = cc.has_header('native_buffer/native_buffer.h',
|
||||
required: harmony_ohcodec_gl_opt) and \
|
||||
cc.has_header('EGL/eglext.h', required: harmony_ohcodec_gl_opt) and \
|
||||
cc.has_header('GLES2/gl2ext.h', required: harmony_ohcodec_gl_opt)
|
||||
features += {'harmony-ohcodec-gl': harmony_ohcodec_gl_headers and
|
||||
harmony_ohcodec_gl_egl.found() and harmony_ohcodec_gl_lib.found()}
|
||||
if harmony_ohcodec_gl_opt.enabled() and not features['harmony-ohcodec-gl']
|
||||
error('HarmonyOS OHCodec GPU interop headers or EGL/native_buffer libraries were not found')
|
||||
endif
|
||||
if features['harmony-ohcodec-gl']
|
||||
dependencies += [harmony_ohcodec_gl_egl, harmony_ohcodec_gl_lib]
|
||||
sources += files('video/out/hwdec/hwdec_ohcodec_gl.c')
|
||||
endif
|
||||
|
||||
if features['egl'] or features['egl-android'] or features['egl-angle-win32']
|
||||
sources += files('video/out/opengl/egl_helpers.c')
|
||||
endif
|
||||
|
||||
@@ -51,6 +51,8 @@ option('openal', type: 'feature', value: 'disabled', description: 'OpenAL audio
|
||||
option('audiotrack', type: 'feature', value: 'auto', description: 'Android AudioTrack audio output')
|
||||
option('aaudio', type: 'feature', value: 'auto', description: 'Android AAudio audio output')
|
||||
option('harmony-audio', type: 'feature', value: 'auto', description: 'HarmonyOS OHAudio output')
|
||||
option('harmony-ohcodec', type: 'feature', value: 'auto', description: 'HarmonyOS OHCodec hardware decode')
|
||||
option('harmony-ohcodec-gl', type: 'feature', value: 'disabled', description: 'EXPERIMENTAL HarmonyOS OHCodec GPU interop')
|
||||
option('opensles', type: 'feature', value: 'auto', description: 'OpenSL ES audio output')
|
||||
option('oss-audio', type: 'feature', value: 'auto', description: 'OSSv4 audio output')
|
||||
option('pipewire', type: 'feature', value: 'auto', description: 'PipeWire audio output')
|
||||
|
||||
@@ -13,4 +13,5 @@ PROJECT_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
"$PROJECT_ROOT/scripts/build-font-stack-ohos.sh"
|
||||
"$PROJECT_ROOT/scripts/build-libplacebo-ohos.sh"
|
||||
"$PROJECT_ROOT/scripts/build-mpv-ohos.sh"
|
||||
"$PROJECT_ROOT/scripts/build-mpv-ohos-software.sh"
|
||||
"$PROJECT_ROOT/scripts/verify-ohos-prefix.sh"
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
. "$(dirname -- "$0")/setup-ohos-env.sh"
|
||||
BUILD=$OHOS_BUILD_ROOT/build/mpv-software
|
||||
LOG=$OHOS_BUILD_ROOT/logs/mpv-software.log
|
||||
|
||||
mkdir -p "$OHOS_BUILD_ROOT/logs"
|
||||
test -f "$PREFIX/lib/libavcodec.a" || "$PROJECT_ROOT/scripts/build-ffmpeg-ohos.sh"
|
||||
test -f "$PREFIX/lib/libass.a" || "$PROJECT_ROOT/scripts/build-font-stack-ohos.sh"
|
||||
test -f "$PREFIX/lib/libplacebo.a" || "$PROJECT_ROOT/scripts/build-libplacebo-ohos.sh"
|
||||
|
||||
if test -f "$BUILD/meson-private/coredata.dat"; then
|
||||
setup_mode=--wipe
|
||||
else
|
||||
setup_mode=
|
||||
fi
|
||||
|
||||
if ! meson setup "$BUILD" "$PROJECT_ROOT" $setup_mode \
|
||||
--cross-file "$CROSS_FILE" --prefix "$PREFIX" --buildtype release \
|
||||
-Ddefault_library=shared -Dauto_features=disabled \
|
||||
-Dcplayer=false -Dlibmpv=true -Dtests=false -Dfuzzers=false \
|
||||
-Dgpl=true -Dbuild-date=false -Dplain-gl=enabled -Dgl=enabled \
|
||||
-Dzlib=enabled -Dvector=enabled -Dlua=disabled -Dharmony-audio=enabled \
|
||||
-Dharmony-ohcodec=disabled -Dharmony-ohcodec-gl=disabled > "$LOG" 2>&1; then
|
||||
tail -n 180 "$LOG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! meson compile -C "$BUILD" >> "$LOG" 2>&1; then
|
||||
tail -n 220 "$LOG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LIBMPV=$BUILD/libmpv.so
|
||||
test -f "$LIBMPV" || { printf 'Missing software libmpv: %s\n' "$LIBMPV" >&2; exit 1; }
|
||||
"$PROJECT_ROOT/scripts/verify-ohos-binary.sh" "$LIBMPV"
|
||||
printf 'software-libmpv=%s\n' "$LIBMPV"
|
||||
@@ -22,7 +22,8 @@ if ! meson setup "$BUILD" "$PROJECT_ROOT" $setup_mode \
|
||||
-Ddefault_library=shared -Dauto_features=disabled \
|
||||
-Dcplayer=false -Dlibmpv=true -Dtests=false -Dfuzzers=false \
|
||||
-Dgpl=true -Dbuild-date=false -Dplain-gl=enabled -Dgl=enabled \
|
||||
-Dzlib=enabled -Dvector=enabled -Dlua=disabled -Dharmony-audio=enabled > "$LOG" 2>&1; then
|
||||
-Dzlib=enabled -Dvector=enabled -Dlua=disabled -Dharmony-audio=enabled \
|
||||
-Dharmony-ohcodec=enabled -Dharmony-ohcodec-gl=disabled > "$LOG" 2>&1; then
|
||||
tail -n 180 "$LOG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -60,6 +60,9 @@ static const struct {
|
||||
{IMGFMT_VDPAU, AV_PIX_FMT_VDPAU},
|
||||
{IMGFMT_VIDEOTOOLBOX, AV_PIX_FMT_VIDEOTOOLBOX},
|
||||
{IMGFMT_MEDIACODEC, AV_PIX_FMT_MEDIACODEC},
|
||||
#if HAVE_HARMONY_OHCODEC
|
||||
{IMGFMT_OHCODEC, AV_PIX_FMT_OHCODEC},
|
||||
#endif
|
||||
{IMGFMT_VAAPI, AV_PIX_FMT_VAAPI},
|
||||
{IMGFMT_DXVA2, AV_PIX_FMT_DXVA2_VLD},
|
||||
{IMGFMT_D3D11, AV_PIX_FMT_D3D11},
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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 2.1 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "video/harmony_hwdec.h"
|
||||
|
||||
static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static struct mpv_harmony_hwdec_stats stats = {
|
||||
.state = MP_HARMONY_HWDEC_DISABLED,
|
||||
};
|
||||
|
||||
static void copy_text(char *dst, const char *src)
|
||||
{
|
||||
snprintf(dst, MP_HARMONY_HWDEC_TEXT, "%s", src ? src : "");
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_requested(int requested)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
stats.hwdec_requested = !!requested;
|
||||
stats.state = requested ? MP_HARMONY_HWDEC_REQUESTED : MP_HARMONY_HWDEC_DISABLED;
|
||||
if (!requested) {
|
||||
stats.hwdec_active = 0;
|
||||
stats.fallback_reason[0] = '\0';
|
||||
stats.decoder_name[0] = '\0';
|
||||
}
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_state(enum mp_harmony_hwdec_state state)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
stats.state = state;
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_active(const char *backend,
|
||||
const char *device_type)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
stats.hwdec_active = 1;
|
||||
stats.state = MP_HARMONY_HWDEC_ACTIVE;
|
||||
copy_text(stats.hwdec_backend, backend);
|
||||
copy_text(stats.hw_device_type, device_type);
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_decoder(const char *decoder_name)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
copy_text(stats.decoder_name, decoder_name);
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_capability(const char *summary)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
copy_text(stats.capability_summary, summary);
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_fallback(const char *reason)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
stats.hwdec_active = 0;
|
||||
stats.state = MP_HARMONY_HWDEC_FALLBACK;
|
||||
stats.hw_fallback_count++;
|
||||
copy_text(stats.fallback_reason, reason);
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_record_hardware_frame(void)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
stats.hardware_frame_count++;
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_record_software_frame(void)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
stats.software_frame_count++;
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_record_surface_present(void)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
stats.surface_present_count++;
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_record_gpu_interop_frame(void)
|
||||
{
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
stats.gpu_interop_frame_count++;
|
||||
stats.native_buffer_import_count++;
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
}
|
||||
|
||||
MPV_EXPORT int mpv_harmony_hwdec_get_stats(struct mpv_harmony_hwdec_stats *out)
|
||||
{
|
||||
if (!out)
|
||||
return 0;
|
||||
pthread_mutex_lock(&stats_lock);
|
||||
*out = stats;
|
||||
pthread_mutex_unlock(&stats_lock);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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 2.1 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef MPV_HARMONY_HWDEC_H
|
||||
#define MPV_HARMONY_HWDEC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "mpv/client.h"
|
||||
|
||||
struct OH_AVBuffer;
|
||||
struct mp_log;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum mp_harmony_hwdec_state {
|
||||
MP_HARMONY_HWDEC_DISABLED,
|
||||
MP_HARMONY_HWDEC_REQUESTED,
|
||||
MP_HARMONY_HWDEC_CAPABILITY_CHECK,
|
||||
MP_HARMONY_HWDEC_DEVICE_CREATED,
|
||||
MP_HARMONY_HWDEC_DECODER_CREATED,
|
||||
MP_HARMONY_HWDEC_DIRECT_SURFACE,
|
||||
MP_HARMONY_HWDEC_GPU_INTEROP,
|
||||
MP_HARMONY_HWDEC_ACTIVE,
|
||||
MP_HARMONY_HWDEC_FALLBACK,
|
||||
MP_HARMONY_HWDEC_ERROR,
|
||||
};
|
||||
|
||||
#define MP_HARMONY_HWDEC_TEXT 128
|
||||
|
||||
struct mpv_harmony_hwdec_stats {
|
||||
int hwdec_requested;
|
||||
int hwdec_active;
|
||||
int state;
|
||||
int64_t hardware_frame_count;
|
||||
int64_t software_frame_count;
|
||||
int64_t hw_fallback_count;
|
||||
int64_t native_buffer_import_count;
|
||||
int64_t native_buffer_import_failure;
|
||||
int64_t surface_present_count;
|
||||
int64_t gpu_interop_frame_count;
|
||||
char hwdec_backend[MP_HARMONY_HWDEC_TEXT];
|
||||
char hw_device_type[MP_HARMONY_HWDEC_TEXT];
|
||||
char decoder_name[MP_HARMONY_HWDEC_TEXT];
|
||||
char fallback_reason[MP_HARMONY_HWDEC_TEXT];
|
||||
char capability_summary[MP_HARMONY_HWDEC_TEXT];
|
||||
};
|
||||
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_requested(int requested);
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_state(enum mp_harmony_hwdec_state state);
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_active(const char *backend,
|
||||
const char *device_type);
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_decoder(const char *decoder_name);
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_capability(const char *summary);
|
||||
MPV_EXPORT void mpv_harmony_hwdec_set_fallback(const char *reason);
|
||||
MPV_EXPORT void mpv_harmony_hwdec_record_hardware_frame(void);
|
||||
MPV_EXPORT void mpv_harmony_hwdec_record_software_frame(void);
|
||||
MPV_EXPORT void mpv_harmony_hwdec_record_surface_present(void);
|
||||
MPV_EXPORT void mpv_harmony_hwdec_record_gpu_interop_frame(void);
|
||||
MPV_EXPORT int mpv_harmony_hwdec_get_stats(struct mpv_harmony_hwdec_stats *stats);
|
||||
MPV_EXPORT int mpv_harmony_ohcodec_gl_probe(struct mp_log *log,
|
||||
struct OH_AVBuffer *buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -130,6 +130,9 @@ static const struct hwcontext_fns *const hwcontext_fns[] = {
|
||||
#endif
|
||||
#if HAVE_VDPAU
|
||||
&hwcontext_fns_vdpau,
|
||||
#endif
|
||||
#if HAVE_HARMONY_OHCODEC
|
||||
&hwcontext_fns_ohcodec,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -124,5 +124,6 @@ extern const struct hwcontext_fns hwcontext_fns_drmprime;
|
||||
extern const struct hwcontext_fns hwcontext_fns_dxva2;
|
||||
extern const struct hwcontext_fns hwcontext_fns_vaapi;
|
||||
extern const struct hwcontext_fns hwcontext_fns_vdpau;
|
||||
extern const struct hwcontext_fns hwcontext_fns_ohcodec;
|
||||
|
||||
#endif
|
||||
@@ -276,6 +276,7 @@ enum mp_imgfmt {
|
||||
IMGFMT_DXVA2, // IDirect3DSurface9 (NV12/P010/P016)
|
||||
IMGFMT_MMAL, // MMAL_BUFFER_HEADER_T
|
||||
IMGFMT_MEDIACODEC, // AVMediaCodecBuffer
|
||||
IMGFMT_OHCODEC, // OH_AVBuffer backed by HarmonyOS OHCodec
|
||||
IMGFMT_CUDA, // CUDA Buffer
|
||||
|
||||
// Not an actual format; base for mpv-specific descriptor table.
|
||||
|
||||
@@ -38,6 +38,7 @@ extern const struct ra_hwdec_driver ra_hwdec_drmprime;
|
||||
extern const struct ra_hwdec_driver ra_hwdec_drmprime_overlay;
|
||||
extern const struct ra_hwdec_driver ra_hwdec_aimagereader;
|
||||
extern const struct ra_hwdec_driver ra_hwdec_vulkan;
|
||||
extern const struct ra_hwdec_driver ra_hwdec_ohcodec;
|
||||
|
||||
const struct ra_hwdec_driver *const ra_hwdec_drivers[] = {
|
||||
#if HAVE_D3D_HWACCEL
|
||||
@@ -79,6 +80,9 @@ const struct ra_hwdec_driver *const ra_hwdec_drivers[] = {
|
||||
#if HAVE_VULKAN
|
||||
&ra_hwdec_vulkan,
|
||||
#endif
|
||||
#if HAVE_HARMONY_OHCODEC
|
||||
&ra_hwdec_ohcodec,
|
||||
#endif
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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 2.1 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/hwcontext.h>
|
||||
#include <libavutil/hwcontext_oh.h>
|
||||
|
||||
#include <multimedia/player_framework/native_avcapability.h>
|
||||
#include <native_window/external_window.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "video/harmony_hwdec.h"
|
||||
#include "video/out/gpu/hwdec.h"
|
||||
|
||||
static const int supported_formats[] = {IMGFMT_OHCODEC, 0};
|
||||
|
||||
struct priv {
|
||||
struct mp_hwdec_ctx hwctx;
|
||||
OHNativeWindow *window;
|
||||
bool frame_seen;
|
||||
};
|
||||
|
||||
static void capability_summary(char *summary, size_t summary_size,
|
||||
const char *mime, OH_AVCapability *capability)
|
||||
{
|
||||
if (!capability) {
|
||||
snprintf(summary, summary_size, "%s:unavailable", mime);
|
||||
return;
|
||||
}
|
||||
|
||||
OH_AVRange width = {0}, height = {0}, fps = {0};
|
||||
const int32_t *profiles = NULL;
|
||||
uint32_t profile_count = 0;
|
||||
const int32_t *levels = NULL;
|
||||
uint32_t level_count = 0;
|
||||
const int32_t *pixel_formats = NULL;
|
||||
uint32_t pixel_format_count = 0;
|
||||
int width_ok = OH_AVCapability_GetVideoWidthRange(capability, &width) == 0;
|
||||
int height_ok = OH_AVCapability_GetVideoHeightRange(capability, &height) == 0;
|
||||
int fps_ok = OH_AVCapability_GetVideoFrameRateRange(capability, &fps) == 0;
|
||||
int profile_ok = OH_AVCapability_GetSupportedProfiles(capability, &profiles,
|
||||
&profile_count) == 0;
|
||||
int level_ok = profile_ok && profile_count > 0 &&
|
||||
OH_AVCapability_GetSupportedLevelsForProfile(capability, profiles[0],
|
||||
&levels, &level_count) == 0;
|
||||
int pixel_ok = OH_AVCapability_GetVideoSupportedPixelFormats(capability,
|
||||
&pixel_formats,
|
||||
&pixel_format_count) == 0;
|
||||
const char *name = OH_AVCapability_GetName(capability);
|
||||
snprintf(summary, summary_size,
|
||||
"%s:name=%s hw=%d width=%s%d-%d height=%s%d-%d fps=%s%d-%d profiles=%s%u levels=%s%u pixfmt=%s%u",
|
||||
mime, name ? name : "unknown", OH_AVCapability_IsHardware(capability),
|
||||
width_ok ? "" : "?", width.minVal, width.maxVal,
|
||||
height_ok ? "" : "?", height.minVal, height.maxVal,
|
||||
fps_ok ? "" : "?", fps.minVal, fps.maxVal,
|
||||
profile_ok ? "" : "?", profile_count,
|
||||
level_ok ? "" : "?", level_count,
|
||||
pixel_ok ? "" : "?", pixel_format_count);
|
||||
}
|
||||
|
||||
static int query_capabilities(struct ra_hwdec *hw)
|
||||
{
|
||||
static const struct {
|
||||
const char *mime;
|
||||
const char *name;
|
||||
} codecs[] = {
|
||||
{"video/avc", "h264"},
|
||||
{"video/hevc", "hevc"},
|
||||
};
|
||||
bool found = false;
|
||||
char capability_text[MP_HARMONY_HWDEC_TEXT] = {0};
|
||||
|
||||
mpv_harmony_hwdec_set_state(MP_HARMONY_HWDEC_CAPABILITY_CHECK);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
OH_AVCapability *capability =
|
||||
OH_AVCodec_GetCapabilityByCategory(codecs[i].mime, false, HARDWARE);
|
||||
char current[MP_HARMONY_HWDEC_TEXT];
|
||||
capability_summary(current, sizeof(current), codecs[i].mime, capability);
|
||||
if (i > 0 && capability_text[0])
|
||||
snprintf(capability_text + strlen(capability_text),
|
||||
sizeof(capability_text) - strlen(capability_text), ";");
|
||||
snprintf(capability_text + strlen(capability_text),
|
||||
sizeof(capability_text) - strlen(capability_text), "%s", current);
|
||||
if (capability) {
|
||||
found = true;
|
||||
MP_VERBOSE(hw, "%s OHCodec capability: %s\n", codecs[i].name,
|
||||
OH_AVCapability_GetName(capability));
|
||||
} else {
|
||||
MP_VERBOSE(hw, "%s OHCodec hardware capability unavailable\n",
|
||||
codecs[i].name);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
mpv_harmony_hwdec_set_fallback("OHCodec hardware capability unavailable");
|
||||
return -1;
|
||||
}
|
||||
mpv_harmony_hwdec_set_capability(capability_text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init(struct ra_hwdec *hw)
|
||||
{
|
||||
struct priv *p = hw->priv;
|
||||
if (query_capabilities(hw) < 0)
|
||||
return -1;
|
||||
p->window = ra_get_native_resource(hw->ra_ctx->ra, "ohos_direct_surface");
|
||||
if (!p->window) {
|
||||
MP_VERBOSE(hw, "direct surface unavailable; refusing to share the libmpv EGL surface\n");
|
||||
mpv_harmony_hwdec_set_fallback("exclusive ohos_direct_surface is unavailable");
|
||||
return -1;
|
||||
}
|
||||
if (OH_NativeWindow_NativeObjectReference(p->window) != 0) {
|
||||
MP_ERR(hw, "OHNativeWindow reference failed\n");
|
||||
mpv_harmony_hwdec_set_fallback("OHNativeWindow reference failed");
|
||||
p->window = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
AVBufferRef *device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_OHCODEC);
|
||||
if (!device_ref) {
|
||||
mpv_harmony_hwdec_set_fallback("OHCodec hwdevice allocation failed");
|
||||
OH_NativeWindow_NativeObjectUnreference(p->window);
|
||||
p->window = NULL;
|
||||
return -1;
|
||||
}
|
||||
AVHWDeviceContext *device = (AVHWDeviceContext *)device_ref->data;
|
||||
AVOHCodecDeviceContext *oh = device->hwctx;
|
||||
oh->native_window = p->window;
|
||||
mpv_harmony_hwdec_set_state(MP_HARMONY_HWDEC_DEVICE_CREATED);
|
||||
if (av_hwdevice_ctx_init(device_ref) < 0) {
|
||||
MP_ERR(hw, "OHCodec hwdevice initialization failed\n");
|
||||
mpv_harmony_hwdec_set_fallback("OHCodec hwdevice initialization failed");
|
||||
av_buffer_unref(&device_ref);
|
||||
OH_NativeWindow_NativeObjectUnreference(p->window);
|
||||
p->window = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->hwctx = (struct mp_hwdec_ctx) {
|
||||
.driver_name = hw->driver->name,
|
||||
.av_device_ref = device_ref,
|
||||
.supported_formats = supported_formats,
|
||||
.hw_imgfmt = IMGFMT_OHCODEC,
|
||||
};
|
||||
hwdec_devices_add(hw->devs, &p->hwctx);
|
||||
mpv_harmony_hwdec_set_state(MP_HARMONY_HWDEC_DIRECT_SURFACE);
|
||||
MP_WARN(hw, "EXPERIMENTAL direct surface mode owns a separate OHNativeWindow; "
|
||||
"OpenGL composition is not used\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int overlay_frame(struct ra_hwdec *hw, struct mp_image *image,
|
||||
struct mp_rect *src, struct mp_rect *dst, bool newframe)
|
||||
{
|
||||
(void)src;
|
||||
(void)dst;
|
||||
struct priv *p = hw->priv;
|
||||
if (image && newframe) {
|
||||
if (!p->frame_seen) {
|
||||
p->frame_seen = true;
|
||||
mpv_harmony_hwdec_set_state(MP_HARMONY_HWDEC_DECODER_CREATED);
|
||||
mpv_harmony_hwdec_set_active(hw->driver->name, "ohcodec");
|
||||
}
|
||||
mpv_harmony_hwdec_record_hardware_frame();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uninit(struct ra_hwdec *hw)
|
||||
{
|
||||
struct priv *p = hw->priv;
|
||||
hwdec_devices_remove(hw->devs, &p->hwctx);
|
||||
av_buffer_unref(&p->hwctx.av_device_ref);
|
||||
if (p->window) {
|
||||
OH_NativeWindow_NativeObjectUnreference(p->window);
|
||||
p->window = NULL;
|
||||
}
|
||||
if (p->hwctx.av_device_ref == NULL)
|
||||
mpv_harmony_hwdec_set_state(MP_HARMONY_HWDEC_DISABLED);
|
||||
}
|
||||
|
||||
const struct ra_hwdec_driver ra_hwdec_ohcodec = {
|
||||
.name = "ohcodec-direct-surface-experimental",
|
||||
.priv_size = sizeof(struct priv),
|
||||
.imgfmts = {IMGFMT_OHCODEC, 0},
|
||||
.device_type = AV_HWDEVICE_TYPE_OHCODEC,
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.overlay_frame = overlay_frame,
|
||||
};
|
||||
|
||||
static AVBufferRef *create_dev(struct mpv_global *global, struct mp_log *log,
|
||||
struct hwcontext_create_dev_params *params)
|
||||
{
|
||||
(void)global;
|
||||
(void)params;
|
||||
mp_msg(log, MSGL_V, "OHCodec copy mode requires an exclusive OHNativeWindow; refusing a null-surface device\n");
|
||||
mpv_harmony_hwdec_set_fallback("OHCodec copy mode has no exclusive OHNativeWindow");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool is_codec_allowed(AVBufferRef *device_ref, int av_codec_id)
|
||||
{
|
||||
(void)device_ref;
|
||||
return av_codec_id == AV_CODEC_ID_H264 || av_codec_id == AV_CODEC_ID_HEVC;
|
||||
}
|
||||
|
||||
const struct hwcontext_fns hwcontext_fns_ohcodec = {
|
||||
.av_hwdevice_type = AV_HWDEVICE_TYPE_OHCODEC,
|
||||
.create_dev = create_dev,
|
||||
.is_codec_allowed = is_codec_allowed,
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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 2.1 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
#include <multimedia/player_framework/native_avbuffer.h>
|
||||
#include <native_buffer/native_buffer.h>
|
||||
#include <native_window/external_window.h>
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "video/harmony_hwdec.h"
|
||||
|
||||
MPV_EXPORT int mpv_harmony_ohcodec_gl_probe(struct mp_log *log,
|
||||
struct OH_AVBuffer *buffer)
|
||||
{
|
||||
(void)EGL_OHOS_image_native_buffer;
|
||||
(void)EGL_NATIVE_BUFFER_OHOS;
|
||||
(void)eglCreateImageKHR;
|
||||
(void)glEGLImageTargetTexture2DOES;
|
||||
if (!buffer) {
|
||||
mp_msg(log, MSGL_V, "OHCodec GL probe requires an OH_AVBuffer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
OH_NativeBuffer *native = OH_AVBuffer_GetNativeBuffer(buffer);
|
||||
if (!native) {
|
||||
mpv_harmony_hwdec_set_fallback("OH_AVBuffer_GetNativeBuffer failed");
|
||||
return -1;
|
||||
}
|
||||
OHNativeWindowBuffer *window_buffer =
|
||||
OH_NativeWindow_CreateNativeWindowBufferFromNativeBuffer(native);
|
||||
OH_NativeBuffer_Unreference(native);
|
||||
if (!window_buffer) {
|
||||
mpv_harmony_hwdec_set_fallback(
|
||||
"OH_NativeWindowBuffer conversion failed");
|
||||
return -1;
|
||||
}
|
||||
OH_NativeWindow_DestroyNativeWindowBuffer(window_buffer);
|
||||
mp_msg(log, MSGL_V, "OHCodec GL probe reached NativeBuffer conversion; "
|
||||
"upstream output ownership still blocks EGL import\n");
|
||||
mpv_harmony_hwdec_set_fallback(
|
||||
"OHCODEC-BLOCKER-001: upstream has no non-surface output mode");
|
||||
return -1;
|
||||
}
|
||||
Reference in new issue
Block a user