224 lines
8.2 KiB
C
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,
|
|
};
|