Files
Velix-MPV-Core/video/out/hwdec/hwdec_ohcodec.c
T

224 lines
8.2 KiB
C

/*
* 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,
};