This commit is contained in:
mofeng-git
2025-12-28 18:19:16 +08:00
commit d143d158e4
771 changed files with 220548 additions and 0 deletions

44
.dockerignore Normal file
View File

@@ -0,0 +1,44 @@
# Rust
/target/
Cargo.lock
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
.claude/
# OS
.DS_Store
Thumbs.db
# Local data
/data/
*.db
*.db-shm
*.db-wal
# Certificates
*.pem
*.crt
*.key
# Logs
*.log
# Build artifacts
/dist/
# Frontend (built files)
/web/node_modules/
.mcp.json
CLAUDE.md
.gemini/settings.json
/hwcodec/target
/One-KVM
/jetKVM
/ustreamer

38
.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
# Rust
/target/
Cargo.lock
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
.claude/
# OS
.DS_Store
Thumbs.db
# Local data
/data/
*.db
*.db-shm
*.db-wal
# Certificates
*.pem
*.crt
*.key
# Logs
*.log
# Build artifacts
/dist/
# Frontend (built files)
/web/node_modules/
.mcp.json
CLAUDE.md
.gemini/settings.json

141
Cargo.toml Normal file
View File

@@ -0,0 +1,141 @@
[package]
name = "one-kvm"
version = "0.1.0"
edition = "2021"
authors = ["SilentWind"]
description = "A open and lightweight IP-KVM solution written in Rust"
license = "GPL-3.0"
repository = "https://github.com/mofeng-git/One-KVM"
keywords = ["kvm", "ipkvm", "remote-management", "embedded"]
categories = ["embedded", "network-programming"]
[dependencies]
# Async runtime
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7", features = ["rt"] }
# Web framework
axum = { version = "0.7", features = ["ws", "multipart", "tokio"] }
axum-extra = { version = "0.9", features = ["typed-header", "cookie"] }
tower-http = { version = "0.5", features = ["fs", "cors", "trace", "compression-gzip"] }
# Database - Use bundled SQLite for static linking
sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] }
# Serialization
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# Logging
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
# Error handling
thiserror = "1"
anyhow = "1"
# Authentication
argon2 = "0.5"
rand = "0.8"
# Utilities
uuid = { version = "1", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
base64 = "0.22"
nix = { version = "0.29", features = ["fs", "net", "hostname"] }
# HTTP client (for URL downloads)
# Use rustls-tls by default, but allow native-tls for systems with older GLIBC
reqwest = { version = "0.12", features = ["stream", "rustls-tls"], default-features = false }
urlencoding = "2"
# Static file embedding
rust-embed = { version = "8", features = ["compression"] }
mime_guess = "2"
# TLS/HTTPS
rustls = { version = "0.23", features = ["ring"] }
rustls-pemfile = "2"
tokio-rustls = { version = "0.26", features = ["ring"] }
rcgen = "0.13"
axum-server = { version = "0.7", features = ["tls-rustls"] }
# CLI argument parsing
clap = { version = "4", features = ["derive"] }
# Time
time = "0.3"
# Video capture (V4L2)
v4l = "0.14"
# JPEG encoding (libjpeg-turbo, SIMD accelerated)
turbojpeg = "1.1"
# Bytes handling
bytes = "1"
bytemuck = { version = "1.14", features = ["derive"] }
# Frame deduplication (hash-based comparison)
xxhash-rust = { version = "0.8", features = ["xxh64"] }
# Async channels
async-stream = "0.3"
futures = "0.3"
# WebSocket client (for ttyd proxy)
tokio-tungstenite = "0.24"
# High-performance synchronization
parking_lot = "0.12"
arc-swap = "1.7"
# WebRTC
webrtc = "0.14"
rtp = "0.14"
# Audio (ALSA capture + Opus encoding)
# Note: audiopus links to libopus.so (unavoidable for audio support)
alsa = "0.9"
audiopus = "0.2"
# HID (serial port for CH9329)
serialport = "4"
async-trait = "0.1"
libc = "0.2"
# Ventoy bootable image support
ventoy-img = { path = "libs/ventoy-img-rs" }
# ATX (GPIO control)
gpio-cdev = "0.6"
# H264 hardware/software encoding (hwcodec from rustdesk)
hwcodec = { path = "libs/hwcodec" }
# High-performance pixel format conversion (libyuv)
libyuv = { path = "res/vcpkg/libyuv" }
# TypeScript type generation
typeshare = "1.0"
[dev-dependencies]
tokio-test = "0.4"
tempfile = "3"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true
panic = "abort"
# Static linking profile for musl targets
[profile.release-static]
inherits = "release"
opt-level = "z" # Optimize for size
# Cross-compilation targets
# aarch64-unknown-linux-gnu (ARM64) - Primary target
# armv7-unknown-linux-gnueabihf (ARMv7)
# x86_64-unknown-linux-gnu (x86_64)

49
Cross.toml Normal file
View File

@@ -0,0 +1,49 @@
# Cross-compilation configuration for One-KVM
# Uses custom Debian 12 based images for consistent build/runtime environment
# See: https://github.com/cross-rs/cross
[build]
# Default Docker image settings
default-target = "x86_64-unknown-linux-gnu"
[build.env]
passthrough = [
"RUST_BACKTRACE",
"CARGO_INCREMENTAL",
]
# x86_64 target - use custom Debian 12 image
[target.x86_64-unknown-linux-gnu]
dockerfile = "build/cross/Dockerfile.x86_64"
[target.x86_64-unknown-linux-gnu.env]
passthrough = [
"PKG_CONFIG_ALLOW_CROSS=1",
"PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig",
]
# ARM64 target - use custom Debian 12 image with multiarch
[target.aarch64-unknown-linux-gnu]
dockerfile = "build/cross/Dockerfile.aarch64"
[target.aarch64-unknown-linux-gnu.env]
passthrough = [
"PKG_CONFIG_ALLOW_CROSS=1",
"PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig",
"CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc",
"CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc",
"CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++",
]
# ARMv7 target - use custom Debian 12 image with multiarch
[target.armv7-unknown-linux-gnueabihf]
dockerfile = "build/cross/Dockerfile.armv7"
[target.armv7-unknown-linux-gnueabihf.env]
passthrough = [
"PKG_CONFIG_ALLOW_CROSS=1",
"PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig:/usr/share/pkgconfig",
"CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc",
"CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc",
"CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++",
]

80
README.md Normal file
View File

@@ -0,0 +1,80 @@
# One-KVM
<p align="center">
<strong>开放轻量的 IP-KVM 解决方案,实现 BIOS 级远程管理</strong>
</p>
<p align="center">
<a href="#功能特性">功能特性</a>
<a href="#快速开始">快速开始</a>
</p>
---
## 介绍
One-KVM 是一个用 Rust 编写的开放轻量的 IP-KVM基于 IP 的键盘、视频、鼠标)解决方案,让你可以通过网络远程控制计算机,包括 BIOS 级别的操作。
**当前软件处于开发早期阶段,各种功能和细节还有待完善,欢迎体验,但请勿应用于生产环境。**
## 功能特性
### 核心功能
| 功能 | 说明 |
|------|------|
| 视频采集 | HDMI USB 采集卡支持,提供 MJPEG/H264/H265/VP8/VP9 视频流 |
| 键鼠控制 | USB OTG HID 或 CH340 + CH39329 HID支持绝对/相对鼠标模式 |
| 虚拟U盘 | USB Mass Storage支持 ISO/IMG 镜像挂载和 Ventoy 虚拟U盘模式 |
| ATX 电源控制 | GPIO 控制电源/重启按钮 |
| 音频传输 | ALSA 采集 + Opus 编码HTTP/WebRTC |
### 硬件编码
支持自动检测和选择硬件加速:
- **VAAPI** - Intel/AMD GPU
- **RKMPP** - Rockchip SoC (**尚未实现**)
- **V4L2 M2M** - 通用硬件编码器 (**尚未实现**)
- **软件编码** - CPU 编码
### 其他特性
- 单二进制部署,依赖更轻量
- Web UI 配置,无需编辑配置文件,多语言支持 (中文/英文)
- 内置 Web 终端 (ttyd),内网穿透支持 (gostc)P2P 组网支持 (EasyTier)
## 快速开始
### Docker 运行
```bash
docker run -d --privileged \
-v /dev:/dev \
-v /sys/kernel/config:/sys/kernel/config \
--net=host \
siletwind0/one-kvm
```
访问 http://IP:8080
### 环境变量
| 变量 | 说明 | 默认值 |
|------|------|--------|
| `ENABLE_HTTPS` | 启用 HTTPS | `false` |
| `HTTP_PORT` | HTTP 端口 | `8080` |
| `VERBOSE` | 日志级别 (1/2/3) | - |
## 致谢
感谢以下项目:
- [PiKVM](https://github.com/pikvm/pikvm) - 原始 Python 版 IP-KVM
- [RustDesk](https://github.com/rustdesk/rustdesk) - hwcodec 硬件编码库
- [ttyd](https://github.com/tsl0922/ttyd) - Web 终端
- [EasyTier](https://github.com/EasyTier/EasyTier) - P2P 组网
## 许可证
待定

35
build.rs Normal file
View File

@@ -0,0 +1,35 @@
fn main() {
// Set BUILD_DATE environment variable for compile-time access
// Use system time to avoid adding chrono as a build dependency
let now = std::time::SystemTime::now();
let duration = now.duration_since(std::time::UNIX_EPOCH).unwrap();
let secs = duration.as_secs();
// Convert Unix timestamp to date (simplified calculation)
// Days since epoch
let days = secs / 86400;
// Calculate year, month, day from days since 1970-01-01
let (year, month, day) = days_to_ymd(days as i64);
let build_date = format!("{:04}-{:02}-{:02}", year, month, day);
println!("cargo:rustc-env=BUILD_DATE={}", build_date);
// Rerun if the script itself changes
println!("cargo:rerun-if-changed=build.rs");
}
/// Convert days since Unix epoch to year-month-day
fn days_to_ymd(days: i64) -> (i32, u32, u32) {
// Algorithm from http://howardhinnant.github.io/date_algorithms.html
let z = days + 719468;
let era = if z >= 0 { z } else { z - 146096 } / 146097;
let doe = (z - era * 146097) as u32;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = yoe as i64 + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let year = if m <= 2 { y + 1 } else { y };
(year as i32, m, d)
}

76
build/Dockerfile.runtime Normal file
View File

@@ -0,0 +1,76 @@
# One-KVM Runtime Image
# This Dockerfile only packages pre-compiled binaries (no compilation)
# Used after cross-compiling with `cross build`
ARG TARGETPLATFORM=linux/amd64
FROM debian:12-slim
ARG TARGETPLATFORM
# Install runtime dependencies only (matches build environment)
RUN apt-get update && apt-get install -y --no-install-recommends \
# Runtime libraries
libasound2 \
libv4l-0 \
libudev1 \
zlib1g \
libjpeg62-turbo \
libyuv0 \
# FFmpeg runtime
libavcodec59 \
libavformat59 \
libavutil57 \
libswscale6 \
libswresample4 \
# Video codecs
libx264-164 \
libx265-199 \
libvpx7 \
# Audio codec
libopus0 \
# Hardware acceleration
libva2 \
libva-drm2 \
libdrm2 \
# X11 (for VAAPI)
libx11-6 \
libxcb1 \
# Utilities
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Intel Media SDK runtime for x86_64 only
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
apt-get update && \
apt-get install -y --no-install-recommends libmfx1 && \
rm -rf /var/lib/apt/lists/*; \
fi
# Create directories
RUN mkdir -p /etc/one-kvm/ventoy
# Copy init script
COPY init.sh /init.sh
# Copy binaries (these are placed by the build script)
COPY one-kvm /usr/bin/one-kvm
COPY ttyd /usr/bin/ttyd
COPY gostc /usr/bin/gostc
COPY easytier-core /usr/bin/easytier-core
# Copy ventoy resources if they exist
COPY ventoy/ /etc/one-kvm/ventoy/
# Set permissions and verify
RUN chmod +x /init.sh /usr/bin/one-kvm /usr/bin/ttyd /usr/bin/gostc /usr/bin/easytier-core && \
/usr/bin/one-kvm --help
# Expose ports
EXPOSE 8080 8443
# Data volume
VOLUME ["/etc/one-kvm"]
# Entrypoint
CMD ["/init.sh"]

View File

@@ -0,0 +1,66 @@
# Cross-compilation image for ARM64 based on Debian 12
# Uses multiarch to install ARM64 libraries on x86_64 host
FROM debian:12
# Install Rust toolchain
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable \
&& rm -rf /var/lib/apt/lists/*
ENV PATH="/root/.cargo/bin:${PATH}"
# Add ARM64 architecture
RUN dpkg --add-architecture arm64
# Install cross-compiler and native build tools
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
pkg-config \
cmake \
nasm \
git \
libclang-dev \
llvm \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
&& rm -rf /var/lib/apt/lists/*
# Install ARM64 development libraries
RUN apt-get update && apt-get install -y --no-install-recommends \
libasound2-dev:arm64 \
libv4l-dev:arm64 \
libudev-dev:arm64 \
zlib1g-dev:arm64 \
libjpeg62-turbo-dev:arm64 \
libyuv-dev:arm64 \
libavcodec-dev:arm64 \
libavformat-dev:arm64 \
libavutil-dev:arm64 \
libswscale-dev:arm64 \
libswresample-dev:arm64 \
libx264-dev:arm64 \
libx265-dev:arm64 \
libvpx-dev:arm64 \
libopus-dev:arm64 \
libva-dev:arm64 \
libdrm-dev:arm64 \
libx11-dev:arm64 \
libxcb1-dev:arm64 \
libxau-dev:arm64 \
libxdmcp-dev:arm64 \
&& rm -rf /var/lib/apt/lists/*
# Add Rust target
RUN rustup target add aarch64-unknown-linux-gnu
# Configure environment for cross-compilation
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \
CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \
AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar \
PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig \
PKG_CONFIG_ALLOW_CROSS=1

View File

@@ -0,0 +1,66 @@
# Cross-compilation image for ARMv7 based on Debian 12
# Uses multiarch to install ARMv7 libraries on x86_64 host
FROM debian:12
# Install Rust toolchain
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable \
&& rm -rf /var/lib/apt/lists/*
ENV PATH="/root/.cargo/bin:${PATH}"
# Add armhf architecture
RUN dpkg --add-architecture armhf
# Install cross-compiler and native build tools
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
pkg-config \
cmake \
nasm \
git \
libclang-dev \
llvm \
gcc-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf \
libc6-dev-armhf-cross \
&& rm -rf /var/lib/apt/lists/*
# Install ARMv7 development libraries
RUN apt-get update && apt-get install -y --no-install-recommends \
libasound2-dev:armhf \
libv4l-dev:armhf \
libudev-dev:armhf \
zlib1g-dev:armhf \
libjpeg62-turbo-dev:armhf \
libyuv-dev:armhf \
libavcodec-dev:armhf \
libavformat-dev:armhf \
libavutil-dev:armhf \
libswscale-dev:armhf \
libswresample-dev:armhf \
libx264-dev:armhf \
libx265-dev:armhf \
libvpx-dev:armhf \
libopus-dev:armhf \
libva-dev:armhf \
libdrm-dev:armhf \
libx11-dev:armhf \
libxcb1-dev:armhf \
libxau-dev:armhf \
libxdmcp-dev:armhf \
&& rm -rf /var/lib/apt/lists/*
# Add Rust target
RUN rustup target add armv7-unknown-linux-gnueabihf
# Configure environment for cross-compilation
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \
CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ \
AR_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-ar \
PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig \
PKG_CONFIG_ALLOW_CROSS=1

View File

@@ -0,0 +1,57 @@
# Cross-compilation image for x86_64 based on Debian 12
# Matches the runtime environment exactly
FROM debian:12
# Install Rust toolchain
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable \
&& rm -rf /var/lib/apt/lists/*
ENV PATH="/root/.cargo/bin:${PATH}"
# Install build dependencies (same as runtime Debian 12)
RUN apt-get update && apt-get install -y --no-install-recommends \
# Build tools
build-essential \
pkg-config \
cmake \
nasm \
git \
libclang-dev \
llvm \
# Core system libraries
libasound2-dev \
libv4l-dev \
libudev-dev \
zlib1g-dev \
# Video/image processing
libjpeg62-turbo-dev \
libyuv-dev \
# FFmpeg and codecs
libavcodec-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libswresample-dev \
# Video codec libraries
libx264-dev \
libx265-dev \
libvpx-dev \
# Audio codec
libopus-dev \
# Hardware acceleration
libva-dev \
libdrm-dev \
libmfx-dev \
# X11 libraries
libx11-dev \
libxcb1-dev \
libxau-dev \
libxdmcp-dev \
&& rm -rf /var/lib/apt/lists/*
# Add Rust target
RUN rustup target add x86_64-unknown-linux-gnu

39
build/init.sh Normal file
View File

@@ -0,0 +1,39 @@
#!/bin/bash
# One-KVM initialization script
# Container entrypoint to start the one-kvm service
set -e
# Start one-kvm with default options
# Additional options can be passed via environment variables
EXTRA_ARGS=""
# Enable HTTPS if requested
if [ "${ENABLE_HTTPS:-false}" = "true" ]; then
EXTRA_ARGS="$EXTRA_ARGS --enable-https"
fi
# Custom bind address
if [ -n "$BIND_ADDRESS" ]; then
EXTRA_ARGS="$EXTRA_ARGS -a $BIND_ADDRESS"
fi
# Custom port
if [ -n "$HTTP_PORT" ]; then
EXTRA_ARGS="$EXTRA_ARGS -p $HTTP_PORT"
fi
# Verbosity level
if [ -n "$VERBOSE" ]; then
case "$VERBOSE" in
1) EXTRA_ARGS="$EXTRA_ARGS -v" ;;
2) EXTRA_ARGS="$EXTRA_ARGS -vv" ;;
3) EXTRA_ARGS="$EXTRA_ARGS -vvv" ;;
esac
fi
echo "[INFO] Starting one-kvm..."
echo "[INFO] Extra arguments: $EXTRA_ARGS"
# Execute one-kvm
exec /usr/bin/one-kvm $EXTRA_ARGS

358
build/package-docker.sh Executable file
View File

@@ -0,0 +1,358 @@
#!/bin/bash
# One-KVM Docker Image Packaging Script
# Packages pre-compiled binaries into runtime Docker images
#
# Prerequisites:
# 1. Build binaries first: cross build --release --target <target>
# 2. Docker with buildx support
#
# Usage:
# ./build-docker.sh --platform linux/amd64 --load
# ./build-docker.sh --platform linux/arm64 --load
# ./build-docker.sh --push --tag v1.0.0
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
echo_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
echo_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Configuration
REGISTRY="${REGISTRY:-}" # e.g., docker.io/username or ghcr.io/username
IMAGE_NAME="${IMAGE_NAME:-one-kvm}"
TAG="${TAG:-latest}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
STAGING_DIR="$PROJECT_ROOT/build-staging"
# Full image name with registry
get_full_image_name() {
if [ -n "$REGISTRY" ]; then
echo "$REGISTRY/$IMAGE_NAME"
else
echo "$IMAGE_NAME"
fi
}
# Detect current platform
CURRENT_ARCH=$(uname -m)
case "$CURRENT_ARCH" in
x86_64) DEFAULT_PLATFORM="linux/amd64" ;;
aarch64) DEFAULT_PLATFORM="linux/arm64" ;;
armv7l) DEFAULT_PLATFORM="linux/arm/v7" ;;
*) DEFAULT_PLATFORM="linux/amd64" ;;
esac
# Parse arguments
PLATFORMS=""
PUSH=false
LOAD=false
BUILD_BINARY=false
while [[ $# -gt 0 ]]; do
case $1 in
--platform)
PLATFORMS="$2"
shift 2
;;
--push)
PUSH=true
shift
;;
--load)
LOAD=true
shift
;;
--tag)
TAG="$2"
shift 2
;;
--registry)
REGISTRY="$2"
shift 2
;;
--build)
BUILD_BINARY=true
shift
;;
--help)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Package pre-compiled One-KVM binaries into Docker images."
echo ""
echo "Options:"
echo " --platform PLATFORM Target platform (linux/amd64, linux/arm64, linux/arm/v7)"
echo " Use comma to specify multiple: linux/amd64,linux/arm64"
echo " Default: $DEFAULT_PLATFORM"
echo " --registry REGISTRY Container registry (e.g., docker.io/user, ghcr.io/user)"
echo " --push Push image to registry"
echo " --load Load image to local Docker (single platform only)"
echo " --tag TAG Image tag (default: latest)"
echo " --build Also build the binary with cross (optional)"
echo " --help Show this help"
echo ""
echo "Examples:"
echo " # Build for current platform and load locally"
echo " $0 --platform linux/arm64 --load"
echo ""
echo " # Build and push single platform"
echo " $0 --platform linux/arm64 --registry docker.io/user --push"
echo ""
echo " # Build multi-arch and push (creates unified manifest)"
echo " $0 --platform linux/amd64,linux/arm64,linux/arm/v7 --registry docker.io/user --push"
exit 0
;;
*)
echo_error "Unknown option: $1"
exit 1
;;
esac
done
# Default platform
if [ -z "$PLATFORMS" ]; then
PLATFORMS="$DEFAULT_PLATFORM"
fi
# Validate single platform for --load
if [ "$LOAD" = true ] && [[ "$PLATFORMS" == *","* ]]; then
echo_error "Cannot use --load with multiple platforms"
exit 1
fi
# Map platform to Rust target
platform_to_target() {
case "$1" in
"linux/amd64") echo "x86_64-unknown-linux-gnu" ;;
"linux/arm64") echo "aarch64-unknown-linux-gnu" ;;
"linux/arm/v7") echo "armv7-unknown-linux-gnueabihf" ;;
*) echo_error "Unknown platform: $1"; exit 1 ;;
esac
}
# Map platform to tool download names
get_tool_urls() {
local platform="$1"
case "$platform" in
"linux/amd64")
TTYD_URL="https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.x86_64"
GOSTC_URL="https://github.com/SianHH/gostc-open/releases/download/v2.0.9/gostc_linux_amd64_v1.tar.gz"
EASYTIER_URL="https://github.com/EasyTier/EasyTier/releases/download/v2.4.5/easytier-linux-x86_64-v2.4.5.zip"
EASYTIER_DIR="easytier-linux-x86_64"
;;
"linux/arm64")
TTYD_URL="https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.aarch64"
GOSTC_URL="https://github.com/SianHH/gostc-open/releases/download/v2.0.9/gostc_linux_arm64_v8.0.tar.gz"
EASYTIER_URL="https://github.com/EasyTier/EasyTier/releases/download/v2.4.5/easytier-linux-aarch64-v2.4.5.zip"
EASYTIER_DIR="easytier-linux-aarch64"
;;
"linux/arm/v7")
TTYD_URL="https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.armhf"
GOSTC_URL="https://github.com/SianHH/gostc-open/releases/download/v2.0.9/gostc_linux_arm_7.tar.gz"
EASYTIER_URL="https://github.com/EasyTier/EasyTier/releases/download/v2.4.5/easytier-linux-armv7hf-v2.4.5.zip"
EASYTIER_DIR="easytier-linux-armv7hf"
;;
esac
}
# Download tools for a platform
download_tools() {
local platform="$1"
local staging="$2"
get_tool_urls "$platform"
echo_info "Downloading tools for $platform..."
# ttyd
if [ ! -f "$staging/ttyd" ]; then
curl -fsSL "$TTYD_URL" -o "$staging/ttyd"
chmod +x "$staging/ttyd"
fi
# gostc
if [ ! -f "$staging/gostc" ]; then
curl -fsSL "$GOSTC_URL" -o /tmp/gostc.tar.gz
tar -xzf /tmp/gostc.tar.gz -C "$staging"
chmod +x "$staging/gostc"
rm /tmp/gostc.tar.gz
fi
# easytier
if [ ! -f "$staging/easytier-core" ]; then
curl -fsSL "$EASYTIER_URL" -o /tmp/easytier.zip
unzip -o /tmp/easytier.zip -d /tmp/easytier
cp "/tmp/easytier/$EASYTIER_DIR/easytier-core" "$staging/easytier-core"
chmod +x "$staging/easytier-core"
rm -rf /tmp/easytier.zip /tmp/easytier
fi
}
# Build and package for a single platform
build_for_platform() {
local platform="$1"
local target=$(platform_to_target "$platform")
local staging="$STAGING_DIR/$target"
echo_info "=========================================="
echo_info "Processing: $platform ($target)"
echo_info "=========================================="
# Create staging directory
mkdir -p "$staging/ventoy"
# Build binary if requested
if [ "$BUILD_BINARY" = true ]; then
echo_info "Building binary with cross..."
cd "$PROJECT_ROOT"
cross build --release --target "$target"
fi
# Check binary exists
local binary="$PROJECT_ROOT/target/$target/release/one-kvm"
if [ ! -f "$binary" ]; then
echo_error "Binary not found: $binary"
echo_error "Build it first: cross build --release --target $target"
exit 1
fi
# Copy binary to staging
echo_info "Copying binary..."
cp "$binary" "$staging/one-kvm"
# Download tools
download_tools "$platform" "$staging"
# Copy init script
cp "$PROJECT_ROOT/build/init.sh" "$staging/init.sh"
# Copy ventoy resources if they exist
if [ -d "$PROJECT_ROOT/res/ventoy" ]; then
cp -r "$PROJECT_ROOT/res/ventoy/"* "$staging/ventoy/" 2>/dev/null || true
fi
# Ensure ventoy dir exists (even if empty) for COPY command
touch "$staging/ventoy/.keep"
# Copy Dockerfile
cp "$PROJECT_ROOT/build/Dockerfile.runtime" "$staging/Dockerfile"
# Build Docker image
echo_info "Building Docker image..."
local full_image=$(get_full_image_name)
local arch_tag="${target//_/-}"
local build_cmd="docker buildx build --platform $platform"
build_cmd="$build_cmd --build-arg TARGETPLATFORM=$platform"
if [ "$PUSH" = true ]; then
build_cmd="$build_cmd --push"
elif [ "$LOAD" = true ]; then
build_cmd="$build_cmd --load"
fi
# For multi-platform push, only tag with arch-specific name
# The unified tag will be created via manifest later
if [ "$PUSH" = true ] && [[ "$PLATFORMS" == *","* ]]; then
build_cmd="$build_cmd -t $full_image:$TAG-$arch_tag"
else
build_cmd="$build_cmd -t $full_image:$TAG"
build_cmd="$build_cmd -t $full_image:$TAG-$arch_tag"
fi
build_cmd="$build_cmd $staging"
echo_info "Running: $build_cmd"
eval "$build_cmd"
echo_info "Done: $full_image:$TAG-$arch_tag"
}
# Main
main() {
local full_image=$(get_full_image_name)
echo_info "One-KVM Docker Image Builder"
echo_info "Image: $full_image:$TAG"
echo_info "Platforms: $PLATFORMS"
if [ -n "$REGISTRY" ]; then
echo_info "Registry: $REGISTRY"
fi
echo ""
# Validate: push requires registry for multi-arch
if [ "$PUSH" = true ] && [ -z "$REGISTRY" ]; then
echo_warn "No registry specified. Images will be pushed to Docker Hub default."
echo_warn "Consider using --registry to specify a registry."
fi
# Process each platform
IFS=',' read -ra PLATFORM_ARRAY <<< "$PLATFORMS"
for platform in "${PLATFORM_ARRAY[@]}"; do
build_for_platform "$platform"
echo ""
done
# Create multi-arch manifest if pushing multiple platforms
if [ "$PUSH" = true ] && [ ${#PLATFORM_ARRAY[@]} -gt 1 ]; then
echo_info "Creating multi-arch manifest..."
local manifest_images=""
for platform in "${PLATFORM_ARRAY[@]}"; do
local target=$(platform_to_target "$platform")
local arch_tag="${target//_/-}"
manifest_images="$manifest_images $full_image:$TAG-$arch_tag"
done
# Remove existing manifest if any
docker manifest rm "$full_image:$TAG" 2>/dev/null || true
# Create and push manifest
echo_info "Creating manifest: $full_image:$TAG"
echo_info "Source images:$manifest_images"
if docker manifest create "$full_image:$TAG" $manifest_images; then
echo_info "Pushing manifest..."
docker manifest push "$full_image:$TAG"
echo_info "Multi-arch manifest pushed: $full_image:$TAG"
else
echo_warn "docker manifest failed, trying buildx imagetools..."
docker buildx imagetools create -t "$full_image:$TAG" $manifest_images
fi
fi
echo_info "=========================================="
echo_info "Build completed successfully!"
echo_info "=========================================="
if [ "$LOAD" = true ]; then
echo ""
echo "Run the container:"
echo " docker run -d --privileged \\"
echo " -p 8080:8080 \\"
echo " -v /dev:/dev \\"
echo " $full_image:$TAG"
fi
if [ "$PUSH" = true ] && [ ${#PLATFORM_ARRAY[@]} -gt 1 ]; then
echo ""
echo "Multi-arch image available:"
echo " docker pull $full_image:$TAG"
echo ""
echo "Or pull specific architecture:"
for platform in "${PLATFORM_ARRAY[@]}"; do
local target=$(platform_to_target "$platform")
local arch_tag="${target//_/-}"
echo " docker pull $full_image:$TAG-$arch_tag # $platform"
done
fi
}
main "$@"

24
libs/hwcodec/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
/target
/Cargo.lock
.vscode
/ffmpeg/*
!/ffmpeg/windows
/ffmpeg/windows/debug
!/ffmpeg/linux
/ffmpeg/linux/debug
!/ffmpeg/mac
/ffmpeg/mac/debug
!/ffmpeg/android
/ffmpeg/android/debug
!/ffmpeg/ios
/ffmpeg/ios/debug
/input
/output
*.diff
dev/vs/AMFTest/x64
dev/vs/MFXTest/x64
dev/vs/ShaderCompileTool/x64
.vs
bmp
texture
.DS_Store

3
libs/hwcodec/.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "externals"]
path = externals
url = https://github.com/rustdesk-org/externals.git

29
libs/hwcodec/Cargo.toml Normal file
View File

@@ -0,0 +1,29 @@
[package]
name = "hwcodec"
version = "0.7.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = []
vram = []
[dependencies]
log = "0.4"
serde_derive = "1.0"
serde = "1.0"
serde_json = "1.0"
[build-dependencies]
cc = "1.0"
bindgen = "0.59"
[dev-dependencies]
env_logger = "0.10"
rand = "0.8"
[target.'cfg(target_os="windows")'.dev-dependencies]
capture = { path = "dev/capture" }
render = { path = "dev/render" }
tool = { path = "dev/tool" }

79
libs/hwcodec/README.md Normal file
View File

@@ -0,0 +1,79 @@
# A real-time hardware codec library for [RustDesk](https://github.com/rustdesk/rustdesk) based on FFmpeg
## Codec
### Windows
| GPU | FFmpeg ram | FFmpeg vram | sdk vram |
| ------------- | ---------------- | ----------- | -------- |
| intel encode | qsv | qsv | Y |
| intel decode | d3d11 | d3d11 | Y |
| nvidia encode | nvenc(nv12->d3d11)| nvenc(d3d11)| Y |
| nvidia decode | d3d11 | d3d11 | N |
| amd encode | amf | amf | Y |
| amd decode | d3d11 | d3d11 | Y |
#### Notes
* The reason for discarding the codecs using Cucontext is discussed in the following forum thread: https://forums.developer.nvidia.com/t/cuctxdestroy-causing-system-freeze-and-black-screen/290542/1.
Based on the information above, there are several optimizations and changes made to the codec:
- FFmpeg encoding AV_PIX_FMT_NV12 directly: The codec is modified to transfer AV_PIX_FMT_NV12 to AV_PIX_FMT_D3D11. This is done because FFmpeg doesn't use Cucontext if the device type is AV_HWDEVICE_TYPE_D3D11VA.
- FFmpeg decoding with AV_HWDEVICE_TYPE_CUDA acceleration: This functionality is disabled and replaced with AV_HWDEVICE_TYPE_D3D11VA. The decoding process now utilizes D3D11VA acceleration instead of CUDA.
- SDK decoding with CUDA acceleration: The CUDA acceleration support is disabled.
* amd sdk remove h265 support, https://github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/432
### Linux
| GPU | FFmpeg ram |
| ------------- | -------------- |
| intel encode | vaapi |
| intel decode | vaapi |
| nvidia encode | vaapi, nvnec |
| nvidia decode | vaapi, nvdec |
| amd encode | vaapi, amf |
| amd decode | vaapi |
#### Issue
* vaapi: only tested on intel with `va-driver-all`, and hevc_vaapi encoding not supported on my pc
* remove hevc_vaapi because of possible poor quality
* amf: not tested, https://github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/378
### MacOS
| FFmpeg ram encode | FFmpeg ram decode |
| ------------------ | ------------------ |
| h265 only | Y |
### Android
| FFmpeg ram encode |
| ------------------ |
| Y |
## System requirements
* intel
Windows Intel(r) graphics driver since 27.20.100.8935 version.
[Hardware Platforms Supported by the Intel(R) Media SDK GPU Runtime](https://www.intel.com/content/www/us/en/docs/onevpl/upgrade-from-msdk/2023-1/onevpl-hardware-support-details.html#HARDWARE-PLATFORMS-SUPPORTED-BY-THE-INTEL-R-MEDIA-SDK-GPU-RUNTIME)
https://www.intel.com/content/www/us/en/docs/onevpl/developer-reference-media-intel-hardware/1-1/overview.html
* AMD
AMD Radeon Software Adrenalin Edition 23.1.2 (22.40.01.34) or newer
https://github.com/GPUOpen-LibrariesAndSDKs/AMF
* nvidia
Windows: Driver version 471.41 or higher
https://docs.nvidia.com/video-technologies/video-codec-sdk/11.1/read-me/index.html
https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new?ncid=em-prod-816193

521
libs/hwcodec/build.rs Normal file
View File

@@ -0,0 +1,521 @@
use cc::Build;
use std::{
env,
path::{Path, PathBuf},
};
fn main() {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let externals_dir = manifest_dir.join("externals");
let cpp_dir = manifest_dir.join("cpp");
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed=deps");
println!("cargo:rerun-if-changed={}", externals_dir.display());
println!("cargo:rerun-if-changed={}", cpp_dir.display());
let mut builder = Build::new();
build_common(&mut builder);
ffmpeg::build_ffmpeg(&mut builder);
#[cfg(all(windows, feature = "vram"))]
sdk::build_sdk(&mut builder);
builder.static_crt(true).compile("hwcodec");
}
fn build_common(builder: &mut Build) {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let common_dir = manifest_dir.join("cpp").join("common");
bindgen::builder()
.header(common_dir.join("common.h").to_string_lossy().to_string())
.header(common_dir.join("callback.h").to_string_lossy().to_string())
.rustified_enum("*")
.parse_callbacks(Box::new(CommonCallbacks))
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("common_ffi.rs"))
.unwrap();
// system
#[cfg(windows)]
{
["d3d11", "dxgi"].map(|lib| println!("cargo:rustc-link-lib={}", lib));
}
builder.include(&common_dir);
// platform
let _platform_path = common_dir.join("platform");
#[cfg(windows)]
{
let win_path = _platform_path.join("win");
builder.include(&win_path);
builder.file(win_path.join("win.cpp"));
}
#[cfg(target_os = "linux")]
{
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let externals_dir = manifest_dir.join("externals");
// ffnvcodec
let ffnvcodec_path = externals_dir
.join("nv-codec-headers_n12.1.14.0")
.join("include")
.join("ffnvcodec");
builder.include(ffnvcodec_path);
let linux_path = _platform_path.join("linux");
builder.include(&linux_path);
builder.file(linux_path.join("linux.cpp"));
}
if target_os == "macos" {
let macos_path = _platform_path.join("mac");
builder.include(&macos_path);
builder.file(macos_path.join("mac.mm"));
}
// tool
builder.files(["log.cpp", "util.cpp"].map(|f| common_dir.join(f)));
}
#[derive(Debug)]
struct CommonCallbacks;
impl bindgen::callbacks::ParseCallbacks for CommonCallbacks {
fn add_derives(&self, name: &str) -> Vec<String> {
let names = vec!["DataFormat", "SurfaceFormat", "API"];
if names.contains(&name) {
vec!["Serialize", "Deserialize"]
.drain(..)
.map(|s| s.to_string())
.collect()
} else {
vec![]
}
}
}
mod ffmpeg {
#[allow(unused_imports)]
use core::panic;
use super::*;
pub fn build_ffmpeg(builder: &mut Build) {
ffmpeg_ffi();
// Try VCPKG first, fallback to system FFmpeg via pkg-config
if let Ok(vcpkg_root) = std::env::var("VCPKG_ROOT") {
link_vcpkg(builder, vcpkg_root.into());
} else {
// Use system FFmpeg via pkg-config
link_system_ffmpeg(builder);
}
link_os();
build_ffmpeg_ram(builder);
#[cfg(feature = "vram")]
build_ffmpeg_vram(builder);
build_mux(builder);
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
if target_os == "macos" || target_os == "ios" {
builder.flag("-std=c++11");
}
}
/// Link system FFmpeg using pkg-config (for Linux development)
fn link_system_ffmpeg(builder: &mut Build) {
use std::process::Command;
let libs = ["libavcodec", "libavutil", "libavformat", "libswscale"];
for lib in &libs {
// Get cflags
if let Ok(output) = Command::new("pkg-config")
.args(["--cflags", lib])
.output()
{
if output.status.success() {
let cflags = String::from_utf8_lossy(&output.stdout);
for flag in cflags.split_whitespace() {
if flag.starts_with("-I") {
builder.include(&flag[2..]);
}
}
}
}
// Get libs - always use dynamic linking on Linux
if let Ok(output) = Command::new("pkg-config")
.args(["--libs", lib])
.output()
{
if output.status.success() {
let libs_str = String::from_utf8_lossy(&output.stdout);
for flag in libs_str.split_whitespace() {
if flag.starts_with("-L") {
println!("cargo:rustc-link-search=native={}", &flag[2..]);
} else if flag.starts_with("-l") {
println!("cargo:rustc-link-lib={}", &flag[2..]);
}
}
} else {
panic!("pkg-config failed for {}. Install FFmpeg development libraries: sudo apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev", lib);
}
} else {
panic!("pkg-config not found. Install pkg-config and FFmpeg development libraries.");
}
}
println!("cargo:info=Using system FFmpeg via pkg-config (dynamic linking)");
}
fn link_vcpkg(builder: &mut Build, mut path: PathBuf) -> PathBuf {
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let mut target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
if target_arch == "x86_64" {
target_arch = "x64".to_owned();
} else if target_arch == "x86" {
target_arch = "x86".to_owned();
} else if target_arch == "loongarch64" {
target_arch = "loongarch64".to_owned();
} else if target_arch == "aarch64" {
target_arch = "arm64".to_owned();
} else {
target_arch = "arm".to_owned();
}
let mut target = if target_os == "macos" {
if target_arch == "x64" {
"x64-osx".to_owned()
} else if target_arch == "arm64" {
"arm64-osx".to_owned()
} else {
format!("{}-{}", target_arch, target_os)
}
} else if target_os == "windows" {
"x64-windows-static".to_owned()
} else {
format!("{}-{}", target_arch, target_os)
};
if target_arch == "x86" {
target = target.replace("x64", "x86");
}
println!("cargo:info={}", target);
path.push("installed");
path.push(target);
println!(
"{}",
format!(
"cargo:rustc-link-search=native={}",
path.join("lib").to_str().unwrap()
)
);
{
let mut static_libs = vec!["avcodec", "avutil", "avformat"];
if target_os == "windows" {
static_libs.push("libmfx");
}
static_libs
.iter()
.map(|lib| println!("cargo:rustc-link-lib=static={}", lib))
.count();
}
let include = path.join("include");
println!("{}", format!("cargo:include={}", include.to_str().unwrap()));
builder.include(&include);
include
}
fn link_os() {
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let dyn_libs: Vec<&str> = if target_os == "windows" {
["User32", "bcrypt", "ole32", "advapi32"].to_vec()
} else if target_os == "linux" {
// Note: VA-API libraries (va, va-drm, va-x11) must remain dynamic
// as they load drivers at runtime. Same for libmfx.
// All dependencies use dynamic linking on Linux.
let mut v = vec!["drm", "X11", "stdc++"];
if target_arch == "x86_64" {
v.push("z");
}
v
} else if target_os == "macos" || target_os == "ios" {
["c++", "m"].to_vec()
} else if target_os == "android" {
// https://github.com/FFmpeg/FFmpeg/commit/98b5e80fd6980e641199e9ce3bc27100e2df17a4
// link to mediandk directly since n7.1
["z", "m", "android", "atomic", "mediandk"].to_vec()
} else {
panic!("unsupported os");
};
for lib in dyn_libs.iter() {
println!("cargo:rustc-link-lib={}", lib);
}
if target_os == "macos" || target_os == "ios" {
println!("cargo:rustc-link-lib=framework=CoreFoundation");
println!("cargo:rustc-link-lib=framework=CoreVideo");
println!("cargo:rustc-link-lib=framework=CoreMedia");
println!("cargo:rustc-link-lib=framework=VideoToolbox");
println!("cargo:rustc-link-lib=framework=AVFoundation");
}
}
fn ffmpeg_ffi() {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let ffmpeg_ram_dir = manifest_dir.join("cpp").join("common");
let ffi_header = ffmpeg_ram_dir
.join("ffmpeg_ffi.h")
.to_string_lossy()
.to_string();
bindgen::builder()
.header(ffi_header)
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("ffmpeg_ffi.rs"))
.unwrap();
}
fn build_ffmpeg_ram(builder: &mut Build) {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let ffmpeg_ram_dir = manifest_dir.join("cpp").join("ffmpeg_ram");
let ffi_header = ffmpeg_ram_dir
.join("ffmpeg_ram_ffi.h")
.to_string_lossy()
.to_string();
bindgen::builder()
.header(ffi_header)
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("ffmpeg_ram_ffi.rs"))
.unwrap();
builder.files(
["ffmpeg_ram_encode.cpp", "ffmpeg_ram_decode.cpp"].map(|f| ffmpeg_ram_dir.join(f)),
);
}
#[cfg(feature = "vram")]
fn build_ffmpeg_vram(builder: &mut Build) {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let ffmpeg_ram_dir = manifest_dir.join("cpp").join("ffmpeg_vram");
let ffi_header = ffmpeg_ram_dir
.join("ffmpeg_vram_ffi.h")
.to_string_lossy()
.to_string();
bindgen::builder()
.header(ffi_header)
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("ffmpeg_vram_ffi.rs"))
.unwrap();
builder.files(
["ffmpeg_vram_decode.cpp", "ffmpeg_vram_encode.cpp"].map(|f| ffmpeg_ram_dir.join(f)),
);
}
fn build_mux(builder: &mut Build) {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let mux_dir = manifest_dir.join("cpp").join("mux");
let mux_header = mux_dir.join("mux_ffi.h").to_string_lossy().to_string();
bindgen::builder()
.header(mux_header)
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("mux_ffi.rs"))
.unwrap();
builder.files(["mux.cpp"].map(|f| mux_dir.join(f)));
}
}
#[cfg(all(windows, feature = "vram"))]
mod sdk {
use super::*;
pub(crate) fn build_sdk(builder: &mut Build) {
build_amf(builder);
build_nv(builder);
build_mfx(builder);
}
fn build_nv(builder: &mut Build) {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let externals_dir = manifest_dir.join("externals");
let common_dir = manifest_dir.join("common");
let nv_dir = manifest_dir.join("cpp").join("nv");
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed={}", common_dir.display());
println!("cargo:rerun-if-changed={}", externals_dir.display());
bindgen::builder()
.header(&nv_dir.join("nv_ffi.h").to_string_lossy().to_string())
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("nv_ffi.rs"))
.unwrap();
// system
#[cfg(target_os = "windows")]
[
"kernel32", "user32", "gdi32", "winspool", "shell32", "ole32", "oleaut32", "uuid",
"comdlg32", "advapi32", "d3d11", "dxgi",
]
.map(|lib| println!("cargo:rustc-link-lib={}", lib));
#[cfg(target_os = "linux")]
println!("cargo:rustc-link-lib=stdc++");
// ffnvcodec
let ffnvcodec_path = externals_dir
.join("nv-codec-headers_n12.1.14.0")
.join("include")
.join("ffnvcodec");
builder.include(ffnvcodec_path);
// video codc sdk
let sdk_path = externals_dir.join("Video_Codec_SDK_12.1.14");
builder.includes([
sdk_path.clone(),
sdk_path.join("Interface"),
sdk_path.join("Samples").join("Utils"),
sdk_path.join("Samples").join("NvCodec"),
sdk_path.join("Samples").join("NvCodec").join("NVEncoder"),
sdk_path.join("Samples").join("NvCodec").join("NVDecoder"),
]);
for file in vec!["NvEncoder.cpp", "NvEncoderD3D11.cpp"] {
builder.file(
sdk_path
.join("Samples")
.join("NvCodec")
.join("NvEncoder")
.join(file),
);
}
for file in vec!["NvDecoder.cpp"] {
builder.file(
sdk_path
.join("Samples")
.join("NvCodec")
.join("NvDecoder")
.join(file),
);
}
// crate
builder.files(["nv_encode.cpp", "nv_decode.cpp"].map(|f| nv_dir.join(f)));
}
fn build_amf(builder: &mut Build) {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let externals_dir = manifest_dir.join("externals");
let amf_dir = manifest_dir.join("cpp").join("amf");
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed={}", externals_dir.display());
bindgen::builder()
.header(amf_dir.join("amf_ffi.h").to_string_lossy().to_string())
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("amf_ffi.rs"))
.unwrap();
// system
#[cfg(windows)]
println!("cargo:rustc-link-lib=ole32");
#[cfg(target_os = "linux")]
println!("cargo:rustc-link-lib=stdc++");
// amf
let amf_path = externals_dir.join("AMF_v1.4.35");
builder.include(format!("{}/amf/public/common", amf_path.display()));
builder.include(amf_path.join("amf"));
for f in vec![
"AMFFactory.cpp",
"AMFSTL.cpp",
"Thread.cpp",
#[cfg(windows)]
"Windows/ThreadWindows.cpp",
#[cfg(target_os = "linux")]
"Linux/ThreadLinux.cpp",
"TraceAdapter.cpp",
] {
builder.file(format!("{}/amf/public/common/{}", amf_path.display(), f));
}
// crate
builder.files(["amf_encode.cpp", "amf_decode.cpp"].map(|f| amf_dir.join(f)));
}
fn build_mfx(builder: &mut Build) {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let externals_dir = manifest_dir.join("externals");
let mfx_dir = manifest_dir.join("cpp").join("mfx");
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed={}", externals_dir.display());
bindgen::builder()
.header(&mfx_dir.join("mfx_ffi.h").to_string_lossy().to_string())
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("mfx_ffi.rs"))
.unwrap();
// MediaSDK
let sdk_path = externals_dir.join("MediaSDK_22.5.4");
// mfx_dispatch
let mfx_path = sdk_path.join("api").join("mfx_dispatch");
// include headers and reuse static lib
builder.include(mfx_path.join("windows").join("include"));
let sample_path = sdk_path.join("samples").join("sample_common");
builder
.includes([
sdk_path.join("api").join("include"),
sample_path.join("include"),
])
.files(
[
"sample_utils.cpp",
"base_allocator.cpp",
"d3d11_allocator.cpp",
"avc_bitstream.cpp",
"avc_spl.cpp",
"avc_nal_spl.cpp",
]
.map(|f| sample_path.join("src").join(f)),
)
.files(
[
"time.cpp",
"atomic.cpp",
"shared_object.cpp",
"thread_windows.cpp",
]
.map(|f| sample_path.join("src").join("vm").join(f)),
);
// link
[
"kernel32", "user32", "gdi32", "winspool", "shell32", "ole32", "oleaut32", "uuid",
"comdlg32", "advapi32", "d3d11", "dxgi",
]
.map(|lib| println!("cargo:rustc-link-lib={}", lib));
builder
.files(["mfx_encode.cpp", "mfx_decode.cpp"].map(|f| mfx_dir.join(f)))
.define("NOMINMAX", None)
.define("MFX_DEPRECATED_OFF", None)
.define("MFX_D3D11_SUPPORT", None);
}
}

View File

@@ -0,0 +1,34 @@
#include "common.h"
#include <iostream>
#include <public/common/TraceAdapter.h>
#include <stdio.h>
#ifndef AMF_FACILITY
#define AMF_FACILITY L"AMFCommon"
#endif
static bool convert_api(amf::AMF_MEMORY_TYPE &rhs) {
// Always use DX11 since it's the only supported API
rhs = amf::AMF_MEMORY_DX11;
return true;
}
static bool convert_surface_format(SurfaceFormat lhs,
amf::AMF_SURFACE_FORMAT &rhs) {
switch (lhs) {
case SURFACE_FORMAT_NV12:
rhs = amf::AMF_SURFACE_NV12;
break;
case SURFACE_FORMAT_RGBA:
rhs = amf::AMF_SURFACE_RGBA;
break;
case SURFACE_FORMAT_BGRA:
rhs = amf::AMF_SURFACE_BGRA;
break;
default:
std::cerr << "unsupported surface format: " << static_cast<int>(lhs)
<< "\n";
return false;
}
return true;
}

View File

@@ -0,0 +1,451 @@
#include <public/common/AMFFactory.h>
#include <public/common/AMFSTL.h>
#include <public/common/ByteArray.h>
#include <public/common/Thread.h>
#include <public/common/TraceAdapter.h>
#include <public/include/components/VideoConverter.h>
#include <public/include/components/VideoDecoderUVD.h>
#include <cstring>
#include <iostream>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "AMFDEC"
#include "log.h"
#define AMF_FACILITY L"AMFDecoder"
#define AMF_CHECK_RETURN(res, msg) \
if (res != AMF_OK) { \
LOG_ERROR(std::string(msg) + ", result code: " + std::to_string(int(res))); \
return res; \
}
namespace {
class AMFDecoder {
private:
// system
void *device_;
int64_t luid_;
std::unique_ptr<NativeDevice> nativeDevice_ = nullptr;
// amf
AMFFactoryHelper AMFFactory_;
amf::AMFContextPtr AMFContext_ = NULL;
amf::AMFComponentPtr AMFDecoder_ = NULL;
amf::AMF_MEMORY_TYPE AMFMemoryType_;
amf::AMF_SURFACE_FORMAT decodeFormatOut_ = amf::AMF_SURFACE_NV12;
amf::AMF_SURFACE_FORMAT textureFormatOut_;
amf::AMFComponentPtr AMFConverter_ = NULL;
int last_width_ = 0;
int last_height_ = 0;
amf_wstring codec_;
bool full_range_ = false;
bool bt709_ = false;
// buffer
std::vector<std::vector<uint8_t>> buffer_;
public:
AMFDecoder(void *device, int64_t luid, amf::AMF_MEMORY_TYPE memoryTypeOut,
amf_wstring codec, amf::AMF_SURFACE_FORMAT textureFormatOut) {
device_ = device;
luid_ = luid;
AMFMemoryType_ = memoryTypeOut;
textureFormatOut_ = textureFormatOut;
codec_ = codec;
}
~AMFDecoder() {}
AMF_RESULT decode(uint8_t *iData, uint32_t iDataSize, DecodeCallback callback,
void *obj) {
AMF_RESULT res = AMF_FAIL;
bool decoded = false;
amf::AMFBufferPtr iDataWrapBuffer = NULL;
res = AMFContext_->CreateBufferFromHostNative(iData, iDataSize,
&iDataWrapBuffer, NULL);
AMF_CHECK_RETURN(res, "CreateBufferFromHostNative failed");
res = AMFDecoder_->SubmitInput(iDataWrapBuffer);
if (res == AMF_RESOLUTION_CHANGED) {
iDataWrapBuffer = NULL;
LOG_INFO(std::string("resolution changed"));
res = AMFDecoder_->Drain();
AMF_CHECK_RETURN(res, "Drain failed");
res = AMFDecoder_->Terminate();
AMF_CHECK_RETURN(res, "Terminate failed");
res = AMFDecoder_->Init(decodeFormatOut_, 0, 0);
AMF_CHECK_RETURN(res, "Init failed");
res = AMFContext_->CreateBufferFromHostNative(iData, iDataSize,
&iDataWrapBuffer, NULL);
AMF_CHECK_RETURN(res, "CreateBufferFromHostNative failed");
res = AMFDecoder_->SubmitInput(iDataWrapBuffer);
}
AMF_CHECK_RETURN(res, "SubmitInput failed");
amf::AMFDataPtr oData = NULL;
auto start = util::now();
do {
res = AMFDecoder_->QueryOutput(&oData);
if (res == AMF_REPEAT) {
amf_sleep(1);
}
} while (res == AMF_REPEAT && util::elapsed_ms(start) < DECODE_TIMEOUT_MS);
if (res == AMF_OK && oData != NULL) {
amf::AMFSurfacePtr surface(oData);
AMF_RETURN_IF_INVALID_POINTER(surface, L"surface is NULL");
if (surface->GetPlanesCount() == 0)
return AMF_FAIL;
// convert texture
amf::AMFDataPtr convertData;
res = Convert(surface, convertData);
AMF_CHECK_RETURN(res, "Convert failed");
amf::AMFSurfacePtr convertSurface(convertData);
if (!convertSurface || convertSurface->GetPlanesCount() == 0)
return AMF_FAIL;
// For DirectX objects, when a pointer to a COM interface is returned,
// GetNative does not call IUnknown::AddRef on the interface being
// returned.
void *native = convertSurface->GetPlaneAt(0)->GetNative();
if (!native)
return AMF_FAIL;
switch (convertSurface->GetMemoryType()) {
case amf::AMF_MEMORY_DX11: {
{
ID3D11Texture2D *src = (ID3D11Texture2D *)native;
D3D11_TEXTURE2D_DESC desc;
src->GetDesc(&desc);
nativeDevice_->EnsureTexture(desc.Width, desc.Height);
nativeDevice_->next();
ID3D11Texture2D *dst = nativeDevice_->GetCurrentTexture();
nativeDevice_->context_->CopyResource(dst, src);
nativeDevice_->context_->Flush();
if (callback)
callback(dst, obj);
decoded = true;
}
break;
} break;
case amf::AMF_MEMORY_OPENCL: {
uint8_t *buf = (uint8_t *)native;
} break;
}
surface = NULL;
convertData = NULL;
convertSurface = NULL;
}
oData = NULL;
iDataWrapBuffer = NULL;
return decoded ? AMF_OK : AMF_FAIL;
return AMF_OK;
}
AMF_RESULT destroy() {
// Terminate converter before terminate decoder get "[AMFDeviceDX11Impl]
// Warning: Possible memory leak detected: DX11 device is being destroyed,
// but has 6 surfaces associated with it. This is OK if there are references
// to the device outside AMF"
if (AMFConverter_ != NULL) {
AMFConverter_->Drain();
AMFConverter_->Terminate();
AMFConverter_ = NULL;
}
if (AMFDecoder_ != NULL) {
AMFDecoder_->Drain();
AMFDecoder_->Terminate();
AMFDecoder_ = NULL;
}
if (AMFContext_ != NULL) {
AMFContext_->Terminate();
AMFContext_ = NULL; // context is the last
}
AMFFactory_.Terminate();
return AMF_OK;
}
AMF_RESULT initialize() {
AMF_RESULT res;
res = AMFFactory_.Init();
AMF_CHECK_RETURN(res, "AMFFactory Init failed");
amf::AMFSetCustomTracer(AMFFactory_.GetTrace());
amf::AMFTraceEnableWriter(AMF_TRACE_WRITER_CONSOLE, true);
amf::AMFTraceSetWriterLevel(AMF_TRACE_WRITER_CONSOLE, AMF_TRACE_WARNING);
res = AMFFactory_.GetFactory()->CreateContext(&AMFContext_);
AMF_CHECK_RETURN(res, "CreateContext failed");
switch (AMFMemoryType_) {
case amf::AMF_MEMORY_DX11:
nativeDevice_ = std::make_unique<NativeDevice>();
if (!nativeDevice_->Init(luid_, (ID3D11Device *)device_, 4)) {
LOG_ERROR(std::string("Init NativeDevice failed"));
return AMF_FAIL;
}
res = AMFContext_->InitDX11(
nativeDevice_->device_.Get()); // can be DX11 device
AMF_CHECK_RETURN(res, "InitDX11 failed");
break;
default:
LOG_ERROR(std::string("unsupported memory type: ") +
std::to_string((int)AMFMemoryType_));
return AMF_FAIL;
}
res = AMFFactory_.GetFactory()->CreateComponent(AMFContext_, codec_.c_str(),
&AMFDecoder_);
AMF_CHECK_RETURN(res, "CreateComponent failed");
res = setParameters();
AMF_CHECK_RETURN(res, "setParameters failed");
res = AMFDecoder_->Init(decodeFormatOut_, 0, 0);
AMF_CHECK_RETURN(res, "Init decoder failed");
return AMF_OK;
}
private:
AMF_RESULT setParameters() {
AMF_RESULT res;
res =
AMFDecoder_->SetProperty(AMF_TIMESTAMP_MODE, amf_int64(AMF_TS_DECODE));
AMF_RETURN_IF_FAILED(
res, L"SetProperty AMF_TIMESTAMP_MODE to AMF_TS_DECODE failed");
res =
AMFDecoder_->SetProperty(AMF_VIDEO_DECODER_REORDER_MODE,
amf_int64(AMF_VIDEO_DECODER_MODE_LOW_LATENCY));
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_DECODER_REORDER_MODE failed");
// color
res = AMFDecoder_->SetProperty<amf_int64>(
AMF_VIDEO_DECODER_COLOR_RANGE,
full_range_ ? AMF_COLOR_RANGE_FULL : AMF_COLOR_RANGE_STUDIO);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_DECODER_COLOR_RANGE failed");
res = AMFDecoder_->SetProperty<amf_int64>(
AMF_VIDEO_DECODER_COLOR_PROFILE,
bt709_ ? (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_709)
: (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_601));
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_DECODER_COLOR_PROFILE failed");
// res = AMFDecoder_->SetProperty<amf_int64>(
// AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC,
// bt709_ ? AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709
// : AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M);
// AMF_CHECK_RETURN(
// res,
// "SetProperty AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC
// failed");
// res = AMFDecoder_->SetProperty<amf_int64>(
// AMF_VIDEO_DECODER_COLOR_PRIMARIES,
// bt709_ ? AMF_COLOR_PRIMARIES_BT709 : AMF_COLOR_PRIMARIES_SMPTE170M);
// AMF_CHECK_RETURN(res,
// "SetProperty AMF_VIDEO_DECODER_COLOR_PRIMARIES failed");
return AMF_OK;
}
AMF_RESULT Convert(IN amf::AMFSurfacePtr &surface,
OUT amf::AMFDataPtr &convertData) {
if (decodeFormatOut_ == textureFormatOut_)
return AMF_OK;
AMF_RESULT res;
int width = surface->GetPlaneAt(0)->GetWidth();
int height = surface->GetPlaneAt(0)->GetHeight();
if (AMFConverter_ != NULL) {
if (width != last_width_ || height != last_height_) {
LOG_INFO(std::string("Convert size changed, (") + std::to_string(last_width_) + "x" +
std::to_string(last_height_) + ") -> (" +
std::to_string(width) + "x" + std::to_string(width) + ")");
AMFConverter_->Terminate();
AMFConverter_ = NULL;
}
}
if (!AMFConverter_) {
res = AMFFactory_.GetFactory()->CreateComponent(
AMFContext_, AMFVideoConverter, &AMFConverter_);
AMF_CHECK_RETURN(res, "Convert CreateComponent failed");
res = AMFConverter_->SetProperty(AMF_VIDEO_CONVERTER_MEMORY_TYPE,
AMFMemoryType_);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_CONVERTER_MEMORY_TYPE failed");
res = AMFConverter_->SetProperty(AMF_VIDEO_CONVERTER_OUTPUT_FORMAT,
textureFormatOut_);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_CONVERTER_OUTPUT_FORMAT failed");
res = AMFConverter_->SetProperty(AMF_VIDEO_CONVERTER_OUTPUT_SIZE,
::AMFConstructSize(width, height));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_CONVERTER_OUTPUT_SIZE failed");
res = AMFConverter_->Init(decodeFormatOut_, width, height);
AMF_CHECK_RETURN(res, "Init converter failed");
// color
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_INPUT_COLOR_RANGE,
full_range_ ? AMF_COLOR_RANGE_FULL : AMF_COLOR_RANGE_STUDIO);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_CONVERTER_INPUT_COLOR_RANGE failed");
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_OUTPUT_COLOR_RANGE, AMF_COLOR_RANGE_FULL);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_CONVERTER_OUTPUT_COLOR_RANGE failed");
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_COLOR_PROFILE,
bt709_ ? (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_709)
: (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_601));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_CONVERTER_COLOR_PROFILE failed");
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_INPUT_TRANSFER_CHARACTERISTIC,
bt709_ ? AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709
: AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_CONVERTER_INPUT_TRANSFER_CHARACTERISTIC "
"failed");
res = AMFConverter_->SetProperty<amf_int64>(
AMF_VIDEO_CONVERTER_INPUT_COLOR_PRIMARIES,
bt709_ ? AMF_COLOR_PRIMARIES_BT709 : AMF_COLOR_PRIMARIES_SMPTE170M);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_CONVERTER_INPUT_COLOR_PRIMARIES failed");
}
last_width_ = width;
last_height_ = height;
res = AMFConverter_->SubmitInput(surface);
AMF_CHECK_RETURN(res, "Convert SubmitInput failed");
res = AMFConverter_->QueryOutput(&convertData);
AMF_CHECK_RETURN(res, "Convert QueryOutput failed");
return AMF_OK;
}
};
bool convert_codec(DataFormat lhs, amf_wstring &rhs) {
switch (lhs) {
case H264:
rhs = AMFVideoDecoderUVD_H264_AVC;
break;
case H265:
rhs = AMFVideoDecoderHW_H265_HEVC;
break;
default:
LOG_ERROR(std::string("unsupported codec: ") + std::to_string(lhs));
return false;
}
return true;
}
} // namespace
#include "amf_common.cpp"
extern "C" {
int amf_destroy_decoder(void *decoder) {
try {
AMFDecoder *dec = (AMFDecoder *)decoder;
if (dec) {
dec->destroy();
delete dec;
dec = NULL;
return 0;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("destroy failed: ") + e.what());
}
return -1;
}
void *amf_new_decoder(void *device, int64_t luid,
DataFormat dataFormat) {
AMFDecoder *dec = NULL;
try {
amf_wstring codecStr;
amf::AMF_MEMORY_TYPE memory;
amf::AMF_SURFACE_FORMAT surfaceFormat;
if (!convert_api(memory)) {
return NULL;
}
if (!convert_codec(dataFormat, codecStr)) {
return NULL;
}
dec = new AMFDecoder(device, luid, memory, codecStr, amf::AMF_SURFACE_BGRA);
if (dec) {
if (dec->initialize() == AMF_OK) {
return dec;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new failed: ") + e.what());
}
if (dec) {
dec->destroy();
delete dec;
dec = NULL;
}
return NULL;
}
int amf_decode(void *decoder, uint8_t *data, int32_t length,
DecodeCallback callback, void *obj) {
try {
AMFDecoder *dec = (AMFDecoder *)decoder;
if (dec->decode(data, length, callback, obj) == AMF_OK) {
return HWCODEC_SUCCESS;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("decode failed: ") + e.what());
}
return HWCODEC_ERR_COMMON;
}
int amf_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum, DataFormat dataFormat,
uint8_t *data, int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_AMD))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
AMFDecoder *p = (AMFDecoder *)amf_new_decoder(
nullptr, currentLuid, dataFormat);
if (!p)
continue;
auto start = util::now();
bool succ = p->decode(data, length, nullptr, nullptr) == AMF_OK;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_AMD;
count += 1;
}
p->destroy();
delete p;
p = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -0,0 +1,611 @@
#include <public/common/AMFFactory.h>
#include <public/common/AMFSTL.h>
#include <public/common/Thread.h>
#include <public/common/TraceAdapter.h>
#include <public/include/components/VideoEncoderAV1.h>
#include <public/include/components/VideoEncoderHEVC.h>
#include <public/include/components/VideoEncoderVCE.h>
#include <public/include/core/Platform.h>
#include <stdio.h>
#include <cstring>
#include <iostream>
#include <math.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "AMFENC"
#include "log.h"
#define AMF_FACILITY L"AMFEncoder"
#define MILLISEC_TIME 10000
namespace {
#define AMF_CHECK_RETURN(res, msg) \
if (res != AMF_OK) { \
LOG_ERROR(std::string(msg) + ", result code: " + std::to_string(int(res))); \
return res; \
}
/** Encoder output packet */
struct encoder_packet {
uint8_t *data; /**< Packet data */
size_t size; /**< Packet size */
int64_t pts; /**< Presentation timestamp */
int64_t dts; /**< Decode timestamp */
int32_t timebase_num; /**< Timebase numerator */
int32_t timebase_den; /**< Timebase denominator */
bool keyframe; /**< Is a keyframe */
/* ---------------------------------------------------------------- */
/* Internal video variables (will be parsed automatically) */
/* DTS in microseconds */
int64_t dts_usec;
/* System DTS in microseconds */
int64_t sys_dts_usec;
};
class AMFEncoder {
public:
DataFormat dataFormat_;
amf::AMFComponentPtr AMFEncoder_ = NULL;
amf::AMFContextPtr AMFContext_ = NULL;
private:
// system
void *handle_;
// AMF Internals
AMFFactoryHelper AMFFactory_;
amf::AMF_MEMORY_TYPE AMFMemoryType_;
amf::AMF_SURFACE_FORMAT AMFSurfaceFormat_ = amf::AMF_SURFACE_BGRA;
std::pair<int32_t, int32_t> resolution_;
amf_wstring codec_;
// const
AMF_COLOR_BIT_DEPTH_ENUM eDepth_ = AMF_COLOR_BIT_DEPTH_8;
int query_timeout_ = ENCODE_TIMEOUT_MS;
int32_t bitRateIn_;
int32_t frameRate_;
int32_t gop_;
bool enable4K_ = false;
bool full_range_ = false;
bool bt709_ = false;
// Buffers
std::vector<uint8_t> packetDataBuffer_;
public:
AMFEncoder(void *handle, amf::AMF_MEMORY_TYPE memoryType, amf_wstring codec,
DataFormat dataFormat, int32_t width, int32_t height,
int32_t bitrate, int32_t framerate, int32_t gop) {
handle_ = handle;
dataFormat_ = dataFormat;
AMFMemoryType_ = memoryType;
resolution_ = std::make_pair(width, height);
codec_ = codec;
bitRateIn_ = bitrate;
frameRate_ = framerate;
gop_ = (gop > 0 && gop < MAX_GOP) ? gop : MAX_GOP;
enable4K_ = width > 1920 && height > 1080;
}
~AMFEncoder() {}
AMF_RESULT encode(void *tex, EncodeCallback callback, void *obj, int64_t ms) {
amf::AMFSurfacePtr surface = NULL;
amf::AMFComputeSyncPointPtr pSyncPoint = NULL;
AMF_RESULT res;
bool encoded = false;
switch (AMFMemoryType_) {
case amf::AMF_MEMORY_DX11:
// https://github.com/GPUOpen-LibrariesAndSDKs/AMF/issues/280
// AMF will not copy the surface during the CreateSurfaceFromDX11Native
// call
res = AMFContext_->CreateSurfaceFromDX11Native(tex, &surface, NULL);
AMF_CHECK_RETURN(res, "CreateSurfaceFromDX11Native failed");
{
amf::AMFDataPtr data1;
surface->Duplicate(surface->GetMemoryType(), &data1);
surface = amf::AMFSurfacePtr(data1);
}
break;
default:
LOG_ERROR(std::string("Unsupported memory type"));
return AMF_NOT_IMPLEMENTED;
break;
}
surface->SetPts(ms * AMF_MILLISECOND);
res = AMFEncoder_->SubmitInput(surface);
AMF_CHECK_RETURN(res, "SubmitInput failed");
amf::AMFDataPtr data = NULL;
res = AMFEncoder_->QueryOutput(&data);
if (res == AMF_OK && data != NULL) {
struct encoder_packet packet;
PacketKeyframe(data, &packet);
amf::AMFBufferPtr pBuffer = amf::AMFBufferPtr(data);
packet.size = pBuffer->GetSize();
if (packet.size > 0) {
if (packetDataBuffer_.size() < packet.size) {
size_t newBufferSize = (size_t)exp2(ceil(log2((double)packet.size)));
packetDataBuffer_.resize(newBufferSize);
}
packet.data = packetDataBuffer_.data();
std::memcpy(packet.data, pBuffer->GetNative(), packet.size);
if (callback)
callback(packet.data, packet.size, packet.keyframe, obj, ms);
encoded = true;
}
pBuffer = NULL;
}
data = NULL;
pSyncPoint = NULL;
surface = NULL;
return encoded ? AMF_OK : AMF_FAIL;
}
AMF_RESULT destroy() {
if (AMFEncoder_) {
AMFEncoder_->Terminate();
AMFEncoder_ = NULL;
}
if (AMFContext_) {
AMFContext_->Terminate();
AMFContext_ = NULL; // AMFContext_ is the last
}
AMFFactory_.Terminate();
return AMF_OK;
}
AMF_RESULT test() {
AMF_RESULT res = AMF_OK;
amf::AMFSurfacePtr surface = nullptr;
res = AMFContext_->AllocSurface(AMFMemoryType_, AMFSurfaceFormat_,
resolution_.first, resolution_.second,
&surface);
AMF_CHECK_RETURN(res, "AllocSurface failed");
if (surface->GetPlanesCount() < 1)
return AMF_FAIL;
void *native = surface->GetPlaneAt(0)->GetNative();
if (!native)
return AMF_FAIL;
int32_t key_obj = 0;
auto start = util::now();
res = encode(native, util_encode::vram_encode_test_callback, &key_obj, 0);
int64_t elapsed = util::elapsed_ms(start);
if (res == AMF_OK && key_obj == 1 && elapsed < TEST_TIMEOUT_MS) {
return AMF_OK;
}
return AMF_FAIL;
}
AMF_RESULT initialize() {
AMF_RESULT res;
res = AMFFactory_.Init();
if (res != AMF_OK) {
std::cerr << "AMF init failed, error code = " << res << "\n";
return res;
}
amf::AMFSetCustomTracer(AMFFactory_.GetTrace());
amf::AMFTraceEnableWriter(AMF_TRACE_WRITER_CONSOLE, true);
amf::AMFTraceSetWriterLevel(AMF_TRACE_WRITER_CONSOLE, AMF_TRACE_WARNING);
// AMFContext_
res = AMFFactory_.GetFactory()->CreateContext(&AMFContext_);
AMF_CHECK_RETURN(res, "CreateContext failed");
switch (AMFMemoryType_) {
case amf::AMF_MEMORY_DX11:
res = AMFContext_->InitDX11(handle_); // can be DX11 device
AMF_CHECK_RETURN(res, "InitDX11 failed");
break;
default:
LOG_ERROR(std::string("unsupported amf memory type"));
return AMF_FAIL;
}
// component: encoder
res = AMFFactory_.GetFactory()->CreateComponent(AMFContext_, codec_.c_str(),
&AMFEncoder_);
AMF_CHECK_RETURN(res, "CreateComponent failed");
res = SetParams(codec_);
AMF_CHECK_RETURN(res, "Could not set params in encoder.");
res = AMFEncoder_->Init(AMFSurfaceFormat_, resolution_.first,
resolution_.second);
AMF_CHECK_RETURN(res, "encoder->Init() failed");
return AMF_OK;
}
private:
AMF_RESULT SetParams(const amf_wstring &codecStr) {
AMF_RESULT res;
if (codecStr == amf_wstring(AMFVideoEncoderVCE_AVC)) {
// ------------- Encoder params usage---------------
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_USAGE,
AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_USAGE failed");
// ------------- Encoder params static---------------
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_FRAMESIZE,
::AMFConstructSize(resolution_.first, resolution_.second));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_FRAMESIZE failed, (" +
std::to_string(resolution_.first) + "," +
std::to_string(resolution_.second) + ")");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_LOWLATENCY_MODE, true);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_LOWLATENCY_MODE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_QUALITY_PRESET,
AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_QUALITY_PRESET failed");
res =
AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH, eDepth_);
AMF_CHECK_RETURN(res,
"SetProperty(AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD,
AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD");
if (enable4K_) {
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_PROFILE,
AMF_VIDEO_ENCODER_PROFILE_HIGH);
AMF_CHECK_RETURN(res, "SetProperty(AMF_VIDEO_ENCODER_PROFILE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_PROFILE_LEVEL,
AMF_H264_LEVEL__5_1);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_PROFILE_LEVEL failed");
}
// color
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_FULL_RANGE_COLOR,
full_range_);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_FULL_RANGE_COLOR");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE,
bt709_ ? (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_709)
: (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_601));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE");
// https://github.com/obsproject/obs-studio/blob/e27b013d4754e0e81119ab237ffedce8fcebcbbf/plugins/obs-ffmpeg/texture-amf.cpp#L924
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC,
bt709_ ? AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709
: AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES,
bt709_ ? AMF_COLOR_PRIMARIES_BT709 : AMF_COLOR_PRIMARIES_SMPTE170M);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES");
// ------------- Encoder params dynamic ---------------
AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_B_PIC_PATTERN, 0);
// do not check error for AMF_VIDEO_ENCODER_B_PIC_PATTERN
// - can be not supported - check Capability Manager
// sample
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_QUERY_TIMEOUT,
query_timeout_); // ms
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_QUERY_TIMEOUT failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE,
bitRateIn_);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_TARGET_BITRATE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_FRAMERATE,
::AMFConstructRate(frameRate_, 1));
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_FRAMERATE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_IDR_PERIOD, gop_);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_IDR_PERIOD failed");
} else if (codecStr == amf_wstring(AMFVideoEncoder_HEVC)) {
// ------------- Encoder params usage---------------
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_HEVC_USAGE,
AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY);
AMF_CHECK_RETURN(res, "SetProperty AMF_VIDEO_ENCODER_HEVC_USAGE failed");
// ------------- Encoder params static---------------
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_HEVC_FRAMESIZE,
::AMFConstructSize(resolution_.first, resolution_.second));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_FRAMESIZE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_LOWLATENCY_MODE,
true);
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_LOWLATENCY_MODE failed");
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET,
AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH,
eDepth_);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH failed");
res = AMFEncoder_->SetProperty(
AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD,
AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD failed");
if (enable4K_) {
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_TIER,
AMF_VIDEO_ENCODER_HEVC_TIER_HIGH);
AMF_CHECK_RETURN(res, "SetProperty(AMF_VIDEO_ENCODER_HEVC_TIER failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_PROFILE_LEVEL,
AMF_LEVEL_5_1);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_PROFILE_LEVEL failed");
}
// color
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE,
full_range_ ? AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_FULL
: AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_STUDIO);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE failed");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE,
bt709_ ? (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_709)
: (full_range_ ? AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601
: AMF_VIDEO_CONVERTER_COLOR_PROFILE_601));
AMF_CHECK_RETURN(
res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE failed");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC,
bt709_ ? AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709
: AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M);
AMF_CHECK_RETURN(
res, "SetProperty "
"AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC failed");
res = AMFEncoder_->SetProperty<amf_int64>(
AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES,
bt709_ ? AMF_COLOR_PRIMARIES_BT709 : AMF_COLOR_PRIMARIES_SMPTE170M);
AMF_CHECK_RETURN(
res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES failed");
// ------------- Encoder params dynamic ---------------
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT,
query_timeout_); // ms
AMF_CHECK_RETURN(
res, "SetProperty(AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE,
bitRateIn_);
AMF_CHECK_RETURN(
res, "SetProperty AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_FRAMERATE,
::AMFConstructRate(frameRate_, 1));
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_FRAMERATE failed");
res = AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_GOP_SIZE,
gop_); // todo
AMF_CHECK_RETURN(res,
"SetProperty AMF_VIDEO_ENCODER_HEVC_GOP_SIZE failed");
} else {
return AMF_FAIL;
}
return AMF_OK;
}
void PacketKeyframe(amf::AMFDataPtr &pData, struct encoder_packet *packet) {
if (AMFVideoEncoderVCE_AVC == codec_) {
uint64_t pktType;
pData->GetProperty(AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE, &pktType);
packet->keyframe = AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR == pktType ||
AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_I == pktType;
} else if (AMFVideoEncoder_HEVC == codec_) {
uint64_t pktType;
pData->GetProperty(AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE, &pktType);
packet->keyframe =
AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_IDR == pktType ||
AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_I == pktType;
}
}
};
bool convert_codec(DataFormat lhs, amf_wstring &rhs) {
switch (lhs) {
case H264:
rhs = AMFVideoEncoderVCE_AVC;
break;
case H265:
rhs = AMFVideoEncoder_HEVC;
break;
default:
LOG_ERROR(std::string("unsupported codec: ") + std::to_string((int)lhs));
return false;
}
return true;
}
} // namespace
#include "amf_common.cpp"
extern "C" {
int amf_destroy_encoder(void *encoder) {
try {
AMFEncoder *enc = (AMFEncoder *)encoder;
enc->destroy();
delete enc;
enc = NULL;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("destroy failed: ") + e.what());
}
return -1;
}
void *amf_new_encoder(void *handle, int64_t luid,
DataFormat dataFormat, int32_t width, int32_t height,
int32_t kbs, int32_t framerate, int32_t gop) {
AMFEncoder *enc = NULL;
try {
amf_wstring codecStr;
if (!convert_codec(dataFormat, codecStr)) {
return NULL;
}
amf::AMF_MEMORY_TYPE memoryType;
if (!convert_api(memoryType)) {
return NULL;
}
enc = new AMFEncoder(handle, memoryType, codecStr, dataFormat, width,
height, kbs * 1000, framerate, gop);
if (enc) {
if (AMF_OK == enc->initialize()) {
return enc;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new failed: ") + e.what());
}
if (enc) {
enc->destroy();
delete enc;
enc = NULL;
}
return NULL;
}
int amf_encode(void *encoder, void *tex, EncodeCallback callback, void *obj,
int64_t ms) {
try {
AMFEncoder *enc = (AMFEncoder *)encoder;
return -enc->encode(tex, callback, obj, ms);
} catch (const std::exception &e) {
LOG_ERROR(std::string("encode failed: ") + e.what());
}
return -1;
}
int amf_driver_support() {
try {
AMFFactoryHelper factory;
AMF_RESULT res = factory.Init();
if (res == AMF_OK) {
factory.Terminate();
return 0;
}
} catch (const std::exception &e) {
}
return -1;
}
int amf_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
DataFormat dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_AMD))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
AMFEncoder *e = (AMFEncoder *)amf_new_encoder(
(void *)adapter.get()->device_.Get(), currentLuid,
dataFormat, width, height, kbs, framerate, gop);
if (!e)
continue;
if (e->test() == AMF_OK) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_AMD;
count += 1;
}
e->destroy();
delete e;
e = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test ") + std::to_string(kbs) + " failed: " + e.what());
}
return -1;
}
int amf_set_bitrate(void *encoder, int32_t kbs) {
try {
AMFEncoder *enc = (AMFEncoder *)encoder;
AMF_RESULT res = AMF_FAIL;
switch (enc->dataFormat_) {
case H264:
res = enc->AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE,
kbs * 1000);
break;
case H265:
res = enc->AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE,
kbs * 1000);
break;
}
return res == AMF_OK ? 0 : -1;
} catch (const std::exception &e) {
LOG_ERROR(std::string("set bitrate to ") + std::to_string(kbs) +
"k failed: " + e.what());
}
return -1;
}
int amf_set_framerate(void *encoder, int32_t framerate) {
try {
AMFEncoder *enc = (AMFEncoder *)encoder;
AMF_RESULT res = AMF_FAIL;
AMFRate rate = ::AMFConstructRate(framerate, 1);
switch (enc->dataFormat_) {
case H264:
res = enc->AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_FRAMERATE, rate);
break;
case H265:
res =
enc->AMFEncoder_->SetProperty(AMF_VIDEO_ENCODER_HEVC_FRAMERATE, rate);
break;
}
return res == AMF_OK ? 0 : -1;
} catch (const std::exception &e) {
LOG_ERROR(std::string("set framerate to ") + std::to_string(framerate) +
" failed: " + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -0,0 +1,39 @@
#ifndef AMF_FFI_H
#define AMF_FFI_H
#include "../common/callback.h"
#include <stdbool.h>
int amf_driver_support();
void *amf_new_encoder(void *handle, int64_t luid,
int32_t data_format, int32_t width, int32_t height,
int32_t bitrate, int32_t framerate, int32_t gop);
int amf_encode(void *encoder, void *texture, EncodeCallback callback, void *obj,
int64_t ms);
int amf_destroy_encoder(void *encoder);
void *amf_new_decoder(void *device, int64_t luid,
int32_t dataFormat);
int amf_decode(void *decoder, uint8_t *data, int32_t length,
DecodeCallback callback, void *obj);
int amf_destroy_decoder(void *decoder);
int amf_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int amf_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, uint8_t *data,
int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int amf_set_bitrate(void *encoder, int32_t kbs);
int amf_set_framerate(void *encoder, int32_t framerate);
#endif // AMF_FFI_H

View File

@@ -0,0 +1,11 @@
#ifndef CALLBACK_H
#define CALLBACK_H
#include <stdint.h>
typedef void (*EncodeCallback)(const uint8_t *data, int32_t len, int32_t key,
const void *obj, int64_t pts);
typedef void (*DecodeCallback)(void *opaque, const void *obj);
#endif // CALLBACK_H

View File

@@ -0,0 +1,57 @@
#ifndef COMMON_H
#define COMMON_H
#include <stdint.h>
#define MAX_GOP 0x7FFFFFFF // i32 max
#define TEST_TIMEOUT_MS 1000
#define ENCODE_TIMEOUT_MS 1000
#define DECODE_TIMEOUT_MS 1000
enum AdapterVendor {
ADAPTER_VENDOR_AMD = 0x1002,
ADAPTER_VENDOR_INTEL = 0x8086,
ADAPTER_VENDOR_NVIDIA = 0x10DE,
ADAPTER_VENDOR_UNKNOWN = 0,
};
enum SurfaceFormat {
SURFACE_FORMAT_BGRA,
SURFACE_FORMAT_RGBA,
SURFACE_FORMAT_NV12,
};
enum DataFormat {
H264,
H265,
VP8,
VP9,
AV1,
MJPEG,
};
// same as Driver
enum Vendor {
VENDOR_NV = 0,
VENDOR_AMD = 1,
VENDOR_INTEL = 2,
VENDOR_FFMPEG = 3
};
enum Quality { Quality_Default, Quality_High, Quality_Medium, Quality_Low };
enum RateControl {
RC_DEFAULT,
RC_CBR,
RC_VBR,
RC_CQ,
};
enum HwcodecErrno {
HWCODEC_SUCCESS = 0,
HWCODEC_ERR_COMMON = -1,
HWCODEC_ERR_HEVC_COULD_NOT_FIND_POC = -2,
};
#endif // COMMON_H

View File

@@ -0,0 +1,29 @@
#ifndef FFMPEG_H
#define FFMPEG_H
#define AV_LOG_QUIET -8
#define AV_LOG_PANIC 0
#define AV_LOG_FATAL 8
#define AV_LOG_ERROR 16
#define AV_LOG_WARNING 24
#define AV_LOG_INFO 32
#define AV_LOG_VERBOSE 40
#define AV_LOG_DEBUG 48
#define AV_LOG_TRACE 56
enum AVPixelFormat {
AV_PIX_FMT_YUV420P = 0,
AV_PIX_FMT_YUYV422 = 1,
AV_PIX_FMT_YUV422P = 4, // planar YUV 4:2:2
AV_PIX_FMT_YUVJ420P = 12, // JPEG full-range YUV420P (same layout as YUV420P)
AV_PIX_FMT_YUVJ422P = 13, // JPEG full-range YUV422P (same layout as YUV422P)
AV_PIX_FMT_NV12 = 23,
AV_PIX_FMT_NV21 = 24,
};
int av_log_get_level(void);
void av_log_set_level(int level);
void hwcodec_set_av_log_callback();
void hwcodec_set_flag_could_not_find_ref_with_poc();
#endif

View File

@@ -0,0 +1,54 @@
#include "log.h"
extern "C" {
#include <libavutil/log.h>
}
namespace gol {
enum {
LOG_LEVEL_ERROR = 0,
LOG_LEVEL_WARN = 1,
LOG_LEVEL_INFO = 2,
LOG_LEVEL_DEBUG = 3,
LOG_LEVEL_TRACE = 4,
};
extern "C" void hwcodec_log(int level, const char *message);
extern "C" void hwcodec_av_log_callback(int level, const char *message);
void log_to_rust(int level, const std::string &message) {
const char *cstr = message.c_str();
hwcodec_log(level, cstr);
}
void error(const std::string &message) {
log_to_rust(LOG_LEVEL_ERROR, message);
}
void warn(const std::string &message) { log_to_rust(LOG_LEVEL_WARN, message); }
void info(const std::string &message) { log_to_rust(LOG_LEVEL_INFO, message); }
void debug(const std::string &message) {
log_to_rust(LOG_LEVEL_DEBUG, message);
}
void trace(const std::string &message) {
log_to_rust(LOG_LEVEL_TRACE, message);
}
void av_log_callback(void *ptr, int level, const char *fmt, va_list vl) {
(void)ptr;
if (level > av_log_get_level()) {
return;
}
char line[1024] = {0};
vsnprintf(line, sizeof(line), fmt, vl);
hwcodec_av_log_callback(level, line);
};
} // namespace gol
extern "C" void hwcodec_set_av_log_callback() {
av_log_set_callback(gol::av_log_callback);
}

View File

@@ -0,0 +1,66 @@
#ifndef LOG_H
#define LOG_H
extern "C" {
#include <libavutil/attributes.h>
#include <libavutil/error.h>
}
#include <sstream>
#include <string>
#ifndef LOG_MODULE
#define LOG_MODULE "*"
#endif
namespace gol {
void error(const std::string &message);
void warn(const std::string &message);
void info(const std::string &message);
void debug(const std::string &message);
void trace(const std::string &message);
} // namespace gol
#define LOG_ERROR(message) \
gol::error(std::string("[") + LOG_MODULE + "] " + message)
#define LOG_WARN(message) \
gol::warn(std::string("[") + LOG_MODULE + "] " + message)
#define LOG_INFO(message) \
gol::info(std::string("[") + LOG_MODULE + "] " + message)
#define LOG_DEBUG(message) \
gol::debug(std::string("[") + LOG_MODULE + "] " + message)
#define LOG_TRACE(message) \
gol::trace(std::string("[") + LOG_MODULE + "] " + message)
// https://github.com/joncampbell123/composite-video-simulator/issues/5#issuecomment-611885908
#ifdef av_err2str
#undef av_err2str
av_always_inline std::string av_err2string(int errnum) {
char str[AV_ERROR_MAX_STRING_SIZE];
return av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, errnum);
}
#define av_err2str(err) av_err2string(err).c_str()
#endif // av_err2str
#ifdef _WIN32
#define HRB(f) MS_CHECK(f, return false;)
#define HRI(f) MS_CHECK(f, return -1;)
#define HRP(f) MS_CHECK(f, return nullptr;)
#define MS_CHECK(f, ...) \
do { \
HRESULT __ms_hr__ = (f); \
if (FAILED(__ms_hr__)) { \
std::stringstream ss; \
ss << "ERROR@" << __FILE__ << ":" << __LINE__ << " " << __FUNCTION__ \
<< " hr=0x" << std::hex << __ms_hr__ << std::dec << " " \
<< std::error_code(__ms_hr__, std::system_category()).message(); \
std::string result = ss.str(); \
LOG_ERROR(result); \
__VA_ARGS__ \
} \
} while (false)
#endif
#endif

View File

@@ -0,0 +1,163 @@
#include "linux.h"
#include "../../log.h"
#include <cstring>
#include <dlfcn.h>
#include <dynlink_cuda.h>
#include <dynlink_loader.h>
#include <errno.h>
#include <exception> // Include the necessary header file
#include <signal.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <fcntl.h>
namespace
{
void load_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl,
CuvidFunctions **pp_cvdl)
{
if (cuda_load_functions(pp_cuda_dl, NULL) < 0)
{
LOG_TRACE(std::string("cuda_load_functions failed"));
throw "cuda_load_functions failed";
}
if (nvenc_load_functions(pp_nvenc_dl, NULL) < 0)
{
LOG_TRACE(std::string("nvenc_load_functions failed"));
throw "nvenc_load_functions failed";
}
if (cuvid_load_functions(pp_cvdl, NULL) < 0)
{
LOG_TRACE(std::string("cuvid_load_functions failed"));
throw "cuvid_load_functions failed";
}
}
void free_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl,
CuvidFunctions **pp_cvdl)
{
if (*pp_cvdl)
{
cuvid_free_functions(pp_cvdl);
*pp_cvdl = NULL;
}
if (*pp_nvenc_dl)
{
nvenc_free_functions(pp_nvenc_dl);
*pp_nvenc_dl = NULL;
}
if (*pp_cuda_dl)
{
cuda_free_functions(pp_cuda_dl);
*pp_cuda_dl = NULL;
}
}
} // namespace
int linux_support_nv()
{
try
{
CudaFunctions *cuda_dl = NULL;
NvencFunctions *nvenc_dl = NULL;
CuvidFunctions *cvdl = NULL;
load_driver(&cuda_dl, &nvenc_dl, &cvdl);
free_driver(&cuda_dl, &nvenc_dl, &cvdl);
return 0;
}
catch (...)
{
LOG_TRACE(std::string("nvidia driver not support"));
}
return -1;
}
int linux_support_amd()
{
#if defined(__x86_64__) || defined(__aarch64__)
#define AMF_DLL_NAME L"libamfrt64.so.1"
#define AMF_DLL_NAMEA "libamfrt64.so.1"
#else
#define AMF_DLL_NAME L"libamfrt32.so.1"
#define AMF_DLL_NAMEA "libamfrt32.so.1"
#endif
void *handle = dlopen(AMF_DLL_NAMEA, RTLD_LAZY);
if (!handle)
{
return -1;
}
dlclose(handle);
return 0;
}
int linux_support_intel()
{
const char *libs[] =
{"libvpl.so", "libmfx.so", "libmfx-gen.so.1.2", "libmfxhw64.so.1"};
for (size_t i = 0; i < sizeof(libs) / sizeof(libs[0]); i++)
{
void *handle = dlopen(libs[i], RTLD_LAZY);
if (handle)
{
dlclose(handle);
return 0;
}
}
return -1;
}
int setup_parent_death_signal() {
// Set up parent death signal to ensure this process dies if parent dies
// This prevents orphaned processes especially when running with different
// user permissions
int ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
if (ret != 0) {
LOG_ERROR(std::string("Failed to set parent death signal:") + std::to_string(errno));
return -1;
} else {
return 0;
}
}
// Check for Rockchip MPP (Media Process Platform) support
// Returns 0 if supported, -1 otherwise
int linux_support_rkmpp() {
// Check for MPP service device (primary method)
if (access("/dev/mpp_service", F_OK) == 0) {
LOG_TRACE(std::string("RKMPP: Found /dev/mpp_service"));
return 0;
}
// Fallback: check for RGA (Rockchip Graphics Acceleration) device
if (access("/dev/rga", F_OK) == 0) {
LOG_TRACE(std::string("RKMPP: Found /dev/rga"));
return 0;
}
LOG_TRACE(std::string("RKMPP: No Rockchip MPP device found"));
return -1;
}
// Check for V4L2 Memory-to-Memory (M2M) codec support
// Returns 0 if a M2M capable device is found, -1 otherwise
int linux_support_v4l2m2m() {
// Check common V4L2 M2M device paths used by various ARM SoCs
const char *m2m_devices[] = {
"/dev/video10", // Common M2M encoder device
"/dev/video11", // Common M2M decoder device
"/dev/video0", // Some SoCs use video0 for M2M
};
for (size_t i = 0; i < sizeof(m2m_devices) / sizeof(m2m_devices[0]); i++) {
if (access(m2m_devices[i], F_OK) == 0) {
// Device exists, check if it's an M2M device by trying to open it
int fd = open(m2m_devices[i], O_RDWR | O_NONBLOCK);
if (fd >= 0) {
close(fd);
LOG_TRACE(std::string("V4L2 M2M: Found device ") + m2m_devices[i]);
return 0;
}
}
}
LOG_TRACE(std::string("V4L2 M2M: No M2M device found"));
return -1;
}

View File

@@ -0,0 +1,11 @@
#ifndef LINUX_H
#define LINUX_H
extern "C" int linux_support_nv();
extern "C" int linux_support_amd();
extern "C" int linux_support_intel();
extern "C" int linux_support_rkmpp();
extern "C" int linux_support_v4l2m2m();
extern "C" int setup_parent_death_signal();
#endif

View File

@@ -0,0 +1,167 @@
#include <AVFoundation/AVFoundation.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreMedia/CoreMedia.h>
#include <MacTypes.h>
#include <VideoToolbox/VideoToolbox.h>
#include <cstdlib>
#include <pthread.h>
#include <ratio>
#include <sys/_types/_int32_t.h>
#include <sys/event.h>
#include <unistd.h>
#include "../../log.h"
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
// ---------------------- Core: More Robust Hardware Encoder Detection ----------------------
static int32_t hasHardwareEncoder(bool h265) {
CMVideoCodecType codecType = h265 ? kCMVideoCodecType_HEVC : kCMVideoCodecType_H264;
// ---------- Path A: Quick Query with Enable + Require ----------
// Note: Require implies Enable, but setting both here makes it easier to bypass the strategy on some models that default to a software encoder.
CFMutableDictionaryRef spec = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
CFDictionaryRef properties = NULL;
CFStringRef outID = NULL;
// Use 1280x720 for capability detection to reduce the probability of "no hardware encoding" due to resolution/level issues.
OSStatus result = VTCopySupportedPropertyDictionaryForEncoder(1280, 720, codecType, spec, &outID, &properties);
if (properties) CFRelease(properties);
if (outID) CFRelease(outID);
if (spec) CFRelease(spec);
if (result == noErr) {
// Explicitly found an encoder that meets the "hardware-only" specification.
return 1;
}
// Reaching here means either no encoder satisfying Require was found (common), or another error occurred.
// For all failure cases, continue with the safer "session-level confirmation" path to avoid misjudgment.
// ---------- Path B: Create Session and Read UsingHardwareAcceleratedVideoEncoder ----------
CFMutableDictionaryRef enableOnly = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(enableOnly, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
VTCompressionSessionRef session = NULL;
// Also use 1280x720 to reduce profile/level interference
OSStatus st = VTCompressionSessionCreate(kCFAllocatorDefault,
1280, 720, codecType,
enableOnly, /* encoderSpecification */
NULL, /* sourceImageBufferAttributes */
NULL, /* compressedDataAllocator */
NULL, /* outputCallback */
NULL, /* outputRefCon */
&session);
if (enableOnly) CFRelease(enableOnly);
if (st != noErr || !session) {
// Creation failed, considered no hardware available.
return 0;
}
// First, explicitly prepare the encoding process to give VideoToolbox a chance to choose between software/hardware.
OSStatus prepareStatus = VTCompressionSessionPrepareToEncodeFrames(session);
if (prepareStatus != noErr) {
VTCompressionSessionInvalidate(session);
CFRelease(session);
return 0;
}
// Query the session's read-only property: whether it is using a hardware encoder.
CFBooleanRef usingHW = NULL;
st = VTSessionCopyProperty(session,
kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder,
kCFAllocatorDefault,
(void **)&usingHW);
Boolean isHW = (st == noErr && usingHW && CFBooleanGetValue(usingHW));
if (usingHW) CFRelease(usingHW);
VTCompressionSessionInvalidate(session);
CFRelease(session);
return isHW ? 1 : 0;
}
// -------------- Your Public Interface: Unchanged ------------------
extern "C" void checkVideoToolboxSupport(int32_t *h264Encoder, int32_t *h265Encoder, int32_t *h264Decoder, int32_t *h265Decoder) {
// https://stackoverflow.com/questions/50956097/determine-if-ios-device-can-support-hevc-encoding
*h264Encoder = 0; // H.264 encoder support is disabled due to frequent reliability issues (see encode.rs)
*h265Encoder = hasHardwareEncoder(true);
*h264Decoder = VTIsHardwareDecodeSupported(kCMVideoCodecType_H264);
*h265Decoder = VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC);
return;
}
extern "C" uint64_t GetHwcodecGpuSignature() {
int32_t h264Encoder = 0;
int32_t h265Encoder = 0;
int32_t h264Decoder = 0;
int32_t h265Decoder = 0;
checkVideoToolboxSupport(&h264Encoder, &h265Encoder, &h264Decoder, &h265Decoder);
return (uint64_t)h264Encoder << 24 | (uint64_t)h265Encoder << 16 | (uint64_t)h264Decoder << 8 | (uint64_t)h265Decoder;
}
static void *parent_death_monitor_thread(void *arg) {
int kq = (intptr_t)arg;
struct kevent events[1];
int ret = kevent(kq, NULL, 0, events, 1, NULL);
if (ret > 0) {
// Parent process died, terminate this process
LOG_INFO("Parent process died, terminating hwcodec check process");
exit(1);
}
return NULL;
}
extern "C" int setup_parent_death_signal() {
// On macOS, use kqueue to monitor parent process death
pid_t parent_pid = getppid();
int kq = kqueue();
if (kq == -1) {
LOG_DEBUG("Failed to create kqueue for parent monitoring");
return -1;
}
struct kevent event;
EV_SET(&event, parent_pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0,
NULL);
int ret = kevent(kq, &event, 1, NULL, 0, NULL);
if (ret == -1) {
LOG_ERROR("Failed to register parent death monitoring on macOS\n");
close(kq);
return -1;
} else {
// Spawn a thread to monitor parent death
pthread_t monitor_thread;
ret = pthread_create(&monitor_thread, NULL, parent_death_monitor_thread,
(void *)(intptr_t)kq);
if (ret != 0) {
LOG_ERROR("Failed to create parent death monitor thread");
close(kq);
return -1;
}
// Detach the thread so it can run independently
pthread_detach(monitor_thread);
return 0;
}
}

View File

@@ -0,0 +1,187 @@
#include <atomic>
#include <chrono>
#include <cstdio>
#include <list>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <d3d11.h>
#include <dxgi.h>
#include <wrl/client.h>
#include "../../common.h"
#include "win.h"
#define IF_FAILED_THROW(X) \
if (FAILED(hr = (X))) { \
throw hr; \
}
using Microsoft::WRL::ComPtr;
static HRESULT CreateBmpFile(LPCWSTR wszBmpFile, BYTE *pData,
const UINT uiFrameSize, const UINT uiWidth,
const UINT uiHeight) {
HRESULT hr = S_OK;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwWritten;
UINT uiStride;
BYTE header24[54] = {0x42, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
DWORD dwSizeFile = uiWidth * uiHeight * 3;
dwSizeFile += 54;
header24[2] = dwSizeFile & 0x000000ff;
header24[3] = static_cast<BYTE>((dwSizeFile & 0x0000ff00) >> 8);
header24[4] = static_cast<BYTE>((dwSizeFile & 0x00ff0000) >> 16);
header24[5] = (dwSizeFile & 0xff000000) >> 24;
dwSizeFile -= 54;
header24[18] = uiWidth & 0x000000ff;
header24[19] = (uiWidth & 0x0000ff00) >> 8;
header24[20] = static_cast<BYTE>((uiWidth & 0x00ff0000) >> 16);
header24[21] = (uiWidth & 0xff000000) >> 24;
header24[22] = uiHeight & 0x000000ff;
header24[23] = (uiHeight & 0x0000ff00) >> 8;
header24[24] = static_cast<BYTE>((uiHeight & 0x00ff0000) >> 16);
header24[25] = (uiHeight & 0xff000000) >> 24;
header24[34] = dwSizeFile & 0x000000ff;
header24[35] = (dwSizeFile & 0x0000ff00) >> 8;
header24[36] = static_cast<BYTE>((dwSizeFile & 0x00ff0000) >> 16);
header24[37] = static_cast<BYTE>((dwSizeFile & 0xff000000) >> 24);
try {
hFile = CreateFileW(wszBmpFile, GENERIC_WRITE, FILE_SHARE_READ, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
IF_FAILED_THROW(hFile == INVALID_HANDLE_VALUE ? E_FAIL : S_OK);
IF_FAILED_THROW(WriteFile(hFile, (LPCVOID)header24, 54, &dwWritten, 0) ==
FALSE);
IF_FAILED_THROW(dwWritten == 0 ? E_FAIL : S_OK);
uiStride = uiWidth * 3;
BYTE *Tmpbufsrc = pData + (uiFrameSize - uiStride);
for (UINT i = 0; i < uiHeight; i++) {
IF_FAILED_THROW(WriteFile(hFile, (LPCVOID)Tmpbufsrc, uiStride, &dwWritten,
0) == FALSE);
IF_FAILED_THROW(dwWritten == 0 ? E_FAIL : S_OK);
Tmpbufsrc -= uiStride;
}
} catch (HRESULT) {
}
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
return hr;
}
static std::string GetDirectoryFromFilename(const std::string &filename) {
size_t lastSeparator = filename.find_last_of("/\\");
if (lastSeparator != std::string::npos) {
return filename.substr(0, lastSeparator);
}
return "";
}
static bool createBgraBmpFile(ID3D11Device *device, ID3D11Texture2D *texture,
const std::string &filename) {
D3D11_TEXTURE2D_DESC desc = {};
ComPtr<ID3D11DeviceContext> deviceContext;
HRESULT hr;
texture->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.BindFlags = 0;
ComPtr<ID3D11Texture2D> bgraStagingTexture;
hr = device->CreateTexture2D(&desc, nullptr,
bgraStagingTexture.GetAddressOf());
IF_FAILED_THROW(hr);
device->GetImmediateContext(deviceContext.ReleaseAndGetAddressOf());
deviceContext->CopyResource(bgraStagingTexture.Get(), texture);
D3D11_MAPPED_SUBRESOURCE ResourceDesc = {};
deviceContext->Map(bgraStagingTexture.Get(), 0, D3D11_MAP_READ, 0,
&ResourceDesc);
UINT uiImageSize = desc.Width * desc.Height * 3;
BYTE *pDataRgb = new (std::nothrow) BYTE[uiImageSize];
BYTE *pDataRgbaColor = (BYTE *)ResourceDesc.pData;
BYTE *pDataRgbColor = pDataRgb;
for (UINT i = 0; i < desc.Height; i++) {
for (UINT j = 0; j < desc.Width; j++) {
if (desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM) {
// bgr bgra
*pDataRgbColor++ = *pDataRgbaColor++;
*pDataRgbColor++ = *pDataRgbaColor++;
*pDataRgbColor++ = *pDataRgbaColor++;
pDataRgbaColor++;
} else {
// bgr rgba
pDataRgbColor[0] = pDataRgbaColor[2];
pDataRgbColor[1] = pDataRgbaColor[1];
pDataRgbColor[2] = pDataRgbaColor[0];
pDataRgbColor += 3;
pDataRgbaColor += 4;
}
}
}
auto dir = GetDirectoryFromFilename(filename);
DWORD attrib = GetFileAttributesA(dir.c_str());
if (attrib == INVALID_FILE_ATTRIBUTES ||
!(attrib & FILE_ATTRIBUTE_DIRECTORY)) {
if (!CreateDirectoryA(dir.c_str(), NULL)) {
std::cout << "Failed to create directory: " << dir << std::endl;
return false;
} else {
std::cout << "Directory created: " << dir << std::endl;
}
} else {
// already exists
}
int size = MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, nullptr, 0);
wchar_t *wszBmpFile = new wchar_t[size];
MultiByteToWideChar(CP_UTF8, 0, filename.c_str(), -1, wszBmpFile, size);
hr =
CreateBmpFile(wszBmpFile, pDataRgb, uiImageSize, desc.Width, desc.Height);
delete[] pDataRgb;
delete[] wszBmpFile;
IF_FAILED_THROW(hr);
deviceContext->Unmap(bgraStagingTexture.Get(), 0);
}
void SaveBgraBmps(ID3D11Device *device, void *texture, int cycle) {
if (!texture)
return;
static int index = 0;
if (index++ % cycle == 0) {
auto now = std::chrono::system_clock::now();
auto time_t_now = std::chrono::system_clock::to_time_t(now);
std::tm local_tm;
localtime_s(&local_tm, &time_t_now);
char buffer[80];
std::strftime(buffer, 80, "%H_%M_%S", &local_tm);
std::string filename = std::string("bmps") + "/" + std::to_string(index) +
"_" + buffer + ".bmp";
createBgraBmpFile(device, (ID3D11Texture2D *)texture, filename);
}
}

View File

@@ -0,0 +1,58 @@
#include "win.h"
#include <fstream>
bool dumpTexture(ID3D11Device *device, ID3D11Texture2D *texture, int cropW,
int cropH, const string &filename) {
const char *dir = "texture";
DWORD attrib = GetFileAttributesA(dir);
if (attrib == INVALID_FILE_ATTRIBUTES ||
!(attrib & FILE_ATTRIBUTE_DIRECTORY)) {
if (!CreateDirectoryA(dir, NULL)) {
std::cout << "Failed to create directory: " << dir << std::endl;
return false;
} else {
std::cout << "Directory created: " << dir << std::endl;
}
} else {
// already exists
}
D3D11_TEXTURE2D_DESC desc = {};
ComPtr<ID3D11DeviceContext> deviceContext;
HRESULT hr;
texture->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.BindFlags = 0;
ComPtr<ID3D11Texture2D> stagingTexture;
hr = device->CreateTexture2D(&desc, nullptr, stagingTexture.GetAddressOf());
if (FAILED(hr)) {
return false;
}
device->GetImmediateContext(deviceContext.ReleaseAndGetAddressOf());
deviceContext->CopyResource(stagingTexture.Get(), texture);
D3D11_MAPPED_SUBRESOURCE mappedResource = {};
deviceContext->Map(stagingTexture.Get(), 0, D3D11_MAP_READ, 0,
&mappedResource);
string path = string(dir) + "/" + filename;
std::ofstream file(path, std::ios::binary | std::ios::app);
if (desc.Format == DXGI_FORMAT_NV12) {
int Pitch = mappedResource.RowPitch;
uint8_t *Y = (uint8_t *)mappedResource.pData;
uint8_t *U =
(uint8_t *)mappedResource.pData + desc.Height * mappedResource.RowPitch;
uint8_t *V = (desc.Format == DXGI_FORMAT_P010) ? U + 2 : U + 1;
for (int i = 0; i < cropH; i++) {
file.write((const char *)(Y + i * Pitch), cropW);
}
int ChromaH = cropH / 2;
int ChromaW = cropW;
for (int i = 0; i < ChromaH; i++) {
file.write((const char *)(U + i * Pitch), ChromaW);
}
}
deviceContext->Unmap(stagingTexture.Get(), 0);
file.close();
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,919 @@
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
//
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// POSITION 0 xyzw 0 NONE float xyzw
// TEXCOORD 0 xy 1 NONE float xy
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_POSITION 0 xyzw 0 POS float xyzw
// TEXCOORD 0 xy 1 NONE float xy
//
//
// Runtime generated constant mappings:
//
// Target Reg Constant Description
// ---------- --------------------------------------------------
// c0 Vertex Shader position offset
//
//
// Level9 shader bytecode:
//
vs_2_x
dcl_texcoord v0 // input<0,1,2,3>
dcl_texcoord1 v1 // input<4,5>
#line 12 "D:\rustdesk\gpu_video_codec\vs\ShaderCompileTool\nv_vertex_shader.hlsl"
mad oPos.xy, v0.w, c0, v0 // ::VS<0,1>
mov oPos.zw, v0 // ::VS<2,3>
mov oT0.xy, v1 // ::VS<4,5>
// approximately 3 instruction slots used
vs_4_0
dcl_input v0.xyzw
dcl_input v1.xy
dcl_output_siv o0.xyzw, position
dcl_output o1.xy
//
// Initial variable locations:
// v0.x <- input.Pos.x; v0.y <- input.Pos.y; v0.z <- input.Pos.z; v0.w <- input.Pos.w;
// v1.x <- input.Tex.x; v1.y <- input.Tex.y;
// o1.x <- <VS return value>.Tex.x; o1.y <- <VS return value>.Tex.y;
// o0.x <- <VS return value>.Pos.x; o0.y <- <VS return value>.Pos.y; o0.z <- <VS return value>.Pos.z; o0.w <- <VS return value>.Pos.w
//
#line 14 "D:\rustdesk\gpu_video_codec\vs\ShaderCompileTool\nv_vertex_shader.hlsl"
mov o0.xyzw, v0.xyzw
mov o1.xy, v1.xyxx
ret
// Approximately 3 instruction slots used
#endif
const BYTE g_VS[] = {
68, 88, 66, 67, 17, 156, 6, 174, 73, 86, 39, 50, 168, 176, 148,
24, 52, 224, 94, 107, 1, 0, 0, 0, 56, 50, 0, 0, 7, 0,
0, 0, 60, 0, 0, 0, 76, 2, 0, 0, 188, 2, 0, 0, 196,
48, 0, 0, 64, 49, 0, 0, 140, 49, 0, 0, 224, 49, 0, 0,
65, 111, 110, 57, 8, 2, 0, 0, 8, 2, 0, 0, 0, 2, 254,
255, 224, 1, 0, 0, 40, 0, 0, 0, 0, 0, 36, 0, 0, 0,
36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 1, 0, 36, 0, 0,
0, 0, 0, 1, 2, 254, 255, 254, 255, 100, 0, 68, 66, 85, 71,
40, 0, 0, 0, 100, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 112, 0, 0, 0, 5, 0, 0, 0, 116, 0, 0, 0, 2, 0,
0, 0, 60, 1, 0, 0, 156, 0, 0, 0, 68, 58, 92, 114, 117,
115, 116, 100, 101, 115, 107, 92, 103, 112, 117, 95, 118, 105, 100, 101,
111, 95, 99, 111, 100, 101, 99, 92, 118, 115, 92, 83, 104, 97, 100,
101, 114, 67, 111, 109, 112, 105, 108, 101, 84, 111, 111, 108, 92, 110,
118, 95, 118, 101, 114, 116, 101, 120, 95, 115, 104, 97, 100, 101, 114,
46, 104, 108, 115, 108, 0, 171, 40, 0, 0, 0, 0, 0, 255, 255,
152, 1, 0, 0, 0, 0, 255, 255, 164, 1, 0, 0, 12, 0, 0,
0, 176, 1, 0, 0, 12, 0, 0, 0, 196, 1, 0, 0, 14, 0,
0, 0, 208, 1, 0, 0, 86, 83, 0, 80, 111, 115, 0, 171, 1,
0, 3, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0,
84, 101, 120, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0,
0, 0, 0, 0, 0, 159, 0, 0, 0, 164, 0, 0, 0, 180, 0,
0, 0, 184, 0, 0, 0, 5, 0, 0, 0, 1, 0, 6, 0, 1,
0, 2, 0, 200, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0,
255, 255, 255, 255, 3, 0, 0, 0, 255, 255, 255, 255, 2, 0, 3,
0, 4, 0, 0, 0, 4, 0, 5, 0, 255, 255, 255, 255, 105, 110,
112, 117, 116, 0, 171, 171, 5, 0, 0, 0, 1, 0, 6, 0, 1,
0, 2, 0, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
2, 0, 3, 0, 1, 0, 0, 0, 4, 0, 5, 0, 255, 255, 255,
255, 0, 0, 0, 0, 156, 0, 0, 0, 216, 0, 0, 0, 3, 0,
0, 0, 232, 0, 0, 0, 156, 0, 0, 0, 12, 1, 0, 0, 20,
1, 0, 0, 2, 0, 0, 0, 36, 1, 0, 0, 77, 105, 99, 114,
111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32,
83, 104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105, 108, 101, 114,
32, 49, 48, 46, 49, 0, 31, 0, 0, 2, 5, 0, 0, 128, 0,
0, 15, 144, 31, 0, 0, 2, 5, 0, 1, 128, 1, 0, 15, 144,
4, 0, 0, 4, 0, 0, 3, 192, 0, 0, 255, 144, 0, 0, 228,
160, 0, 0, 228, 144, 1, 0, 0, 2, 0, 0, 12, 192, 0, 0,
228, 144, 1, 0, 0, 2, 0, 0, 3, 224, 1, 0, 228, 144, 255,
255, 0, 0, 83, 72, 68, 82, 104, 0, 0, 0, 64, 0, 1, 0,
26, 0, 0, 0, 95, 0, 0, 3, 242, 16, 16, 0, 0, 0, 0,
0, 95, 0, 0, 3, 50, 16, 16, 0, 1, 0, 0, 0, 103, 0,
0, 4, 242, 32, 16, 0, 0, 0, 0, 0, 1, 0, 0, 0, 101,
0, 0, 3, 50, 32, 16, 0, 1, 0, 0, 0, 54, 0, 0, 5,
242, 32, 16, 0, 0, 0, 0, 0, 70, 30, 16, 0, 0, 0, 0,
0, 54, 0, 0, 5, 50, 32, 16, 0, 1, 0, 0, 0, 70, 16,
16, 0, 1, 0, 0, 0, 62, 0, 0, 1, 83, 80, 68, 66, 0,
46, 0, 0, 77, 105, 99, 114, 111, 115, 111, 102, 116, 32, 67, 47,
67, 43, 43, 32, 77, 83, 70, 32, 55, 46, 48, 48, 13, 10, 26,
68, 83, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 23, 0,
0, 0, 132, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 56, 0, 128, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 5, 0, 0, 0, 32, 0,
0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0,
0, 0, 0, 6, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 46,
49, 1, 118, 98, 147, 101, 1, 0, 0, 0, 201, 198, 0, 59, 60,
109, 111, 65, 179, 15, 4, 145, 38, 240, 113, 148, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 81, 51, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68, 51, 68, 83, 72, 68, 82, 0, 104, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 32, 0, 0, 96, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 198, 90, 0, 0, 117, 131, 1, 0, 156, 39, 3, 0, 156,
202, 1, 0, 38, 247, 2, 0, 69, 103, 0, 0, 109, 24, 1, 0,
248, 34, 2, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 115, 116, 114, 117, 99, 116, 32, 86, 83, 95, 73,
78, 80, 85, 84, 13, 10, 123, 13, 10, 32, 32, 32, 32, 102, 108,
111, 97, 116, 52, 32, 80, 111, 115, 32, 58, 32, 80, 79, 83, 73,
84, 73, 79, 78, 59, 13, 10, 32, 32, 32, 32, 102, 108, 111, 97,
116, 50, 32, 84, 101, 120, 32, 58, 32, 84, 69, 88, 67, 79, 79,
82, 68, 59, 13, 10, 125, 59, 13, 10, 13, 10, 115, 116, 114, 117,
99, 116, 32, 86, 83, 95, 79, 85, 84, 80, 85, 84, 13, 10, 123,
13, 10, 32, 32, 32, 32, 102, 108, 111, 97, 116, 52, 32, 80, 111,
115, 32, 58, 32, 83, 86, 95, 80, 79, 83, 73, 84, 73, 79, 78,
59, 13, 10, 32, 32, 32, 32, 102, 108, 111, 97, 116, 50, 32, 84,
101, 120, 32, 58, 32, 84, 69, 88, 67, 79, 79, 82, 68, 59, 13,
10, 125, 59, 13, 10, 86, 83, 95, 79, 85, 84, 80, 85, 84, 32,
86, 83, 40, 86, 83, 95, 73, 78, 80, 85, 84, 32, 105, 110, 112,
117, 116, 41, 13, 10, 123, 13, 10, 32, 32, 32, 32, 114, 101, 116,
117, 114, 110, 32, 105, 110, 112, 117, 116, 59, 13, 10, 125, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 254, 239, 254, 239, 1, 0, 0, 0, 108,
1, 0, 0, 0, 68, 58, 92, 114, 117, 115, 116, 100, 101, 115, 107,
92, 103, 112, 117, 95, 118, 105, 100, 101, 111, 95, 99, 111, 100, 101,
99, 92, 118, 115, 92, 83, 104, 97, 100, 101, 114, 67, 111, 109, 112,
105, 108, 101, 84, 111, 111, 108, 92, 110, 118, 95, 118, 101, 114, 116,
101, 120, 95, 115, 104, 97, 100, 101, 114, 46, 104, 108, 115, 108, 0,
0, 100, 58, 92, 114, 117, 115, 116, 100, 101, 115, 107, 92, 103, 112,
117, 95, 118, 105, 100, 101, 111, 95, 99, 111, 100, 101, 99, 92, 118,
115, 92, 115, 104, 97, 100, 101, 114, 99, 111, 109, 112, 105, 108, 101,
116, 111, 111, 108, 92, 110, 118, 95, 118, 101, 114, 116, 101, 120, 95,
115, 104, 97, 100, 101, 114, 46, 104, 108, 115, 108, 0, 115, 116, 114,
117, 99, 116, 32, 86, 83, 95, 73, 78, 80, 85, 84, 13, 10, 123,
13, 10, 32, 32, 32, 32, 102, 108, 111, 97, 116, 52, 32, 80, 111,
115, 32, 58, 32, 80, 79, 83, 73, 84, 73, 79, 78, 59, 13, 10,
32, 32, 32, 32, 102, 108, 111, 97, 116, 50, 32, 84, 101, 120, 32,
58, 32, 84, 69, 88, 67, 79, 79, 82, 68, 59, 13, 10, 125, 59,
13, 10, 13, 10, 115, 116, 114, 117, 99, 116, 32, 86, 83, 95, 79,
85, 84, 80, 85, 84, 13, 10, 123, 13, 10, 32, 32, 32, 32, 102,
108, 111, 97, 116, 52, 32, 80, 111, 115, 32, 58, 32, 83, 86, 95,
80, 79, 83, 73, 84, 73, 79, 78, 59, 13, 10, 32, 32, 32, 32,
102, 108, 111, 97, 116, 50, 32, 84, 101, 120, 32, 58, 32, 84, 69,
88, 67, 79, 79, 82, 68, 59, 13, 10, 125, 59, 13, 10, 86, 83,
95, 79, 85, 84, 80, 85, 84, 32, 86, 83, 40, 86, 83, 95, 73,
78, 80, 85, 84, 32, 105, 110, 112, 117, 116, 41, 13, 10, 123, 13,
10, 32, 32, 32, 32, 114, 101, 116, 117, 114, 110, 32, 105, 110, 112,
117, 116, 59, 13, 10, 125, 0, 7, 0, 0, 0, 0, 0, 0, 0,
72, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 73, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 27, 226, 48, 1, 128, 0, 0,
0, 11, 23, 208, 112, 24, 61, 218, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0,
0, 0, 73, 0, 0, 0, 40, 0, 0, 0, 27, 226, 48, 1, 78,
31, 42, 3, 219, 0, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0,
73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 66,
0, 60, 17, 16, 1, 0, 0, 0, 1, 10, 0, 1, 0, 243, 2,
93, 88, 10, 0, 1, 0, 243, 2, 93, 88, 77, 105, 99, 114, 111,
115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76, 83, 76, 32, 83,
104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105, 108, 101, 114, 32,
49, 48, 46, 49, 0, 0, 0, 62, 0, 61, 17, 1, 104, 108, 115,
108, 70, 108, 97, 103, 115, 0, 48, 120, 49, 0, 104, 108, 115, 108,
84, 97, 114, 103, 101, 116, 0, 118, 115, 95, 52, 95, 48, 95, 108,
101, 118, 101, 108, 95, 57, 95, 51, 0, 104, 108, 115, 108, 69, 110,
116, 114, 121, 0, 86, 83, 0, 0, 0, 0, 0, 42, 0, 16, 17,
0, 0, 0, 0, 64, 2, 0, 0, 0, 0, 0, 0, 44, 0, 0,
0, 0, 0, 0, 0, 44, 0, 0, 0, 7, 16, 0, 0, 60, 0,
0, 0, 1, 0, 160, 86, 83, 0, 0, 0, 46, 0, 62, 17, 3,
16, 0, 0, 9, 0, 105, 110, 112, 117, 116, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0,
80, 17, 1, 0, 5, 0, 0, 0, 4, 0, 60, 0, 0, 0, 1,
0, 44, 0, 0, 0, 0, 0, 22, 0, 80, 17, 1, 0, 5, 0,
4, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0, 4, 0, 0,
0, 22, 0, 80, 17, 1, 0, 5, 0, 8, 0, 4, 0, 60, 0,
0, 0, 1, 0, 44, 0, 8, 0, 0, 0, 22, 0, 80, 17, 1,
0, 5, 0, 12, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0,
12, 0, 0, 0, 22, 0, 80, 17, 1, 0, 5, 0, 16, 0, 4,
0, 60, 0, 0, 0, 1, 0, 44, 0, 16, 0, 0, 0, 22, 0,
80, 17, 1, 0, 5, 0, 20, 0, 4, 0, 60, 0, 0, 0, 1,
0, 44, 0, 20, 0, 0, 0, 58, 0, 62, 17, 6, 16, 0, 0,
136, 0, 60, 86, 83, 32, 114, 101, 116, 117, 114, 110, 32, 118, 97,
108, 117, 101, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 22, 0, 80, 17, 2, 0, 5, 0,
16, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0, 16, 0, 0,
0, 22, 0, 80, 17, 2, 0, 5, 0, 20, 0, 4, 0, 60, 0,
0, 0, 1, 0, 44, 0, 20, 0, 0, 0, 22, 0, 80, 17, 2,
0, 5, 0, 0, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0,
0, 0, 0, 0, 22, 0, 80, 17, 2, 0, 5, 0, 4, 0, 4,
0, 60, 0, 0, 0, 1, 0, 44, 0, 4, 0, 0, 0, 22, 0,
80, 17, 2, 0, 5, 0, 8, 0, 4, 0, 60, 0, 0, 0, 1,
0, 44, 0, 8, 0, 0, 0, 22, 0, 80, 17, 2, 0, 5, 0,
12, 0, 4, 0, 60, 0, 0, 0, 1, 0, 44, 0, 12, 0, 0,
0, 2, 0, 6, 0, 244, 0, 0, 0, 24, 0, 0, 0, 1, 0,
0, 0, 16, 1, 132, 23, 125, 135, 58, 127, 163, 121, 164, 136, 47,
191, 133, 155, 215, 75, 0, 0, 242, 0, 0, 0, 96, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 104, 0, 0, 0, 0, 0, 0,
0, 6, 0, 0, 0, 84, 0, 0, 0, 60, 0, 0, 0, 14, 0,
0, 128, 60, 0, 0, 0, 14, 0, 0, 0, 80, 0, 0, 0, 14,
0, 0, 128, 80, 0, 0, 0, 14, 0, 0, 0, 100, 0, 0, 0,
14, 0, 0, 128, 100, 0, 0, 0, 14, 0, 0, 0, 5, 0, 17,
0, 5, 0, 17, 0, 5, 0, 17, 0, 5, 0, 17, 0, 5, 0,
17, 0, 5, 0, 17, 0, 246, 0, 0, 0, 4, 0, 0, 0, 0,
0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,
202, 49, 1, 56, 0, 0, 0, 0, 16, 0, 0, 8, 16, 0, 0,
188, 0, 0, 0, 10, 0, 255, 255, 4, 0, 0, 0, 255, 255, 3,
0, 0, 0, 0, 0, 32, 0, 0, 0, 32, 0, 0, 0, 8, 0,
0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 22, 0, 27, 21, 64,
0, 0, 0, 4, 0, 0, 0, 16, 0, 102, 108, 111, 97, 116, 52,
0, 243, 242, 241, 22, 0, 27, 21, 64, 0, 0, 0, 2, 0, 0,
0, 8, 0, 102, 108, 111, 97, 116, 50, 0, 243, 242, 241, 34, 0,
3, 18, 13, 21, 3, 0, 0, 16, 0, 0, 0, 0, 80, 111, 115,
0, 242, 241, 13, 21, 3, 0, 1, 16, 0, 0, 16, 0, 84, 101,
120, 0, 242, 241, 30, 0, 5, 21, 2, 0, 0, 0, 2, 16, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 86, 83, 95, 73,
78, 80, 85, 84, 0, 241, 10, 0, 1, 18, 1, 0, 0, 0, 3,
16, 0, 0, 30, 0, 5, 21, 2, 0, 0, 0, 2, 16, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 86, 83, 95, 79, 85,
84, 80, 85, 84, 0, 10, 0, 24, 21, 5, 16, 0, 0, 1, 0,
1, 0, 14, 0, 8, 16, 6, 16, 0, 0, 23, 0, 1, 0, 4,
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 11, 202, 49, 1, 56, 0, 0, 0, 0, 16, 0, 0, 0, 16,
0, 0, 0, 0, 0, 0, 11, 0, 255, 255, 4, 0, 0, 0, 255,
255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 255, 255, 255, 255, 26, 9, 47, 241, 8, 0, 0, 0,
8, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 18, 0, 37, 17, 0, 0, 0, 0,
136, 0, 0, 0, 1, 0, 86, 83, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 255, 255, 255, 255, 26, 9, 47, 241, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 26, 9, 47, 241,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255,
119, 9, 49, 1, 1, 0, 0, 0, 13, 0, 20, 142, 14, 0, 20,
107, 15, 0, 1, 0, 72, 0, 0, 0, 32, 0, 0, 0, 44, 0,
0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22,
0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0,
0, 32, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 9, 0, 68, 2, 0, 0, 0, 0, 0, 0, 148,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 86, 83, 0, 110, 111, 110, 101, 0, 45, 186, 46,
241, 1, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 32, 0,
0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
0, 2, 0, 7, 0, 0, 0, 0, 0, 1, 0, 255, 255, 255, 255,
0, 0, 0, 0, 104, 0, 0, 0, 8, 2, 0, 0, 0, 0, 0,
0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 68, 58, 92, 114, 117,
115, 116, 100, 101, 115, 107, 92, 103, 112, 117, 95, 118, 105, 100, 101,
111, 95, 99, 111, 100, 101, 99, 92, 118, 115, 92, 83, 104, 97, 100,
101, 114, 67, 111, 109, 112, 105, 108, 101, 84, 111, 111, 108, 92, 110,
118, 95, 118, 101, 114, 116, 101, 120, 95, 115, 104, 97, 100, 101, 114,
46, 104, 108, 115, 108, 0, 0, 254, 239, 254, 239, 1, 0, 0, 0,
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 0, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, 46,
49, 1, 118, 98, 147, 101, 1, 0, 0, 0, 201, 198, 0, 59, 60,
109, 111, 65, 179, 15, 4, 145, 38, 240, 113, 148, 116, 0, 0, 0,
47, 76, 105, 110, 107, 73, 110, 102, 111, 0, 47, 110, 97, 109, 101,
115, 0, 47, 115, 114, 99, 47, 104, 101, 97, 100, 101, 114, 98, 108,
111, 99, 107, 0, 47, 115, 114, 99, 47, 102, 105, 108, 101, 115, 47,
100, 58, 92, 114, 117, 115, 116, 100, 101, 115, 107, 92, 103, 112, 117,
95, 118, 105, 100, 101, 111, 95, 99, 111, 100, 101, 99, 92, 118, 115,
92, 115, 104, 97, 100, 101, 114, 99, 111, 109, 112, 105, 108, 101, 116,
111, 111, 108, 92, 110, 118, 95, 118, 101, 114, 116, 101, 120, 95, 115,
104, 97, 100, 101, 114, 46, 104, 108, 115, 108, 0, 4, 0, 0, 0,
6, 0, 0, 0, 1, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0,
0, 17, 0, 0, 0, 7, 0, 0, 0, 34, 0, 0, 0, 8, 0,
0, 0, 10, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 5,
0, 0, 0, 0, 0, 0, 0, 220, 81, 51, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, 0, 0, 0, 32, 0, 0, 0, 208, 0, 0, 0, 244, 0, 0,
0, 87, 1, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 156, 1,
0, 0, 128, 0, 0, 0, 219, 0, 0, 0, 224, 2, 0, 0, 40,
0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 32, 2, 0, 0,
44, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0,
0, 13, 0, 0, 0, 19, 0, 0, 0, 14, 0, 0, 0, 9, 0,
0, 0, 10, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 12,
0, 0, 0, 7, 0, 0, 0, 6, 0, 0, 0, 15, 0, 0, 0,
16, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 83, 84, 65, 84, 116, 0, 0, 0, 3, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 82, 68, 69, 70, 68, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0,
0, 0, 0, 4, 254, 255, 1, 1, 0, 0, 28, 0, 0, 0, 77,
105, 99, 114, 111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 72, 76,
83, 76, 32, 83, 104, 97, 100, 101, 114, 32, 67, 111, 109, 112, 105,
108, 101, 114, 32, 49, 48, 46, 49, 0, 73, 83, 71, 78, 76, 0,
0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 56, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
15, 15, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 3, 0, 0, 80, 79,
83, 73, 84, 73, 79, 78, 0, 84, 69, 88, 67, 79, 79, 82, 68,
0, 171, 171, 79, 83, 71, 78, 80, 0, 0, 0, 2, 0, 0, 0,
8, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 3, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 68, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1,
0, 0, 0, 3, 12, 0, 0, 83, 86, 95, 80, 79, 83, 73, 84,
73, 79, 78, 0, 84, 69, 88, 67, 79, 79, 82, 68, 0, 171, 171,
171};

View File

@@ -0,0 +1,793 @@
#include <array>
#include <atomic>
#include <chrono>
#include <cstdio>
#include <list>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <d3d11.h>
#include <dxgi.h>
#include "win.h"
#define LOG_MODULE "WIN"
#include "log.h"
#define NUMVERTICES 6
typedef struct _VERTEX {
DirectX::XMFLOAT3 Pos;
DirectX::XMFLOAT2 TexCoord;
} VERTEX;
bool NativeDevice::Init(int64_t luid, ID3D11Device *device, int pool_size) {
if (device) {
if (!InitFromDevice(device))
return false;
} else {
if (!InitFromLuid(luid))
return false;
}
if (!SetMultithreadProtected())
return false;
if (!InitQuery())
return false;
if (!InitVideoDevice())
return false;
count_ = pool_size;
texture_.resize(count_);
std::fill(texture_.begin(), texture_.end(), nullptr);
return true;
}
bool NativeDevice::InitFromLuid(int64_t luid) {
HRESULT hr = S_OK;
HRB(CreateDXGIFactory1(IID_IDXGIFactory1,
(void **)factory1_.ReleaseAndGetAddressOf()));
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
UINT i = 0;
while (!FAILED(
factory1_->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
i++;
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
tmpAdapter->GetDesc1(&desc);
if (LUID(desc) == luid) {
adapter1_.Swap(tmpAdapter);
break;
}
}
if (!adapter1_) {
LOG_ERROR(std::string("Failed to find adapter1_"));
return false;
}
HRB(adapter1_.As(&adapter_));
UINT createDeviceFlags =
D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT;
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
};
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
D3D_FEATURE_LEVEL featureLevel;
D3D_DRIVER_TYPE d3dDriverType =
adapter1_ ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE;
HRB(D3D11CreateDevice(adapter1_.Get(), d3dDriverType, nullptr,
createDeviceFlags, featureLevels, numFeatureLevels,
D3D11_SDK_VERSION, device_.ReleaseAndGetAddressOf(),
&featureLevel, context_.ReleaseAndGetAddressOf()));
if (featureLevel != D3D_FEATURE_LEVEL_11_0) {
LOG_ERROR(std::string("Direct3D Feature Level 11 unsupported."));
return false;
}
return true;
}
bool NativeDevice::InitFromDevice(ID3D11Device *device) {
device_ = device;
device_->GetImmediateContext(context_.ReleaseAndGetAddressOf());
ComPtr<IDXGIDevice> dxgiDevice = nullptr;
HRB(device_.As(&dxgiDevice));
HRB(dxgiDevice->GetAdapter(adapter_.ReleaseAndGetAddressOf()));
HRB(adapter_.As(&adapter1_));
HRB(adapter1_->GetParent(IID_PPV_ARGS(&factory1_)));
return true;
}
bool NativeDevice::SetMultithreadProtected() {
ComPtr<ID3D10Multithread> hmt = nullptr;
HRB(context_.As(&hmt));
if (!hmt->SetMultithreadProtected(TRUE)) {
if (!hmt->GetMultithreadProtected()) {
LOG_ERROR(std::string("Failed to SetMultithreadProtected"));
return false;
}
}
return true;
}
bool NativeDevice::InitQuery() {
D3D11_QUERY_DESC queryDesc;
ZeroMemory(&queryDesc, sizeof(queryDesc));
queryDesc.Query = D3D11_QUERY_EVENT;
queryDesc.MiscFlags = 0;
HRB(device_->CreateQuery(&queryDesc, query_.ReleaseAndGetAddressOf()));
return true;
}
bool NativeDevice::InitVideoDevice() {
HRB(device_.As(&video_device_));
HRB(context_.As(&video_context_));
HRB(video_context_.As(&video_context1_));
return true;
}
bool NativeDevice::Nv12ToBgra(int width, int height,
ID3D11Texture2D *nv12Texture,
ID3D11Texture2D *bgraTexture,
int nv12ArrayIndex) {
if (width != last_nv12_to_bgra_width_ ||
height != last_nv12_to_bgra_height_) {
if (!nv12_to_bgra_set_srv(nv12Texture, width, height))
return false;
if (!nv12_to_bgra_set_view_port(width, height))
return false;
if (!nv12_to_bgra_set_sample())
return false;
if (!nv12_to_bgra_set_shader())
return false;
if (!nv12_to_bgra_set_vertex_buffer())
return false;
}
last_nv12_to_bgra_width_ = width;
last_nv12_to_bgra_height_ = height;
if (!nv12_to_bgra_set_rtv(bgraTexture, width, height))
return false;
D3D11_BOX srcBox;
srcBox.left = 0;
srcBox.top = 0;
srcBox.right = width;
srcBox.bottom = height;
srcBox.front = 0;
srcBox.back = 1;
context_->CopySubresourceRegion(nv12SrvTexture_.Get(), 0, 0, 0, 0,
nv12Texture, nv12ArrayIndex, &srcBox);
if (!nv12_to_bgra_draw())
return false;
return true;
}
bool NativeDevice::nv12_to_bgra_set_srv(ID3D11Texture2D *nv12Texture, int width,
int height) {
SRV_[0].Reset();
SRV_[1].Reset();
D3D11_TEXTURE2D_DESC texDesc = {};
nv12Texture->GetDesc(&texDesc);
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_NV12;
texDesc.SampleDesc.Quality = 0;
texDesc.SampleDesc.Count = 1;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = 0;
texDesc.Width = width;
texDesc.Height = height;
HRB(device_->CreateTexture2D(&texDesc, nullptr,
nv12SrvTexture_.ReleaseAndGetAddressOf()));
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(nv12SrvTexture_.Get(),
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8_UNORM);
HRB(device_->CreateShaderResourceView(nv12SrvTexture_.Get(), &srvDesc,
SRV_[0].ReleaseAndGetAddressOf()));
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(nv12SrvTexture_.Get(),
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8G8_UNORM);
HRB(device_->CreateShaderResourceView(nv12SrvTexture_.Get(), &srvDesc,
SRV_[1].ReleaseAndGetAddressOf()));
// set SRV
std::array<ID3D11ShaderResourceView *, 2> const textureViews = {
SRV_[0].Get(), SRV_[1].Get()};
context_->PSSetShaderResources(0, textureViews.size(), textureViews.data());
return true;
}
bool NativeDevice::nv12_to_bgra_set_rtv(ID3D11Texture2D *bgraTexture, int width,
int height) {
RTV_.Reset();
D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
rtDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtDesc.Texture2D.MipSlice = 0;
HRB(device_->CreateRenderTargetView(bgraTexture, &rtDesc,
RTV_.ReleaseAndGetAddressOf()));
const float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; // clear as black
context_->ClearRenderTargetView(RTV_.Get(), clearColor);
context_->OMSetRenderTargets(1, RTV_.GetAddressOf(), NULL);
return true;
}
bool NativeDevice::nv12_to_bgra_set_view_port(int width, int height) {
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)(width);
vp.Height = (FLOAT)(height);
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
context_->RSSetViewports(1, &vp);
return true;
}
bool NativeDevice::nv12_to_bgra_set_sample() {
samplerLinear_.Reset();
D3D11_SAMPLER_DESC sampleDesc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
HRB(device_->CreateSamplerState(&sampleDesc,
samplerLinear_.ReleaseAndGetAddressOf()));
context_->PSSetSamplers(0, 1, samplerLinear_.GetAddressOf());
return true;
}
bool NativeDevice::nv12_to_bgra_set_shader() {
vertexShader_.Reset();
pixelShader_.Reset();
// https://gist.github.com/RomiTT/9c05d36fe339b899793a3252297a5624
#include "pixel_shader_601.h"
#include "vertex_shader.h"
device_->CreateVertexShader(g_VS, ARRAYSIZE(g_VS), nullptr,
vertexShader_.ReleaseAndGetAddressOf());
device_->CreatePixelShader(g_PS, ARRAYSIZE(g_PS), nullptr,
pixelShader_.ReleaseAndGetAddressOf());
// set InputLayout
constexpr std::array<D3D11_INPUT_ELEMENT_DESC, 2> Layout = {{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,
D3D11_INPUT_PER_VERTEX_DATA, 0},
}};
ComPtr<ID3D11InputLayout> inputLayout = NULL;
HRB(device_->CreateInputLayout(Layout.data(), Layout.size(), g_VS,
ARRAYSIZE(g_VS), inputLayout.GetAddressOf()));
context_->IASetInputLayout(inputLayout.Get());
context_->VSSetShader(vertexShader_.Get(), NULL, 0);
context_->PSSetShader(pixelShader_.Get(), NULL, 0);
return true;
}
bool NativeDevice::nv12_to_bgra_set_vertex_buffer() {
UINT Stride = sizeof(VERTEX);
UINT Offset = 0;
FLOAT blendFactor[4] = {0.f, 0.f, 0.f, 0.f};
context_->OMSetBlendState(nullptr, blendFactor, 0xffffffff);
context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// set VertexBuffers
VERTEX Vertices[NUMVERTICES] = {
{XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)},
};
D3D11_BUFFER_DESC BufferDesc;
RtlZeroMemory(&BufferDesc, sizeof(BufferDesc));
BufferDesc.Usage = D3D11_USAGE_DEFAULT;
BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BufferDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
RtlZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = Vertices;
ComPtr<ID3D11Buffer> VertexBuffer = nullptr;
// Create vertex buffer
HRB(device_->CreateBuffer(&BufferDesc, &InitData, &VertexBuffer));
context_->IASetVertexBuffers(0, 1, VertexBuffer.GetAddressOf(), &Stride,
&Offset);
return true;
}
bool NativeDevice::nv12_to_bgra_draw() {
context_->Draw(NUMVERTICES, 0);
context_->Flush();
return true;
}
bool NativeDevice::EnsureTexture(int width, int height) {
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
if (texture_[0]) {
texture_[0]->GetDesc(&desc);
if ((int)desc.Width == width && (int)desc.Height == height &&
desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM &&
desc.MiscFlags == D3D11_RESOURCE_MISC_SHARED &&
desc.Usage == D3D11_USAGE_DEFAULT) {
return true;
}
}
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
desc.CPUAccessFlags = 0;
for (int i = 0; i < texture_.size(); i++) {
HRB(device_->CreateTexture2D(&desc, nullptr,
texture_[i].ReleaseAndGetAddressOf()));
}
return true;
}
bool NativeDevice::SetTexture(ID3D11Texture2D *texture) {
texture_[index_].Reset();
texture_[index_] = texture;
return true;
}
HANDLE NativeDevice::GetSharedHandle() {
ComPtr<IDXGIResource> resource = nullptr;
HRP(texture_[index_].As(&resource));
HANDLE sharedHandle = nullptr;
HRP(resource->GetSharedHandle(&sharedHandle));
return sharedHandle;
}
ID3D11Texture2D *NativeDevice::GetCurrentTexture() {
return texture_[index_].Get();
}
int NativeDevice::next() {
index_++;
index_ = index_ % count_;
return index_;
}
void NativeDevice::BeginQuery() { context_->Begin(query_.Get()); }
void NativeDevice::EndQuery() { context_->End(query_.Get()); }
bool NativeDevice::Query() {
BOOL bResult = FALSE;
int attempts = 0;
while (!bResult) {
HRESULT hr = context_->GetData(query_.Get(), &bResult, sizeof(BOOL), 0);
if (SUCCEEDED(hr)) {
if (bResult) {
break;
}
}
attempts++;
if (attempts > 100)
Sleep(1);
if (attempts > 1000)
break;
}
return bResult == TRUE;
}
bool NativeDevice::Process(ID3D11Texture2D *in, ID3D11Texture2D *out, int width,
int height,
D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc,
DXGI_COLOR_SPACE_TYPE colorSpace_in,
DXGI_COLOR_SPACE_TYPE colorSpace_out,
int arraySlice) {
D3D11_TEXTURE2D_DESC inDesc = {0};
D3D11_TEXTURE2D_DESC outDesc = {0};
in->GetDesc(&inDesc);
out->GetDesc(&outDesc);
if (memcmp(&last_content_desc_, &content_desc, sizeof(content_desc)) != 0) {
if (video_processor_enumerator_) {
video_processor_enumerator_.Reset();
}
if (video_processor_) {
video_processor_.Reset();
}
}
memcpy(&last_content_desc_, &content_desc, sizeof(content_desc));
if (!video_processor_enumerator_ || !video_processor_) {
HRB(video_device_->CreateVideoProcessorEnumerator(
&content_desc, video_processor_enumerator_.ReleaseAndGetAddressOf()));
HRB(video_device_->CreateVideoProcessor(
video_processor_enumerator_.Get(), 0,
video_processor_.ReleaseAndGetAddressOf()));
// This fix too dark or too light, and also make in/out colorspace work
video_context_->VideoProcessorSetStreamAutoProcessingMode(
video_processor_.Get(), 0, FALSE);
video_context_->VideoProcessorSetStreamFrameFormat(
video_processor_.Get(), 0, D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE);
}
// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/gpu/windows/d3d11_video_processor_proxy.cc#138
// https://chromium.googlesource.com/chromium/src/+/a30440e4cfc7016d4f75a4e108025667e130b78b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
video_context1_->VideoProcessorSetStreamColorSpace1(video_processor_.Get(), 0,
colorSpace_in);
video_context1_->VideoProcessorSetOutputColorSpace1(video_processor_.Get(),
colorSpace_out);
RECT rect = {0};
rect.right = width;
rect.bottom = height;
video_context_->VideoProcessorSetStreamSourceRect(video_processor_.Get(), 0,
true, &rect);
video_context1_->VideoProcessorSetStreamDestRect(video_processor_.Get(), 0,
true, &rect);
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputViewDesc;
ZeroMemory(&InputViewDesc, sizeof(InputViewDesc));
InputViewDesc.FourCC = 0;
InputViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
InputViewDesc.Texture2D.MipSlice = 0;
InputViewDesc.Texture2D.ArraySlice = arraySlice;
ComPtr<ID3D11VideoProcessorInputView> inputView = nullptr;
HRB(video_device_->CreateVideoProcessorInputView(
in, video_processor_enumerator_.Get(), &InputViewDesc,
inputView.ReleaseAndGetAddressOf()));
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc;
ZeroMemory(&OutputViewDesc, sizeof(OutputViewDesc));
OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
OutputViewDesc.Texture2D.MipSlice = 0;
ComPtr<ID3D11VideoProcessorOutputView> outputView = nullptr;
video_device_->CreateVideoProcessorOutputView(
out, video_processor_enumerator_.Get(), &OutputViewDesc,
outputView.ReleaseAndGetAddressOf());
D3D11_VIDEO_PROCESSOR_STREAM StreamData;
ZeroMemory(&StreamData, sizeof(StreamData));
StreamData.Enable = TRUE;
StreamData.pInputSurface = inputView.Get();
HRB(video_context_->VideoProcessorBlt(video_processor_.Get(),
outputView.Get(), 0, 1, &StreamData));
return true;
}
bool NativeDevice::BgraToNv12(ID3D11Texture2D *bgraTexture,
ID3D11Texture2D *nv12Texture, int width,
int height, DXGI_COLOR_SPACE_TYPE colorSpace_in,
DXGI_COLOR_SPACE_TYPE colorSpace_out) {
D3D11_TEXTURE2D_DESC bgraDesc = {0};
D3D11_TEXTURE2D_DESC nv12Desc = {0};
bgraTexture->GetDesc(&bgraDesc);
nv12Texture->GetDesc(&nv12Desc);
if (bgraDesc.Width < width || bgraDesc.Height < height) {
LOG_ERROR(std::string("bgraTexture size is smaller than width and height, ") +
std::to_string(bgraDesc.Width) + "x" +
std::to_string(bgraDesc.Height) + " < " + std::to_string(width) +
"x" + std::to_string(height));
return false;
}
if (nv12Desc.Width < width || nv12Desc.Height < height) {
LOG_ERROR(std::string("nv12Texture size is smaller than width and height,") +
std::to_string(nv12Desc.Width) + "x" +
std::to_string(nv12Desc.Height) + " < " + std::to_string(width) +
"x" + std::to_string(height));
return false;
}
D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc;
ZeroMemory(&contentDesc, sizeof(contentDesc));
contentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
contentDesc.InputFrameRate.Numerator = 30;
contentDesc.InputFrameRate.Denominator = 1;
// TODO: width height always same with desc.Width and desc.Height in test,
// need test for decide to use which one
// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/gpu/windows/d3d11_video_processor_proxy.cc#72
// https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/gpu/windows/media_foundation_video_encode_accelerator_win.cc#2170
contentDesc.InputWidth = width;
contentDesc.InputHeight = height;
contentDesc.OutputWidth = width;
contentDesc.OutputHeight = height;
contentDesc.OutputFrameRate.Numerator = 30;
contentDesc.OutputFrameRate.Denominator = 1;
return Process(bgraTexture, nv12Texture, width, height, contentDesc,
colorSpace_in, colorSpace_out, 0);
}
AdapterVendor NativeDevice::GetVendor() {
DXGI_ADAPTER_DESC1 desc1 = DXGI_ADAPTER_DESC1();
adapter1_->GetDesc1(&desc1);
if (desc1.VendorId == ADAPTER_VENDOR_NVIDIA) {
return ADAPTER_VENDOR_NVIDIA;
} else if (desc1.VendorId == ADAPTER_VENDOR_AMD) {
return ADAPTER_VENDOR_AMD;
} else if (desc1.VendorId == ADAPTER_VENDOR_INTEL) {
return ADAPTER_VENDOR_INTEL;
} else {
return ADAPTER_VENDOR_UNKNOWN;
}
}
bool NativeDevice::support_decode(DataFormat format) {
const GUID *guid = nullptr;
switch (format) {
case H264:
guid = &D3D11_DECODER_PROFILE_H264_VLD_NOFGT;
break;
case H265:
guid = &D3D11_DECODER_PROFILE_HEVC_VLD_MAIN;
break;
default:
return false;
}
BOOL supported = FALSE;
if (S_OK != video_device_->CheckVideoDecoderFormat(guid, DXGI_FORMAT_NV12,
&supported)) {
return false;
}
if (supported) {
DXGI_ADAPTER_DESC1 desc1 = DXGI_ADAPTER_DESC1();
if (FAILED(adapter1_->GetDesc1(&desc1))) {
return false;
}
bool partial =
isFormatHybridDecodedByHardware(format, desc1.VendorId, desc1.DeviceId);
return partial == false;
}
return false;
}
// https://github.com/moonlight-stream/moonlight-qt/blob/9117f6565e4b2a6ba5417282de6bf9360b681f1a/app/streaming/video/ffmpeg-renderers/dxutil.h#L8
bool NativeDevice::isFormatHybridDecodedByHardware(DataFormat format,
unsigned int vendorId,
unsigned int deviceId) {
if (vendorId == ADAPTER_VENDOR_INTEL) {
// Intel seems to encode the series in the high byte of
// the device ID. We want to avoid the "Partial" acceleration
// support explicitly. Those will claim to have HW acceleration
// but perform badly.
// https://en.wikipedia.org/wiki/Intel_Graphics_Technology#Capabilities_(GPU_video_acceleration)
// https://raw.githubusercontent.com/GameTechDev/gpudetect/master/IntelGfx.cfg
switch (deviceId & 0xFF00) {
case 0x0400: // Haswell
case 0x0A00: // Haswell
case 0x0D00: // Haswell
case 0x1600: // Broadwell
case 0x2200: // Cherry Trail and Braswell
// Block these for HEVC to avoid hybrid decode
return format == H265;
default:
break;
}
} else if (vendorId == ADAPTER_VENDOR_NVIDIA) {
// For NVIDIA, we wait to avoid those GPUs with Feature Set E
// for HEVC decoding, since that's hybrid. It appears that Kepler GPUs
// also had some hybrid decode support (per DXVA2 Checker) so we'll
// blacklist those too.
// https://en.wikipedia.org/wiki/Nvidia_PureVideo
// https://bluesky23.yukishigure.com/en/dxvac/deviceInfo/decoder.html
// http://envytools.readthedocs.io/en/latest/hw/pciid.html (missing GM200)
if ((deviceId >= 0x1180 && deviceId <= 0x11BF) || // GK104
(deviceId >= 0x11C0 && deviceId <= 0x11FF) || // GK106
(deviceId >= 0x0FC0 && deviceId <= 0x0FFF) || // GK107
(deviceId >= 0x1000 && deviceId <= 0x103F) || // GK110/GK110B
(deviceId >= 0x1280 && deviceId <= 0x12BF) || // GK208
(deviceId >= 0x1340 && deviceId <= 0x137F) || // GM108
(deviceId >= 0x1380 && deviceId <= 0x13BF) || // GM107
(deviceId >= 0x13C0 && deviceId <= 0x13FF) || // GM204
(deviceId >= 0x1617 && deviceId <= 0x161A) || // GM204
(deviceId == 0x1667) || // GM204
(deviceId >= 0x17C0 && deviceId <= 0x17FF)) { // GM200
// Avoid HEVC on Feature Set E GPUs
return format == H265;
}
}
return false;
}
bool Adapter::Init(IDXGIAdapter1 *adapter1) {
HRESULT hr = S_OK;
adapter1_ = adapter1;
HRB(adapter1_.As(&adapter_));
UINT createDeviceFlags = 0;
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
};
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
D3D_FEATURE_LEVEL featureLevel;
D3D_DRIVER_TYPE d3dDriverType =
adapter1_ ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE;
hr = D3D11CreateDevice(adapter1_.Get(), d3dDriverType, nullptr,
createDeviceFlags, featureLevels, numFeatureLevels,
D3D11_SDK_VERSION, device_.ReleaseAndGetAddressOf(),
&featureLevel, context_.ReleaseAndGetAddressOf());
if (FAILED(hr)) {
return false;
}
if (featureLevel != D3D_FEATURE_LEVEL_11_0) {
std::cerr << "Direct3D Feature Level 11 unsupported." << std::endl;
return false;
}
HRB(adapter1->GetDesc1(&desc1_));
if (desc1_.VendorId == ADAPTER_VENDOR_INTEL) {
if (!SetMultithreadProtected())
return false;
}
return true;
}
bool Adapter::SetMultithreadProtected() {
ComPtr<ID3D10Multithread> hmt = nullptr;
HRB(context_.As(&hmt));
if (!hmt->SetMultithreadProtected(TRUE)) {
if (!hmt->GetMultithreadProtected()) {
std::cerr << "Failed to SetMultithreadProtected" << std::endl;
return false;
}
}
return true;
}
bool Adapters::Init(AdapterVendor vendor) {
HRB(CreateDXGIFactory1(IID_IDXGIFactory1,
(void **)factory1_.ReleaseAndGetAddressOf()));
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
UINT i = 0;
while (!FAILED(
factory1_->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
i++;
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
tmpAdapter->GetDesc1(&desc);
if (desc.VendorId == static_cast<UINT>(vendor)) {
auto adapter = std::make_unique<Adapter>();
if (adapter->Init(tmpAdapter.Get())) {
adapters_.push_back(std::move(adapter));
}
}
}
return true;
}
int Adapters::GetFirstAdapterIndex(AdapterVendor vendor) {
ComPtr<IDXGIFactory1> factory1 = nullptr;
HRI(CreateDXGIFactory1(IID_IDXGIFactory1,
(void **)factory1.ReleaseAndGetAddressOf()));
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
UINT i = 0;
while (!FAILED(
factory1->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
i++;
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
tmpAdapter->GetDesc1(&desc);
if (desc.VendorId == static_cast<UINT>(vendor)) {
return i - 1;
}
}
return -1;
}
// https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version
// https://github.com/citizenfx/fivem/issues/1121
uint64_t GetHwcodecGpuSignature() {
uint64_t signature = 0;
ComPtr<IDXGIFactory1> factory1 = nullptr;
HRI(CreateDXGIFactory1(IID_IDXGIFactory1,
(void **)factory1.ReleaseAndGetAddressOf()));
ComPtr<IDXGIAdapter1> tmpAdapter = nullptr;
UINT i = 0;
while (!FAILED(
factory1->EnumAdapters1(i, tmpAdapter.ReleaseAndGetAddressOf()))) {
i++;
DXGI_ADAPTER_DESC1 desc = {0};
if (SUCCEEDED(tmpAdapter->GetDesc1(&desc))) {
if (desc.VendorId == ADAPTER_VENDOR_NVIDIA ||
desc.VendorId == ADAPTER_VENDOR_AMD ||
desc.VendorId == ADAPTER_VENDOR_INTEL) {
// hardware
signature += desc.VendorId;
signature += desc.DeviceId;
signature += desc.SubSysId;
signature += desc.Revision;
// software
LARGE_INTEGER umd_version;
if SUCCEEDED (tmpAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice),
&umd_version)) {
signature += umd_version.QuadPart;
}
}
}
}
return signature;
}
void hwcodec_get_d3d11_texture_width_height(ID3D11Texture2D *texture, int *w,
int *h) {
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
*w = desc.Width;
*h = desc.Height;
}
int32_t add_process_to_new_job(DWORD process_id) {
HANDLE job_handle = CreateJobObjectW(nullptr, nullptr);
if (job_handle == nullptr) {
LOG_ERROR(std::string("Failed to create job object"));
return -1;
}
JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0};
job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
BOOL result = SetInformationJobObject(
job_handle,
JobObjectExtendedLimitInformation,
&job_info,
sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)
);
if (result == FALSE) {
CloseHandle(job_handle);
LOG_ERROR(std::string("Failed to set job information"));
return -1;
}
// Open the existing process by ID
HANDLE process_handle = OpenProcess(PROCESS_SET_QUOTA | PROCESS_TERMINATE, FALSE, process_id);
if (process_handle == nullptr) {
CloseHandle(job_handle);
LOG_ERROR(std::string("Failed to open process with ID: ") + std::to_string(process_id));
return -1;
}
// Assign the child process to the Job object
BOOL assign_result = AssignProcessToJobObject(job_handle, process_handle);
if (assign_result == FALSE) {
CloseHandle(process_handle);
CloseHandle(job_handle);
LOG_ERROR(std::string("Failed to assign process to job"));
return -1;
}
// Close process handle (but keep job handle)
CloseHandle(process_handle);
return 0;
}

View File

@@ -0,0 +1,135 @@
#ifndef WIN_H
#define WIN_H
#include <DirectXMath.h>
#include <d3d11.h>
#include <d3d11_1.h>
#include <directxcolors.h>
#include <iostream>
#include <vector>
#include <wrl/client.h>
#include "../../common.h"
using Microsoft::WRL::ComPtr;
using namespace std;
using namespace DirectX;
#define SAFE_RELEASE(p) \
{ \
if ((p)) { \
(p)->Release(); \
(p) = nullptr; \
} \
}
#define LUID(desc) \
(((int64_t)desc.AdapterLuid.HighPart << 32) | desc.AdapterLuid.LowPart)
class NativeDevice {
public:
bool Init(int64_t luid, ID3D11Device *device, int pool_size = 1);
bool EnsureTexture(int width, int height);
bool SetTexture(ID3D11Texture2D *texture);
HANDLE GetSharedHandle();
ID3D11Texture2D *GetCurrentTexture();
int next();
void BeginQuery();
void EndQuery();
bool Query();
bool Process(ID3D11Texture2D *in, ID3D11Texture2D *out, int width, int height,
D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc,
DXGI_COLOR_SPACE_TYPE colorSpace_in,
DXGI_COLOR_SPACE_TYPE colorSpace_out, int arraySlice);
bool BgraToNv12(ID3D11Texture2D *bgraTexture, ID3D11Texture2D *nv12Texture,
int width, int height, DXGI_COLOR_SPACE_TYPE colorSpace_in,
DXGI_COLOR_SPACE_TYPE colorSpace_outt);
bool Nv12ToBgra(int width, int height, ID3D11Texture2D *nv12Texture,
ID3D11Texture2D *bgraTexture, int nv12ArrayIndex);
AdapterVendor GetVendor();
bool support_decode(DataFormat format);
private:
bool InitFromLuid(int64_t luid);
bool InitFromDevice(ID3D11Device *device);
bool SetMultithreadProtected();
bool InitQuery();
bool InitVideoDevice();
bool isFormatHybridDecodedByHardware(DataFormat format, unsigned int vendorId,
unsigned int deviceId);
// nv12 to bgra
bool nv12_to_bgra_set_srv(ID3D11Texture2D *nv12Texture, int width,
int height);
bool nv12_to_bgra_set_rtv(ID3D11Texture2D *bgraTexture, int width,
int height);
bool nv12_to_bgra_set_view_port(int width, int height);
bool nv12_to_bgra_set_sample();
bool nv12_to_bgra_set_shader();
bool nv12_to_bgra_set_vertex_buffer();
bool nv12_to_bgra_draw();
public:
// Direct3D 11
ComPtr<IDXGIFactory1> factory1_ = nullptr;
ComPtr<IDXGIAdapter> adapter_ = nullptr;
ComPtr<IDXGIAdapter1> adapter1_ = nullptr;
ComPtr<ID3D11Device> device_ = nullptr;
ComPtr<ID3D11DeviceContext> context_ = nullptr;
ComPtr<ID3D11Query> query_ = nullptr;
ComPtr<ID3D11VideoDevice> video_device_ = nullptr;
ComPtr<ID3D11VideoContext> video_context_ = nullptr;
ComPtr<ID3D11VideoContext1> video_context1_ = nullptr;
ComPtr<ID3D11VideoProcessorEnumerator> video_processor_enumerator_ = nullptr;
ComPtr<ID3D11VideoProcessor> video_processor_ = nullptr;
D3D11_VIDEO_PROCESSOR_CONTENT_DESC last_content_desc_ = {};
ComPtr<ID3D11RenderTargetView> RTV_ = NULL;
ComPtr<ID3D11ShaderResourceView> SRV_[2] = {NULL, NULL};
ComPtr<ID3D11VertexShader> vertexShader_ = NULL;
ComPtr<ID3D11PixelShader> pixelShader_ = NULL;
ComPtr<ID3D11SamplerState> samplerLinear_ = NULL;
ComPtr<ID3D11Texture2D> nv12SrvTexture_ = nullptr;
int count_;
int index_ = 0;
int last_nv12_to_bgra_width_ = 0;
int last_nv12_to_bgra_height_ = 0;
private:
std::vector<ComPtr<ID3D11Texture2D>> texture_;
};
class Adapter {
public:
bool Init(IDXGIAdapter1 *adapter1);
private:
bool SetMultithreadProtected();
public:
ComPtr<IDXGIAdapter> adapter_ = nullptr;
ComPtr<IDXGIAdapter1> adapter1_ = nullptr;
ComPtr<ID3D11Device> device_ = nullptr;
ComPtr<ID3D11DeviceContext> context_ = nullptr;
DXGI_ADAPTER_DESC1 desc1_;
};
class Adapters {
public:
bool Init(AdapterVendor vendor);
static int GetFirstAdapterIndex(AdapterVendor vendor);
public:
ComPtr<IDXGIFactory1> factory1_ = nullptr;
std::vector<std::unique_ptr<Adapter>> adapters_;
};
extern "C" uint64_t GetHwcodecGpuSignature();
extern "C" void hwcodec_get_d3d11_texture_width_height(ID3D11Texture2D *texture, int *w,
int *h);
extern "C" int32_t add_process_to_new_job(DWORD process_id);
#endif

View File

@@ -0,0 +1,11 @@
#ifndef SYSTEM_H
#define SYSTEM_H
#ifdef _WIN32
#include "platform/win/win.h"
#endif
#ifdef __linux__
#include "platform/linux/linux.h"
#endif
#endif // SYSTEM_H

View File

@@ -0,0 +1,357 @@
extern "C" {
#include <libavutil/opt.h>
}
#include "util.h"
#include <limits>
#include <map>
#include <string.h>
#include <vector>
#include "common.h"
#include "common.h"
#define LOG_MODULE "UTIL"
#include "log.h"
namespace util_encode {
void set_av_codec_ctx(AVCodecContext *c, const std::string &name, int kbs,
int gop, int fps) {
c->has_b_frames = 0;
c->max_b_frames = 0;
if (gop > 0 && gop < std::numeric_limits<int16_t>::max()) {
c->gop_size = gop;
c->keyint_min = gop; // Match keyint_min to gop for consistent keyframe interval
} else if (name.find("vaapi") != std::string::npos) {
c->gop_size = fps > 0 ? fps : 30; // Default to 1 second keyframe interval
c->keyint_min = c->gop_size;
} else if (name.find("qsv") != std::string::npos) {
c->gop_size = fps > 0 ? fps : 30;
c->keyint_min = c->gop_size;
} else {
c->gop_size = fps > 0 ? fps : 30;
c->keyint_min = c->gop_size;
}
/* put sample parameters */
// https://github.com/FFmpeg/FFmpeg/blob/415f012359364a77e8394436f222b74a8641a3ee/libavcodec/encode.c#L581
if (kbs > 0) {
c->bit_rate = kbs * 1000;
if (name.find("qsv") != std::string::npos) {
c->rc_max_rate = c->bit_rate;
c->bit_rate--; // cbr with vbr
}
}
/* frames per second */
c->time_base = av_make_q(1, 1000);
c->framerate = av_make_q(fps, 1);
c->flags |= AV_CODEC_FLAG2_LOCAL_HEADER;
c->flags |= AV_CODEC_FLAG_LOW_DELAY;
c->slices = 1;
c->thread_type = FF_THREAD_SLICE;
c->thread_count = c->slices;
// https://github.com/obsproject/obs-studio/blob/3cc7dc0e7cf8b01081dc23e432115f7efd0c8877/plugins/obs-ffmpeg/obs-ffmpeg-mux.c#L160
c->color_range = AVCOL_RANGE_MPEG;
c->colorspace = AVCOL_SPC_SMPTE170M;
c->color_primaries = AVCOL_PRI_SMPTE170M;
c->color_trc = AVCOL_TRC_SMPTE170M;
if (name.find("h264") != std::string::npos) {
c->profile = FF_PROFILE_H264_HIGH;
} else if (name.find("hevc") != std::string::npos) {
c->profile = FF_PROFILE_HEVC_MAIN;
}
}
bool set_lantency_free(void *priv_data, const std::string &name) {
int ret;
if (name.find("nvenc") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "delay", "0", 0)) < 0) {
LOG_ERROR(std::string("nvenc set_lantency_free failed, ret = ") + av_err2str(ret));
return false;
}
}
if (name.find("amf") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "query_timeout", "1000", 0)) < 0) {
LOG_ERROR(std::string("amf set_lantency_free failed, ret = ") + av_err2str(ret));
return false;
}
}
if (name.find("qsv") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "async_depth", "1", 0)) < 0) {
LOG_ERROR(std::string("qsv set_lantency_free failed, ret = ") + av_err2str(ret));
return false;
}
}
if (name.find("vaapi") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "async_depth", "1", 0)) < 0) {
LOG_ERROR(std::string("vaapi set_lantency_free failed, ret = ") + av_err2str(ret));
return false;
}
}
if (name.find("videotoolbox") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "realtime", 1, 0)) < 0) {
LOG_ERROR(std::string("videotoolbox set realtime failed, ret = ") + av_err2str(ret));
return false;
}
if ((ret = av_opt_set_int(priv_data, "prio_speed", 1, 0)) < 0) {
LOG_ERROR(std::string("videotoolbox set prio_speed failed, ret = ") + av_err2str(ret));
return false;
}
}
// libvpx (VP8/VP9) - set realtime mode to avoid frame lag
if (name.find("libvpx") != std::string::npos) {
// deadline: realtime for low-latency streaming
if ((ret = av_opt_set(priv_data, "deadline", "realtime", 0)) < 0) {
LOG_ERROR(std::string("libvpx set deadline realtime failed, ret = ") + av_err2str(ret));
return false;
}
// cpu-used: 6 is good balance for real-time (0-8, higher = faster but lower quality)
if ((ret = av_opt_set_int(priv_data, "cpu-used", 6, 0)) < 0) {
LOG_ERROR(std::string("libvpx set cpu-used failed, ret = ") + av_err2str(ret));
return false;
}
// lag-in-frames: 0 disables frame lag (important for real-time)
if ((ret = av_opt_set_int(priv_data, "lag-in-frames", 0, 0)) < 0) {
LOG_ERROR(std::string("libvpx set lag-in-frames failed, ret = ") + av_err2str(ret));
return false;
}
// row-mt: enable row-based multithreading for VP9
if (name.find("vp9") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "row-mt", 1, 0)) < 0) {
LOG_ERROR(std::string("libvpx-vp9 set row-mt failed, ret = ") + av_err2str(ret));
// row-mt failure is not fatal
}
}
}
return true;
}
bool set_quality(void *priv_data, const std::string &name, int quality) {
int ret = -1;
if (name.find("nvenc") != std::string::npos) {
switch (quality) {
// p7 isn't zero lantency
case Quality_Medium:
if ((ret = av_opt_set(priv_data, "preset", "p4", 0)) < 0) {
LOG_ERROR(std::string("nvenc set opt preset p4 failed, ret = ") + av_err2str(ret));
return false;
}
break;
case Quality_Low:
if ((ret = av_opt_set(priv_data, "preset", "p1", 0)) < 0) {
LOG_ERROR(std::string("nvenc set opt preset p1 failed, ret = ") + av_err2str(ret));
return false;
}
break;
default:
break;
}
}
if (name.find("amf") != std::string::npos) {
switch (quality) {
case Quality_High:
if ((ret = av_opt_set(priv_data, "quality", "quality", 0)) < 0) {
LOG_ERROR(std::string("amf set opt quality quality failed, ret = ") +
av_err2str(ret));
return false;
}
break;
case Quality_Medium:
if ((ret = av_opt_set(priv_data, "quality", "balanced", 0)) < 0) {
LOG_ERROR(std::string("amf set opt quality balanced failed, ret = ") +
av_err2str(ret));
return false;
}
break;
case Quality_Low:
if ((ret = av_opt_set(priv_data, "quality", "speed", 0)) < 0) {
LOG_ERROR(std::string("amf set opt quality speed failed, ret = ") + av_err2str(ret));
return false;
}
break;
default:
break;
}
}
if (name.find("qsv") != std::string::npos) {
switch (quality) {
case Quality_High:
if ((ret = av_opt_set(priv_data, "preset", "veryslow", 0)) < 0) {
LOG_ERROR(std::string("qsv set opt preset veryslow failed, ret = ") +
av_err2str(ret));
return false;
}
break;
case Quality_Medium:
if ((ret = av_opt_set(priv_data, "preset", "medium", 0)) < 0) {
LOG_ERROR(std::string("qsv set opt preset medium failed, ret = ") + av_err2str(ret));
return false;
}
break;
case Quality_Low:
if ((ret = av_opt_set(priv_data, "preset", "veryfast", 0)) < 0) {
LOG_ERROR(std::string("qsv set opt preset veryfast failed, ret = ") +
av_err2str(ret));
return false;
}
break;
default:
break;
}
}
if (name.find("mediacodec") != std::string::npos) {
if (name.find("h264") != std::string::npos) {
if ((ret = av_opt_set(priv_data, "level", "5.1", 0)) < 0) {
LOG_ERROR(std::string("mediacodec set opt level 5.1 failed, ret = ") +
av_err2str(ret));
return false;
}
}
if (name.find("hevc") != std::string::npos) {
// https:en.wikipedia.org/wiki/High_Efficiency_Video_Coding_tiers_and_levels
if ((ret = av_opt_set(priv_data, "level", "h5.1", 0)) < 0) {
LOG_ERROR(std::string("mediacodec set opt level h5.1 failed, ret = ") +
av_err2str(ret));
return false;
}
}
}
return true;
}
struct CodecOptions {
std::string codec_name;
std::string option_name;
std::map<int, std::string> rc_values;
};
bool set_rate_control(AVCodecContext *c, const std::string &name, int rc,
int q) {
if (name.find("qsv") != std::string::npos) {
// https://github.com/LizardByte/Sunshine/blob/3e47cd3cc8fd37a7a88be82444ff4f3c0022856b/src/video.cpp#L1635
c->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
}
std::vector<CodecOptions> codecs = {
{"nvenc", "rc", {{RC_CBR, "cbr"}, {RC_VBR, "vbr"}}},
{"amf", "rc", {{RC_CBR, "cbr"}, {RC_VBR, "vbr_latency"}}},
{"mediacodec",
"bitrate_mode",
{{RC_CBR, "cbr"}, {RC_VBR, "vbr"}, {RC_CQ, "cq"}}},
// {"videotoolbox", "constant_bit_rate", {{RC_CBR, "1"}}},
};
for (const auto &codec : codecs) {
if (name.find(codec.codec_name) != std::string::npos) {
auto it = codec.rc_values.find(rc);
if (it != codec.rc_values.end()) {
int ret = av_opt_set(c->priv_data, codec.option_name.c_str(),
it->second.c_str(), 0);
if (ret < 0) {
LOG_ERROR(codec.codec_name + " set opt " + codec.option_name + " " +
it->second + " failed, ret = " + av_err2str(ret));
return false;
}
if (name.find("mediacodec") != std::string::npos) {
if (rc == RC_CQ) {
if (q >= 0 && q <= 51) {
c->global_quality = q;
}
}
}
}
break;
}
}
return true;
}
bool set_gpu(void *priv_data, const std::string &name, int gpu) {
int ret;
if (gpu < 0)
return -1;
if (name.find("nvenc") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "gpu", gpu, 0)) < 0) {
LOG_ERROR(std::string("nvenc set gpu failed, ret = ") + av_err2str(ret));
return false;
}
}
return true;
}
bool force_hw(void *priv_data, const std::string &name) {
int ret;
if (name.find("_mf") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "hw_encoding", 1, 0)) < 0) {
LOG_ERROR(std::string("mediafoundation set hw_encoding failed, ret = ") +
av_err2str(ret));
return false;
}
}
if (name.find("videotoolbox") != std::string::npos) {
if ((ret = av_opt_set_int(priv_data, "allow_sw", 0, 0)) < 0) {
LOG_ERROR(std::string("mediafoundation set allow_sw failed, ret = ") +
av_err2str(ret));
return false;
}
}
return true;
}
bool set_others(void *priv_data, const std::string &name) {
int ret;
if (name.find("_mf") != std::string::npos) {
// ff_eAVScenarioInfo_DisplayRemoting = 1
if ((ret = av_opt_set_int(priv_data, "scenario", 1, 0)) < 0) {
LOG_ERROR(std::string("mediafoundation set scenario failed, ret = ") +
av_err2str(ret));
return false;
}
}
// NOTE: Removed idr_interval = INT_MAX for VAAPI.
// This was disabling automatic keyframe generation.
// The encoder should respect c->gop_size for keyframe interval.
return true;
}
bool change_bit_rate(AVCodecContext *c, const std::string &name, int kbs) {
if (kbs > 0) {
c->bit_rate = kbs * 1000;
if (name.find("qsv") != std::string::npos) {
c->rc_max_rate = c->bit_rate;
}
}
return true;
}
void vram_encode_test_callback(const uint8_t *data, int32_t len, int32_t key, const void *obj, int64_t pts) {
(void)data;
(void)len;
(void)pts;
if (obj) {
int32_t *pkey = (int32_t *)obj;
*pkey = key;
}
}
} // namespace util_encode
namespace util_decode {
static bool g_flag_could_not_find_ref_with_poc = false;
bool has_flag_could_not_find_ref_with_poc() {
bool v = g_flag_could_not_find_ref_with_poc;
g_flag_could_not_find_ref_with_poc = false;
return v;
}
} // namespace util_decode
extern "C" void hwcodec_set_flag_could_not_find_ref_with_poc() {
util_decode::g_flag_could_not_find_ref_with_poc = true;
}

View File

@@ -0,0 +1,52 @@
#ifndef UTIL_H
#define UTIL_H
#include <string>
#include <chrono>
extern "C" {
#include <libavcodec/avcodec.h>
}
namespace util_encode {
void set_av_codec_ctx(AVCodecContext *c, const std::string &name, int kbs,
int gop, int fps);
bool set_lantency_free(void *priv_data, const std::string &name);
bool set_quality(void *priv_data, const std::string &name, int quality);
bool set_rate_control(AVCodecContext *c, const std::string &name, int rc,
int q);
bool set_gpu(void *priv_data, const std::string &name, int gpu);
bool force_hw(void *priv_data, const std::string &name);
bool set_others(void *priv_data, const std::string &name);
bool change_bit_rate(AVCodecContext *c, const std::string &name, int kbs);
void vram_encode_test_callback(const uint8_t *data, int32_t len, int32_t key, const void *obj, int64_t pts);
} // namespace util
namespace util_decode {
bool has_flag_could_not_find_ref_with_poc();
}
namespace util {
inline std::chrono::steady_clock::time_point now() {
return std::chrono::steady_clock::now();
}
inline int64_t elapsed_ms(std::chrono::steady_clock::time_point start) {
return std::chrono::duration_cast<std::chrono::milliseconds>(now() - start).count();
}
inline bool skip_test(const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount, int64_t currentLuid, int32_t dataFormat) {
for (int32_t i = 0; i < excludeCount; i++) {
if (excludedLuids[i] == currentLuid && excludeFormats[i] == dataFormat) {
return true;
}
}
return false;
}
}
#endif

View File

@@ -0,0 +1,328 @@
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/hw_decode.c
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/decode_video.c
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/log.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
}
#include <memory>
#include <stdbool.h>
#define LOG_MODULE "FFMPEG_RAM_DEC"
#include <log.h>
#include <util.h>
#ifdef _WIN32
#include <libavutil/hwcontext_d3d11va.h>
#endif
#include "common.h"
#include "system.h"
// #define CFG_PKG_TRACE
namespace {
typedef void (*RamDecodeCallback)(const void *obj, int width, int height,
enum AVPixelFormat pixfmt,
int linesize[AV_NUM_DATA_POINTERS],
uint8_t *data[AV_NUM_DATA_POINTERS], int key);
class FFmpegRamDecoder {
public:
AVCodecContext *c_ = NULL;
AVBufferRef *hw_device_ctx_ = NULL;
AVFrame *sw_frame_ = NULL;
AVFrame *frame_ = NULL;
AVPacket *pkt_ = NULL;
bool hwaccel_ = true;
std::string name_;
AVHWDeviceType device_type_ = AV_HWDEVICE_TYPE_NONE;
int thread_count_ = 1;
RamDecodeCallback callback_ = NULL;
DataFormat data_format_;
#ifdef CFG_PKG_TRACE
int in_ = 0;
int out_ = 0;
#endif
FFmpegRamDecoder(const char *name, int device_type, int thread_count,
RamDecodeCallback callback) {
this->name_ = name;
this->device_type_ = (AVHWDeviceType)device_type;
this->thread_count_ = thread_count;
this->callback_ = callback;
}
~FFmpegRamDecoder() {}
void free_decoder() {
if (frame_)
av_frame_free(&frame_);
if (pkt_)
av_packet_free(&pkt_);
if (sw_frame_)
av_frame_free(&sw_frame_);
if (c_)
avcodec_free_context(&c_);
if (hw_device_ctx_)
av_buffer_unref(&hw_device_ctx_);
frame_ = NULL;
pkt_ = NULL;
sw_frame_ = NULL;
c_ = NULL;
hw_device_ctx_ = NULL;
}
int reset() {
if (name_.find("h264") != std::string::npos) {
data_format_ = DataFormat::H264;
} else if (name_.find("hevc") != std::string::npos) {
data_format_ = DataFormat::H265;
} else if (name_.find("mjpeg") != std::string::npos) {
data_format_ = DataFormat::MJPEG;
} else {
LOG_ERROR(std::string("unsupported data format:") + name_);
return -1;
}
free_decoder();
const AVCodec *codec = NULL;
hwaccel_ = device_type_ != AV_HWDEVICE_TYPE_NONE;
int ret;
if (!(codec = avcodec_find_decoder_by_name(name_.c_str()))) {
LOG_ERROR(std::string("avcodec_find_decoder_by_name ") + name_ + " failed");
return -1;
}
if (!(c_ = avcodec_alloc_context3(codec))) {
LOG_ERROR(std::string("Could not allocate video codec context"));
return -1;
}
c_->flags |= AV_CODEC_FLAG_LOW_DELAY;
c_->thread_count =
device_type_ != AV_HWDEVICE_TYPE_NONE ? 1 : thread_count_;
c_->thread_type = FF_THREAD_SLICE;
if (name_.find("qsv") != std::string::npos) {
if ((ret = av_opt_set(c_->priv_data, "async_depth", "1", 0)) < 0) {
LOG_ERROR(std::string("qsv set opt async_depth 1 failed"));
return -1;
}
// https://github.com/FFmpeg/FFmpeg/blob/c6364b711bad1fe2fbd90e5b2798f87080ddf5ea/libavcodec/qsvdec.c#L932
// for disable warning
c_->pkt_timebase = av_make_q(1, 30);
}
if (hwaccel_) {
ret =
av_hwdevice_ctx_create(&hw_device_ctx_, device_type_, NULL, NULL, 0);
if (ret < 0) {
LOG_ERROR(std::string("av_hwdevice_ctx_create failed, ret = ") + av_err2str(ret));
return -1;
}
c_->hw_device_ctx = av_buffer_ref(hw_device_ctx_);
if (!check_support()) {
LOG_ERROR(std::string("check_support failed"));
return -1;
}
if (!(sw_frame_ = av_frame_alloc())) {
LOG_ERROR(std::string("av_frame_alloc failed"));
return -1;
}
}
if (!(pkt_ = av_packet_alloc())) {
LOG_ERROR(std::string("av_packet_alloc failed"));
return -1;
}
if (!(frame_ = av_frame_alloc())) {
LOG_ERROR(std::string("av_frame_alloc failed"));
return -1;
}
if ((ret = avcodec_open2(c_, codec, NULL)) != 0) {
LOG_ERROR(std::string("avcodec_open2 failed, ret = ") + av_err2str(ret));
return -1;
}
#ifdef CFG_PKG_TRACE
in_ = 0;
out_ = 0;
#endif
return 0;
}
int decode(const uint8_t *data, int length, const void *obj) {
int ret = -1;
#ifdef CFG_PKG_TRACE
in_++;
LOG_DEBUG(std::string("delay DI: in:") + in_ + " out:" + out_);
#endif
if (!data || !length) {
LOG_ERROR(std::string("illegal decode parameter"));
return -1;
}
pkt_->data = (uint8_t *)data;
pkt_->size = length;
ret = do_decode(obj);
return ret;
}
private:
int do_decode(const void *obj) {
int ret;
AVFrame *tmp_frame = NULL;
bool decoded = false;
ret = avcodec_send_packet(c_, pkt_);
if (ret < 0) {
LOG_ERROR(std::string("avcodec_send_packet failed, ret = ") + av_err2str(ret));
return ret;
}
auto start = util::now();
while (ret >= 0 && util::elapsed_ms(start) < ENCODE_TIMEOUT_MS) {
if ((ret = avcodec_receive_frame(c_, frame_)) != 0) {
if (ret != AVERROR(EAGAIN)) {
LOG_ERROR(std::string("avcodec_receive_frame failed, ret = ") + av_err2str(ret));
}
goto _exit;
}
if (hwaccel_) {
if (!frame_->hw_frames_ctx) {
LOG_ERROR(std::string("hw_frames_ctx is NULL"));
goto _exit;
}
if ((ret = av_hwframe_transfer_data(sw_frame_, frame_, 0)) < 0) {
LOG_ERROR(std::string("av_hwframe_transfer_data failed, ret = ") +
av_err2str(ret));
goto _exit;
}
tmp_frame = sw_frame_;
} else {
tmp_frame = frame_;
}
decoded = true;
#ifdef CFG_PKG_TRACE
out_++;
LOG_DEBUG(std::string("delay DO: in:") + in_ + " out:" + out_);
#endif
#if FF_API_FRAME_KEY
int key_frame = frame_->flags & AV_FRAME_FLAG_KEY;
#else
int key_frame = frame_->key_frame;
#endif
callback_(obj, tmp_frame->width, tmp_frame->height,
(AVPixelFormat)tmp_frame->format, tmp_frame->linesize,
tmp_frame->data, key_frame);
}
_exit:
av_packet_unref(pkt_);
return decoded ? 0 : -1;
}
bool check_support() {
#ifdef _WIN32
if (device_type_ == AV_HWDEVICE_TYPE_D3D11VA) {
if (!c_->hw_device_ctx) {
LOG_ERROR(std::string("hw_device_ctx is NULL"));
return false;
}
AVHWDeviceContext *deviceContext =
(AVHWDeviceContext *)hw_device_ctx_->data;
if (!deviceContext) {
LOG_ERROR(std::string("deviceContext is NULL"));
return false;
}
AVD3D11VADeviceContext *d3d11vaDeviceContext =
(AVD3D11VADeviceContext *)deviceContext->hwctx;
if (!d3d11vaDeviceContext) {
LOG_ERROR(std::string("d3d11vaDeviceContext is NULL"));
return false;
}
ID3D11Device *device = d3d11vaDeviceContext->device;
if (!device) {
LOG_ERROR(std::string("device is NULL"));
return false;
}
std::unique_ptr<NativeDevice> native_ = std::make_unique<NativeDevice>();
if (!native_) {
LOG_ERROR(std::string("Failed to create native device"));
return false;
}
if (!native_->Init(0, (ID3D11Device *)device, 0)) {
LOG_ERROR(std::string("Failed to init native device"));
return false;
}
if (!native_->support_decode(data_format_)) {
LOG_ERROR(std::string("Failed to check support ") + name_);
return false;
}
return true;
} else {
return true;
}
#else
return true;
#endif
}
};
} // namespace
extern "C" void ffmpeg_ram_free_decoder(FFmpegRamDecoder *decoder) {
try {
if (!decoder)
return;
decoder->free_decoder();
delete decoder;
decoder = NULL;
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_free_decoder exception:") + e.what());
}
}
extern "C" FFmpegRamDecoder *
ffmpeg_ram_new_decoder(const char *name, int device_type, int thread_count,
RamDecodeCallback callback) {
FFmpegRamDecoder *decoder = NULL;
try {
decoder = new FFmpegRamDecoder(name, device_type, thread_count, callback);
if (decoder) {
if (decoder->reset() == 0) {
return decoder;
}
}
} catch (std::exception &e) {
LOG_ERROR(std::string("new decoder exception:") + e.what());
}
if (decoder) {
decoder->free_decoder();
delete decoder;
decoder = NULL;
}
return NULL;
}
extern "C" int ffmpeg_ram_decode(FFmpegRamDecoder *decoder, const uint8_t *data,
int length, const void *obj) {
try {
int ret = decoder->decode(data, length, obj);
if (DataFormat::H265 == decoder->data_format_ && util_decode::has_flag_could_not_find_ref_with_poc()) {
return HWCODEC_ERR_HEVC_COULD_NOT_FIND_POC;
} else {
return ret == 0 ? HWCODEC_SUCCESS : HWCODEC_ERR_COMMON;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_decode exception:") + e.what());
}
return HWCODEC_ERR_COMMON;
}

View File

@@ -0,0 +1,490 @@
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/encode_video.c
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavutil/log.h>
#include <libavutil/opt.h>
}
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#define LOG_MODULE "FFMPEG_RAM_ENC"
#include <log.h>
#include <util.h>
#ifdef _WIN32
#include "win.h"
#endif
static int calculate_offset_length(int pix_fmt, int height, const int *linesize,
int *offset, int *length) {
switch (pix_fmt) {
case AV_PIX_FMT_YUV420P:
offset[0] = linesize[0] * height;
offset[1] = offset[0] + linesize[1] * height / 2;
*length = offset[1] + linesize[2] * height / 2;
break;
case AV_PIX_FMT_NV12:
offset[0] = linesize[0] * height;
*length = offset[0] + linesize[1] * height / 2;
break;
default:
LOG_ERROR(std::string("unsupported pixfmt") + std::to_string(pix_fmt));
return -1;
}
return 0;
}
extern "C" int ffmpeg_ram_get_linesize_offset_length(int pix_fmt, int width,
int height, int align,
int *linesize, int *offset,
int *length) {
AVFrame *frame = NULL;
int ioffset[AV_NUM_DATA_POINTERS] = {0};
int ilength = 0;
int ret = -1;
if (!(frame = av_frame_alloc())) {
LOG_ERROR(std::string("Alloc frame failed"));
goto _exit;
}
frame->format = pix_fmt;
frame->width = width;
frame->height = height;
if ((ret = av_frame_get_buffer(frame, align)) < 0) {
LOG_ERROR(std::string("av_frame_get_buffer, ret = ") + av_err2str(ret));
goto _exit;
}
if (linesize) {
for (int i = 0; i < AV_NUM_DATA_POINTERS; i++)
linesize[i] = frame->linesize[i];
}
if (offset || length) {
ret = calculate_offset_length(pix_fmt, height, frame->linesize, ioffset,
&ilength);
if (ret < 0)
goto _exit;
}
if (offset) {
for (int i = 0; i < AV_NUM_DATA_POINTERS; i++) {
if (ioffset[i] == 0)
break;
offset[i] = ioffset[i];
}
}
if (length)
*length = ilength;
ret = 0;
_exit:
if (frame)
av_frame_free(&frame);
return ret;
}
namespace {
typedef void (*RamEncodeCallback)(const uint8_t *data, int len, int64_t pts,
int key, const void *obj);
class FFmpegRamEncoder {
public:
AVCodecContext *c_ = NULL;
AVFrame *frame_ = NULL;
AVPacket *pkt_ = NULL;
std::string name_;
std::string mc_name_; // for mediacodec
int width_ = 0;
int height_ = 0;
AVPixelFormat pixfmt_ = AV_PIX_FMT_NV12;
int align_ = 0;
int rc_ = 0;
int quality_ = 0;
int kbs_ = 0;
int q_ = 0;
int fps_ = 30;
int gop_ = 0xFFFF;
int thread_count_ = 1;
int gpu_ = 0;
RamEncodeCallback callback_ = NULL;
int offset_[AV_NUM_DATA_POINTERS] = {0};
bool force_keyframe_ = false; // Force next frame to be a keyframe
AVHWDeviceType hw_device_type_ = AV_HWDEVICE_TYPE_NONE;
AVPixelFormat hw_pixfmt_ = AV_PIX_FMT_NONE;
AVBufferRef *hw_device_ctx_ = NULL;
AVFrame *hw_frame_ = NULL;
FFmpegRamEncoder(const char *name, const char *mc_name, int width, int height,
int pixfmt, int align, int fps, int gop, int rc, int quality,
int kbs, int q, int thread_count, int gpu,
RamEncodeCallback callback) {
name_ = name;
mc_name_ = mc_name ? mc_name : "";
width_ = width;
height_ = height;
pixfmt_ = (AVPixelFormat)pixfmt;
align_ = align;
fps_ = fps;
gop_ = gop;
rc_ = rc;
quality_ = quality;
kbs_ = kbs;
q_ = q;
thread_count_ = thread_count;
gpu_ = gpu;
callback_ = callback;
if (name_.find("vaapi") != std::string::npos) {
hw_device_type_ = AV_HWDEVICE_TYPE_VAAPI;
hw_pixfmt_ = AV_PIX_FMT_VAAPI;
} else if (name_.find("nvenc") != std::string::npos) {
#ifdef _WIN32
hw_device_type_ = AV_HWDEVICE_TYPE_D3D11VA;
hw_pixfmt_ = AV_PIX_FMT_D3D11;
#endif
}
}
~FFmpegRamEncoder() {}
void request_keyframe() {
force_keyframe_ = true;
}
bool init(int *linesize, int *offset, int *length) {
const AVCodec *codec = NULL;
int ret;
if (!(codec = avcodec_find_encoder_by_name(name_.c_str()))) {
LOG_ERROR(std::string("Codec ") + name_ + " not found");
return false;
}
if (!(c_ = avcodec_alloc_context3(codec))) {
LOG_ERROR(std::string("Could not allocate video codec context"));
return false;
}
if (hw_device_type_ != AV_HWDEVICE_TYPE_NONE) {
std::string device = "";
#ifdef _WIN32
if (name_.find("nvenc") != std::string::npos) {
int index = Adapters::GetFirstAdapterIndex(
AdapterVendor::ADAPTER_VENDOR_NVIDIA);
if (index >= 0) {
device = std::to_string(index);
}
}
#endif
ret = av_hwdevice_ctx_create(&hw_device_ctx_, hw_device_type_,
device.length() == 0 ? NULL : device.c_str(),
NULL, 0);
if (ret < 0) {
LOG_ERROR(std::string("av_hwdevice_ctx_create failed"));
return false;
}
if (set_hwframe_ctx() != 0) {
LOG_ERROR(std::string("set_hwframe_ctx failed"));
return false;
}
hw_frame_ = av_frame_alloc();
if (!hw_frame_) {
LOG_ERROR(std::string("av_frame_alloc failed"));
return false;
}
if ((ret = av_hwframe_get_buffer(c_->hw_frames_ctx, hw_frame_, 0)) < 0) {
LOG_ERROR(std::string("av_hwframe_get_buffer failed, ret = ") + av_err2str(ret));
return false;
}
if (!hw_frame_->hw_frames_ctx) {
LOG_ERROR(std::string("hw_frame_->hw_frames_ctx is NULL"));
return false;
}
}
if (!(frame_ = av_frame_alloc())) {
LOG_ERROR(std::string("Could not allocate video frame"));
return false;
}
frame_->format = pixfmt_;
frame_->width = width_;
frame_->height = height_;
if ((ret = av_frame_get_buffer(frame_, align_)) < 0) {
LOG_ERROR(std::string("av_frame_get_buffer failed, ret = ") + av_err2str(ret));
return false;
}
if (!(pkt_ = av_packet_alloc())) {
LOG_ERROR(std::string("Could not allocate video packet"));
return false;
}
/* resolution must be a multiple of two */
c_->width = width_;
c_->height = height_;
c_->pix_fmt =
hw_pixfmt_ != AV_PIX_FMT_NONE ? hw_pixfmt_ : (AVPixelFormat)pixfmt_;
c_->sw_pix_fmt = (AVPixelFormat)pixfmt_;
util_encode::set_av_codec_ctx(c_, name_, kbs_, gop_, fps_);
if (!util_encode::set_lantency_free(c_->priv_data, name_)) {
LOG_ERROR(std::string("set_lantency_free failed, name: ") + name_);
return false;
}
// util_encode::set_quality(c_->priv_data, name_, quality_);
util_encode::set_rate_control(c_, name_, rc_, q_);
util_encode::set_gpu(c_->priv_data, name_, gpu_);
util_encode::force_hw(c_->priv_data, name_);
util_encode::set_others(c_->priv_data, name_);
if (name_.find("mediacodec") != std::string::npos) {
if (mc_name_.length() > 0) {
LOG_INFO(std::string("mediacodec codec_name: ") + mc_name_);
if ((ret = av_opt_set(c_->priv_data, "codec_name", mc_name_.c_str(),
0)) < 0) {
LOG_ERROR(std::string("mediacodec codec_name failed, ret = ") + av_err2str(ret));
}
}
}
if ((ret = avcodec_open2(c_, codec, NULL)) < 0) {
LOG_ERROR(std::string("avcodec_open2 failed, ret = ") + av_err2str(ret) +
", name: " + name_);
return false;
}
if (ffmpeg_ram_get_linesize_offset_length(pixfmt_, width_, height_, align_,
NULL, offset_, length) != 0)
return false;
for (int i = 0; i < AV_NUM_DATA_POINTERS; i++) {
linesize[i] = frame_->linesize[i];
offset[i] = offset_[i];
}
return true;
}
int encode(const uint8_t *data, int length, const void *obj, uint64_t ms) {
int ret;
if ((ret = av_frame_make_writable(frame_)) != 0) {
LOG_ERROR(std::string("av_frame_make_writable failed, ret = ") + av_err2str(ret));
return ret;
}
if ((ret = fill_frame(frame_, (uint8_t *)data, length, offset_)) != 0)
return ret;
AVFrame *tmp_frame;
if (hw_device_type_ != AV_HWDEVICE_TYPE_NONE) {
if ((ret = av_hwframe_transfer_data(hw_frame_, frame_, 0)) < 0) {
LOG_ERROR(std::string("av_hwframe_transfer_data failed, ret = ") + av_err2str(ret));
return ret;
}
tmp_frame = hw_frame_;
} else {
tmp_frame = frame_;
}
return do_encode(tmp_frame, obj, ms);
}
void free_encoder() {
if (pkt_)
av_packet_free(&pkt_);
if (frame_)
av_frame_free(&frame_);
if (hw_frame_)
av_frame_free(&hw_frame_);
if (hw_device_ctx_)
av_buffer_unref(&hw_device_ctx_);
if (c_)
avcodec_free_context(&c_);
}
int set_bitrate(int kbs) {
return util_encode::change_bit_rate(c_, name_, kbs) ? 0 : -1;
}
private:
int set_hwframe_ctx() {
AVBufferRef *hw_frames_ref;
AVHWFramesContext *frames_ctx = NULL;
int err = 0;
if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx_))) {
LOG_ERROR(std::string("av_hwframe_ctx_alloc failed"));
return -1;
}
frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
frames_ctx->format = hw_pixfmt_;
frames_ctx->sw_format = (AVPixelFormat)pixfmt_;
frames_ctx->width = width_;
frames_ctx->height = height_;
frames_ctx->initial_pool_size = 1;
if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {
av_buffer_unref(&hw_frames_ref);
return err;
}
c_->hw_frames_ctx = av_buffer_ref(hw_frames_ref);
if (!c_->hw_frames_ctx) {
LOG_ERROR(std::string("av_buffer_ref failed"));
err = -1;
}
av_buffer_unref(&hw_frames_ref);
return err;
}
int do_encode(AVFrame *frame, const void *obj, int64_t ms) {
int ret;
bool encoded = false;
frame->pts = ms;
// Force keyframe if requested
if (force_keyframe_) {
frame->pict_type = AV_PICTURE_TYPE_I;
force_keyframe_ = false;
} else {
frame->pict_type = AV_PICTURE_TYPE_NONE;
}
if ((ret = avcodec_send_frame(c_, frame)) < 0) {
LOG_ERROR(std::string("avcodec_send_frame failed, ret = ") + av_err2str(ret));
return ret;
}
auto start = util::now();
while (ret >= 0 && util::elapsed_ms(start) < DECODE_TIMEOUT_MS) {
if ((ret = avcodec_receive_packet(c_, pkt_)) < 0) {
if (ret != AVERROR(EAGAIN)) {
LOG_ERROR(std::string("avcodec_receive_packet failed, ret = ") + av_err2str(ret));
}
goto _exit;
}
if (!pkt_->data || !pkt_->size) {
LOG_ERROR(std::string("avcodec_receive_packet failed, pkt size is 0"));
goto _exit;
}
encoded = true;
callback_(pkt_->data, pkt_->size, pkt_->pts,
pkt_->flags & AV_PKT_FLAG_KEY, obj);
}
_exit:
av_packet_unref(pkt_);
return encoded ? 0 : -1;
}
int fill_frame(AVFrame *frame, uint8_t *data, int data_length,
const int *const offset) {
switch (frame->format) {
case AV_PIX_FMT_NV12:
if (data_length <
frame->height * (frame->linesize[0] + frame->linesize[1] / 2)) {
LOG_ERROR(std::string("fill_frame: NV12 data length error. data_length:") +
std::to_string(data_length) +
", linesize[0]:" + std::to_string(frame->linesize[0]) +
", linesize[1]:" + std::to_string(frame->linesize[1]));
return -1;
}
frame->data[0] = data;
frame->data[1] = data + offset[0];
break;
case AV_PIX_FMT_YUV420P:
if (data_length <
frame->height * (frame->linesize[0] + frame->linesize[1] / 2 +
frame->linesize[2] / 2)) {
LOG_ERROR(std::string("fill_frame: 420P data length error. data_length:") +
std::to_string(data_length) +
", linesize[0]:" + std::to_string(frame->linesize[0]) +
", linesize[1]:" + std::to_string(frame->linesize[1]) +
", linesize[2]:" + std::to_string(frame->linesize[2]));
return -1;
}
frame->data[0] = data;
frame->data[1] = data + offset[0];
frame->data[2] = data + offset[1];
break;
default:
LOG_ERROR(std::string("fill_frame: unsupported format, ") +
std::to_string(frame->format));
return -1;
}
return 0;
}
};
} // namespace
extern "C" FFmpegRamEncoder *
ffmpeg_ram_new_encoder(const char *name, const char *mc_name, int width,
int height, int pixfmt, int align, int fps, int gop,
int rc, int quality, int kbs, int q, int thread_count,
int gpu, int *linesize, int *offset, int *length,
RamEncodeCallback callback) {
FFmpegRamEncoder *encoder = NULL;
try {
encoder = new FFmpegRamEncoder(name, mc_name, width, height, pixfmt, align,
fps, gop, rc, quality, kbs, q, thread_count,
gpu, callback);
if (encoder) {
if (encoder->init(linesize, offset, length)) {
return encoder;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new FFmpegRamEncoder failed, ") + std::string(e.what()));
}
if (encoder) {
encoder->free_encoder();
delete encoder;
encoder = NULL;
}
return NULL;
}
extern "C" int ffmpeg_ram_encode(FFmpegRamEncoder *encoder, const uint8_t *data,
int length, const void *obj, uint64_t ms) {
try {
return encoder->encode(data, length, obj, ms);
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_encode failed, ") + std::string(e.what()));
}
return -1;
}
extern "C" void ffmpeg_ram_free_encoder(FFmpegRamEncoder *encoder) {
try {
if (!encoder)
return;
encoder->free_encoder();
delete encoder;
encoder = NULL;
} catch (const std::exception &e) {
LOG_ERROR(std::string("free encoder failed, ") + std::string(e.what()));
}
}
extern "C" int ffmpeg_ram_set_bitrate(FFmpegRamEncoder *encoder, int kbs) {
try {
return encoder->set_bitrate(kbs);
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_set_bitrate failed, ") + std::string(e.what()));
}
return -1;
}
extern "C" void ffmpeg_ram_request_keyframe(FFmpegRamEncoder *encoder) {
try {
if (encoder) {
encoder->request_keyframe();
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_request_keyframe failed, ") + std::string(e.what()));
}
}

View File

@@ -0,0 +1,35 @@
#ifndef FFMPEG_RAM_FFI_H
#define FFMPEG_RAM_FFI_H
#include <stdint.h>
#define AV_NUM_DATA_POINTERS 8
typedef void (*RamDecodeCallback)(const void *obj, int width, int height,
int pixfmt,
int linesize[AV_NUM_DATA_POINTERS],
uint8_t *data[AV_NUM_DATA_POINTERS], int key);
typedef void (*RamEncodeCallback)(const uint8_t *data, int len, int64_t pts,
int key, const void *obj);
void *ffmpeg_ram_new_encoder(const char *name, const char *mc_name, int width,
int height, int pixfmt, int align, int fps,
int gop, int rc, int quality, int kbs, int q,
int thread_count, int gpu, int *linesize,
int *offset, int *length,
RamEncodeCallback callback);
void *ffmpeg_ram_new_decoder(const char *name, int device_type,
int thread_count, RamDecodeCallback callback);
int ffmpeg_ram_encode(void *encoder, const uint8_t *data, int length,
const void *obj, int64_t ms);
int ffmpeg_ram_decode(void *decoder, const uint8_t *data, int length,
const void *obj);
void ffmpeg_ram_free_encoder(void *encoder);
void ffmpeg_ram_free_decoder(void *decoder);
int ffmpeg_ram_get_linesize_offset_length(int pix_fmt, int width, int height,
int align, int *linesize, int *offset,
int *length);
int ffmpeg_ram_set_bitrate(void *encoder, int kbs);
void ffmpeg_ram_request_keyframe(void *encoder);
#endif // FFMPEG_RAM_FFI_H

View File

@@ -0,0 +1,410 @@
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/hw_decode.c
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/decode_video.c
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/hwcontext.h>
#include <libavutil/log.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
}
#include <libavutil/hwcontext_d3d11va.h>
#include <memory>
#include <mutex>
#include <stdbool.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#define LOG_MODULE "FFMPEG_VRAM_DEC"
#include <log.h>
#include <util.h>
namespace {
#define USE_SHADER
void lockContext(void *lock_ctx);
void unlockContext(void *lock_ctx);
class FFmpegVRamDecoder {
public:
AVCodecContext *c_ = NULL;
AVBufferRef *hw_device_ctx_ = NULL;
AVCodecParserContext *sw_parser_ctx_ = NULL;
AVFrame *frame_ = NULL;
AVPacket *pkt_ = NULL;
std::unique_ptr<NativeDevice> native_ = nullptr;
ID3D11Device *d3d11Device_ = NULL;
ID3D11DeviceContext *d3d11DeviceContext_ = NULL;
void *device_ = nullptr;
int64_t luid_ = 0;
DataFormat dataFormat_;
std::string name_;
AVHWDeviceType device_type_ = AV_HWDEVICE_TYPE_D3D11VA;
bool bt709_ = false;
bool full_range_ = false;
FFmpegVRamDecoder(void *device, int64_t luid, DataFormat dataFormat) {
device_ = device;
luid_ = luid;
dataFormat_ = dataFormat;
switch (dataFormat) {
case H264:
name_ = "h264";
break;
case H265:
name_ = "hevc";
break;
default:
LOG_ERROR(std::string("unsupported data format"));
break;
}
// Always use DX11 since it's the only API
device_type_ = AV_HWDEVICE_TYPE_D3D11VA;
}
~FFmpegVRamDecoder() {}
void destroy() {
if (frame_)
av_frame_free(&frame_);
if (pkt_)
av_packet_free(&pkt_);
if (c_)
avcodec_free_context(&c_);
if (hw_device_ctx_) {
av_buffer_unref(&hw_device_ctx_);
// AVHWDeviceContext takes ownership of d3d11 object
d3d11Device_ = nullptr;
d3d11DeviceContext_ = nullptr;
} else {
SAFE_RELEASE(d3d11Device_);
SAFE_RELEASE(d3d11DeviceContext_);
}
frame_ = NULL;
pkt_ = NULL;
c_ = NULL;
hw_device_ctx_ = NULL;
}
int reset() {
destroy();
if (!native_) {
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)device_, 4)) {
LOG_ERROR(std::string("Failed to init native device"));
return -1;
}
}
if (!native_->support_decode(dataFormat_)) {
LOG_ERROR(std::string("unsupported data format"));
return -1;
}
d3d11Device_ = native_->device_.Get();
d3d11Device_->AddRef();
d3d11DeviceContext_ = native_->context_.Get();
d3d11DeviceContext_->AddRef();
const AVCodec *codec = NULL;
int ret;
if (!(codec = avcodec_find_decoder_by_name(name_.c_str()))) {
LOG_ERROR(std::string("avcodec_find_decoder_by_name ") + name_ + " failed");
return -1;
}
if (!(c_ = avcodec_alloc_context3(codec))) {
LOG_ERROR(std::string("Could not allocate video codec context"));
return -1;
}
c_->flags |= AV_CODEC_FLAG_LOW_DELAY;
hw_device_ctx_ = av_hwdevice_ctx_alloc(device_type_);
if (!hw_device_ctx_) {
LOG_ERROR(std::string("av_hwdevice_ctx_create failed"));
return -1;
}
AVHWDeviceContext *deviceContext =
(AVHWDeviceContext *)hw_device_ctx_->data;
AVD3D11VADeviceContext *d3d11vaDeviceContext =
(AVD3D11VADeviceContext *)deviceContext->hwctx;
d3d11vaDeviceContext->device = d3d11Device_;
d3d11vaDeviceContext->device_context = d3d11DeviceContext_;
d3d11vaDeviceContext->lock = lockContext;
d3d11vaDeviceContext->unlock = unlockContext;
d3d11vaDeviceContext->lock_ctx = this;
ret = av_hwdevice_ctx_init(hw_device_ctx_);
if (ret < 0) {
LOG_ERROR(std::string("av_hwdevice_ctx_init failed, ret = ") + av_err2str(ret));
return -1;
}
c_->hw_device_ctx = av_buffer_ref(hw_device_ctx_);
if (!(pkt_ = av_packet_alloc())) {
LOG_ERROR(std::string("av_packet_alloc failed"));
return -1;
}
if (!(frame_ = av_frame_alloc())) {
LOG_ERROR(std::string("av_frame_alloc failed"));
return -1;
}
if ((ret = avcodec_open2(c_, codec, NULL)) != 0) {
LOG_ERROR(std::string("avcodec_open2 failed, ret = ") + av_err2str(ret) +
", name=" + name_);
return -1;
}
return 0;
}
int decode(const uint8_t *data, int length, DecodeCallback callback,
const void *obj) {
int ret = -1;
if (!data || !length) {
LOG_ERROR(std::string("illegal decode parameter"));
return -1;
}
pkt_->data = (uint8_t *)data;
pkt_->size = length;
ret = do_decode(callback, obj);
return ret;
}
private:
int do_decode(DecodeCallback callback, const void *obj) {
int ret;
bool decoded = false;
bool locked = false;
ret = avcodec_send_packet(c_, pkt_);
if (ret < 0) {
LOG_ERROR(std::string("avcodec_send_packet failed, ret = ") + av_err2str(ret));
return ret;
}
auto start = util::now();
while (ret >= 0 && util::elapsed_ms(start) < DECODE_TIMEOUT_MS) {
if ((ret = avcodec_receive_frame(c_, frame_)) != 0) {
if (ret != AVERROR(EAGAIN)) {
LOG_ERROR(std::string("avcodec_receive_frame failed, ret = ") + av_err2str(ret));
}
goto _exit;
}
if (frame_->format != AV_PIX_FMT_D3D11) {
LOG_ERROR(std::string("only AV_PIX_FMT_D3D11 is supported"));
goto _exit;
}
lockContext(this);
locked = true;
if (!convert(frame_, callback, obj)) {
LOG_ERROR(std::string("Failed to convert"));
goto _exit;
}
if (callback)
callback(native_->GetCurrentTexture(), obj);
decoded = true;
}
_exit:
if (locked) {
unlockContext(this);
}
av_packet_unref(pkt_);
return decoded ? 0 : -1;
}
bool convert(AVFrame *frame, DecodeCallback callback, const void *obj) {
ID3D11Texture2D *texture = (ID3D11Texture2D *)frame->data[0];
if (!texture) {
LOG_ERROR(std::string("texture is NULL"));
return false;
}
D3D11_TEXTURE2D_DESC desc2D;
texture->GetDesc(&desc2D);
if (desc2D.Format != DXGI_FORMAT_NV12) {
LOG_ERROR(std::string("only DXGI_FORMAT_NV12 is supported"));
return false;
}
if (!native_->EnsureTexture(frame->width, frame->height)) {
LOG_ERROR(std::string("Failed to EnsureTexture"));
return false;
}
native_->next(); // comment out to remove picture shaking
#ifdef USE_SHADER
native_->BeginQuery();
if (!native_->Nv12ToBgra(frame->width, frame->height, texture,
native_->GetCurrentTexture(),
(int)frame->data[1])) {
LOG_ERROR(std::string("Failed to Nv12ToBgra"));
native_->EndQuery();
return false;
}
native_->EndQuery();
native_->Query();
#else
native_->BeginQuery();
// nv12 -> bgra
D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc;
ZeroMemory(&contentDesc, sizeof(contentDesc));
contentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
contentDesc.InputFrameRate.Numerator = 60;
contentDesc.InputFrameRate.Denominator = 1;
// TODO: aligned width, height or crop width, height
contentDesc.InputWidth = frame->width;
contentDesc.InputHeight = frame->height;
contentDesc.OutputWidth = frame->width;
contentDesc.OutputHeight = frame->height;
contentDesc.OutputFrameRate.Numerator = 60;
contentDesc.OutputFrameRate.Denominator = 1;
DXGI_COLOR_SPACE_TYPE colorSpace_out =
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
DXGI_COLOR_SPACE_TYPE colorSpace_in;
if (bt709_) {
if (full_range_) {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709;
} else {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
}
} else {
if (full_range_) {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601;
} else {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
}
}
if (!native_->Process(texture, native_->GetCurrentTexture(), contentDesc,
colorSpace_in, colorSpace_out, (int)frame->data[1])) {
LOG_ERROR(std::string("Failed to process"));
native_->EndQuery();
return false;
}
native_->context_->Flush();
native_->EndQuery();
if (!native_->Query()) {
LOG_ERROR(std::string("Failed to query"));
return false;
}
#endif
return true;
}
};
void lockContext(void *lock_ctx) { (void)lock_ctx; }
void unlockContext(void *lock_ctx) { (void)lock_ctx; }
} // namespace
extern "C" int ffmpeg_vram_destroy_decoder(FFmpegVRamDecoder *decoder) {
try {
if (!decoder)
return 0;
decoder->destroy();
delete decoder;
decoder = NULL;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_free_decoder exception:") + e.what());
}
return -1;
}
extern "C" FFmpegVRamDecoder *ffmpeg_vram_new_decoder(void *device,
int64_t luid,
DataFormat dataFormat) {
FFmpegVRamDecoder *decoder = NULL;
try {
decoder = new FFmpegVRamDecoder(device, luid, dataFormat);
if (decoder) {
if (decoder->reset() == 0) {
return decoder;
}
}
} catch (std::exception &e) {
LOG_ERROR(std::string("new decoder exception:") + e.what());
}
if (decoder) {
decoder->destroy();
delete decoder;
decoder = NULL;
}
return NULL;
}
extern "C" int ffmpeg_vram_decode(FFmpegVRamDecoder *decoder,
const uint8_t *data, int length,
DecodeCallback callback, const void *obj) {
try {
int ret = decoder->decode(data, length, callback, obj);
if (DataFormat::H265 == decoder->dataFormat_ && util_decode::has_flag_could_not_find_ref_with_poc()) {
return HWCODEC_ERR_HEVC_COULD_NOT_FIND_POC;
} else {
return ret == 0 ? HWCODEC_SUCCESS : HWCODEC_ERR_COMMON;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_decode exception:") + e.what());
}
return HWCODEC_ERR_COMMON;
}
extern "C" int ffmpeg_vram_test_decode(int64_t *outLuids, int32_t *outVendors,
int32_t maxDescNum, int32_t *outDescNum,
DataFormat dataFormat,
uint8_t *data, int32_t length,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
int count = 0;
struct VendorMapping {
AdapterVendor adapter_vendor;
int driver_vendor;
};
VendorMapping vendors[] = {
{ADAPTER_VENDOR_INTEL, VENDOR_INTEL},
{ADAPTER_VENDOR_NVIDIA, VENDOR_NV},
{ADAPTER_VENDOR_AMD, VENDOR_AMD}
};
for (auto vendorMap : vendors) {
Adapters adapters;
if (!adapters.Init(vendorMap.adapter_vendor))
continue;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
FFmpegVRamDecoder *p = (FFmpegVRamDecoder *)ffmpeg_vram_new_decoder(
nullptr, LUID(adapter.get()->desc1_), dataFormat);
if (!p)
continue;
auto start = util::now();
bool succ = ffmpeg_vram_decode(p, data, length, nullptr, nullptr) == 0;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = LUID(adapter.get()->desc1_);
outVendors[count] = (int32_t)vendorMap.driver_vendor; // Map adapter vendor to driver vendor
count += 1;
}
p->destroy();
delete p;
p = nullptr;
if (count >= maxDescNum)
break;
}
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
std::cerr << e.what() << '\n';
}
return -1;
}

View File

@@ -0,0 +1,558 @@
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/hwcontext.h>
#include <libavutil/imgutils.h>
#include <libavutil/log.h>
#include <libavutil/opt.h>
}
#ifdef _WIN32
#include <libavutil/hwcontext_d3d11va.h>
#endif
#include <memory>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#define LOG_MODULE "FFMPEG_VRAM_ENC"
#include <log.h>
#include <util.h>
namespace {
void lockContext(void *lock_ctx);
void unlockContext(void *lock_ctx);
enum class EncoderDriver {
NVENC,
AMF,
QSV,
};
class Encoder {
public:
Encoder(EncoderDriver driver, const char *name, AVHWDeviceType device_type,
AVHWDeviceType derived_device_type, AVPixelFormat hw_pixfmt,
AVPixelFormat sw_pixfmt) {
driver_ = driver;
name_ = name;
device_type_ = device_type;
derived_device_type_ = derived_device_type;
hw_pixfmt_ = hw_pixfmt;
sw_pixfmt_ = sw_pixfmt;
};
EncoderDriver driver_;
std::string name_;
AVHWDeviceType device_type_;
AVHWDeviceType derived_device_type_;
AVPixelFormat hw_pixfmt_;
AVPixelFormat sw_pixfmt_;
};
class FFmpegVRamEncoder {
public:
AVCodecContext *c_ = NULL;
AVBufferRef *hw_device_ctx_ = NULL;
AVFrame *frame_ = NULL;
AVFrame *mapped_frame_ = NULL;
ID3D11Texture2D *encode_texture_ = NULL; // no free
AVPacket *pkt_ = NULL;
std::unique_ptr<NativeDevice> native_ = nullptr;
ID3D11Device *d3d11Device_ = NULL;
ID3D11DeviceContext *d3d11DeviceContext_ = NULL;
std::unique_ptr<Encoder> encoder_ = nullptr;
void *handle_ = nullptr;
int64_t luid_;
DataFormat dataFormat_;
int32_t width_ = 0;
int32_t height_ = 0;
int32_t kbs_;
int32_t framerate_;
int32_t gop_;
const int align_ = 0;
const bool full_range_ = false;
const bool bt709_ = false;
FFmpegVRamEncoder(void *handle, int64_t luid, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs,
int32_t framerate, int32_t gop) {
handle_ = handle;
luid_ = luid;
dataFormat_ = dataFormat;
width_ = width;
height_ = height;
kbs_ = kbs;
framerate_ = framerate;
gop_ = gop;
}
~FFmpegVRamEncoder() {}
bool init() {
const AVCodec *codec = NULL;
int ret;
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)handle_)) {
LOG_ERROR(std::string("NativeDevice init failed"));
return false;
}
d3d11Device_ = native_->device_.Get();
d3d11Device_->AddRef();
d3d11DeviceContext_ = native_->context_.Get();
d3d11DeviceContext_->AddRef();
AdapterVendor vendor = native_->GetVendor();
if (!choose_encoder(vendor)) {
return false;
}
LOG_INFO(std::string("encoder name: ") + encoder_->name_);
if (!(codec = avcodec_find_encoder_by_name(encoder_->name_.c_str()))) {
LOG_ERROR(std::string("Codec ") + encoder_->name_ + " not found");
return false;
}
if (!(c_ = avcodec_alloc_context3(codec))) {
LOG_ERROR(std::string("Could not allocate video codec context"));
return false;
}
/* resolution must be a multiple of two */
c_->width = width_;
c_->height = height_;
c_->pix_fmt = encoder_->hw_pixfmt_;
c_->sw_pix_fmt = encoder_->sw_pixfmt_;
util_encode::set_av_codec_ctx(c_, encoder_->name_, kbs_, gop_, framerate_);
if (!util_encode::set_lantency_free(c_->priv_data, encoder_->name_)) {
return false;
}
// util_encode::set_quality(c_->priv_data, encoder_->name_, Quality_Default);
util_encode::set_rate_control(c_, encoder_->name_, RC_CBR, -1);
util_encode::set_others(c_->priv_data, encoder_->name_);
hw_device_ctx_ = av_hwdevice_ctx_alloc(encoder_->device_type_);
if (!hw_device_ctx_) {
LOG_ERROR(std::string("av_hwdevice_ctx_create failed"));
return false;
}
AVHWDeviceContext *deviceContext =
(AVHWDeviceContext *)hw_device_ctx_->data;
AVD3D11VADeviceContext *d3d11vaDeviceContext =
(AVD3D11VADeviceContext *)deviceContext->hwctx;
d3d11vaDeviceContext->device = d3d11Device_;
d3d11vaDeviceContext->device_context = d3d11DeviceContext_;
d3d11vaDeviceContext->lock = lockContext;
d3d11vaDeviceContext->unlock = unlockContext;
d3d11vaDeviceContext->lock_ctx = this;
ret = av_hwdevice_ctx_init(hw_device_ctx_);
if (ret < 0) {
LOG_ERROR(std::string("av_hwdevice_ctx_init failed, ret = ") + av_err2str(ret));
return false;
}
if (encoder_->derived_device_type_ != AV_HWDEVICE_TYPE_NONE) {
AVBufferRef *derived_context = nullptr;
ret = av_hwdevice_ctx_create_derived(
&derived_context, encoder_->derived_device_type_, hw_device_ctx_, 0);
if (ret) {
LOG_ERROR(std::string("av_hwdevice_ctx_create_derived failed, err = ") +
av_err2str(ret));
return false;
}
av_buffer_unref(&hw_device_ctx_);
hw_device_ctx_ = derived_context;
}
c_->hw_device_ctx = av_buffer_ref(hw_device_ctx_);
if (!set_hwframe_ctx()) {
return false;
}
if (!(pkt_ = av_packet_alloc())) {
LOG_ERROR(std::string("Could not allocate video packet"));
return false;
}
if ((ret = avcodec_open2(c_, codec, NULL)) < 0) {
LOG_ERROR(std::string("avcodec_open2 failed, ret = ") + av_err2str(ret) +
", name: " + encoder_->name_);
return false;
}
if (!(frame_ = av_frame_alloc())) {
LOG_ERROR(std::string("Could not allocate video frame"));
return false;
}
frame_->format = c_->pix_fmt;
frame_->width = c_->width;
frame_->height = c_->height;
frame_->color_range = c_->color_range;
frame_->color_primaries = c_->color_primaries;
frame_->color_trc = c_->color_trc;
frame_->colorspace = c_->colorspace;
frame_->chroma_location = c_->chroma_sample_location;
if ((ret = av_hwframe_get_buffer(c_->hw_frames_ctx, frame_, 0)) < 0) {
LOG_ERROR(std::string("av_frame_get_buffer failed, ret = ") + av_err2str(ret));
return false;
}
if (frame_->format == AV_PIX_FMT_QSV) {
mapped_frame_ = av_frame_alloc();
if (!mapped_frame_) {
LOG_ERROR(std::string("Could not allocate mapped video frame"));
return false;
}
mapped_frame_->format = AV_PIX_FMT_D3D11;
ret = av_hwframe_map(mapped_frame_, frame_,
AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
if (ret) {
LOG_ERROR(std::string("av_hwframe_map failed, err = ") + av_err2str(ret));
return false;
}
encode_texture_ = (ID3D11Texture2D *)mapped_frame_->data[0];
} else {
encode_texture_ = (ID3D11Texture2D *)frame_->data[0];
}
return true;
}
int encode(void *texture, EncodeCallback callback, void *obj, int64_t ms) {
if (!convert(texture))
return -1;
return do_encode(callback, obj, ms);
}
void destroy() {
if (pkt_)
av_packet_free(&pkt_);
if (frame_)
av_frame_free(&frame_);
if (mapped_frame_)
av_frame_free(&mapped_frame_);
if (c_)
avcodec_free_context(&c_);
if (hw_device_ctx_) {
av_buffer_unref(&hw_device_ctx_);
// AVHWDeviceContext takes ownership of d3d11 object
d3d11Device_ = nullptr;
d3d11DeviceContext_ = nullptr;
} else {
SAFE_RELEASE(d3d11Device_);
SAFE_RELEASE(d3d11DeviceContext_);
}
}
int set_bitrate(int kbs) {
return util_encode::change_bit_rate(c_, encoder_->name_, kbs) ? 0 : -1;
}
int set_framerate(int framerate) {
c_->time_base = av_make_q(1, framerate);
c_->framerate = av_inv_q(c_->time_base);
return 0;
}
private:
bool choose_encoder(AdapterVendor vendor) {
if (ADAPTER_VENDOR_NVIDIA == vendor) {
const char *name = nullptr;
if (dataFormat_ == H264) {
name = "h264_nvenc";
} else if (dataFormat_ == H265) {
name = "hevc_nvenc";
} else {
LOG_ERROR(std::string("Unsupported data format: ") + std::to_string(dataFormat_));
return false;
}
encoder_ = std::make_unique<Encoder>(
EncoderDriver::NVENC, name, AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_NONE, AV_PIX_FMT_D3D11, AV_PIX_FMT_NV12);
return true;
} else if (ADAPTER_VENDOR_AMD == vendor) {
const char *name = nullptr;
if (dataFormat_ == H264) {
name = "h264_amf";
} else if (dataFormat_ == H265) {
name = "hevc_amf";
} else {
LOG_ERROR(std::string("Unsupported data format: ") + std::to_string(dataFormat_));
return false;
}
encoder_ = std::make_unique<Encoder>(
EncoderDriver::AMF, name, AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_NONE, AV_PIX_FMT_D3D11, AV_PIX_FMT_NV12);
return true;
} else if (ADAPTER_VENDOR_INTEL == vendor) {
const char *name = nullptr;
if (dataFormat_ == H264) {
name = "h264_qsv";
} else if (dataFormat_ == H265) {
name = "hevc_qsv";
} else {
LOG_ERROR(std::string("Unsupported data format: ") + std::to_string(dataFormat_));
return false;
}
encoder_ = std::make_unique<Encoder>(
EncoderDriver::QSV, name, AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_QSV, AV_PIX_FMT_QSV, AV_PIX_FMT_NV12);
return true;
} else {
LOG_ERROR(std::string("Unsupported vendor: ") + std::to_string(vendor));
return false;
}
return false;
}
int do_encode(EncodeCallback callback, const void *obj, int64_t ms) {
int ret;
bool encoded = false;
frame_->pts = ms;
if ((ret = avcodec_send_frame(c_, frame_)) < 0) {
LOG_ERROR(std::string("avcodec_send_frame failed, ret = ") + av_err2str(ret));
return ret;
}
auto start = util::now();
while (ret >= 0 && util::elapsed_ms(start) < ENCODE_TIMEOUT_MS) {
if ((ret = avcodec_receive_packet(c_, pkt_)) < 0) {
if (ret != AVERROR(EAGAIN)) {
LOG_ERROR(std::string("avcodec_receive_packet failed, ret = ") + av_err2str(ret));
}
goto _exit;
}
if (!pkt_->data || !pkt_->size) {
LOG_ERROR(std::string("avcodec_receive_packet failed, pkt size is 0"));
goto _exit;
}
encoded = true;
if (callback)
callback(pkt_->data, pkt_->size, pkt_->flags & AV_PKT_FLAG_KEY, obj,
pkt_->pts);
}
_exit:
av_packet_unref(pkt_);
return encoded ? 0 : -1;
}
bool convert(void *texture) {
if (frame_->format == AV_PIX_FMT_D3D11 ||
frame_->format == AV_PIX_FMT_QSV) {
ID3D11Texture2D *texture2D = (ID3D11Texture2D *)encode_texture_;
D3D11_TEXTURE2D_DESC desc;
texture2D->GetDesc(&desc);
if (desc.Format != DXGI_FORMAT_NV12) {
LOG_ERROR(std::string("convert: texture format mismatch, ") +
std::to_string(desc.Format) +
" != " + std::to_string(DXGI_FORMAT_NV12));
return false;
}
DXGI_COLOR_SPACE_TYPE colorSpace_in =
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
DXGI_COLOR_SPACE_TYPE colorSpace_out;
if (bt709_) {
if (full_range_) {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709;
} else {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
}
} else {
if (full_range_) {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601;
} else {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
}
}
if (!native_->BgraToNv12((ID3D11Texture2D *)texture, texture2D, width_,
height_, colorSpace_in, colorSpace_out)) {
LOG_ERROR(std::string("convert: BgraToNv12 failed"));
return false;
}
return true;
} else {
LOG_ERROR(std::string("convert: unsupported format, ") +
std::to_string(frame_->format));
return false;
}
}
bool set_hwframe_ctx() {
AVBufferRef *hw_frames_ref;
AVHWFramesContext *frames_ctx = NULL;
int err = 0;
bool ret = true;
if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx_))) {
LOG_ERROR(std::string("av_hwframe_ctx_alloc failed."));
return false;
}
frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
frames_ctx->format = encoder_->hw_pixfmt_;
frames_ctx->sw_format = encoder_->sw_pixfmt_;
frames_ctx->width = width_;
frames_ctx->height = height_;
frames_ctx->initial_pool_size = 0;
if (encoder_->device_type_ == AV_HWDEVICE_TYPE_D3D11VA) {
frames_ctx->initial_pool_size = 1;
AVD3D11VAFramesContext *frames_hwctx =
(AVD3D11VAFramesContext *)frames_ctx->hwctx;
frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET;
frames_hwctx->MiscFlags = 0;
}
if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {
LOG_ERROR(std::string("av_hwframe_ctx_init failed."));
av_buffer_unref(&hw_frames_ref);
return false;
}
c_->hw_frames_ctx = av_buffer_ref(hw_frames_ref);
if (!c_->hw_frames_ctx) {
LOG_ERROR(std::string("av_buffer_ref failed"));
ret = false;
}
av_buffer_unref(&hw_frames_ref);
return ret;
}
};
void lockContext(void *lock_ctx) { (void)lock_ctx; }
void unlockContext(void *lock_ctx) { (void)lock_ctx; }
} // namespace
extern "C" {
FFmpegVRamEncoder *ffmpeg_vram_new_encoder(void *handle, int64_t luid,
DataFormat dataFormat, int32_t width,
int32_t height, int32_t kbs,
int32_t framerate, int32_t gop) {
FFmpegVRamEncoder *encoder = NULL;
try {
encoder = new FFmpegVRamEncoder(handle, luid, dataFormat, width,
height, kbs, framerate, gop);
if (encoder) {
if (encoder->init()) {
return encoder;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new FFmpegVRamEncoder failed, ") + std::string(e.what()));
}
if (encoder) {
encoder->destroy();
delete encoder;
encoder = NULL;
}
return NULL;
}
int ffmpeg_vram_encode(FFmpegVRamEncoder *encoder, void *texture,
EncodeCallback callback, void *obj, int64_t ms) {
try {
return encoder->encode(texture, callback, obj, ms);
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_vram_encode failed, ") + std::string(e.what()));
}
return -1;
}
void ffmpeg_vram_destroy_encoder(FFmpegVRamEncoder *encoder) {
try {
if (!encoder)
return;
encoder->destroy();
delete encoder;
encoder = NULL;
} catch (const std::exception &e) {
LOG_ERROR(std::string("free encoder failed, ") + std::string(e.what()));
}
}
int ffmpeg_vram_set_bitrate(FFmpegVRamEncoder *encoder, int kbs) {
try {
return encoder->set_bitrate(kbs);
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_ram_set_bitrate failed, ") + std::string(e.what()));
}
return -1;
}
int ffmpeg_vram_set_framerate(FFmpegVRamEncoder *encoder, int32_t framerate) {
try {
return encoder->set_bitrate(framerate);
} catch (const std::exception &e) {
LOG_ERROR(std::string("ffmpeg_vram_set_framerate failed, ") + std::string(e.what()));
}
return -1;
}
int ffmpeg_vram_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs,
int32_t framerate, int32_t gop,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
int count = 0;
struct VendorMapping {
AdapterVendor adapter_vendor;
int driver_vendor;
};
VendorMapping vendors[] = {
{ADAPTER_VENDOR_INTEL, VENDOR_INTEL},
{ADAPTER_VENDOR_NVIDIA, VENDOR_NV},
{ADAPTER_VENDOR_AMD, VENDOR_AMD}
};
for (auto vendorMap : vendors) {
Adapters adapters;
if (!adapters.Init(vendorMap.adapter_vendor))
continue;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
FFmpegVRamEncoder *e = (FFmpegVRamEncoder *)ffmpeg_vram_new_encoder(
(void *)adapter.get()->device_.Get(), currentLuid,
dataFormat, width, height, kbs, framerate, gop);
if (!e)
continue;
if (e->native_->EnsureTexture(e->width_, e->height_)) {
e->native_->next();
int32_t key_obj = 0;
auto start = util::now();
bool succ = ffmpeg_vram_encode(e, e->native_->GetCurrentTexture(), util_encode::vram_encode_test_callback,
&key_obj, 0) == 0 && key_obj == 1;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = (int32_t)vendorMap.driver_vendor; // Map adapter vendor to driver vendor
count += 1;
}
}
e->destroy();
delete e;
e = nullptr;
if (count >= maxDescNum)
break;
}
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -0,0 +1,32 @@
#ifndef FFMPEG_VRAM_FFI_H
#define FFMPEG_VRAM_FFI_H
#include "../common/callback.h"
#include <stdbool.h>
void *ffmpeg_vram_new_decoder(void *device, int64_t luid,
int32_t codecID);
int ffmpeg_vram_decode(void *decoder, uint8_t *data, int len,
DecodeCallback callback, void *obj);
int ffmpeg_vram_destroy_decoder(void *decoder);
int ffmpeg_vram_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum,
int32_t dataFormat, uint8_t *data, int32_t length,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
void *ffmpeg_vram_new_encoder(void *handle, int64_t luid,
int32_t dataFormat, int32_t width, int32_t height,
int32_t kbs, int32_t framerate, int32_t gop);
int ffmpeg_vram_encode(void *encoder, void *tex, EncodeCallback callback,
void *obj, int64_t ms);
int ffmpeg_vram_destroy_encoder(void *encoder);
int ffmpeg_vram_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum,
int32_t dataFormat, int32_t width, int32_t height,
int32_t kbs, int32_t framerate, int32_t gop,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int ffmpeg_vram_set_bitrate(void *encoder, int32_t kbs);
int ffmpeg_vram_set_framerate(void *encoder, int32_t framerate);
#endif // FFMPEG_VRAM_FFI_H

View File

@@ -0,0 +1,481 @@
#include <cstring>
#include <d3d11_allocator.h>
#include <libavutil/pixfmt.h>
#include <sample_defs.h>
#include <sample_utils.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "MFXDEC"
#include "log.h"
#define CHECK_STATUS(X, MSG) \
{ \
mfxStatus __sts = (X); \
if (__sts != MFX_ERR_NONE) { \
MSDK_PRINT_RET_MSG(__sts, MSG); \
LOG_ERROR(std::string(MSG) + "failed, sts=" + std::to_string((int)__sts)); \
return __sts; \
} \
}
#define USE_SHADER
namespace {
class VplDecoder {
public:
std::unique_ptr<NativeDevice> native_ = nullptr;
MFXVideoSession session_;
MFXVideoDECODE *mfxDEC_ = NULL;
std::vector<mfxFrameSurface1> pmfxSurfaces_;
mfxVideoParam mfxVideoParams_;
bool initialized_ = false;
D3D11FrameAllocator d3d11FrameAllocator_;
mfxFrameAllocResponse mfxResponse_;
void *device_;
int64_t luid_;
DataFormat codecID_;
bool bt709_ = false;
bool full_range_ = false;
VplDecoder(void *device, int64_t luid, DataFormat codecID) {
device_ = device;
luid_ = luid;
codecID_ = codecID;
ZeroMemory(&mfxVideoParams_, sizeof(mfxVideoParams_));
ZeroMemory(&mfxResponse_, sizeof(mfxResponse_));
}
~VplDecoder() {}
int destroy() {
if (mfxDEC_) {
mfxDEC_->Close();
delete mfxDEC_;
mfxDEC_ = NULL;
}
return 0;
}
mfxStatus init() {
mfxStatus sts = MFX_ERR_NONE;
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)device_, 4)) {
LOG_ERROR(std::string("Failed to initialize native device"));
return MFX_ERR_DEVICE_FAILED;
}
sts = InitializeMFX();
CHECK_STATUS(sts, "InitializeMFX");
// Create Media SDK decoder
mfxDEC_ = new MFXVideoDECODE(session_);
if (!mfxDEC_) {
LOG_ERROR(std::string("Failed to create MFXVideoDECODE"));
return MFX_ERR_NOT_INITIALIZED;
}
memset(&mfxVideoParams_, 0, sizeof(mfxVideoParams_));
if (!convert_codec(codecID_, mfxVideoParams_.mfx.CodecId)) {
LOG_ERROR(std::string("Unsupported codec"));
return MFX_ERR_UNSUPPORTED;
}
mfxVideoParams_.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY;
// AsyncDepth: sSpecifies how many asynchronous operations an
// application performs before the application explicitly synchronizes the
// result. If zero, the value is not specified
mfxVideoParams_.AsyncDepth = 1; // Not important.
// DecodedOrder: For AVC and HEVC, used to instruct the decoder
// to return output frames in the decoded order. Must be zero for all other
// decoders.
mfxVideoParams_.mfx.DecodedOrder = true; // Not important.
mfxVideoParams_.mfx.FrameInfo.FrameRateExtN = 30;
mfxVideoParams_.mfx.FrameInfo.FrameRateExtD = 1;
mfxVideoParams_.mfx.FrameInfo.AspectRatioW = 1;
mfxVideoParams_.mfx.FrameInfo.AspectRatioH = 1;
mfxVideoParams_.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
mfxVideoParams_.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
// Validate video decode parameters (optional)
sts = mfxDEC_->Query(&mfxVideoParams_, &mfxVideoParams_);
CHECK_STATUS(sts, "Query");
return MFX_ERR_NONE;
}
int decode(uint8_t *data, int len, DecodeCallback callback, void *obj) {
mfxStatus sts = MFX_ERR_NONE;
mfxSyncPoint syncp;
mfxFrameSurface1 *pmfxOutSurface = NULL;
bool decoded = false;
mfxBitstream mfxBS;
setBitStream(&mfxBS, data, len);
if (!initialized_) {
sts = initializeDecode(&mfxBS, false);
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("initializeDecode failed, sts=") + std::to_string((int)sts));
return -1;
}
initialized_ = true;
}
setBitStream(&mfxBS, data, len);
auto start = util::now();
do {
if (util::elapsed_ms(start) > DECODE_TIMEOUT_MS) {
LOG_ERROR(std::string("decode timeout"));
break;
}
int nIndex =
GetFreeSurfaceIndex(pmfxSurfaces_.data(),
pmfxSurfaces_.size()); // Find free frame surface
if (nIndex >= pmfxSurfaces_.size()) {
LOG_ERROR(std::string("GetFreeSurfaceIndex failed, nIndex=") +
std::to_string(nIndex));
break;
}
sts = mfxDEC_->DecodeFrameAsync(&mfxBS, &pmfxSurfaces_[nIndex],
&pmfxOutSurface, &syncp);
if (MFX_ERR_NONE == sts) {
if (!syncp) {
LOG_ERROR(std::string("should not happen, syncp is NULL while error is none"));
break;
}
sts = session_.SyncOperation(syncp, 1000);
if (MFX_ERR_NONE != sts) {
LOG_ERROR(std::string("SyncOperation failed, sts=") + std::to_string((int)sts));
break;
}
if (!pmfxOutSurface) {
LOG_ERROR(std::string("pmfxOutSurface is null"));
break;
}
if (!convert(pmfxOutSurface)) {
LOG_ERROR(std::string("Failed to convert"));
break;
}
if (callback)
callback(native_->GetCurrentTexture(), obj);
decoded = true;
break;
} else if (MFX_WRN_DEVICE_BUSY == sts) {
LOG_INFO(std::string("Device busy"));
Sleep(1);
continue;
} else if (MFX_ERR_INCOMPATIBLE_VIDEO_PARAM == sts) {
// https://github.com/Intel-Media-SDK/MediaSDK/blob/master/doc/mediasdk-man.md#multiple-sequence-headers
LOG_INFO(std::string("Incompatible video param, reset decoder"));
// https://github.com/FFmpeg/FFmpeg/blob/f84412d6f4e9c1f1d1a2491f9337d7e789c688ba/libavcodec/qsvdec.c#L736
setBitStream(&mfxBS, data, len);
sts = initializeDecode(&mfxBS, true);
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("initializeDecode failed, sts=") + std::to_string((int)sts));
break;
}
Sleep(1);
continue;
} else if (MFX_WRN_VIDEO_PARAM_CHANGED == sts) {
LOG_TRACE(std::string("new sequence header"));
sts = mfxDEC_->GetVideoParam(&mfxVideoParams_);
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("GetVideoParam failed, sts=") + std::to_string((int)sts));
}
continue;
} else if (MFX_ERR_MORE_SURFACE == sts) {
LOG_INFO(std::string("More surface"));
Sleep(1);
continue;
} else {
LOG_ERROR(std::string("DecodeFrameAsync failed, sts=") + std::to_string(sts));
break;
}
// double confirm, check continue
} while (MFX_ERR_NONE == sts || MFX_WRN_DEVICE_BUSY == sts ||
MFX_ERR_INCOMPATIBLE_VIDEO_PARAM == sts ||
MFX_WRN_VIDEO_PARAM_CHANGED == sts || MFX_ERR_MORE_SURFACE == sts);
if (!decoded) {
LOG_ERROR(std::string("decode failed, sts=") + std::to_string(sts));
}
return decoded ? 0 : -1;
}
private:
mfxStatus InitializeMFX() {
mfxStatus sts = MFX_ERR_NONE;
mfxIMPL impl = MFX_IMPL_HARDWARE_ANY | MFX_IMPL_VIA_D3D11;
mfxVersion ver = {{0, 1}};
D3D11AllocatorParams allocParams;
sts = session_.Init(impl, &ver);
CHECK_STATUS(sts, "session Init");
sts = session_.SetHandle(MFX_HANDLE_D3D11_DEVICE, native_->device_.Get());
CHECK_STATUS(sts, "SetHandle");
allocParams.bUseSingleTexture = false; // important
allocParams.pDevice = native_->device_.Get();
allocParams.uncompressedResourceMiscFlags = 0;
sts = d3d11FrameAllocator_.Init(&allocParams);
CHECK_STATUS(sts, "init D3D11FrameAllocator");
sts = session_.SetFrameAllocator(&d3d11FrameAllocator_);
CHECK_STATUS(sts, "SetFrameAllocator");
return MFX_ERR_NONE;
}
bool convert_codec(DataFormat dataFormat, mfxU32 &CodecId) {
switch (dataFormat) {
case H264:
CodecId = MFX_CODEC_AVC;
return true;
case H265:
CodecId = MFX_CODEC_HEVC;
return true;
}
return false;
}
mfxStatus initializeDecode(mfxBitstream *mfxBS, bool reinit) {
mfxStatus sts = MFX_ERR_NONE;
mfxFrameAllocRequest Request;
memset(&Request, 0, sizeof(Request));
mfxU16 numSurfaces;
mfxU16 width, height;
mfxU8 bitsPerPixel = 12; // NV12
mfxU32 surfaceSize;
mfxU8 *surfaceBuffers;
// mfxExtVideoSignalInfo got MFX_ERR_INVALID_VIDEO_PARAM
// mfxExtVideoSignalInfo video_signal_info = {0};
// https://spec.oneapi.io/versions/1.1-rev-1/elements/oneVPL/source/API_ref/VPL_func_vid_decode.html#mfxvideodecode-decodeheader
sts = mfxDEC_->DecodeHeader(mfxBS, &mfxVideoParams_);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
sts = mfxDEC_->QueryIOSurf(&mfxVideoParams_, &Request);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
numSurfaces = Request.NumFrameSuggested;
// Request.Type |= WILL_READ; // This line is only required for Windows
// DirectX11 to ensure that surfaces can be retrieved by the application
// Allocate surfaces for decoder
if (reinit) {
sts = d3d11FrameAllocator_.FreeFrames(&mfxResponse_);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
}
sts = d3d11FrameAllocator_.AllocFrames(&Request, &mfxResponse_);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
// Allocate surface headers (mfxFrameSurface1) for decoder
pmfxSurfaces_.resize(numSurfaces);
for (int i = 0; i < numSurfaces; i++) {
memset(&pmfxSurfaces_[i], 0, sizeof(mfxFrameSurface1));
pmfxSurfaces_[i].Info = mfxVideoParams_.mfx.FrameInfo;
pmfxSurfaces_[i].Data.MemId =
mfxResponse_
.mids[i]; // MID (memory id) represents one video NV12 surface
}
// Initialize the Media SDK decoder
if (reinit) {
// https://github.com/FFmpeg/FFmpeg/blob/f84412d6f4e9c1f1d1a2491f9337d7e789c688ba/libavcodec/qsvdec.c#L181
sts = mfxDEC_->Close();
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
}
sts = mfxDEC_->Init(&mfxVideoParams_);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
return MFX_ERR_NONE;
}
void setBitStream(mfxBitstream *mfxBS, uint8_t *data, int len) {
memset(mfxBS, 0, sizeof(mfxBitstream));
mfxBS->Data = data;
mfxBS->DataLength = len;
mfxBS->MaxLength = len;
mfxBS->DataFlag = MFX_BITSTREAM_COMPLETE_FRAME;
}
bool convert(mfxFrameSurface1 *pmfxOutSurface) {
mfxStatus sts = MFX_ERR_NONE;
mfxHDLPair pair = {NULL};
sts = d3d11FrameAllocator_.GetFrameHDL(pmfxOutSurface->Data.MemId,
(mfxHDL *)&pair);
if (MFX_ERR_NONE != sts) {
LOG_ERROR(std::string("Failed to GetFrameHDL"));
return false;
}
ID3D11Texture2D *texture = (ID3D11Texture2D *)pair.first;
D3D11_TEXTURE2D_DESC desc2D;
texture->GetDesc(&desc2D);
if (!native_->EnsureTexture(pmfxOutSurface->Info.CropW,
pmfxOutSurface->Info.CropH)) {
LOG_ERROR(std::string("Failed to EnsureTexture"));
return false;
}
native_->next(); // comment out to remove picture shaking
#ifdef USE_SHADER
native_->BeginQuery();
if (!native_->Nv12ToBgra(pmfxOutSurface->Info.CropW,
pmfxOutSurface->Info.CropH, texture,
native_->GetCurrentTexture(), 0)) {
LOG_ERROR(std::string("Failed to Nv12ToBgra"));
native_->EndQuery();
return false;
}
native_->EndQuery();
native_->Query();
#else
native_->BeginQuery();
// nv12 -> bgra
D3D11_VIDEO_PROCESSOR_CONTENT_DESC contentDesc;
ZeroMemory(&contentDesc, sizeof(contentDesc));
contentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
contentDesc.InputFrameRate.Numerator = 60;
contentDesc.InputFrameRate.Denominator = 1;
// TODO: aligned width, height or crop width, height
contentDesc.InputWidth = pmfxOutSurface->Info.CropW;
contentDesc.InputHeight = pmfxOutSurface->Info.CropH;
contentDesc.OutputWidth = pmfxOutSurface->Info.CropW;
contentDesc.OutputHeight = pmfxOutSurface->Info.CropH;
contentDesc.OutputFrameRate.Numerator = 60;
contentDesc.OutputFrameRate.Denominator = 1;
DXGI_COLOR_SPACE_TYPE colorSpace_out =
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
DXGI_COLOR_SPACE_TYPE colorSpace_in;
if (bt709_) {
if (full_range_) {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709;
} else {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
}
} else {
if (full_range_) {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601;
} else {
colorSpace_in = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
}
}
if (!native_->Process(texture, native_->GetCurrentTexture(), contentDesc,
colorSpace_in, colorSpace_out, 0)) {
LOG_ERROR(std::string("Failed to process"));
native_->EndQuery();
return false;
}
native_->context_->Flush();
native_->EndQuery();
if (!native_->Query()) {
LOG_ERROR(std::string("Failed to query"));
return false;
}
#endif
return true;
}
};
} // namespace
extern "C" {
int mfx_destroy_decoder(void *decoder) {
VplDecoder *p = (VplDecoder *)decoder;
if (p) {
p->destroy();
delete p;
p = NULL;
}
return 0;
}
void *mfx_new_decoder(void *device, int64_t luid, DataFormat codecID) {
VplDecoder *p = NULL;
try {
p = new VplDecoder(device, luid, codecID);
if (p) {
if (p->init() == MFX_ERR_NONE) {
return p;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new failed: ") + e.what());
}
if (p) {
p->destroy();
delete p;
p = NULL;
}
return NULL;
}
int mfx_decode(void *decoder, uint8_t *data, int len, DecodeCallback callback,
void *obj) {
try {
VplDecoder *p = (VplDecoder *)decoder;
if (p->decode(data, len, callback, obj) == 0) {
return HWCODEC_SUCCESS;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("decode failed: ") + e.what());
}
return HWCODEC_ERR_COMMON;
}
int mfx_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum, DataFormat dataFormat,
uint8_t *data, int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_INTEL))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
VplDecoder *p = (VplDecoder *)mfx_new_decoder(
nullptr, currentLuid, dataFormat);
if (!p)
continue;
auto start = util::now();
bool succ = mfx_decode(p, data, length, nullptr, nullptr) == 0;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_INTEL;
count += 1;
}
p->destroy();
delete p;
p = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
std::cerr << e.what() << '\n';
}
return -1;
}
} // extern "C"

View File

@@ -0,0 +1,709 @@
#include <cstring>
#include <iostream>
#include <libavutil/pixfmt.h>
#include <limits>
#include <sample_defs.h>
#include <sample_utils.h>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "MFXENC"
#include "log.h"
// #define CONFIG_USE_VPP
#define CONFIG_USE_D3D_CONVERT
#define CHECK_STATUS(X, MSG) \
{ \
mfxStatus __sts = (X); \
if (__sts != MFX_ERR_NONE) { \
LOG_ERROR(std::string(MSG) + " failed, sts=" + std::to_string((int)__sts)); \
return __sts; \
} \
}
namespace {
mfxStatus MFX_CDECL simple_getHDL(mfxHDL pthis, mfxMemId mid, mfxHDL *handle) {
mfxHDLPair *pair = (mfxHDLPair *)handle;
pair->first = mid;
pair->second = (mfxHDL)(UINT)0;
return MFX_ERR_NONE;
}
mfxFrameAllocator frameAllocator{{}, NULL, NULL, NULL,
NULL, simple_getHDL, NULL};
mfxStatus InitSession(MFXVideoSession &session) {
mfxInitParam mfxparams{};
mfxIMPL impl = MFX_IMPL_HARDWARE_ANY | MFX_IMPL_VIA_D3D11;
mfxparams.Implementation = impl;
mfxparams.Version.Major = 1;
mfxparams.Version.Minor = 0;
mfxparams.GPUCopy = MFX_GPUCOPY_OFF;
return session.InitEx(mfxparams);
}
class VplEncoder {
public:
std::unique_ptr<NativeDevice> native_ = nullptr;
MFXVideoSession session_;
MFXVideoENCODE *mfxENC_ = nullptr;
std::vector<mfxFrameSurface1> encSurfaces_;
std::vector<mfxU8> bstData_;
mfxBitstream mfxBS_;
mfxVideoParam mfxEncParams_;
mfxExtBuffer *extbuffers_[4] = {NULL, NULL, NULL, NULL};
mfxExtCodingOption coding_option_;
mfxExtCodingOption2 coding_option2_;
mfxExtCodingOption3 coding_option3_;
mfxExtVideoSignalInfo signal_info_;
ComPtr<ID3D11Texture2D> nv12Texture_ = nullptr;
// vpp
#ifdef CONFIG_USE_VPP
MFXVideoVPP *mfxVPP_ = nullptr;
mfxVideoParam vppParams_;
mfxExtBuffer *vppExtBuffers_[1] = {NULL};
mfxExtVPPDoNotUse vppDontUse_;
mfxU32 vppDontUseArgList_[4];
std::vector<mfxFrameSurface1> vppSurfaces_;
#endif
void *handle_ = nullptr;
int64_t luid_;
DataFormat dataFormat_;
int32_t width_ = 0;
int32_t height_ = 0;
int32_t kbs_;
int32_t framerate_;
int32_t gop_;
bool full_range_ = false;
bool bt709_ = false;
VplEncoder(void *handle, int64_t luid, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs, int32_t framerate,
int32_t gop) {
handle_ = handle;
luid_ = luid;
dataFormat_ = dataFormat;
width_ = width;
height_ = height;
kbs_ = kbs;
framerate_ = framerate;
gop_ = gop;
}
~VplEncoder() {}
mfxStatus Reset() {
mfxStatus sts = MFX_ERR_NONE;
if (!native_) {
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)handle_)) {
LOG_ERROR(std::string("failed to init native device"));
return MFX_ERR_DEVICE_FAILED;
}
}
sts = resetMFX();
CHECK_STATUS(sts, "resetMFX");
#ifdef CONFIG_USE_VPP
sts = resetVpp();
CHECK_STATUS(sts, "resetVpp");
#endif
sts = resetEnc();
CHECK_STATUS(sts, "resetEnc");
return MFX_ERR_NONE;
}
int encode(ID3D11Texture2D *tex, EncodeCallback callback, void *obj,
int64_t ms) {
mfxStatus sts = MFX_ERR_NONE;
int nEncSurfIdx =
GetFreeSurfaceIndex(encSurfaces_.data(), encSurfaces_.size());
if (nEncSurfIdx >= encSurfaces_.size()) {
LOG_ERROR(std::string("no free enc surface"));
return -1;
}
mfxFrameSurface1 *encSurf = &encSurfaces_[nEncSurfIdx];
#ifdef CONFIG_USE_VPP
mfxSyncPoint syncp;
sts = vppOneFrame(tex, encSurf, syncp);
syncp = NULL;
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("vppOneFrame failed, sts=") + std::to_string((int)sts));
return -1;
}
#elif defined(CONFIG_USE_D3D_CONVERT)
DXGI_COLOR_SPACE_TYPE colorSpace_in =
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
DXGI_COLOR_SPACE_TYPE colorSpace_out;
if (bt709_) {
if (full_range_) {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709;
} else {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
}
} else {
if (full_range_) {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601;
} else {
colorSpace_out = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
}
}
if (!nv12Texture_) {
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
tex->GetDesc(&desc);
desc.Format = DXGI_FORMAT_NV12;
desc.MiscFlags = 0;
HRI(native_->device_->CreateTexture2D(
&desc, NULL, nv12Texture_.ReleaseAndGetAddressOf()));
}
if (!native_->BgraToNv12(tex, nv12Texture_.Get(), width_, height_,
colorSpace_in, colorSpace_out)) {
LOG_ERROR(std::string("failed to convert to NV12"));
return -1;
}
encSurf->Data.MemId = nv12Texture_.Get();
#else
encSurf->Data.MemId = tex;
#endif
return encodeOneFrame(encSurf, callback, obj, ms);
}
void destroy() {
if (mfxENC_) {
// - It is recommended to close Media SDK components first, before
// releasing allocated surfaces, since
// some surfaces may still be locked by internal Media SDK resources.
mfxENC_->Close();
delete mfxENC_;
mfxENC_ = NULL;
}
#ifdef CONFIG_USE_VPP
if (mfxVPP_) {
mfxVPP_->Close();
delete mfxVPP_;
mfxVPP_ = NULL;
}
#endif
// session closed automatically on destruction
}
private:
mfxStatus resetMFX() {
mfxStatus sts = MFX_ERR_NONE;
sts = InitSession(session_);
CHECK_STATUS(sts, "InitSession");
sts = session_.SetHandle(MFX_HANDLE_D3D11_DEVICE, native_->device_.Get());
CHECK_STATUS(sts, "SetHandle");
sts = session_.SetFrameAllocator(&frameAllocator);
CHECK_STATUS(sts, "SetFrameAllocator");
return MFX_ERR_NONE;
}
#ifdef CONFIG_USE_VPP
mfxStatus resetVpp() {
mfxStatus sts = MFX_ERR_NONE;
memset(&vppParams_, 0, sizeof(vppParams_));
vppParams_.IOPattern =
MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY;
vppParams_.vpp.In.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
vppParams_.vpp.In.FrameRateExtN = framerate_;
vppParams_.vpp.In.FrameRateExtD = 1;
vppParams_.vpp.In.Width = MSDK_ALIGN16(width_);
vppParams_.vpp.In.Height =
(MFX_PICSTRUCT_PROGRESSIVE == vppParams_.vpp.In.PicStruct)
? MSDK_ALIGN16(height_)
: MSDK_ALIGN32(height_);
vppParams_.vpp.In.CropX = 0;
vppParams_.vpp.In.CropY = 0;
vppParams_.vpp.In.CropW = width_;
vppParams_.vpp.In.CropH = height_;
vppParams_.vpp.In.Shift = 0;
memcpy(&vppParams_.vpp.Out, &vppParams_.vpp.In, sizeof(vppParams_.vpp.Out));
vppParams_.vpp.In.FourCC = MFX_FOURCC_RGB4;
vppParams_.vpp.Out.FourCC = MFX_FOURCC_NV12;
vppParams_.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
vppParams_.vpp.Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
vppParams_.AsyncDepth = 1;
vppParams_.ExtParam = vppExtBuffers_;
vppParams_.NumExtParam = 1;
vppExtBuffers_[0] = (mfxExtBuffer *)&vppDontUse_;
vppDontUse_.Header.BufferId = MFX_EXTBUFF_VPP_DONOTUSE;
vppDontUse_.Header.BufferSz = sizeof(vppDontUse_);
vppDontUse_.AlgList = vppDontUseArgList_;
vppDontUse_.NumAlg = 4;
vppDontUseArgList_[0] = MFX_EXTBUFF_VPP_DENOISE;
vppDontUseArgList_[1] = MFX_EXTBUFF_VPP_SCENE_ANALYSIS;
vppDontUseArgList_[2] = MFX_EXTBUFF_VPP_DETAIL;
vppDontUseArgList_[3] = MFX_EXTBUFF_VPP_PROCAMP;
if (mfxVPP_) {
mfxVPP_->Close();
delete mfxVPP_;
mfxVPP_ = NULL;
}
mfxVPP_ = new MFXVideoVPP(session_);
if (!mfxVPP_) {
LOG_ERROR(std::string("Failed to create MFXVideoVPP"));
return MFX_ERR_MEMORY_ALLOC;
}
sts = mfxVPP_->Query(&vppParams_, &vppParams_);
CHECK_STATUS(sts, "vpp query");
mfxFrameAllocRequest vppAllocRequest;
ZeroMemory(&vppAllocRequest, sizeof(vppAllocRequest));
memcpy(&vppAllocRequest.Info, &vppParams_.vpp.In, sizeof(mfxFrameInfo));
sts = mfxVPP_->QueryIOSurf(&vppParams_, &vppAllocRequest);
CHECK_STATUS(sts, "vpp QueryIOSurf");
vppSurfaces_.resize(vppAllocRequest.NumFrameSuggested);
for (int i = 0; i < vppAllocRequest.NumFrameSuggested; i++) {
memset(&vppSurfaces_[i], 0, sizeof(mfxFrameSurface1));
memcpy(&vppSurfaces_[i].Info, &vppParams_.vpp.In, sizeof(mfxFrameInfo));
}
sts = mfxVPP_->Init(&vppParams_);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
CHECK_STATUS(sts, "vpp init");
return MFX_ERR_NONE;
}
#endif
mfxStatus resetEnc() {
mfxStatus sts = MFX_ERR_NONE;
memset(&mfxEncParams_, 0, sizeof(mfxEncParams_));
// Basic
if (!convert_codec(dataFormat_, mfxEncParams_.mfx.CodecId)) {
LOG_ERROR(std::string("unsupported dataFormat: ") + std::to_string(dataFormat_));
return MFX_ERR_UNSUPPORTED;
}
// mfxEncParams_.mfx.LowPower = MFX_CODINGOPTION_ON;
mfxEncParams_.mfx.BRCParamMultiplier = 0;
// Frame Info
mfxEncParams_.mfx.FrameInfo.FrameRateExtN = framerate_;
mfxEncParams_.mfx.FrameInfo.FrameRateExtD = 1;
#ifdef CONFIG_USE_VPP
mfxEncParams_.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
mfxEncParams_.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
#elif defined(CONFIG_USE_D3D_CONVERT)
mfxEncParams_.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
mfxEncParams_.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
#else
mfxEncParams_.mfx.FrameInfo.FourCC = MFX_FOURCC_BGR4;
mfxEncParams_.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
#endif
mfxEncParams_.mfx.FrameInfo.BitDepthLuma = 8;
mfxEncParams_.mfx.FrameInfo.BitDepthChroma = 8;
mfxEncParams_.mfx.FrameInfo.Shift = 0;
mfxEncParams_.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
mfxEncParams_.mfx.FrameInfo.CropX = 0;
mfxEncParams_.mfx.FrameInfo.CropY = 0;
mfxEncParams_.mfx.FrameInfo.CropW = width_;
mfxEncParams_.mfx.FrameInfo.CropH = height_;
// Width must be a multiple of 16
// Height must be a multiple of 16 in case of frame picture and a multiple
// of 32 in case of field picture
mfxEncParams_.mfx.FrameInfo.Width = MSDK_ALIGN16(width_);
mfxEncParams_.mfx.FrameInfo.Height =
(MFX_PICSTRUCT_PROGRESSIVE == mfxEncParams_.mfx.FrameInfo.PicStruct)
? MSDK_ALIGN16(height_)
: MSDK_ALIGN32(height_);
// Encoding Options
mfxEncParams_.mfx.EncodedOrder = 0;
mfxEncParams_.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
// Configuration for low latency
mfxEncParams_.AsyncDepth = 1; // 1 is best for low latency
mfxEncParams_.mfx.GopRefDist =
1; // 1 is best for low latency, I and P frames only
mfxEncParams_.mfx.GopPicSize = (gop_ > 0 && gop_ < 0xFFFF) ? gop_ : 0xFFFF;
// quality
// https://www.intel.com/content/www/us/en/developer/articles/technical/common-bitrate-control-methods-in-intel-media-sdk.html
mfxEncParams_.mfx.TargetUsage = MFX_TARGETUSAGE_BEST_SPEED;
mfxEncParams_.mfx.RateControlMethod = MFX_RATECONTROL_VBR;
mfxEncParams_.mfx.InitialDelayInKB = 0;
mfxEncParams_.mfx.BufferSizeInKB = 512;
mfxEncParams_.mfx.TargetKbps = kbs_;
mfxEncParams_.mfx.MaxKbps = kbs_;
mfxEncParams_.mfx.NumSlice = 1;
mfxEncParams_.mfx.NumRefFrame = 0;
if (H264 == dataFormat_) {
mfxEncParams_.mfx.CodecLevel = MFX_LEVEL_AVC_51;
mfxEncParams_.mfx.CodecProfile = MFX_PROFILE_AVC_MAIN;
} else if (H265 == dataFormat_) {
mfxEncParams_.mfx.CodecLevel = MFX_LEVEL_HEVC_51;
mfxEncParams_.mfx.CodecProfile = MFX_PROFILE_HEVC_MAIN;
}
resetEncExtParams();
// Create Media SDK encoder
if (mfxENC_) {
mfxENC_->Close();
delete mfxENC_;
mfxENC_ = NULL;
}
mfxENC_ = new MFXVideoENCODE(session_);
if (!mfxENC_) {
LOG_ERROR(std::string("failed to create MFXVideoENCODE"));
return MFX_ERR_NOT_INITIALIZED;
}
// Validate video encode parameters (optional)
// - In this example the validation result is written to same structure
// - MFX_WRN_INCOMPATIBLE_VIDEO_PARAM is returned if some of the video
// parameters are not supported,
// instead the encoder will select suitable parameters closest matching
// the requested configuration
sts = mfxENC_->Query(&mfxEncParams_, &mfxEncParams_);
MSDK_IGNORE_MFX_STS(sts, MFX_WRN_INCOMPATIBLE_VIDEO_PARAM);
CHECK_STATUS(sts, "Query");
mfxFrameAllocRequest EncRequest;
memset(&EncRequest, 0, sizeof(EncRequest));
sts = mfxENC_->QueryIOSurf(&mfxEncParams_, &EncRequest);
CHECK_STATUS(sts, "QueryIOSurf");
// Allocate surface headers (mfxFrameSurface1) for encoder
encSurfaces_.resize(EncRequest.NumFrameSuggested);
for (int i = 0; i < EncRequest.NumFrameSuggested; i++) {
memset(&encSurfaces_[i], 0, sizeof(mfxFrameSurface1));
memcpy(&encSurfaces_[i].Info, &mfxEncParams_.mfx.FrameInfo,
sizeof(mfxFrameInfo));
}
// Initialize the Media SDK encoder
sts = mfxENC_->Init(&mfxEncParams_);
CHECK_STATUS(sts, "Init");
// Retrieve video parameters selected by encoder.
// - BufferSizeInKB parameter is required to set bit stream buffer size
sts = mfxENC_->GetVideoParam(&mfxEncParams_);
CHECK_STATUS(sts, "GetVideoParam");
// Prepare Media SDK bit stream buffer
memset(&mfxBS_, 0, sizeof(mfxBS_));
mfxBS_.MaxLength = mfxEncParams_.mfx.BufferSizeInKB * 1024;
bstData_.resize(mfxBS_.MaxLength);
mfxBS_.Data = bstData_.data();
return MFX_ERR_NONE;
}
#ifdef CONFIG_USE_VPP
mfxStatus vppOneFrame(void *texture, mfxFrameSurface1 *out,
mfxSyncPoint syncp) {
mfxStatus sts = MFX_ERR_NONE;
int surfIdx =
GetFreeSurfaceIndex(vppSurfaces_.data(),
vppSurfaces_.size()); // Find free frame surface
if (surfIdx >= vppSurfaces_.size()) {
LOG_ERROR(std::string("No free vpp surface"));
return MFX_ERR_MORE_SURFACE;
}
mfxFrameSurface1 *in = &vppSurfaces_[surfIdx];
in->Data.MemId = texture;
for (;;) {
sts = mfxVPP_->RunFrameVPPAsync(in, out, NULL, &syncp);
if (MFX_ERR_NONE < sts &&
!syncp) // repeat the call if warning and no output
{
if (MFX_WRN_DEVICE_BUSY == sts)
MSDK_SLEEP(1); // wait if device is busy
} else if (MFX_ERR_NONE < sts && syncp) {
sts = MFX_ERR_NONE; // ignore warnings if output is available
break;
} else {
break; // not a warning
}
}
if (MFX_ERR_NONE == sts) {
sts = session_.SyncOperation(
syncp, 1000); // Synchronize. Wait until encoded frame is ready
CHECK_STATUS(sts, "SyncOperation");
}
return sts;
}
#endif
int encodeOneFrame(mfxFrameSurface1 *in, EncodeCallback callback, void *obj,
int64_t ms) {
mfxStatus sts = MFX_ERR_NONE;
mfxSyncPoint syncp;
bool encoded = false;
auto start = util::now();
do {
if (util::elapsed_ms(start) > ENCODE_TIMEOUT_MS) {
LOG_ERROR(std::string("encode timeout"));
break;
}
mfxBS_.DataLength = 0;
mfxBS_.DataOffset = 0;
mfxBS_.TimeStamp = ms * 90; // ms to 90KHZ
mfxBS_.DecodeTimeStamp = mfxBS_.TimeStamp;
sts = mfxENC_->EncodeFrameAsync(NULL, in, &mfxBS_, &syncp);
if (MFX_ERR_NONE == sts) {
if (!syncp) {
LOG_ERROR(std::string("should not happen, error is none while syncp is null"));
break;
}
sts = session_.SyncOperation(
syncp, 1000); // Synchronize. Wait until encoded frame is ready
if (MFX_ERR_NONE != sts) {
LOG_ERROR(std::string("SyncOperation failed, sts=") + std::to_string(sts));
break;
}
if (mfxBS_.DataLength <= 0) {
LOG_ERROR(std::string("mfxBS_.DataLength <= 0"));
break;
}
int key = (mfxBS_.FrameType & MFX_FRAMETYPE_I) ||
(mfxBS_.FrameType & MFX_FRAMETYPE_IDR);
if (callback)
callback(mfxBS_.Data + mfxBS_.DataOffset, mfxBS_.DataLength, key, obj,
ms);
encoded = true;
break;
} else if (MFX_WRN_DEVICE_BUSY == sts) {
LOG_INFO(std::string("device busy"));
Sleep(1);
continue;
} else if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) {
LOG_ERROR(std::string("not enough buffer, size=") +
std::to_string(mfxBS_.MaxLength));
if (mfxBS_.MaxLength < 10 * 1024 * 1024) {
mfxBS_.MaxLength *= 2;
bstData_.resize(mfxBS_.MaxLength);
mfxBS_.Data = bstData_.data();
Sleep(1);
continue;
} else {
break;
}
} else {
LOG_ERROR(std::string("EncodeFrameAsync failed, sts=") + std::to_string(sts));
break;
}
// double confirm, check continue
} while (MFX_WRN_DEVICE_BUSY == sts || MFX_ERR_NOT_ENOUGH_BUFFER == sts);
if (!encoded) {
LOG_ERROR(std::string("encode failed, sts=") + std::to_string(sts));
}
return encoded ? 0 : -1;
}
void resetEncExtParams() {
// coding option
memset(&coding_option_, 0, sizeof(mfxExtCodingOption));
coding_option_.Header.BufferId = MFX_EXTBUFF_CODING_OPTION;
coding_option_.Header.BufferSz = sizeof(mfxExtCodingOption);
coding_option_.NalHrdConformance = MFX_CODINGOPTION_OFF;
extbuffers_[0] = (mfxExtBuffer *)&coding_option_;
// coding option2
memset(&coding_option2_, 0, sizeof(mfxExtCodingOption2));
coding_option2_.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
coding_option2_.Header.BufferSz = sizeof(mfxExtCodingOption2);
coding_option2_.RepeatPPS = MFX_CODINGOPTION_OFF;
extbuffers_[1] = (mfxExtBuffer *)&coding_option2_;
// coding option3
memset(&coding_option3_, 0, sizeof(mfxExtCodingOption3));
coding_option3_.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3;
coding_option3_.Header.BufferSz = sizeof(mfxExtCodingOption3);
extbuffers_[2] = (mfxExtBuffer *)&coding_option3_;
// signal info
memset(&signal_info_, 0, sizeof(mfxExtVideoSignalInfo));
signal_info_.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
signal_info_.Header.BufferSz = sizeof(mfxExtVideoSignalInfo);
signal_info_.VideoFormat = 5;
signal_info_.ColourDescriptionPresent = 1;
signal_info_.VideoFullRange = !!full_range_;
signal_info_.MatrixCoefficients =
bt709_ ? AVCOL_SPC_BT709 : AVCOL_SPC_SMPTE170M;
signal_info_.ColourPrimaries =
bt709_ ? AVCOL_PRI_BT709 : AVCOL_PRI_SMPTE170M;
signal_info_.TransferCharacteristics =
bt709_ ? AVCOL_TRC_BT709 : AVCOL_TRC_SMPTE170M;
// https://github.com/GStreamer/gstreamer/blob/651dcb49123ec516e7c582e4a49a5f3f15c10f93/subprojects/gst-plugins-bad/sys/qsv/gstqsvh264enc.cpp#L1647
extbuffers_[3] = (mfxExtBuffer *)&signal_info_;
mfxEncParams_.ExtParam = extbuffers_;
mfxEncParams_.NumExtParam = 4;
}
bool convert_codec(DataFormat dataFormat, mfxU32 &CodecId) {
switch (dataFormat) {
case H264:
CodecId = MFX_CODEC_AVC;
return true;
case H265:
CodecId = MFX_CODEC_HEVC;
return true;
}
return false;
}
};
} // namespace
extern "C" {
int mfx_driver_support() {
MFXVideoSession session;
return InitSession(session) == MFX_ERR_NONE ? 0 : -1;
}
int mfx_destroy_encoder(void *encoder) {
VplEncoder *p = (VplEncoder *)encoder;
if (p) {
p->destroy();
delete p;
p = NULL;
}
return 0;
}
void *mfx_new_encoder(void *handle, int64_t luid,
DataFormat dataFormat, int32_t w, int32_t h, int32_t kbs,
int32_t framerate, int32_t gop) {
VplEncoder *p = NULL;
try {
p = new VplEncoder(handle, luid, dataFormat, w, h, kbs, framerate,
gop);
if (!p) {
return NULL;
}
mfxStatus sts = p->Reset();
if (sts == MFX_ERR_NONE) {
return p;
} else {
LOG_ERROR(std::string("Init failed, sts=") + std::to_string(sts));
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("Exception: ") + e.what());
}
if (p) {
p->destroy();
delete p;
p = NULL;
}
return NULL;
}
int mfx_encode(void *encoder, ID3D11Texture2D *tex, EncodeCallback callback,
void *obj, int64_t ms) {
try {
return ((VplEncoder *)encoder)->encode(tex, callback, obj, ms);
} catch (const std::exception &e) {
LOG_ERROR(std::string("Exception: ") + e.what());
}
return -1;
}
int mfx_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
DataFormat dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_INTEL))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
VplEncoder *e = (VplEncoder *)mfx_new_encoder(
(void *)adapter.get()->device_.Get(), currentLuid,
dataFormat, width, height, kbs, framerate, gop);
if (!e)
continue;
if (e->native_->EnsureTexture(e->width_, e->height_)) {
e->native_->next();
int32_t key_obj = 0;
auto start = util::now();
bool succ = mfx_encode(e, e->native_->GetCurrentTexture(), util_encode::vram_encode_test_callback, &key_obj,
0) == 0 && key_obj == 1;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_INTEL;
count += 1;
}
}
e->destroy();
delete e;
e = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
// https://github.com/Intel-Media-SDK/MediaSDK/blob/master/doc/mediasdk-man.md#dynamic-bitrate-change
// https://github.com/Intel-Media-SDK/MediaSDK/blob/master/doc/mediasdk-man.md#mfxinfomfx
// https://spec.oneapi.io/onevpl/2.4.0/programming_guide/VPL_prg_encoding.html#configuration-change
int mfx_set_bitrate(void *encoder, int32_t kbs) {
try {
VplEncoder *p = (VplEncoder *)encoder;
mfxStatus sts = MFX_ERR_NONE;
// https://github.com/GStreamer/gstreamer/blob/e19428a802c2f4ee9773818aeb0833f93509a1c0/subprojects/gst-plugins-bad/sys/qsv/gstqsvencoder.cpp#L1312
p->kbs_ = kbs;
p->mfxENC_->GetVideoParam(&p->mfxEncParams_);
p->mfxEncParams_.mfx.TargetKbps = kbs;
p->mfxEncParams_.mfx.MaxKbps = kbs;
sts = p->mfxENC_->Reset(&p->mfxEncParams_);
if (sts != MFX_ERR_NONE) {
LOG_ERROR(std::string("reset failed, sts=") + std::to_string(sts));
return -1;
}
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("Exception: ") + e.what());
}
return -1;
}
int mfx_set_framerate(void *encoder, int32_t framerate) {
LOG_WARN("not support change framerate");
return -1;
}
}

View File

@@ -0,0 +1,39 @@
#ifndef MFX_FFI_H
#define MFX_FFI_H
#include "../common/callback.h"
#include <stdbool.h>
int mfx_driver_support();
void *mfx_new_encoder(void *handle, int64_t luid,
int32_t dataFormat, int32_t width, int32_t height,
int32_t kbs, int32_t framerate, int32_t gop);
int mfx_encode(void *encoder, void *tex, EncodeCallback callback, void *obj,
int64_t ms);
int mfx_destroy_encoder(void *encoder);
void *mfx_new_decoder(void *device, int64_t luid,
int32_t dataFormat);
int mfx_decode(void *decoder, uint8_t *data, int len, DecodeCallback callback,
void *obj);
int mfx_destroy_decoder(void *decoder);
int mfx_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int mfx_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, uint8_t *data,
int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int mfx_set_bitrate(void *encoder, int32_t kbs);
int mfx_set_framerate(void *encoder, int32_t framerate);
#endif // MFX_FFI_H

View File

@@ -0,0 +1,188 @@
// https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/muxing.c
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/timestamp.h>
}
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LOG_MODULE "MUX"
#include <log.h>
namespace {
typedef struct OutputStream {
AVStream *st;
AVPacket *tmp_pkt;
} OutputStream;
class Muxer {
public:
OutputStream video_st;
AVFormatContext *oc = NULL;
int framerate;
int64_t start_ms;
int64_t last_pts;
int got_first;
Muxer() {}
void destroy() {
OutputStream *ost = &video_st;
if (ost && ost->tmp_pkt)
av_packet_free(&ost->tmp_pkt);
if (oc && oc->pb && !(oc->oformat->flags & AVFMT_NOFILE))
avio_closep(&oc->pb);
if (oc)
avformat_free_context(oc);
}
bool init(const char *filename, int width, int height, int is265,
int framerate) {
OutputStream *ost = &video_st;
ost->st = NULL;
ost->tmp_pkt = NULL;
int ret;
if ((ret = avformat_alloc_output_context2(&oc, NULL, NULL, filename)) < 0) {
LOG_ERROR(std::string("avformat_alloc_output_context2 failed, ret = ") +
std::to_string(ret));
return false;
}
ost->st = avformat_new_stream(oc, NULL);
if (!ost->st) {
LOG_ERROR(std::string("avformat_new_stream failed"));
return false;
}
ost->st->id = oc->nb_streams - 1;
ost->st->codecpar->codec_id = is265 ? AV_CODEC_ID_H265 : AV_CODEC_ID_H264;
ost->st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
ost->st->codecpar->width = width;
ost->st->codecpar->height = height;
if (!(oc->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
LOG_ERROR(std::string("avio_open failed, ret = ") + std::to_string(ret));
return false;
}
}
ost->tmp_pkt = av_packet_alloc();
if (!ost->tmp_pkt) {
LOG_ERROR(std::string("av_packet_alloc failed"));
return false;
}
ret = avformat_write_header(oc, NULL);
if (ret < 0) {
LOG_ERROR(std::string("avformat_write_header failed"));
return false;
}
this->framerate = framerate;
this->start_ms = 0;
this->last_pts = 0;
this->got_first = 0;
return true;
}
int write_video_frame(const uint8_t *data, int len, int64_t pts_ms, int key) {
OutputStream *ost = &video_st;
AVPacket *pkt = ost->tmp_pkt;
AVFormatContext *fmt_ctx = oc;
int ret;
if (framerate <= 0)
return -3;
if (!got_first) {
if (key != 1)
return -2;
start_ms = pts_ms;
}
int64_t pts = (pts_ms - start_ms); // use write timestamp
if (pts <= last_pts && got_first) {
pts = last_pts + 1000 / framerate;
}
got_first = 1;
pkt->data = (uint8_t *)data;
pkt->size = len;
pkt->pts = pts;
pkt->dts = pkt->pts; // no B-frame
int64_t duration = pkt->pts - last_pts;
last_pts = pkt->pts;
pkt->duration = duration > 0 ? duration : 1000 / framerate; // predict
AVRational rational;
rational.num = 1;
rational.den = 1000;
av_packet_rescale_ts(pkt, rational,
ost->st->time_base); // ms -> stream timebase
pkt->stream_index = ost->st->index;
if (key == 1) {
pkt->flags |= AV_PKT_FLAG_KEY;
} else {
pkt->flags &= ~AV_PKT_FLAG_KEY;
}
ret = av_write_frame(fmt_ctx, pkt);
if (ret < 0) {
LOG_ERROR(std::string("av_write_frame failed, ret = ") + std::to_string(ret));
return -1;
}
return 0;
}
};
} // namespace
extern "C" Muxer *hwcodec_new_muxer(const char *filename, int width, int height,
int is265, int framerate) {
Muxer *muxer = NULL;
try {
muxer = new Muxer();
if (muxer) {
if (muxer->init(filename, width, height, is265, framerate)) {
return muxer;
}
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("new muxer exception: ") + std::string(e.what()));
}
if (muxer) {
muxer->destroy();
delete muxer;
muxer = NULL;
}
return NULL;
}
extern "C" int hwcodec_write_video_frame(Muxer *muxer, const uint8_t *data,
int len, int64_t pts_ms, int key) {
try {
return muxer->write_video_frame(data, len, pts_ms, key);
} catch (const std::exception &e) {
LOG_ERROR(std::string("write_video_frame exception: ") + std::string(e.what()));
}
return -1;
}
extern "C" int hwcodec_write_tail(Muxer *muxer) {
return av_write_trailer(muxer->oc);
}
extern "C" void hwcodec_free_muxer(Muxer *muxer) {
try {
if (!muxer)
return;
muxer->destroy();
delete muxer;
muxer = NULL;
} catch (const std::exception &e) {
LOG_ERROR(std::string("free_muxer exception: ") + std::string(e.what()));
}
}

View File

@@ -0,0 +1,15 @@
#ifndef MUX_FFI_H
#define MUX_FFI_H
#include <stdint.h>
void *hwcodec_new_muxer(const char *filename, int width, int height, int is265,
int framerate);
int hwcodec_write_video_frame(void *muxer, const uint8_t *data, int len,
int64_t pts_ms, int key);
int hwcodec_write_tail(void *muxer);
void hwcodec_free_muxer(void *muxer);
#endif // FFI_H

View File

@@ -0,0 +1,693 @@
#define FFNV_LOG_FUNC
#define FFNV_DEBUG_LOG_FUNC
#include <DirectXMath.h>
#include <Samples/NvCodec/NvDecoder/NvDecoder.h>
#include <Samples/Utils/NvCodecUtils.h>
#include <algorithm>
#include <array>
#include <d3dcompiler.h>
#include <directxcolors.h>
#include <iostream>
#include <libavutil/pixfmt.h>
#include <thread>
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "CUVID"
#include "log.h"
#define NUMVERTICES 6
using namespace DirectX;
namespace {
#define succ(call) ((call) == 0)
class CUVIDAutoUnmapper {
CudaFunctions *cudl_ = NULL;
CUgraphicsResource *pCuResource_ = NULL;
public:
CUVIDAutoUnmapper(CudaFunctions *cudl, CUgraphicsResource *pCuResource)
: cudl_(cudl), pCuResource_(pCuResource) {
if (!succ(cudl->cuGraphicsMapResources(1, pCuResource, 0))) {
LOG_TRACE(std::string("cuGraphicsMapResources failed"));
NVDEC_THROW_ERROR("cuGraphicsMapResources failed", CUDA_ERROR_UNKNOWN);
}
}
~CUVIDAutoUnmapper() {
if (!succ(cudl_->cuGraphicsUnmapResources(1, pCuResource_, 0))) {
LOG_TRACE(std::string("cuGraphicsUnmapResources failed"));
// NVDEC_THROW_ERROR("cuGraphicsUnmapResources failed",
// CUDA_ERROR_UNKNOWN);
}
}
};
class CUVIDAutoCtxPopper {
CudaFunctions *cudl_ = NULL;
public:
CUVIDAutoCtxPopper(CudaFunctions *cudl, CUcontext cuContext) : cudl_(cudl) {
if (!succ(cudl->cuCtxPushCurrent(cuContext))) {
LOG_TRACE(std::string("cuCtxPushCurrent failed"));
NVDEC_THROW_ERROR("cuCtxPopCurrent failed", CUDA_ERROR_UNKNOWN);
}
}
~CUVIDAutoCtxPopper() {
if (!succ(cudl_->cuCtxPopCurrent(NULL))) {
LOG_TRACE(std::string("cuCtxPopCurrent failed"));
// NVDEC_THROW_ERROR("cuCtxPopCurrent failed", CUDA_ERROR_UNKNOWN);
}
}
};
void load_driver(CudaFunctions **pp_cudl, CuvidFunctions **pp_cvdl) {
if (cuda_load_functions(pp_cudl, NULL) < 0) {
LOG_TRACE(std::string("cuda_load_functions failed"));
NVDEC_THROW_ERROR("cuda_load_functions failed", CUDA_ERROR_UNKNOWN);
}
if (cuvid_load_functions(pp_cvdl, NULL) < 0) {
LOG_TRACE(std::string("cuvid_load_functions failed"));
NVDEC_THROW_ERROR("cuvid_load_functions failed", CUDA_ERROR_UNKNOWN);
}
}
void free_driver(CudaFunctions **pp_cudl, CuvidFunctions **pp_cvdl) {
if (*pp_cvdl) {
cuvid_free_functions(pp_cvdl);
*pp_cvdl = NULL;
}
if (*pp_cudl) {
cuda_free_functions(pp_cudl);
*pp_cudl = NULL;
}
}
typedef struct _VERTEX {
DirectX::XMFLOAT3 Pos;
DirectX::XMFLOAT2 TexCoord;
} VERTEX;
class CuvidDecoder {
public:
CudaFunctions *cudl_ = NULL;
CuvidFunctions *cvdl_ = NULL;
NvDecoder *dec_ = NULL;
CUcontext cuContext_ = NULL;
CUgraphicsResource cuResource_[2] = {NULL, NULL}; // r8, r8g8
ComPtr<ID3D11Texture2D> textures_[2] = {NULL, NULL};
ComPtr<ID3D11RenderTargetView> RTV_ = NULL;
ComPtr<ID3D11ShaderResourceView> SRV_[2] = {NULL, NULL};
ComPtr<ID3D11VertexShader> vertexShader_ = NULL;
ComPtr<ID3D11PixelShader> pixelShader_ = NULL;
ComPtr<ID3D11SamplerState> samplerLinear_ = NULL;
std::unique_ptr<NativeDevice> native_ = nullptr;
void *device_;
int64_t luid_;
DataFormat dataFormat_;
bool prepare_tried_ = false;
bool prepare_ok_ = false;
int width_ = 0;
int height_ = 0;
CUVIDEOFORMAT last_video_format_ = {};
public:
CuvidDecoder(void *device, int64_t luid, DataFormat dataFormat) {
device_ = device;
luid_ = luid;
dataFormat_ = dataFormat;
ZeroMemory(&last_video_format_, sizeof(last_video_format_));
load_driver(&cudl_, &cvdl_);
}
~CuvidDecoder() {}
bool init() {
if (!succ(cudl_->cuInit(0))) {
LOG_ERROR(std::string("cuInit failed"));
return false;
}
CUdevice cuDevice = 0;
native_ = std::make_unique<NativeDevice>();
if (!native_->Init(luid_, (ID3D11Device *)device_, 4)) {
LOG_ERROR(std::string("Failed to init native device"));
return false;
}
if (!succ(cudl_->cuD3D11GetDevice(&cuDevice, native_->adapter_.Get()))) {
LOG_ERROR(std::string("Failed to get cuDevice"));
return false;
}
if (!succ(cudl_->cuCtxCreate(&cuContext_, 0, cuDevice))) {
LOG_ERROR(std::string("Failed to create cuContext"));
return false;
}
if (!create_nvdecoder()) {
LOG_ERROR(std::string("Failed to create nvdecoder"));
return false;
}
return true;
}
// ref: HandlePictureDisplay
int decode(uint8_t *data, int len, DecodeCallback callback, void *obj) {
int nFrameReturned = decode_and_recreate(data, len);
if (nFrameReturned == -2) {
nFrameReturned = dec_->Decode(data, len, CUVID_PKT_ENDOFPICTURE);
}
if (nFrameReturned <= 0) {
return -1;
}
last_video_format_ = dec_->GetLatestVideoFormat();
cudaVideoSurfaceFormat format = dec_->GetOutputFormat();
int width = dec_->GetWidth();
int height = dec_->GetHeight();
if (prepare_tried_ && (width != width_ || height != height_)) {
LOG_INFO(std::string("resolution changed, (") + std::to_string(width_) + "," +
std::to_string(height_) + ") -> (" + std::to_string(width) +
"," + std::to_string(height) + ")");
reset_prepare();
width_ = width;
height_ = height;
}
if (!prepare()) {
LOG_ERROR(std::string("prepare failed"));
return -1;
}
bool decoded = false;
for (int i = 0; i < nFrameReturned; i++) {
uint8_t *pFrame = dec_->GetFrame();
native_->BeginQuery();
if (!copy_cuda_frame(pFrame)) {
LOG_ERROR(std::string("copy_cuda_frame failed"));
native_->EndQuery();
return -1;
}
if (!native_->EnsureTexture(width, height)) {
LOG_ERROR(std::string("EnsureTexture failed"));
native_->EndQuery();
return -1;
}
native_->next();
if (!set_rtv(native_->GetCurrentTexture())) {
LOG_ERROR(std::string("set_rtv failed"));
native_->EndQuery();
return -1;
}
if (!draw()) {
LOG_ERROR(std::string("draw failed"));
native_->EndQuery();
return -1;
}
native_->EndQuery();
if (!native_->Query()) {
LOG_ERROR(std::string("Query failed"));
}
if (callback)
callback(native_->GetCurrentTexture(), obj);
decoded = true;
}
return decoded ? 0 : -1;
}
void destroy() {
if (dec_) {
delete dec_;
dec_ = nullptr;
}
if (cudl_ && cuContext_) {
cudl_->cuCtxPushCurrent(cuContext_);
for (int i = 0; i < 2; i++) {
if (cuResource_[i]) {
cudl_->cuGraphicsUnregisterResource(cuResource_[i]);
cuResource_[i] = NULL;
}
}
cudl_->cuCtxPopCurrent(NULL);
cudl_->cuCtxDestroy(cuContext_);
cuContext_ = NULL;
}
free_driver(&cudl_, &cvdl_);
}
private:
void reset_prepare() {
prepare_tried_ = false;
prepare_ok_ = false;
if (cudl_ && cuContext_) {
cudl_->cuCtxPushCurrent(cuContext_);
for (int i = 0; i < 2; i++) {
if (cuResource_[i])
cudl_->cuGraphicsUnregisterResource(cuResource_[i]);
}
cudl_->cuCtxPopCurrent(NULL);
}
for (int i = 0; i < 2; i++) {
textures_[i].Reset();
SRV_[i].Reset();
}
RTV_.Reset();
vertexShader_.Reset();
pixelShader_.Reset();
samplerLinear_.Reset();
}
bool prepare() {
if (prepare_tried_) {
return prepare_ok_;
}
prepare_tried_ = true;
if (!set_srv())
return false;
if (!set_view_port())
return false;
if (!set_sample())
return false;
if (!set_shader())
return false;
if (!set_vertex_buffer())
return false;
if (!register_texture())
return false;
prepare_ok_ = true;
return true;
}
bool copy_cuda_frame(unsigned char *dpNv12) {
int width = dec_->GetWidth();
int height = dec_->GetHeight();
int chromaHeight = dec_->GetChromaHeight();
CUVIDAutoCtxPopper ctxPoper(cudl_, cuContext_);
for (int i = 0; i < 2; i++) {
CUarray dstArray;
CUVIDAutoUnmapper unmapper(cudl_, &cuResource_[i]);
if (!succ(cudl_->cuGraphicsSubResourceGetMappedArray(
&dstArray, cuResource_[i], 0, 0)))
return false;
CUDA_MEMCPY2D m = {0};
m.srcMemoryType = CU_MEMORYTYPE_DEVICE;
m.srcDevice = (CUdeviceptr)(dpNv12 + (width * height) * i);
m.srcPitch = width; // pitch
m.dstMemoryType = CU_MEMORYTYPE_ARRAY;
m.dstArray = dstArray;
m.WidthInBytes = width;
m.Height = i == 0 ? height : chromaHeight;
if (!succ(cudl_->cuMemcpy2D(&m)))
return false;
}
return true;
}
bool draw() {
native_->context_->Draw(NUMVERTICES, 0);
native_->context_->Flush();
return true;
}
// return:
// >=0: nFrameReturned
// -1: failed
// -2: recreated, please decode again
int decode_and_recreate(uint8_t *data, int len) {
try {
int nFrameReturned = dec_->Decode(data, len, CUVID_PKT_ENDOFPICTURE);
if (nFrameReturned <= 0)
return -1;
CUVIDEOFORMAT video_format = dec_->GetLatestVideoFormat();
auto d1 = last_video_format_.display_area;
auto d2 = video_format.display_area;
// reconfigure may cause wrong display area
if (last_video_format_.coded_width != 0 &&
(d1.left != d2.left || d1.right != d2.right || d1.top != d2.top ||
d1.bottom != d2.bottom)) {
LOG_INFO(
std::string("recreate, display area changed from (") + std::to_string(d1.left) +
", " + std::to_string(d1.top) + ", " + std::to_string(d1.right) +
", " + std::to_string(d1.bottom) + ") to (" +
std::to_string(d2.left) + ", " + std::to_string(d2.top) + ", " +
std::to_string(d2.right) + ", " + std::to_string(d2.bottom) + ")");
if (create_nvdecoder()) {
return -2;
} else {
LOG_ERROR(std::string("create_nvdecoder failed"));
}
return -1;
} else {
return nFrameReturned;
}
} catch (const std::exception &e) {
unsigned int maxWidth = dec_->GetMaxWidth();
unsigned int maxHeight = dec_->GetMaxHeight();
CUVIDEOFORMAT video_format = dec_->GetLatestVideoFormat();
// https://github.com/NVIDIA/DALI/blob/4f5ee72b287cfbbe0d400734416ff37bd8027099/dali/operators/reader/loader/video/frames_decoder_gpu.cc#L212
if (maxWidth > 0 && (video_format.coded_width > maxWidth ||
video_format.coded_height > maxHeight)) {
LOG_INFO(std::string("recreate, exceed maxWidth/maxHeight: (") +
std::to_string(video_format.coded_width) + ", " +
std::to_string(video_format.coded_height) + " > (" +
std::to_string(maxWidth) + ", " + std::to_string(maxHeight) +
")");
if (create_nvdecoder()) {
return -2;
} else {
LOG_ERROR(std::string("create_nvdecoder failed"));
}
} else {
LOG_ERROR(std::string("Exception decode_and_recreate: ") + e.what());
}
}
return -1;
}
bool set_srv() {
int width = dec_->GetWidth();
int height = dec_->GetHeight();
int chromaHeight = dec_->GetChromaHeight();
LOG_TRACE(std::string("width:") + std::to_string(width) +
", height:" + std::to_string(height) +
", chromaHeight:" + std::to_string(chromaHeight));
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.MiscFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
HRB(native_->device_->CreateTexture2D(
&desc, nullptr, textures_[0].ReleaseAndGetAddressOf()));
desc.Format = DXGI_FORMAT_R8G8_UNORM;
desc.Width = width / 2;
desc.Height = chromaHeight;
HRB(native_->device_->CreateTexture2D(
&desc, nullptr, textures_[1].ReleaseAndGetAddressOf()));
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(textures_[0].Get(),
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8_UNORM);
HRB(native_->device_->CreateShaderResourceView(
textures_[0].Get(), &srvDesc, SRV_[0].ReleaseAndGetAddressOf()));
srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(textures_[1].Get(),
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8G8_UNORM);
HRB(native_->device_->CreateShaderResourceView(
textures_[1].Get(), &srvDesc, SRV_[1].ReleaseAndGetAddressOf()));
// set SRV
std::array<ID3D11ShaderResourceView *, 2> const textureViews = {
SRV_[0].Get(), SRV_[1].Get()};
native_->context_->PSSetShaderResources(0, textureViews.size(),
textureViews.data());
return true;
}
bool set_rtv(ID3D11Texture2D *texture) {
D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
rtDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtDesc.Texture2D.MipSlice = 0;
HRB(native_->device_->CreateRenderTargetView(
texture, &rtDesc, RTV_.ReleaseAndGetAddressOf()));
const float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; // clear as black
native_->context_->ClearRenderTargetView(RTV_.Get(), clearColor);
native_->context_->OMSetRenderTargets(1, RTV_.GetAddressOf(), NULL);
return true;
}
bool set_view_port() {
int width = dec_->GetWidth();
int height = dec_->GetHeight();
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)(width);
vp.Height = (FLOAT)(height);
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
native_->context_->RSSetViewports(1, &vp);
return true;
}
bool set_sample() {
D3D11_SAMPLER_DESC sampleDesc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
HRB(native_->device_->CreateSamplerState(
&sampleDesc, samplerLinear_.ReleaseAndGetAddressOf()));
native_->context_->PSSetSamplers(0, 1, samplerLinear_.GetAddressOf());
return true;
}
bool set_shader() {
// https://gist.github.com/RomiTT/9c05d36fe339b899793a3252297a5624
#include "pixel_shader_601.h"
#include "vertex_shader.h"
native_->device_->CreateVertexShader(
g_VS, ARRAYSIZE(g_VS), nullptr, vertexShader_.ReleaseAndGetAddressOf());
native_->device_->CreatePixelShader(g_PS, ARRAYSIZE(g_PS), nullptr,
pixelShader_.ReleaseAndGetAddressOf());
// set InputLayout
constexpr std::array<D3D11_INPUT_ELEMENT_DESC, 2> Layout = {{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,
D3D11_INPUT_PER_VERTEX_DATA, 0},
}};
ComPtr<ID3D11InputLayout> inputLayout = NULL;
HRB(native_->device_->CreateInputLayout(Layout.data(), Layout.size(), g_VS,
ARRAYSIZE(g_VS),
inputLayout.GetAddressOf()));
native_->context_->IASetInputLayout(inputLayout.Get());
native_->context_->VSSetShader(vertexShader_.Get(), NULL, 0);
native_->context_->PSSetShader(pixelShader_.Get(), NULL, 0);
return true;
}
bool set_vertex_buffer() {
UINT Stride = sizeof(VERTEX);
UINT Offset = 0;
FLOAT blendFactor[4] = {0.f, 0.f, 0.f, 0.f};
native_->context_->OMSetBlendState(nullptr, blendFactor, 0xffffffff);
native_->context_->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// set VertexBuffers
VERTEX Vertices[NUMVERTICES] = {
{XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)},
};
D3D11_BUFFER_DESC BufferDesc;
RtlZeroMemory(&BufferDesc, sizeof(BufferDesc));
BufferDesc.Usage = D3D11_USAGE_DEFAULT;
BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BufferDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
RtlZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = Vertices;
ComPtr<ID3D11Buffer> VertexBuffer = nullptr;
// Create vertex buffer
HRB(native_->device_->CreateBuffer(&BufferDesc, &InitData, &VertexBuffer));
native_->context_->IASetVertexBuffers(0, 1, VertexBuffer.GetAddressOf(),
&Stride, &Offset);
return true;
}
bool register_texture() {
CUVIDAutoCtxPopper ctxPoper(cudl_, cuContext_);
bool ret = true;
for (int i = 0; i < 2; i++) {
if (!succ(cudl_->cuGraphicsD3D11RegisterResource(
&cuResource_[i], textures_[i].Get(),
CU_GRAPHICS_REGISTER_FLAGS_NONE))) {
ret = false;
break;
}
if (!succ(cudl_->cuGraphicsResourceSetMapFlags(
cuResource_[i], CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD))) {
ret = false;
break;
}
}
return ret;
}
bool dataFormat_to_cuCodecID(DataFormat dataFormat, cudaVideoCodec &cuda) {
switch (dataFormat) {
case H264:
cuda = cudaVideoCodec_H264;
break;
case H265:
cuda = cudaVideoCodec_HEVC;
break;
default:
return false;
}
return true;
}
bool create_nvdecoder() {
LOG_TRACE(std::string("create nvdecoder"));
bool bUseDeviceFrame = true;
bool bLowLatency = true;
bool bDeviceFramePitched = false; // width=pitch
cudaVideoCodec cudaCodecID;
if (!dataFormat_to_cuCodecID(dataFormat_, cudaCodecID)) {
return false;
}
if (dec_) {
delete dec_;
dec_ = nullptr;
}
dec_ = new NvDecoder(cudl_, cvdl_, cuContext_, bUseDeviceFrame, cudaCodecID,
bLowLatency, bDeviceFramePitched);
return true;
}
};
} // namespace
extern "C" {
int nv_decode_driver_support() {
try {
CudaFunctions *cudl = NULL;
CuvidFunctions *cvdl = NULL;
load_driver(&cudl, &cvdl);
free_driver(&cudl, &cvdl);
return 0;
} catch (const std::exception &e) {
}
return -1;
}
int nv_destroy_decoder(void *decoder) {
try {
CuvidDecoder *p = (CuvidDecoder *)decoder;
if (p) {
p->destroy();
delete p;
p = NULL;
}
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("destroy failed: ") + e.what());
}
return -1;
}
void *nv_new_decoder(void *device, int64_t luid,
DataFormat dataFormat) {
CuvidDecoder *p = NULL;
try {
p = new CuvidDecoder(device, luid, dataFormat);
if (!p) {
goto _exit;
}
if (p->init())
return p;
} catch (const std::exception &ex) {
LOG_ERROR(std::string("destroy failed: ") + ex.what());
goto _exit;
}
_exit:
if (p) {
p->destroy();
delete p;
p = NULL;
}
return NULL;
}
int nv_decode(void *decoder, uint8_t *data, int len, DecodeCallback callback,
void *obj) {
try {
CuvidDecoder *p = (CuvidDecoder *)decoder;
if (p->decode(data, len, callback, obj) == 0 ) {
return HWCODEC_SUCCESS;
}
} catch (const std::exception &e) {
LOG_ERROR(std::string("decode failed: ") + e.what());
}
return HWCODEC_ERR_COMMON;
}
int nv_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum,
int32_t *outDescNum, DataFormat dataFormat,
uint8_t *data, int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_NVIDIA))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
CuvidDecoder *p = (CuvidDecoder *)nv_new_decoder(
nullptr, currentLuid, dataFormat);
if (!p)
continue;
auto start = util::now();
bool succ = nv_decode(p, data, length, nullptr, nullptr) == 0;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_NV;
count += 1;
}
p->destroy();
delete p;
p = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -0,0 +1,464 @@
#define FFNV_LOG_FUNC
#define FFNV_DEBUG_LOG_FUNC
#include <Samples/NvCodec/NvEncoder/NvEncoderD3D11.h>
#include <Samples/Utils/Logger.h>
#include <Samples/Utils/NvCodecUtils.h>
#include <Samples/Utils/NvEncoderCLIOptions.h>
#include <dynlink_cuda.h>
#include <dynlink_loader.h>
#include <fstream>
#include <iostream>
#include <libavutil/pixfmt.h>
#include <memory>
#include <d3d11.h>
#include <d3d9.h>
#include <wrl/client.h>
using Microsoft::WRL::ComPtr;
#include "callback.h"
#include "common.h"
#include "system.h"
#include "util.h"
#define LOG_MODULE "NVENC"
#include "log.h"
simplelogger::Logger *logger =
simplelogger::LoggerFactory::CreateConsoleLogger();
namespace {
// #define CONFIG_NV_OPTIMUS_FOR_DEV
#define succ(call) ((call) == 0)
void load_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl) {
if (cuda_load_functions(pp_cuda_dl, NULL) < 0) {
LOG_TRACE(std::string("cuda_load_functions failed"));
NVENC_THROW_ERROR("cuda_load_functions failed", NV_ENC_ERR_GENERIC);
}
if (nvenc_load_functions(pp_nvenc_dl, NULL) < 0) {
LOG_TRACE(std::string("nvenc_load_functions failed"));
NVENC_THROW_ERROR("nvenc_load_functions failed", NV_ENC_ERR_GENERIC);
}
}
void free_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl) {
if (*pp_nvenc_dl) {
nvenc_free_functions(pp_nvenc_dl);
*pp_nvenc_dl = NULL;
}
if (*pp_cuda_dl) {
cuda_free_functions(pp_cuda_dl);
*pp_cuda_dl = NULL;
}
}
class NvencEncoder {
public:
std::unique_ptr<NativeDevice> native_ = nullptr;
NvEncoderD3D11 *pEnc_ = nullptr;
CudaFunctions *cuda_dl_ = nullptr;
NvencFunctions *nvenc_dl_ = nullptr;
void *handle_ = nullptr;
int64_t luid_;
DataFormat dataFormat_;
int32_t width_;
int32_t height_;
int32_t kbs_;
int32_t framerate_;
int32_t gop_;
bool full_range_ = false;
bool bt709_ = false;
NV_ENC_CONFIG encodeConfig_ = {0};
NvencEncoder(void *handle, int64_t luid, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs, int32_t framerate,
int32_t gop) {
handle_ = handle;
luid_ = luid;
dataFormat_ = dataFormat;
width_ = width;
height_ = height;
kbs_ = kbs;
framerate_ = framerate;
gop_ = gop;
load_driver(&cuda_dl_, &nvenc_dl_);
}
~NvencEncoder() {}
bool init() {
GUID guidCodec;
switch (dataFormat_) {
case H264:
guidCodec = NV_ENC_CODEC_H264_GUID;
break;
case H265:
guidCodec = NV_ENC_CODEC_HEVC_GUID;
break;
default:
LOG_ERROR(std::string("dataFormat not support, dataFormat: ") +
std::to_string(dataFormat_));
return false;
}
if (!succ(cuda_dl_->cuInit(0))) {
LOG_TRACE(std::string("cuInit failed"));
return false;
}
native_ = std::make_unique<NativeDevice>();
#ifdef CONFIG_NV_OPTIMUS_FOR_DEV
if (!native_->Init(luid_, nullptr))
return false;
#else
if (!native_->Init(luid_, (ID3D11Device *)handle_)) {
LOG_ERROR(std::string("d3d device init failed"));
return false;
}
#endif
CUdevice cuDevice = 0;
if (!succ(cuda_dl_->cuD3D11GetDevice(&cuDevice, native_->adapter_.Get()))) {
LOG_ERROR(std::string("Failed to get cuDevice"));
return false;
}
int nExtraOutputDelay = 0;
pEnc_ = new NvEncoderD3D11(cuda_dl_, nvenc_dl_, native_->device_.Get(),
width_, height_, NV_ENC_BUFFER_FORMAT_ARGB,
nExtraOutputDelay, false, false); // no delay
NV_ENC_INITIALIZE_PARAMS initializeParams = {0};
ZeroMemory(&initializeParams, sizeof(initializeParams));
ZeroMemory(&encodeConfig_, sizeof(encodeConfig_));
initializeParams.encodeConfig = &encodeConfig_;
pEnc_->CreateDefaultEncoderParams(
&initializeParams, guidCodec,
NV_ENC_PRESET_P3_GUID /*NV_ENC_PRESET_LOW_LATENCY_HP_GUID*/,
NV_ENC_TUNING_INFO_LOW_LATENCY);
// no delay
initializeParams.encodeConfig->frameIntervalP = 1;
initializeParams.encodeConfig->rcParams.lookaheadDepth = 0;
// bitrate
initializeParams.encodeConfig->rcParams.averageBitRate = kbs_ * 1000;
// framerate
initializeParams.frameRateNum = framerate_;
initializeParams.frameRateDen = 1;
// gop
initializeParams.encodeConfig->gopLength =
(gop_ > 0 && gop_ < MAX_GOP) ? gop_ : NVENC_INFINITE_GOPLENGTH;
// rc method
initializeParams.encodeConfig->rcParams.rateControlMode =
NV_ENC_PARAMS_RC_CBR;
// color
if (dataFormat_ == H264) {
setup_h264(initializeParams.encodeConfig);
} else {
setup_hevc(initializeParams.encodeConfig);
}
pEnc_->CreateEncoder(&initializeParams);
return true;
}
int encode(void *texture, EncodeCallback callback, void *obj, int64_t ms) {
bool encoded = false;
std::vector<NvPacket> vPacket;
const NvEncInputFrame *pEncInput = pEnc_->GetNextInputFrame();
// TODO: sdk can ensure the inputPtr's width, height same as width_,
// height_, does capture's frame can ensure width height same with width_,
// height_ ?
ID3D11Texture2D *pBgraTextyure =
reinterpret_cast<ID3D11Texture2D *>(pEncInput->inputPtr);
#ifdef CONFIG_NV_OPTIMUS_FOR_DEV
copy_texture(texture, pBgraTextyure);
#else
native_->context_->CopyResource(
pBgraTextyure, reinterpret_cast<ID3D11Texture2D *>(texture));
#endif
NV_ENC_PIC_PARAMS picParams = {0};
picParams.inputTimeStamp = ms;
pEnc_->EncodeFrame(vPacket);
for (NvPacket &packet : vPacket) {
int32_t key = (packet.pictureType == NV_ENC_PIC_TYPE_IDR ||
packet.pictureType == NV_ENC_PIC_TYPE_I)
? 1
: 0;
if (packet.data.size() > 0) {
if (callback)
callback(packet.data.data(), packet.data.size(), key, obj, ms);
encoded = true;
}
}
return encoded ? 0 : -1;
}
void destroy() {
if (pEnc_) {
pEnc_->DestroyEncoder();
delete pEnc_;
pEnc_ = nullptr;
}
free_driver(&cuda_dl_, &nvenc_dl_);
}
void setup_h264(NV_ENC_CONFIG *encodeConfig) {
NV_ENC_CODEC_CONFIG *encodeCodecConfig = &encodeConfig->encodeCodecConfig;
NV_ENC_CONFIG_H264 *h264 = &encodeCodecConfig->h264Config;
NV_ENC_CONFIG_H264_VUI_PARAMETERS *vui = &h264->h264VUIParameters;
vui->videoFullRangeFlag = !!full_range_;
vui->colourMatrix = bt709_ ? NV_ENC_VUI_MATRIX_COEFFS_BT709 : NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M;
vui->colourPrimaries = bt709_ ? NV_ENC_VUI_COLOR_PRIMARIES_BT709 : NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M;
vui->transferCharacteristics =
bt709_ ? NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709 : NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M;
vui->colourDescriptionPresentFlag = 1;
vui->videoSignalTypePresentFlag = 1;
h264->sliceMode = 3;
h264->sliceModeData = 1;
h264->repeatSPSPPS = 1;
// Specifies the chroma format. Should be set to 1 for yuv420 input, 3 for
// yuv444 input
h264->chromaFormatIDC = 1;
h264->level = NV_ENC_LEVEL_AUTOSELECT;
encodeConfig->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
}
void setup_hevc(NV_ENC_CONFIG *encodeConfig) {
NV_ENC_CODEC_CONFIG *encodeCodecConfig = &encodeConfig->encodeCodecConfig;
NV_ENC_CONFIG_HEVC *hevc = &encodeCodecConfig->hevcConfig;
NV_ENC_CONFIG_HEVC_VUI_PARAMETERS *vui = &hevc->hevcVUIParameters;
vui->videoFullRangeFlag = !!full_range_;
vui->colourMatrix = bt709_ ? NV_ENC_VUI_MATRIX_COEFFS_BT709 : NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M;
vui->colourPrimaries = bt709_ ? NV_ENC_VUI_COLOR_PRIMARIES_BT709 : NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M;
vui->transferCharacteristics =
bt709_ ? NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709 : NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M;
vui->colourDescriptionPresentFlag = 1;
vui->videoSignalTypePresentFlag = 1;
hevc->sliceMode = 3;
hevc->sliceModeData = 1;
hevc->repeatSPSPPS = 1;
// Specifies the chroma format. Should be set to 1 for yuv420 input, 3 for
// yuv444 input
hevc->chromaFormatIDC = 1;
hevc->level = NV_ENC_LEVEL_AUTOSELECT;
hevc->outputPictureTimingSEI = 1;
hevc->tier = NV_ENC_TIER_HEVC_MAIN;
encodeConfig->profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID;
}
private:
#ifdef CONFIG_NV_OPTIMUS_FOR_DEV
int copy_texture(void *src, void *dst) {
ComPtr<ID3D11Device> src_device = (ID3D11Device *)handle_;
ComPtr<ID3D11DeviceContext> src_deviceContext;
src_device->GetImmediateContext(src_deviceContext.ReleaseAndGetAddressOf());
ComPtr<ID3D11Texture2D> src_tex = (ID3D11Texture2D *)src;
ComPtr<ID3D11Texture2D> dst_tex = (ID3D11Texture2D *)dst;
HRESULT hr;
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
src_tex->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.BindFlags = 0;
desc.MiscFlags = 0;
ComPtr<ID3D11Texture2D> staging_tex;
src_device->CreateTexture2D(&desc, NULL,
staging_tex.ReleaseAndGetAddressOf());
src_deviceContext->CopyResource(staging_tex.Get(), src_tex.Get());
D3D11_MAPPED_SUBRESOURCE map;
src_deviceContext->Map(staging_tex.Get(), 0, D3D11_MAP_READ, 0, &map);
std::unique_ptr<uint8_t[]> buffer(
new uint8_t[desc.Width * desc.Height * 4]);
memcpy(buffer.get(), map.pData, desc.Width * desc.Height * 4);
src_deviceContext->Unmap(staging_tex.Get(), 0);
D3D11_BOX Box;
Box.left = 0;
Box.right = desc.Width;
Box.top = 0;
Box.bottom = desc.Height;
Box.front = 0;
Box.back = 1;
native_->context_->UpdateSubresource(dst_tex.Get(), 0, &Box, buffer.get(),
desc.Width * 4,
desc.Width * desc.Height * 4);
return 0;
}
#endif
};
} // namespace
extern "C" {
int nv_encode_driver_support() {
try {
CudaFunctions *cuda_dl = NULL;
NvencFunctions *nvenc_dl = NULL;
load_driver(&cuda_dl, &nvenc_dl);
free_driver(&cuda_dl, &nvenc_dl);
return 0;
} catch (const std::exception &e) {
LOG_TRACE(std::string("driver not support, ") + e.what());
}
return -1;
}
int nv_destroy_encoder(void *encoder) {
try {
NvencEncoder *e = (NvencEncoder *)encoder;
if (e) {
e->destroy();
delete e;
e = NULL;
}
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("destroy failed: ") + e.what());
}
return -1;
}
void *nv_new_encoder(void *handle, int64_t luid, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs,
int32_t framerate, int32_t gop) {
NvencEncoder *e = NULL;
try {
e = new NvencEncoder(handle, luid, dataFormat, width, height, kbs,
framerate, gop);
if (!e->init()) {
goto _exit;
}
return e;
} catch (const std::exception &ex) {
LOG_ERROR(std::string("new failed: ") + ex.what());
goto _exit;
}
_exit:
if (e) {
e->destroy();
delete e;
e = NULL;
}
return NULL;
}
int nv_encode(void *encoder, void *texture, EncodeCallback callback, void *obj,
int64_t ms) {
try {
NvencEncoder *e = (NvencEncoder *)encoder;
return e->encode(texture, callback, obj, ms);
} catch (const std::exception &e) {
LOG_ERROR(std::string("encode failed: ") + e.what());
}
return -1;
}
// ref: Reconfigure API
#define RECONFIGURE_HEAD \
NvencEncoder *enc = (NvencEncoder *)e; \
NV_ENC_CONFIG sEncodeConfig = {0}; \
NV_ENC_INITIALIZE_PARAMS sInitializeParams = {0}; \
sInitializeParams.encodeConfig = &sEncodeConfig; \
enc->pEnc_->GetInitializeParams(&sInitializeParams); \
NV_ENC_RECONFIGURE_PARAMS params = {0}; \
params.version = NV_ENC_RECONFIGURE_PARAMS_VER; \
params.reInitEncodeParams = sInitializeParams;
#define RECONFIGURE_TAIL \
if (enc->pEnc_->Reconfigure(&params)) { \
return 0; \
}
int nv_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
DataFormat dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate,
int32_t gop, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount) {
try {
Adapters adapters;
if (!adapters.Init(ADAPTER_VENDOR_NVIDIA))
return -1;
int count = 0;
for (auto &adapter : adapters.adapters_) {
int64_t currentLuid = LUID(adapter.get()->desc1_);
if (util::skip_test(excludedLuids, excludeFormats, excludeCount, currentLuid, dataFormat)) {
continue;
}
NvencEncoder *e = (NvencEncoder *)nv_new_encoder(
(void *)adapter.get()->device_.Get(), currentLuid,
dataFormat, width, height, kbs, framerate, gop);
if (!e)
continue;
if (e->native_->EnsureTexture(e->width_, e->height_)) {
e->native_->next();
int32_t key_obj = 0;
auto start = util::now();
bool succ = nv_encode(e, e->native_->GetCurrentTexture(), util_encode::vram_encode_test_callback, &key_obj,
0) == 0 && key_obj == 1;
int64_t elapsed = util::elapsed_ms(start);
if (succ && elapsed < TEST_TIMEOUT_MS) {
outLuids[count] = currentLuid;
outVendors[count] = VENDOR_NV;
count += 1;
}
}
e->destroy();
delete e;
e = nullptr;
if (count >= maxDescNum)
break;
}
*outDescNum = count;
return 0;
} catch (const std::exception &e) {
LOG_ERROR(std::string("test failed: ") + e.what());
}
return -1;
}
int nv_set_bitrate(void *e, int32_t kbs) {
try {
RECONFIGURE_HEAD
params.reInitEncodeParams.encodeConfig->rcParams.averageBitRate =
kbs * 1000;
RECONFIGURE_TAIL
} catch (const std::exception &e) {
LOG_ERROR(std::string("set bitrate to ") + std::to_string(kbs) +
"k failed: " + e.what());
}
return -1;
}
int nv_set_framerate(void *e, int32_t framerate) {
try {
RECONFIGURE_HEAD
params.reInitEncodeParams.frameRateNum = framerate;
params.reInitEncodeParams.frameRateDen = 1;
RECONFIGURE_TAIL
} catch (const std::exception &e) {
LOG_ERROR(std::string("set framerate failed: ") + e.what());
}
return -1;
}
} // extern "C"

View File

@@ -0,0 +1,40 @@
#ifndef NV_FFI_H
#define NV_FFI_H
#include "../common/callback.h"
#include <stdbool.h>
int nv_encode_driver_support();
int nv_decode_driver_support();
void *nv_new_encoder(void *handle, int64_t luid,
int32_t dataFormat, int32_t width, int32_t height,
int32_t bitrate, int32_t framerate, int32_t gop);
int nv_encode(void *encoder, void *tex, EncodeCallback callback, void *obj,
int64_t ms);
int nv_destroy_encoder(void *encoder);
void *nv_new_decoder(void *device, int64_t luid, int32_t codecID);
int nv_decode(void *decoder, uint8_t *data, int len, DecodeCallback callback,
void *obj);
int nv_destroy_decoder(void *decoder);
int nv_test_encode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, int32_t width,
int32_t height, int32_t kbs, int32_t framerate, int32_t gop,
const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int nv_test_decode(int64_t *outLuids, int32_t *outVendors, int32_t maxDescNum, int32_t *outDescNum,
int32_t dataFormat, uint8_t *data,
int32_t length, const int64_t *excludedLuids, const int32_t *excludeFormats, int32_t excludeCount);
int nv_set_bitrate(void *encoder, int32_t kbs);
int nv_set_framerate(void *encoder, int32_t framerate);
#endif // NV_FFI_H

View File

@@ -0,0 +1,13 @@
[package]
name = "capture"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4"
[build-dependencies]
cc = "1.0"
bindgen = "0.59"

View File

@@ -0,0 +1,51 @@
use cc::Build;
use std::{
env,
path::{Path, PathBuf},
};
fn main() {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let externals_dir = manifest_dir
.parent()
.unwrap()
.parent()
.unwrap()
.join("externals");
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed={}", externals_dir.display());
let ffi_header = "src/dxgi_ffi.h";
bindgen::builder()
.header(ffi_header)
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("capture_ffi.rs"))
.unwrap();
let mut builder = Build::new();
// system
#[cfg(windows)]
["d3d11", "dxgi"].map(|lib| println!("cargo:rustc-link-lib={}", lib));
#[cfg(target_os = "linux")]
println!("cargo:rustc-link-lib=stdc++");
#[cfg(windows)]
{
// dxgi
let dxgi_path = externals_dir.join("nvEncDXGIOutputDuplicationSample");
builder.include(&dxgi_path);
for f in vec!["DDAImpl.cpp"] {
builder.file(format!("{}/{}", dxgi_path.display(), f));
}
builder.file("src/dxgi.cpp");
}
// crate
builder
.cpp(false)
.static_crt(true)
.warnings(false)
.compile("capture");
}

View File

@@ -0,0 +1,42 @@
#include <DDA.h>
#include <Windows.h>
#include <string>
extern "C" void *dxgi_new_capturer(int64_t luid) {
DemoApplication *d = new DemoApplication(luid);
HRESULT hr = d->Init();
if (FAILED(hr)) {
delete d;
d = NULL;
return NULL;
}
return d;
}
extern "C" void *dxgi_device(void *capturer) {
DemoApplication *d = (DemoApplication *)capturer;
return d->Device();
}
extern "C" int dxgi_width(const void *capturer) {
DemoApplication *d = (DemoApplication *)capturer;
return d->width();
}
extern "C" int dxgi_height(const void *capturer) {
DemoApplication *d = (DemoApplication *)capturer;
return d->height();
}
extern "C" void *dxgi_capture(void *capturer, int wait_ms) {
DemoApplication *d = (DemoApplication *)capturer;
void *texture = d->Capture(wait_ms);
return texture;
}
extern "C" void destroy_dxgi_capturer(void *capturer) {
DemoApplication *d = (DemoApplication *)capturer;
if (d)
delete d;
}

View File

@@ -0,0 +1,42 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::os::raw::c_void;
include!(concat!(env!("OUT_DIR"), "/capture_ffi.rs"));
pub struct Capturer {
inner: *mut c_void,
}
impl Capturer {
pub fn new(luid: i64) -> Result<Self, ()> {
let inner = unsafe { dxgi_new_capturer(luid) };
if inner.is_null() {
Err(())
} else {
Ok(Self { inner })
}
}
pub unsafe fn device(&mut self) -> *mut c_void {
dxgi_device(self.inner)
}
pub unsafe fn width(&self) -> i32 {
dxgi_width(self.inner)
}
pub unsafe fn height(&self) -> i32 {
dxgi_height(self.inner)
}
pub unsafe fn capture(&mut self, wait_ms: i32) -> *mut c_void {
dxgi_capture(self.inner, wait_ms)
}
pub unsafe fn drop(&mut self) {
destroy_dxgi_capturer(self.inner);
}
}

View File

@@ -0,0 +1,13 @@
#ifndef FFI_H
#define FFI_H
#include <stdint.h>
void *dxgi_new_capturer(int64_t luid);
void *dxgi_device(void *capturer);
int dxgi_width(const void *capturer);
int dxgi_height(const void *capturer);
void *dxgi_capture(void *capturer, int wait_ms);
void destroy_dxgi_capturer(void *capturer);
#endif // FFI_H

View File

@@ -0,0 +1,2 @@
#[cfg(windows)]
pub mod dxgi;

View File

@@ -0,0 +1,13 @@
[package]
name = "render"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4"
[build-dependencies]
cc = "1.0"
bindgen = "0.59"

View File

@@ -0,0 +1,50 @@
use cc::Build;
use std::{
env,
path::{Path, PathBuf},
};
fn main() {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let externals_dir = manifest_dir
.parent()
.unwrap()
.parent()
.unwrap()
.join("externals");
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed={}", externals_dir.display());
let ffi_header = "src/render_ffi.h";
bindgen::builder()
.header(ffi_header)
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("render_ffi.rs"))
.unwrap();
let mut builder = Build::new();
// system
#[cfg(windows)]
["d3d11", "dxgi", "User32"].map(|lib| println!("cargo:rustc-link-lib={}", lib));
#[cfg(target_os = "linux")]
println!("cargo:rustc-link-lib=stdc++");
#[cfg(windows)]
{
let sdl_dir = externals_dir.join("SDL");
builder.include(sdl_dir.join("include"));
let sdl_lib_path = sdl_dir.join("lib").join("x64");
builder.file(manifest_dir.join("src").join("dxgi_sdl.cpp"));
println!("cargo:rustc-link-search=native={}", sdl_lib_path.display());
println!("cargo:rustc-link-lib=SDL2");
}
// crate
builder
.cpp(false)
.static_crt(true)
.warnings(false)
.compile("render");
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,581 @@
#include <atomic>
#include <chrono>
#include <cstdio>
#include <list>
#include <mutex>
#include <thread>
#include <vector>
#include <DirectXMath.h>
#include <SDL.h>
#include <SDL_syswm.h>
#include <d3d11.h>
#include <d3d11_1.h>
#include <d3d11_2.h>
#include <d3d11_3.h>
#include <d3d11_4.h>
#include <dxgi.h>
#include <dxgi1_2.h>
#include <iostream>
#include <wrl/client.h>
using Microsoft::WRL::ComPtr;
#define SAFE_RELEASE(p) \
{ \
if ((p)) { \
(p)->Release(); \
(p) = nullptr; \
} \
}
#define LUID(desc) \
(((int64_t)desc.AdapterLuid.HighPart << 32) | desc.AdapterLuid.LowPart)
#define HRB(f) MS_CHECK(f, return false;)
#define HRI(f) MS_CHECK(f, return -1;)
#define HRP(f) MS_CHECK(f, return nullptr;)
#define MS_CHECK(f, ...) \
do { \
HRESULT __ms_hr__ = (f); \
if (FAILED(__ms_hr__)) { \
std::clog \
<< #f " ERROR@" << __LINE__ << __FUNCTION__ << ": (" << std::hex \
<< __ms_hr__ << std::dec << ") " \
<< std::error_code(__ms_hr__, std::system_category()).message() \
<< std::endl \
<< std::flush; \
__VA_ARGS__ \
} \
} while (false)
#define MS_THROW(f, ...) MS_CHECK(f, throw std::runtime_error(#f);)
#define LUID(desc) \
(((int64_t)desc.AdapterLuid.HighPart << 32) | desc.AdapterLuid.LowPart)
#ifndef CSO_DIR
#define CSO_DIR "dev/render/res"
#endif
struct AdatperOutputs {
IDXGIAdapter1 *adapter;
DXGI_ADAPTER_DESC1 desc;
AdatperOutputs() : adapter(nullptr){};
AdatperOutputs(AdatperOutputs &&src) noexcept {
adapter = src.adapter;
src.adapter = nullptr;
desc = src.desc;
}
AdatperOutputs(const AdatperOutputs &src) {
adapter = src.adapter;
adapter->AddRef();
desc = src.desc;
}
~AdatperOutputs() {
if (adapter)
adapter->Release();
}
};
bool get_first_adapter_output(IDXGIFactory2 *factory2,
IDXGIAdapter1 **adapter_out,
IDXGIOutput1 **output_out, int64_t luid) {
UINT num_adapters = 0;
AdatperOutputs curent_adapter;
IDXGIAdapter1 *selected_adapter = nullptr;
IDXGIOutput1 *selected_output = nullptr;
HRESULT hr = S_OK;
bool found = false;
while (factory2->EnumAdapters1(num_adapters, &curent_adapter.adapter) !=
DXGI_ERROR_NOT_FOUND) {
++num_adapters;
DXGI_ADAPTER_DESC1 desc = DXGI_ADAPTER_DESC1();
curent_adapter.adapter->GetDesc1(&desc);
if (LUID(desc) != luid) {
continue;
}
selected_adapter = curent_adapter.adapter;
selected_adapter->AddRef();
IDXGIOutput *output;
if (curent_adapter.adapter->EnumOutputs(0, &output) !=
DXGI_ERROR_NOT_FOUND) {
IDXGIOutput1 *temp;
hr = output->QueryInterface(IID_PPV_ARGS(&temp));
if (SUCCEEDED(hr)) {
selected_output = temp;
}
}
found = true;
break;
}
*adapter_out = selected_adapter;
*output_out = selected_output;
return found;
}
class dx_device_context {
public:
dx_device_context(int64_t luid) {
// This is what matters (the 1)
// Only a guid of fatory2 will not work
HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory2));
if (FAILED(hr))
exit(hr);
if (!get_first_adapter_output(factory2, &adapter1, &output1, luid)) {
std::cout << "no render adapter found" << std::endl;
exit(-1);
}
D3D_FEATURE_LEVEL levels[]{D3D_FEATURE_LEVEL_11_0};
hr = D3D11CreateDevice(
adapter1, D3D_DRIVER_TYPE_UNKNOWN, NULL,
D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT,
levels, 1, D3D11_SDK_VERSION, &device, NULL, &context);
if (FAILED(hr))
exit(hr);
hr = device->QueryInterface(IID_PPV_ARGS(&video_device));
if (FAILED(hr))
exit(hr);
hr = context->QueryInterface(IID_PPV_ARGS(&video_context));
if (FAILED(hr))
exit(hr);
hr = context->QueryInterface(IID_PPV_ARGS(&hmt));
if (FAILED(hr))
exit(hr);
// This is required for MFXVideoCORE_SetHandle
hr = hmt->SetMultithreadProtected(TRUE);
if (FAILED(hr))
exit(hr);
}
~dx_device_context() {
if (hmt)
hmt->Release();
if (video_context)
video_context->Release();
if (video_device)
video_device->Release();
if (context)
context->Release();
if (device)
device->Release();
if (output1)
output1->Release();
if (adapter1)
adapter1->Release();
if (factory2)
factory2->Release();
}
IDXGIFactory2 *factory2 = nullptr;
IDXGIAdapter1 *adapter1 = nullptr;
IDXGIOutput1 *output1 = nullptr;
ID3D11Device *device = nullptr;
ID3D11DeviceContext *context = nullptr;
ID3D11VideoDevice *video_device = nullptr;
ID3D11VideoContext *video_context = nullptr;
ID3D10Multithread *hmt = nullptr;
HMODULE debug_mod = nullptr;
};
class simplerenderer {
public:
simplerenderer(HWND in_window, dx_device_context &dev_ctx)
: ctx(dev_ctx), window(in_window) {
ctx.factory2->MakeWindowAssociation(in_window, 0);
sampler_view = nullptr;
D3D11_SAMPLER_DESC desc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
HRESULT hr = ctx.device->CreateSamplerState(&desc, &sampler_interp);
if (FAILED(hr))
exit(hr);
init_fbo0(window);
init_vbo();
init_shaders();
}
~simplerenderer() {
// render_thread.join();
if (sampler_interp)
sampler_interp->Release();
if (sampler_view)
sampler_view->Release();
if (vbo)
vbo->Release();
if (vao)
vao->Release();
if (frag)
frag->Release();
if (vert)
vert->Release();
if (fbo0)
fbo0->Release();
if (fbo0rbo)
fbo0rbo->Release();
if (swapchain)
swapchain->Release();
}
private:
void init_fbo0(HWND window) {
DXGI_SWAP_CHAIN_DESC1 swp_desc{
1280, 720, DXGI_FORMAT_B8G8R8A8_UNORM, FALSE, DXGI_SAMPLE_DESC{1, 0},
DXGI_USAGE_RENDER_TARGET_OUTPUT, 3, DXGI_SCALING_STRETCH,
// DXGI_SWAP_EFFECT_DISCARD,
DXGI_SWAP_EFFECT_FLIP_DISCARD, DXGI_ALPHA_MODE_UNSPECIFIED, 0};
HRESULT hr = ctx.factory2->CreateSwapChainForHwnd(
ctx.device, window, &swp_desc, NULL, NULL, &swapchain);
if (FAILED(hr))
exit(hr);
fbo0 = nullptr;
fbo0rbo = nullptr;
RECT rect;
GetClientRect(window, &rect);
hr = swapchain->GetBuffer(0, IID_PPV_ARGS(&fbo0rbo));
if (FAILED(hr))
exit(hr);
hr = ctx.device->CreateRenderTargetView(fbo0rbo, nullptr, &fbo0);
if (FAILED(hr))
exit(hr);
D3D11_VIEWPORT VP{};
VP.Width = static_cast<FLOAT>(rect.right - rect.left);
VP.Height = static_cast<FLOAT>(rect.bottom - rect.top);
VP.MinDepth = 0.0f;
VP.MaxDepth = 1.0f;
VP.TopLeftX = 0;
VP.TopLeftY = 0;
ctx.context->RSSetViewports(1, &VP);
}
void init_vbo() {
struct vertex {
DirectX::XMFLOAT3 Pos;
DirectX::XMFLOAT2 TexCoord;
};
vertex points[4]{{DirectX::XMFLOAT3(-1, 1, 0), DirectX::XMFLOAT2(0, 0)},
{DirectX::XMFLOAT3(1, 1, 0), DirectX::XMFLOAT2(1, 0)},
{DirectX::XMFLOAT3(-1, -1, 0), DirectX::XMFLOAT2(0, 1)},
{DirectX::XMFLOAT3(1, -1, 0), DirectX::XMFLOAT2(1, 1)}};
D3D11_BUFFER_DESC vbo_desc{};
vbo_desc.ByteWidth = sizeof(vertex) * 4;
vbo_desc.Usage = D3D11_USAGE_IMMUTABLE;
vbo_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA initial_data{};
initial_data.pSysMem = points;
HRESULT hr = ctx.device->CreateBuffer(&vbo_desc, &initial_data, &vbo);
if (FAILED(hr))
exit(hr);
vbo_stride = sizeof(vertex);
vbo_offset = 0;
}
void init_shaders() {
uint8_t *shader_bytecode = nullptr;
size_t bytecode_len = 0;
// read file
FILE *shader_file = fopen(CSO_DIR "/frag.cso", "rb");
fseek(shader_file, 0, SEEK_END);
bytecode_len = ftell(shader_file);
fseek(shader_file, 0, SEEK_SET);
shader_bytecode = (uint8_t *)malloc(bytecode_len);
fread(shader_bytecode, 1, bytecode_len, shader_file);
HRESULT hr = ctx.device->CreatePixelShader(shader_bytecode, bytecode_len,
nullptr, &frag);
if (FAILED(hr))
exit(hr);
// free(shader_bytecode);
shader_file = freopen(CSO_DIR "/vert.cso", "rb", shader_file);
fseek(shader_file, 0, SEEK_END);
bytecode_len = ftell(shader_file);
fseek(shader_file, 0, SEEK_SET);
shader_bytecode = (uint8_t *)malloc(bytecode_len);
fread(shader_bytecode, 1, bytecode_len, shader_file);
// fclose(shader_file);
hr = ctx.device->CreateVertexShader(shader_bytecode, bytecode_len, nullptr,
&vert);
if (FAILED(hr))
exit(hr);
D3D11_INPUT_ELEMENT_DESC input_desc[]{
// name, vertex attrib index, format, unpack alignment, instance
// releated
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,
D3D11_INPUT_PER_VERTEX_DATA, 0},
};
hr = ctx.device->CreateInputLayout(input_desc, 2, shader_bytecode,
bytecode_len, &vao);
if (FAILED(hr))
exit(hr);
// free(shader_bytecode);
ctx.context->VSSetShader(vert, nullptr, 0);
ctx.context->IASetInputLayout(vao);
ctx.context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
ctx.context->IASetVertexBuffers(0, 1, &vbo, &vbo_stride, &vbo_offset);
ctx.context->PSSetShader(frag, nullptr, 0);
ctx.context->PSSetSamplers(0, 1, &sampler_interp);
}
void bind_texture(ID3D11Texture2D *texture) {
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
D3D11_SHADER_RESOURCE_VIEW_DESC shader_resource_desc{};
shader_resource_desc.Format = desc.Format;
;
shader_resource_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shader_resource_desc.Texture2D = {0, 1};
if (sampler_view)
sampler_view->Release();
HRESULT hr = ctx.device->CreateShaderResourceView(
texture, &shader_resource_desc, &sampler_view);
if (FAILED(hr))
exit(hr);
ctx.context->PSSetShaderResources(0, 1, &sampler_view);
}
void resize_swapchain(uint32_t width, uint32_t height) {
if (fbo0)
fbo0->Release();
if (fbo0rbo)
fbo0rbo->Release();
HRESULT hr =
swapchain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0);
hr = swapchain->GetBuffer(0, IID_PPV_ARGS(&fbo0rbo));
if (FAILED(hr))
exit(hr);
hr = ctx.device->CreateRenderTargetView(fbo0rbo, nullptr, &fbo0);
if (FAILED(hr))
exit(hr);
D3D11_VIEWPORT VP{};
VP.Width = static_cast<FLOAT>(width);
VP.Height = static_cast<FLOAT>(height);
VP.MinDepth = 0.0f;
VP.MaxDepth = 1.0f;
VP.TopLeftX = 0;
VP.TopLeftY = 0;
ctx.context->RSSetViewports(1, &VP);
}
std::chrono::high_resolution_clock::time_point last_fps_time =
std::chrono::high_resolution_clock::now();
public:
void render_frame(ID3D11Texture2D *texture) {
if (!occluded) {
bind_texture(texture);
if (need_resize.load(std::memory_order_acquire)) {
atomic_packed_32x2 temp;
temp.packed.store(client_size.packed.load(std::memory_order_relaxed),
std::memory_order_relaxed);
resize_swapchain(temp.separate.width, temp.separate.height);
}
ctx.context->OMSetRenderTargets(1, &fbo0, nullptr);
ctx.context->Draw(4, 0);
HRESULT hr = swapchain->Present(0, 0);
if (FAILED(hr))
exit(hr);
if (hr == DXGI_STATUS_OCCLUDED) {
occluded = true;
}
frame_count++;
std::chrono::high_resolution_clock::time_point current =
std::chrono::high_resolution_clock::now();
if (current - last_fps_time >= std::chrono::seconds(1)) {
int fps = frame_count - last_frame_count;
last_frame_count = frame_count;
last_fps_time = current;
std::cout << fps << " Hz" << std::endl;
}
} else {
HRESULT hr = swapchain->Present(0, DXGI_PRESENT_TEST);
if (FAILED(hr))
exit(hr);
if (!DXGI_STATUS_OCCLUDED) {
occluded = false;
}
}
}
void set_size(uint32_t width, uint32_t height) {
atomic_packed_32x2 temp;
temp.separate.width = width;
temp.separate.height = height;
client_size.packed.store(temp.packed.load(std::memory_order_relaxed),
std::memory_order_relaxed);
}
HWND window;
dx_device_context &ctx;
IDXGISwapChain1 *swapchain;
ID3D11Texture2D *fbo0rbo;
ID3D11RenderTargetView *fbo0;
ID3D11VertexShader *vert;
ID3D11PixelShader *frag;
ID3D11InputLayout *vao;
ID3D11Buffer *vbo;
ID3D11ShaderResourceView *sampler_view;
ID3D11SamplerState *sampler_interp;
UINT vbo_stride;
UINT vbo_offset;
std::thread render_thread;
std::atomic_bool running;
std::atomic_bool need_resize;
bool occluded = false;
int frame_count = 0;
int last_frame_count = 0;
struct atomic_packed_32x2 {
union {
struct detail {
uint32_t width;
uint32_t height;
} separate;
std::atomic_uint64_t packed;
};
atomic_packed_32x2();
} client_size;
};
simplerenderer::atomic_packed_32x2::atomic_packed_32x2(void) {}
class Render {
public:
Render(int64_t luid, bool inputSharedHandle);
int Init();
int RenderTexture(ID3D11Texture2D *);
std::unique_ptr<std::thread> message_thread;
std::unique_ptr<simplerenderer> renderer;
bool running = false;
std::unique_ptr<dx_device_context> ctx;
// dx_device_context ctx;
int64_t luid;
bool inputSharedHandle;
};
Render::Render(int64_t luid, bool inputSharedHandle) {
// ctx.reset(new dx_device_context());
this->luid = luid;
this->inputSharedHandle = inputSharedHandle;
ctx = std::make_unique<dx_device_context>(luid);
};
static void run(Render *self) {
SetProcessDPIAware();
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow(
"test window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280,
720, SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_SysWMinfo info{};
SDL_GetWindowWMInfo(window, &info);
{
self->renderer.reset(new simplerenderer(info.info.win.window, *self->ctx));
MONITORINFOEX monitor_info{};
monitor_info.cbSize = sizeof(monitor_info);
DXGI_OUTPUT_DESC screen_desc;
if (self->ctx->output1) {
HRESULT hr = self->ctx->output1->GetDesc(&screen_desc);
GetMonitorInfo(screen_desc.Monitor, &monitor_info);
}
self->running = true;
bool maximized = false;
while (self->running) {
SDL_Event event;
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_CLOSE:
// capturer.Stop();
self->running = false;
break;
case SDL_WINDOWEVENT_MAXIMIZED:
if (self->ctx->output1) {
int border_l, border_r, border_t, border_b;
SDL_GetWindowBordersSize(window, &border_t, &border_l, &border_b,
&border_r);
int max_w = monitor_info.rcWork.right - monitor_info.rcWork.left;
int max_h =
monitor_info.rcWork.bottom - monitor_info.rcWork.top - border_t;
SDL_SetWindowSize(window, max_w, max_h);
SDL_SetWindowPosition(window, monitor_info.rcWork.left, border_t);
maximized = true;
}
break;
case SDL_WINDOWEVENT_RESTORED:
maximized = false;
break;
case SDL_WINDOWEVENT_RESIZED:
if (self->ctx->output1) {
int max_w, max_h;
SDL_GetWindowMaximumSize(window, &max_w, &max_h);
double aspect = double(screen_desc.DesktopCoordinates.right -
screen_desc.DesktopCoordinates.left) /
(screen_desc.DesktopCoordinates.bottom -
screen_desc.DesktopCoordinates.top);
int temp = event.window.data1 * event.window.data2;
int width = sqrt(temp * aspect) + 0.5;
int height = sqrt(temp / aspect) + 0.5;
int pos_x, pos_y;
SDL_GetWindowPosition(window, &pos_x, &pos_y);
int ori_w, ori_h;
SDL_GetWindowSize(window, &ori_w, &ori_h);
SDL_SetWindowPosition(window, pos_x + ((ori_w - width) / 2),
pos_y + ((ori_h - height) / 2));
SDL_SetWindowSize(window, width, height);
}
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
self->renderer->set_size(event.window.data1, event.window.data2);
break;
default:
break;
}
break;
default:
break;
}
if (event.type == SDL_WINDOWEVENT) {
}
}
}
SDL_DestroyWindow(window);
SDL_Quit();
exit(0);
}
int Render::Init() {
message_thread.reset(new std::thread(run, this));
return 0;
}
int Render::RenderTexture(ID3D11Texture2D *texture) {
renderer->render_frame(texture);
return 0;
}
extern "C" void *CreateDXGIRender(int64_t luid, bool inputSharedHandle) {
Render *p = new Render(luid, inputSharedHandle);
p->Init();
return p;
}
extern "C" int DXGIRenderTexture(void *render, HANDLE handle) {
Render *self = (Render *)render;
if (!self->running)
return 0;
ComPtr<ID3D11Texture2D> texture = nullptr;
if (self->inputSharedHandle) {
ComPtr<IDXGIResource> resource = nullptr;
ComPtr<ID3D11Texture2D> tex_ = nullptr;
MS_THROW(self->ctx->device->OpenSharedResource(
handle, __uuidof(ID3D11Texture2D),
(void **)resource.ReleaseAndGetAddressOf()));
MS_THROW(resource.As(&tex_));
texture = tex_.Get();
} else {
texture = (ID3D11Texture2D *)handle;
}
self->RenderTexture(texture.Get());
return 0;
}
extern "C" void DestroyDXGIRender(void *render) {
Render *self = (Render *)render;
self->running = false;
if (self->message_thread)
self->message_thread->join();
}
extern "C" void *DXGIDevice(void *render) {
Render *self = (Render *)render;
return self->ctx->device;
}

View File

@@ -0,0 +1,39 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::os::raw::c_void;
include!(concat!(env!("OUT_DIR"), "/render_ffi.rs"));
pub struct Render {
inner: *mut c_void,
}
impl Render {
pub fn new(luid: i64, input_shared_handle: bool) -> Result<Self, ()> {
let inner = unsafe { CreateDXGIRender(luid, input_shared_handle) };
if inner.is_null() {
Err(())
} else {
Ok(Self { inner })
}
}
pub unsafe fn render(&mut self, tex: *mut c_void) -> Result<(), i32> {
let result = DXGIRenderTexture(self.inner, tex);
if result == 0 {
Ok(())
} else {
Err(result)
}
}
pub unsafe fn device(&mut self) -> *mut c_void {
DXGIDevice(self.inner)
}
pub unsafe fn drop(&mut self) {
DestroyDXGIRender(self.inner);
}
}

View File

@@ -0,0 +1,11 @@
#ifndef RENDER_FFI_H
#define RENDER_FFI_H
#include <stdbool.h>
void *CreateDXGIRender(long long luid, bool inputSharedHandle);
int DXGIRenderTexture(void *render, void *tex);
void DestroyDXGIRender(void *render);
void *DXGIDevice(void *render);
#endif // RENDER_FFI_H

View File

@@ -0,0 +1,13 @@
[package]
name = "tool"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = "0.4"
[build-dependencies]
cc = "1.0"
bindgen = "0.59"

View File

@@ -0,0 +1,39 @@
use cc::Build;
use std::{
env,
path::{Path, PathBuf},
};
fn main() {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
println!("cargo:rerun-if-changed=src");
let ffi_header = "src/tool_ffi.h";
bindgen::builder()
.header(ffi_header)
.rustified_enum("*")
.generate()
.unwrap()
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("tool_ffi.rs"))
.unwrap();
let mut builder = Build::new();
builder.include(
manifest_dir
.parent()
.unwrap()
.parent()
.unwrap()
.join("cpp")
.join("common"),
);
builder.file("src/tool.cpp");
// crate
builder
.cpp(false)
.static_crt(true)
.warnings(false)
.compile("tool");
}

View File

@@ -0,0 +1,44 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::os::raw::c_void;
include!(concat!(env!("OUT_DIR"), "/tool_ffi.rs"));
pub struct Tool {
inner: *mut c_void,
}
impl Tool {
pub fn new(luid: i64) -> Result<Self, ()> {
let inner = unsafe { tool_new(luid) };
if inner.is_null() {
Err(())
} else {
Ok(Self { inner })
}
}
pub fn device(&mut self) -> *mut c_void {
unsafe { tool_device(self.inner) }
}
pub fn get_texture(&mut self, width: i32, height: i32) -> *mut c_void {
unsafe { tool_get_texture(self.inner, width, height) }
}
pub fn get_texture_size(&mut self, texture: *mut c_void) -> (i32, i32) {
let mut width = 0;
let mut height = 0;
unsafe { tool_get_texture_size(self.inner, texture, &mut width, &mut height) }
(width, height)
}
}
impl Drop for Tool {
fn drop(&mut self) {
unsafe { tool_destroy(self.inner) }
self.inner = std::ptr::null_mut();
}
}

View File

@@ -0,0 +1,67 @@
#include <memory.h>
#include "common.h"
#include "system.h"
namespace {
class Tool {
public:
std::unique_ptr<NativeDevice> native_;
bool initialized_ = false;
public:
Tool(int64_t luid) {
native_ = std::make_unique<NativeDevice>();
initialized_ = native_->Init(luid, nullptr, 1);
}
ID3D11Texture2D *GetTexture(int width, int height) {
native_->EnsureTexture(width, height);
return native_->GetCurrentTexture();
}
void getSize(ID3D11Texture2D *texture, int *width, int *height) {
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
*width = desc.Width;
*height = desc.Height;
}
};
} // namespace
extern "C" {
void *tool_new(int64_t luid) {
Tool *t = new Tool(luid);
if (t && !t->initialized_) {
delete t;
return nullptr;
}
return t;
}
void *tool_device(void *tool) {
Tool *t = (Tool *)tool;
return t->native_->device_.Get();
}
void *tool_get_texture(void *tool, int width, int height) {
Tool *t = (Tool *)tool;
return t->GetTexture(width, height);
}
void tool_get_texture_size(void *tool, void *texture, int *width, int *height) {
Tool *t = (Tool *)tool;
t->getSize((ID3D11Texture2D *)texture, width, height);
}
void tool_destroy(void *tool) {
Tool *t = (Tool *)tool;
if (t) {
delete t;
t = nullptr;
}
}
} // extern "C"

View File

@@ -0,0 +1,12 @@
#ifndef TOOL_FFI_H
#define TOOL_FFI_H
#include <stdint.h>
void *tool_new(int64_t luid);
void *tool_device(void *tool);
void *tool_get_texture(void *tool, int width, int height);
void tool_get_texture_size(void *tool, void *texture, int *width, int *height);
void tool_destroy(void *tool);
#endif // TOOL_FFI_H

View File

@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33627.172
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AMFTest", "AMFTest.vcxproj", "{59599E6A-52F7-44DD-9EC5-487342FF33F8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{59599E6A-52F7-44DD-9EC5-487342FF33F8}.Debug|x64.ActiveCfg = Debug|x64
{59599E6A-52F7-44DD-9EC5-487342FF33F8}.Debug|x64.Build.0 = Debug|x64
{59599E6A-52F7-44DD-9EC5-487342FF33F8}.Debug|x86.ActiveCfg = Debug|Win32
{59599E6A-52F7-44DD-9EC5-487342FF33F8}.Debug|x86.Build.0 = Debug|Win32
{59599E6A-52F7-44DD-9EC5-487342FF33F8}.Release|x64.ActiveCfg = Release|x64
{59599E6A-52F7-44DD-9EC5-487342FF33F8}.Release|x64.Build.0 = Release|x64
{59599E6A-52F7-44DD-9EC5-487342FF33F8}.Release|x86.ActiveCfg = Release|Win32
{59599E6A-52F7-44DD-9EC5-487342FF33F8}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E1168B32-184A-4268-AC43-21E66A82F076}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{59599e6a-52f7-44dd-9ec5-487342ff33f8}</ProjectGuid>
<RootNamespace>AMFTest</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;CSO_DIR="../../render/res";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\externals\AMF_v1.4.29\amf;..\..\externals\AMF_v1.4.29\amf\public\common;..\..\common\src;..\..\common\src\platform\win;..\..\externals\nvEncDXGIOutputDuplicationSample;..\..\codec\src;..\..\externals\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\..\externals\SDL\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\amf\src\common.cpp" />
<ClCompile Include="..\..\amf\src\decode.cpp" />
<ClCompile Include="..\..\amf\src\encode.cpp" />
<ClCompile Include="..\..\capture\src\dxgi.cpp" />
<ClCompile Include="..\..\codec\src\data.c" />
<ClCompile Include="..\..\codec\src\utils.c" />
<ClCompile Include="..\..\common\src\log.cpp" />
<ClCompile Include="..\..\common\src\platform\win\win.cpp" />
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\AMFFactory.cpp" />
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\AMFSTL.cpp" />
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\Thread.cpp" />
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\TraceAdapter.cpp" />
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\Windows\ThreadWindows.cpp" />
<ClCompile Include="..\..\externals\nvEncDXGIOutputDuplicationSample\DDAImpl.cpp" />
<ClCompile Include="..\..\externals\nvEncDXGIOutputDuplicationSample\Preproc.cpp" />
<ClCompile Include="..\..\render\src\dxgi_sdl.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="source">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="source\externals">
<UniqueIdentifier>{a09eabb2-ceac-4062-b19e-244b29176c2b}</UniqueIdentifier>
</Filter>
<Filter Include="source\externals\AMF_v1.4.29">
<UniqueIdentifier>{8adb5a7a-3fba-4330-9a43-dc4251f2ff2f}</UniqueIdentifier>
</Filter>
<Filter Include="source\externals\nvEncDXGIOutputDuplicationSample">
<UniqueIdentifier>{2bc8fd1a-5082-40fb-a6b5-ac8c3b84d450}</UniqueIdentifier>
</Filter>
<Filter Include="source\render">
<UniqueIdentifier>{6edc9023-4c96-4474-b544-20059b2e32ac}</UniqueIdentifier>
</Filter>
<Filter Include="source\codec">
<UniqueIdentifier>{ccee2176-d5f2-46b3-b138-538ac9bd7d13}</UniqueIdentifier>
</Filter>
<Filter Include="source\common">
<UniqueIdentifier>{8a1b8e43-9bdb-4e28-98d9-854c8e8bf107}</UniqueIdentifier>
</Filter>
<Filter Include="source\capture">
<UniqueIdentifier>{a65898d7-8992-40d0-9141-7b4f10415e1f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>source</Filter>
</ClCompile>
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\AMFFactory.cpp">
<Filter>source\externals\AMF_v1.4.29</Filter>
</ClCompile>
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\AMFSTL.cpp">
<Filter>source\externals\AMF_v1.4.29</Filter>
</ClCompile>
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\Thread.cpp">
<Filter>source\externals\AMF_v1.4.29</Filter>
</ClCompile>
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\Windows\ThreadWindows.cpp">
<Filter>source\externals\AMF_v1.4.29</Filter>
</ClCompile>
<ClCompile Include="..\..\externals\AMF_v1.4.29\amf\public\common\TraceAdapter.cpp">
<Filter>source\externals\AMF_v1.4.29</Filter>
</ClCompile>
<ClCompile Include="..\..\externals\nvEncDXGIOutputDuplicationSample\DDAImpl.cpp">
<Filter>source\externals\nvEncDXGIOutputDuplicationSample</Filter>
</ClCompile>
<ClCompile Include="..\..\externals\nvEncDXGIOutputDuplicationSample\Preproc.cpp">
<Filter>source\externals\nvEncDXGIOutputDuplicationSample</Filter>
</ClCompile>
<ClCompile Include="..\..\common\src\platform\win\win.cpp">
<Filter>source\common</Filter>
</ClCompile>
<ClCompile Include="..\..\capture\src\dxgi.cpp">
<Filter>source\capture</Filter>
</ClCompile>
<ClCompile Include="..\..\render\src\dxgi_sdl.cpp">
<Filter>source\render</Filter>
</ClCompile>
<ClCompile Include="..\..\codec\src\data.c">
<Filter>source\codec</Filter>
</ClCompile>
<ClCompile Include="..\..\codec\src\utils.c">
<Filter>source\codec</Filter>
</ClCompile>
<ClCompile Include="..\..\amf\src\common.cpp">
<Filter>source\codec</Filter>
</ClCompile>
<ClCompile Include="..\..\amf\src\decode.cpp">
<Filter>source\codec</Filter>
</ClCompile>
<ClCompile Include="..\..\amf\src\encode.cpp">
<Filter>source\codec</Filter>
</ClCompile>
<ClCompile Include="..\..\common\src\log.cpp">
<Filter>source\common</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@@ -0,0 +1,109 @@
#include <Windows.h>
#include <callback.h>
#include <common.h>
#include <iostream>
#include <stdint.h>
#include <system.h>
extern "C" {
void *dxgi_new_capturer(int64_t luid);
void *dxgi_device(void *self);
int dxgi_width(const void *self);
int dxgi_height(const void *self);
void *dxgi_capture(void *self, int wait_ms);
void destroy_dxgi_capturer(void *self);
void *amf_new_encoder(void *hdl, int64_t luid, API api, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs,
int32_t framerate, int32_t gop);
int amf_encode(void *e, void *tex, EncodeCallback callback, void *obj);
int amf_destroy_encoder(void *e);
void *amf_new_decoder(void *device, int64_t luid, int32_t api,
int32_t dataFormat, bool outputSharedHandle);
int amf_decode(void *decoder, uint8_t *data, int32_t length,
DecodeCallback callback, void *obj);
int amf_destroy_decoder(void *decoder);
void *CreateDXGIRender(long long luid, bool inputSharedHandle);
int DXGIRenderTexture(void *render, HANDLE shared_handle);
void DestroyDXGIRender(void *render);
void *DXGIDevice(void *render);
}
static const uint8_t *encode_data;
static int32_t encode_len;
static void *decode_shared_handle;
extern "C" static void encode_callback(const uint8_t *data, int32_t len,
int32_t key, const void *obj) {
encode_data = data;
encode_len = len;
std::cerr << "encode len" << len << std::endl;
}
extern "C" static void decode_callback(void *shared_handle, const void *obj) {
decode_shared_handle = shared_handle;
}
extern "C" void log_gpucodec(int level, const char *message) {
std::cout << message << std::endl;
}
int main() {
Adapters adapters;
adapters.Init(ADAPTER_VENDOR_AMD);
if (adapters.adapters_.size() == 0) {
std::cout << "no amd adapter" << std::endl;
return -1;
}
int64_t luid = LUID(adapters.adapters_[0].get()->desc1_);
DataFormat dataFormat = H264;
void *dup = dxgi_new_capturer(luid);
if (!dup) {
std::cerr << "create duplicator failed" << std::endl;
return -1;
}
int width = dxgi_width(dup);
int height = dxgi_height(dup);
std::cout << "width: " << width << " height: " << height << std::endl;
void *device = dxgi_device(dup);
void *encoder = amf_new_encoder(device, luid, API_DX11, dataFormat, width,
height, 4000, 30, 0xFFFF);
if (!encoder) {
std::cerr << "create encoder failed" << std::endl;
return -1;
}
void *render = CreateDXGIRender(luid, false);
if (!render) {
std::cerr << "create render failed" << std::endl;
return -1;
}
void *decoder =
amf_new_decoder(DXGIDevice(render), luid, API_DX11, dataFormat, false);
if (!decoder) {
std::cerr << "create decoder failed" << std::endl;
return -1;
}
while (true) {
void *texture = dxgi_capture(dup, 100);
if (!texture) {
std::cerr << "texture is NULL" << std::endl;
continue;
}
if (0 != amf_encode(encoder, texture, encode_callback, NULL)) {
std::cerr << "encode failed" << std::endl;
continue;
}
if (0 != amf_decode(decoder, (uint8_t *)encode_data, encode_len,
decode_callback, NULL)) {
std::cerr << "decode failed" << std::endl;
continue;
}
if (0 != DXGIRenderTexture(render, decode_shared_handle)) {
std::cerr << "render failed" << std::endl;
continue;
}
}
// no release temporarily
}

View File

@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33627.172
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MFXTest", "MFXTest.vcxproj", "{1FBDACB6-9142-40DA-9B50-5F591F1DD0AD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1FBDACB6-9142-40DA-9B50-5F591F1DD0AD}.Debug|x64.ActiveCfg = Debug|x64
{1FBDACB6-9142-40DA-9B50-5F591F1DD0AD}.Debug|x64.Build.0 = Debug|x64
{1FBDACB6-9142-40DA-9B50-5F591F1DD0AD}.Debug|x86.ActiveCfg = Debug|Win32
{1FBDACB6-9142-40DA-9B50-5F591F1DD0AD}.Debug|x86.Build.0 = Debug|Win32
{1FBDACB6-9142-40DA-9B50-5F591F1DD0AD}.Release|x64.ActiveCfg = Release|x64
{1FBDACB6-9142-40DA-9B50-5F591F1DD0AD}.Release|x64.Build.0 = Release|x64
{1FBDACB6-9142-40DA-9B50-5F591F1DD0AD}.Release|x86.ActiveCfg = Release|Win32
{1FBDACB6-9142-40DA-9B50-5F591F1DD0AD}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0B559C12-4403-4530-9A4E-7F4DF6235164}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{1fbdacb6-9142-40da-9b50-5f591f1dd0ad}</ProjectGuid>
<RootNamespace>MFXTest</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;CSO_DIR="../../render/res";MFX_D3D11_SUPPORT;NOMINMAX=1;MFX_DEPRECATED_OFF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>D:\rustdesk\gpucodec\externals\libvpl_v2023.4.0\tools\legacy\media_sdk_compatibility_headers;..\..\..\externals\libvpl_v2023.4.0\libvpl;..\..\..\externals\libvpl_v2023.4.0\api;..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src;..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\include;..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\include\vm;..\..\..\externals\nvEncDXGIOutputDuplicationSample;..\..\..\native\common;..\..\..\native\gpucodec;..\..\..\externals\SDL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\..\..\externals\SDL\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_config_interface\mfx_config_interface.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_config_interface\mfx_config_interface_string_api.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_config.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_loader.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_log.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_lowlatency.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_msdk.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_critical_section.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_dispatcher.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_dispatcher_log.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_dispatcher_main.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_driver_store_loader.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_dxva2_device.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_function_table.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_library_iterator.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_load_dll.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_win_reg_key.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\avc_bitstream.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\avc_nal_spl.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\avc_spl.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\base_allocator.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\d3d11_allocator.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\sample_utils.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\vm\atomic.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\vm\shared_object.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\vm\thread_windows.cpp" />
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\vm\time.cpp" />
<ClCompile Include="..\..\..\externals\nvEncDXGIOutputDuplicationSample\DDAImpl.cpp" />
<ClCompile Include="..\..\..\native\common\log.cpp" />
<ClCompile Include="..\..\..\native\common\platform\win\win.cpp" />
<ClCompile Include="..\..\..\native\gpucodec\data.c" />
<ClCompile Include="..\..\..\native\gpucodec\gpucodec_utils.c" />
<ClCompile Include="..\..\..\native\vpl\vpl_decode.cpp" />
<ClCompile Include="..\..\..\native\vpl\vpl_encode.cpp" />
<ClCompile Include="..\..\capture\src\dxgi.cpp" />
<ClCompile Include="..\..\render\src\dxgi_sdl.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Source Files\capture">
<UniqueIdentifier>{bd78eb0e-0894-4f11-a2f5-2cc02731bc52}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\codec">
<UniqueIdentifier>{dd2d68d6-6559-413b-948b-f718b34cde0a}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\external">
<UniqueIdentifier>{fd52358a-9b99-43b4-b717-f3a77580a52b}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\external\nvEncDXGIOutputDuplicationSample">
<UniqueIdentifier>{bdbd71f9-8ed4-4bdd-a8c3-612f5858504d}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\render">
<UniqueIdentifier>{2089080f-479d-4d89-8cf5-7ac8e52b11d1}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\common">
<UniqueIdentifier>{3514d21d-4a74-44b9-b21b-bea909cda0b2}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\external\vpl">
<UniqueIdentifier>{ad9c7b01-cd0a-40a2-afe6-f81133ac0e15}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\external\vpl\libvpl">
<UniqueIdentifier>{ad00df31-d9af-432b-b452-b3ab9c2b381c}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\external\vpl\libvpl\src">
<UniqueIdentifier>{b5498eaa-1ff8-4c2b-b5c3-7c71a0a2efcc}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\external\vpl\libvpl\src\windows">
<UniqueIdentifier>{bbca452b-a73f-4d37-a393-627c0db2617c}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\external\vpl\libvpl\src\mfx_config_interface">
<UniqueIdentifier>{cb497035-08af-4b38-9abd-e4616861d366}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\external\vpl\sample_common">
<UniqueIdentifier>{85b55a79-fab7-428c-bc6c-fdf0a58b4704}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\external\vpl\sample_common\vm">
<UniqueIdentifier>{a3824aee-0800-467f-964b-61a76000e160}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\capture\src\dxgi.cpp">
<Filter>Source Files\capture</Filter>
</ClCompile>
<ClCompile Include="..\..\render\src\dxgi_sdl.cpp">
<Filter>Source Files\render</Filter>
</ClCompile>
<ClCompile Include="..\..\..\native\common\log.cpp">
<Filter>Source Files\common</Filter>
</ClCompile>
<ClCompile Include="..\..\..\native\common\platform\win\win.cpp">
<Filter>Source Files\common</Filter>
</ClCompile>
<ClCompile Include="..\..\..\native\gpucodec\data.c">
<Filter>Source Files\codec</Filter>
</ClCompile>
<ClCompile Include="..\..\..\native\gpucodec\gpucodec_utils.c">
<Filter>Source Files\codec</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_critical_section.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_dispatcher.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_dispatcher_log.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_dispatcher_main.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_driver_store_loader.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_dxva2_device.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_function_table.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_library_iterator.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_load_dll.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\windows\mfx_win_reg_key.cpp">
<Filter>Source Files\external\vpl\libvpl\src\windows</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_config_interface\mfx_config_interface.cpp">
<Filter>Source Files\external\vpl\libvpl\src\mfx_config_interface</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_config_interface\mfx_config_interface_string_api.cpp">
<Filter>Source Files\external\vpl\libvpl\src\mfx_config_interface</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl.cpp">
<Filter>Source Files\external\vpl\libvpl\src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_config.cpp">
<Filter>Source Files\external\vpl\libvpl\src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_loader.cpp">
<Filter>Source Files\external\vpl\libvpl\src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_log.cpp">
<Filter>Source Files\external\vpl\libvpl\src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_lowlatency.cpp">
<Filter>Source Files\external\vpl\libvpl\src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\libvpl\src\mfx_dispatcher_vpl_msdk.cpp">
<Filter>Source Files\external\vpl\libvpl\src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\avc_bitstream.cpp">
<Filter>Source Files\external\vpl\sample_common</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\avc_nal_spl.cpp">
<Filter>Source Files\external\vpl\sample_common</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\avc_spl.cpp">
<Filter>Source Files\external\vpl\sample_common</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\base_allocator.cpp">
<Filter>Source Files\external\vpl\sample_common</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\sample_utils.cpp">
<Filter>Source Files\external\vpl\sample_common</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\vm\atomic.cpp">
<Filter>Source Files\external\vpl\sample_common\vm</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\vm\shared_object.cpp">
<Filter>Source Files\external\vpl\sample_common\vm</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\vm\thread_windows.cpp">
<Filter>Source Files\external\vpl\sample_common\vm</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\vm\time.cpp">
<Filter>Source Files\external\vpl\sample_common\vm</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\nvEncDXGIOutputDuplicationSample\DDAImpl.cpp">
<Filter>Source Files\external\nvEncDXGIOutputDuplicationSample</Filter>
</ClCompile>
<ClCompile Include="..\..\..\native\vpl\vpl_decode.cpp">
<Filter>Source Files\codec</Filter>
</ClCompile>
<ClCompile Include="..\..\..\native\vpl\vpl_encode.cpp">
<Filter>Source Files\codec</Filter>
</ClCompile>
<ClCompile Include="..\..\..\externals\libvpl_v2023.4.0\tools\legacy\sample_common\src\d3d11_allocator.cpp">
<Filter>Source Files\external\vpl\sample_common</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@@ -0,0 +1,111 @@
#include <Windows.h>
#include <callback.h>
#include <common.h>
#include <iostream>
#include <stdint.h>
#include <system.h>
extern "C" {
void *dxgi_new_capturer(int64_t luid);
void *dxgi_device(void *self);
int dxgi_width(const void *self);
int dxgi_height(const void *self);
void *dxgi_capture(void *self, int wait_ms);
void destroy_dxgi_capturer(void *self);
void *vpl_new_encoder(void *hdl, int64_t luid, API api, DataFormat dataFormat,
int32_t width, int32_t height, int32_t kbs,
int32_t framerate, int32_t gop);
int vpl_encode(void *e, void *tex, EncodeCallback callback, void *obj);
int vpl_destroy_encoder(void *e);
void *vpl_new_decoder(void *device, int64_t luid, int32_t api,
int32_t dataFormat, bool outputSharedHandle);
int vpl_decode(void *decoder, uint8_t *data, int32_t length,
DecodeCallback callback, void *obj);
int vpl_destroy_decoder(void *decoder);
void *CreateDXGIRender(long long luid, bool inputSharedHandle);
int DXGIRenderTexture(void *render, HANDLE shared_handle);
void DestroyDXGIRender(void *render);
void *DXGIDevice(void *render);
}
static const uint8_t *encode_data;
static int32_t encode_len;
static void *decode_shared_handle;
extern "C" static void encode_callback(const uint8_t *data, int32_t len,
int32_t key, const void *obj) {
encode_data = data;
encode_len = len;
}
extern "C" static void decode_callback(void *shared_handle, const void *obj) {
decode_shared_handle = shared_handle;
}
extern "C" void log_gpucodec(int level, const char *message) {
std::cout << message << std::endl;
}
int main() {
Adapters adapters;
adapters.Init(ADAPTER_VENDOR_INTEL);
if (adapters.adapters_.size() == 0) {
std::cout << "no intel adapter" << std::endl;
return -1;
}
int64_t luid = LUID(adapters.adapters_[0].get()->desc1_);
DataFormat dataFormat = H264;
void *dup = dxgi_new_capturer(luid);
if (!dup) {
std::cerr << "create duplicator failed" << std::endl;
return -1;
}
int width = dxgi_width(dup);
int height = dxgi_height(dup);
std::cout << "width: " << width << " height: " << height << std::endl;
void *device = dxgi_device(dup);
void *encoder = vpl_new_encoder(device, luid, API_DX11, dataFormat, width,
height, 4000, 30, 0xFFFF);
if (!encoder) {
std::cerr << "create encoder failed" << std::endl;
return -1;
}
void *render = CreateDXGIRender(luid, false);
if (!render) {
std::cerr << "create render failed" << std::endl;
return -1;
}
void *decoder =
vpl_new_decoder(DXGIDevice(render), luid, API_DX11, dataFormat, false);
if (!decoder) {
std::cerr << "create decoder failed" << std::endl;
return -1;
}
while (true) {
void *texture = dxgi_capture(dup, 100);
if (!texture) {
std::cerr << "texture is NULL" << std::endl;
continue;
}
if (0 != vpl_encode(encoder, texture, encode_callback, NULL)) {
std::cerr << "encode failed" << std::endl;
continue;
}
if (0 != vpl_decode(decoder, (uint8_t *)encode_data, encode_len,
decode_callback, NULL)) {
std::cerr << "decode failed" << std::endl;
continue;
}
if (0 != DXGIRenderTexture(render, decode_shared_handle)) {
std::cerr << "render failed" << std::endl;
continue;
}
}
// no release temporarily
}

View File

@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33627.172
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShaderCompileTool", "ShaderCompileTool.vcxproj", "{AB626EFE-F38C-4587-B79C-29FC898FBC96}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AB626EFE-F38C-4587-B79C-29FC898FBC96}.Debug|x64.ActiveCfg = Debug|x64
{AB626EFE-F38C-4587-B79C-29FC898FBC96}.Debug|x64.Build.0 = Debug|x64
{AB626EFE-F38C-4587-B79C-29FC898FBC96}.Debug|x86.ActiveCfg = Debug|Win32
{AB626EFE-F38C-4587-B79C-29FC898FBC96}.Debug|x86.Build.0 = Debug|Win32
{AB626EFE-F38C-4587-B79C-29FC898FBC96}.Release|x64.ActiveCfg = Release|x64
{AB626EFE-F38C-4587-B79C-29FC898FBC96}.Release|x64.Build.0 = Release|x64
{AB626EFE-F38C-4587-B79C-29FC898FBC96}.Release|x86.ActiveCfg = Release|Win32
{AB626EFE-F38C-4587-B79C-29FC898FBC96}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9A483A95-1588-48EF-A2C9-2B4731AFCAB2}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{ab626efe-f38c-4587-b79c-29fc898fbc96}</ProjectGuid>
<RootNamespace>ShaderCompileTool</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<FxCompile Include="nv_vertex_shader.hlsl">
<ObjectFileOutput Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</ObjectFileOutput>
<EntryPointName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">VS</EntryPointName>
<DisableOptimizations Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</DisableOptimizations>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Vertex</ShaderType>
<HeaderFileOutput Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).h</HeaderFileOutput>
</FxCompile>
<FxCompile Include="nv_pixel_shader_601.hlsl">
<ObjectFileOutput Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</ObjectFileOutput>
<EntryPointName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">PS</EntryPointName>
<DisableOptimizations Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</DisableOptimizations>
<ShaderType Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Pixel</ShaderType>
<HeaderFileOutput Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).h</HeaderFileOutput>
</FxCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<FxCompile Include="nv_pixel_shader_601.hlsl" />
<FxCompile Include="nv_vertex_shader.hlsl" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@@ -0,0 +1,20 @@
Texture2D g_txFrame0 : register(t0);
Texture2D g_txFrame1 : register(t1);
SamplerState g_Sam : register(s0);
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD0;
};
float4 PS(PS_INPUT input) : SV_TARGET{
float y = g_txFrame0.Sample(g_Sam, input.Tex).r;
y = 1.164383561643836 * (y - 0.0625);
float2 uv = g_txFrame1.Sample(g_Sam, input.Tex).rg - float2(0.5f, 0.5f);
float u = uv.x;
float v = uv.y;
float r = saturate(y + 1.596026785714286 * v);
float g = saturate(y - 0.812967647237771 * v - 0.391762290094914 * u);
float b = saturate(y + 2.017232142857142 * u);
return float4(r, g, b, 1.0f);
}

View File

@@ -0,0 +1,15 @@
struct VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD;
};
struct VS_OUTPUT
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD;
};
VS_OUTPUT VS(VS_INPUT input)
{
return input;
}

View File

@@ -0,0 +1,214 @@
use env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV};
#[cfg(feature = "vram")]
use hwcodec::{
common::MAX_GOP,
vram::{DynamicContext, FeatureContext},
};
use hwcodec::{
common::{DataFormat, Quality::*, RateControl::*},
ffmpeg::AVPixelFormat::*,
ffmpeg_ram::{
decode::{DecodeContext, Decoder},
encode::{EncodeContext, Encoder},
ffmpeg_linesize_offset_length, CodecInfo,
},
};
#[cfg(feature = "vram")]
use tool::Tool;
fn main() {
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info"));
let max_align = 16;
setup_ram(max_align);
#[cfg(feature = "vram")]
setup_vram(max_align);
}
fn setup_ram(max_align: i32) {
let encoders = Encoder::available_encoders(
EncodeContext {
name: String::from(""),
mc_name: None,
width: 1920,
height: 1080,
pixfmt: AV_PIX_FMT_NV12,
align: 0,
fps: 30,
gop: 60,
rc: RC_CBR,
quality: Quality_Default,
kbs: 0,
q: -1,
thread_count: 1,
},
None,
);
let decoders = Decoder::available_decoders();
let h264_encoders = encoders
.iter()
.filter(|info| info.name.contains("h264"))
.cloned()
.collect::<Vec<_>>();
let h265_encoders = encoders
.iter()
.filter(|info| info.name.contains("hevc"))
.cloned()
.collect::<Vec<_>>();
let h264_decoders = decoders
.iter()
.filter(|info| info.format == DataFormat::H264)
.cloned()
.collect::<Vec<_>>();
let h265_decoders = decoders
.iter()
.filter(|info| info.format == DataFormat::H265)
.cloned()
.collect::<Vec<_>>();
let start_width = 1920;
let start_height = 1080;
let step = 2;
for width in (start_width..=(start_width + max_align)).step_by(step) {
for height in (start_height..=(start_height + max_align)).step_by(step) {
for encode_info in &h264_encoders {
test_ram(width, height, encode_info.clone(), h264_decoders[0].clone());
}
for decode_info in &h264_decoders {
test_ram(width, height, h264_encoders[0].clone(), decode_info.clone());
}
for encode_info in &h265_encoders {
test_ram(width, height, encode_info.clone(), h265_decoders[0].clone());
}
for decode_info in &h265_decoders {
test_ram(width, height, h265_encoders[0].clone(), decode_info.clone());
}
}
}
}
fn test_ram(width: i32, height: i32, encode_info: CodecInfo, decode_info: CodecInfo) {
println!(
"Test {}x{}: {} -> {}",
width, height, encode_info.name, decode_info.name
);
let encode_ctx = EncodeContext {
name: encode_info.name.clone(),
mc_name: None,
width,
height,
pixfmt: AV_PIX_FMT_NV12,
align: 0,
kbs: 0,
fps: 30,
gop: 60,
quality: Quality_Default,
rc: RC_CBR,
thread_count: 1,
q: -1,
};
let decode_ctx = DecodeContext {
name: decode_info.name.clone(),
device_type: decode_info.hwdevice,
thread_count: 4,
};
let (_, _, len) = ffmpeg_linesize_offset_length(
encode_ctx.pixfmt,
encode_ctx.width as _,
encode_ctx.height as _,
encode_ctx.align as _,
)
.unwrap();
let mut video_encoder = Encoder::new(encode_ctx).unwrap();
let mut video_decoder = Decoder::new(decode_ctx).unwrap();
let buf: Vec<u8> = vec![0; len as usize];
let encode_frames = video_encoder.encode(&buf, 0).unwrap();
assert_eq!(encode_frames.len(), 1);
let docode_frames = video_decoder.decode(&encode_frames[0].data).unwrap();
assert_eq!(docode_frames.len(), 1);
assert_eq!(docode_frames[0].width, width);
assert_eq!(docode_frames[0].height, height);
println!(
"Pass {}x{}: {} -> {} {:?}",
width, height, encode_info.name, decode_info.name, decode_info.hwdevice
)
}
#[cfg(feature = "vram")]
fn setup_vram(max_align: i32) {
let encoders = hwcodec::vram::encode::available(DynamicContext {
device: None,
width: 1920,
height: 1080,
kbitrate: 1000,
framerate: 30,
gop: MAX_GOP as _,
});
let decoders = hwcodec::vram::decode::available();
let start_width = 1920;
let start_height = 1080;
let step = 2;
for width in (start_width..=(start_width + max_align)).step_by(step) {
for height in (start_height..=(start_height + max_align)).step_by(step) {
for encode_info in &encoders {
if let Some(decoder) = decoders.iter().find(|d| {
d.luid == encode_info.luid && d.data_format == encode_info.data_format
}) {
test_vram(width, height, encode_info.clone(), decoder.clone());
}
}
for decode_info in &decoders {
if let Some(encoder) = encoders.iter().find(|e| {
e.luid == decode_info.luid && e.data_format == decode_info.data_format
}) {
test_vram(width, height, encoder.clone(), decode_info.clone());
}
}
}
}
}
#[cfg(feature = "vram")]
fn test_vram(
width: i32,
height: i32,
encode_info: FeatureContext,
decode_info: hwcodec::vram::DecodeContext,
) {
println!(
"Test {}x{}: {:?} {:?} -> {:?}",
width, height, encode_info.data_format, encode_info.driver, decode_info.driver
);
let mut tool = Tool::new(encode_info.luid).unwrap();
let encode_ctx = hwcodec::vram::EncodeContext {
f: encode_info.clone(),
d: hwcodec::vram::DynamicContext {
device: Some(tool.device()),
width,
height,
kbitrate: 1000,
framerate: 30,
gop: MAX_GOP as _,
},
};
let mut encoder = hwcodec::vram::encode::Encoder::new(encode_ctx).unwrap();
let mut decoder = hwcodec::vram::decode::Decoder::new(hwcodec::vram::DecodeContext {
device: Some(tool.device()),
..decode_info.clone()
})
.unwrap();
let encode_frames = encoder.encode(tool.get_texture(width, height), 0).unwrap();
assert_eq!(encode_frames.len(), 1);
let decoder_frames = decoder.decode(&encode_frames[0].data).unwrap();
assert_eq!(decoder_frames.len(), 1);
let (decoded_width, decoded_height) = tool.get_texture_size(decoder_frames[0].texture);
assert_eq!(decoded_width, width);
assert_eq!(decoded_height, height);
println!(
"Pass {}x{}: {:?} {:?} -> {:?}",
width, height, encode_info.data_format, encode_info.driver, decode_info.driver
);
}

View File

@@ -0,0 +1,67 @@
use env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV};
use hwcodec::{
common::{get_gpu_signature, Quality::*, RateControl::*},
ffmpeg::AVPixelFormat,
ffmpeg_ram::{
decode::Decoder,
encode::{EncodeContext, Encoder},
},
};
fn main() {
let start = std::time::Instant::now();
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info"));
ram();
#[cfg(feature = "vram")]
vram();
log::info!(
"signature: {:?}, elapsed: {:?}",
get_gpu_signature(),
start.elapsed()
);
}
fn ram() {
println!("ram:");
println!("encoders:");
let ctx = EncodeContext {
name: String::from(""),
mc_name: None,
width: 1280,
height: 720,
pixfmt: AVPixelFormat::AV_PIX_FMT_NV12,
align: 0,
kbs: 1000,
fps: 30,
gop: i32::MAX,
quality: Quality_Default,
rc: RC_CBR,
q: -1,
thread_count: 1,
};
let encoders = Encoder::available_encoders(ctx.clone(), None);
encoders.iter().map(|e| println!("{:?}", e)).count();
println!("decoders:");
let decoders = Decoder::available_decoders();
decoders.iter().map(|e| println!("{:?}", e)).count();
}
#[cfg(feature = "vram")]
fn vram() {
use hwcodec::common::MAX_GOP;
use hwcodec::vram::{decode, encode, DynamicContext};
println!("vram:");
println!("encoders:");
let encoders = encode::available(DynamicContext {
width: 1920,
height: 1080,
kbitrate: 5000,
framerate: 30,
gop: MAX_GOP as _,
device: None,
});
encoders.iter().map(|e| println!("{:?}", e)).count();
println!("decoders:");
let decoders = decode::available();
decoders.iter().map(|e| println!("{:?}", e)).count();
}

View File

@@ -0,0 +1,147 @@
use env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV};
use hwcodec::{
common::{Quality::*, RateControl::*},
ffmpeg::AVPixelFormat,
ffmpeg_ram::{
decode::{DecodeContext, Decoder},
encode::{EncodeContext, Encoder},
CodecInfo, CodecInfos,
},
};
use rand::random;
use std::io::Write;
use std::time::Instant;
fn main() {
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info"));
let ctx = EncodeContext {
name: String::from(""),
mc_name: None,
width: 1920,
height: 1080,
pixfmt: AVPixelFormat::AV_PIX_FMT_NV12,
align: 0,
kbs: 5000,
fps: 30,
gop: 60,
quality: Quality_Default,
rc: RC_DEFAULT,
thread_count: 4,
q: -1,
};
let yuv_count = 10;
println!("benchmark");
let yuvs = prepare_yuv(ctx.width as _, ctx.height as _, yuv_count);
let encoders = Encoder::available_encoders(ctx.clone(), None);
log::info!("encoders: {:?}", encoders);
let best = CodecInfo::prioritized(encoders.clone());
for info in encoders {
test_encoder(info.clone(), ctx.clone(), &yuvs, is_best(&best, &info));
}
let (h264s, h265s) = prepare_h26x(best, ctx.clone(), &yuvs);
let decoders = Decoder::available_decoders();
log::info!("decoders: {:?}", decoders);
let best = CodecInfo::prioritized(decoders.clone());
for info in decoders {
let h26xs = if info.name.contains("h264") {
&h264s
} else {
&h265s
};
if h26xs.len() == yuv_count {
test_decoder(info.clone(), h26xs, is_best(&best, &info));
}
}
}
fn test_encoder(info: CodecInfo, ctx: EncodeContext, yuvs: &Vec<Vec<u8>>, best: bool) {
let mut ctx = ctx;
ctx.name = info.name;
let mut encoder = Encoder::new(ctx.clone()).unwrap();
let start = Instant::now();
for yuv in yuvs {
let _ = encoder
.encode(yuv, start.elapsed().as_millis() as _)
.unwrap();
}
println!(
"{}{}: {:?}",
if best { "*" } else { "" },
ctx.name,
start.elapsed() / yuvs.len() as _
);
}
fn test_decoder(info: CodecInfo, h26xs: &Vec<Vec<u8>>, best: bool) {
let ctx = DecodeContext {
name: info.name,
device_type: info.hwdevice,
thread_count: 4,
};
let mut decoder = Decoder::new(ctx.clone()).unwrap();
let start = Instant::now();
let mut cnt = 0;
for h26x in h26xs {
let _ = decoder.decode(h26x).unwrap();
cnt += 1;
}
let device = format!("{:?}", ctx.device_type).to_lowercase();
let device = device.split("_").last().unwrap();
println!(
"{}{} {}: {:?}",
if best { "*" } else { "" },
ctx.name,
device,
start.elapsed() / cnt
);
}
fn prepare_yuv(width: usize, height: usize, count: usize) -> Vec<Vec<u8>> {
let mut ret = vec![];
for index in 0..count {
let linesize = width * 3 / 2;
let mut yuv = vec![0u8; linesize * height];
for y in 0..height {
for x in 0..linesize {
yuv[linesize * y + x] = random();
}
}
ret.push(yuv);
print!("\rprepare {}/{}", index + 1, count);
std::io::stdout().flush().ok();
}
println!();
ret
}
fn prepare_h26x(
best: CodecInfos,
ctx: EncodeContext,
yuvs: &Vec<Vec<u8>>,
) -> (Vec<Vec<u8>>, Vec<Vec<u8>>) {
let f = |info: Option<CodecInfo>| {
let mut h26xs = vec![];
if let Some(info) = info {
let mut ctx = ctx.clone();
ctx.name = info.name;
let mut encoder = Encoder::new(ctx).unwrap();
for yuv in yuvs {
let h26x = encoder.encode(yuv, 0).unwrap();
for frame in h26x {
h26xs.push(frame.data.to_vec());
}
}
}
h26xs
};
(f(best.h264), f(best.h265))
}
fn is_best(best: &CodecInfos, info: &CodecInfo) -> bool {
Some(info.clone()) == best.h264 || Some(info.clone()) == best.h265
}

View File

@@ -0,0 +1,117 @@
use env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV};
use hwcodec::{
common::{Quality::*, RateControl::*},
ffmpeg::{AVHWDeviceType::*, AVPixelFormat::*},
ffmpeg_ram::{
decode::{DecodeContext, Decoder},
encode::{EncodeContext, Encoder},
ffmpeg_linesize_offset_length,
},
};
use std::{
fs::File,
io::{Read, Write},
};
fn main() {
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info"));
let encode_ctx = EncodeContext {
name: String::from("h264_nvenc"),
mc_name: None,
width: 1920,
height: 1080,
pixfmt: AV_PIX_FMT_NV12,
align: 0,
kbs: 0,
fps: 30,
gop: 60,
quality: Quality_Default,
rc: RC_DEFAULT,
thread_count: 4,
q: -1,
};
let decode_ctx = DecodeContext {
name: String::from("hevc"),
device_type: AV_HWDEVICE_TYPE_D3D11VA,
thread_count: 4,
};
let _ = std::thread::spawn(move || test_encode_decode(encode_ctx, decode_ctx)).join();
}
fn test_encode_decode(encode_ctx: EncodeContext, decode_ctx: DecodeContext) {
let size: usize;
if let Ok((_, _, len)) = ffmpeg_linesize_offset_length(
encode_ctx.pixfmt,
encode_ctx.width as _,
encode_ctx.height as _,
encode_ctx.align as _,
) {
size = len as _;
} else {
return;
}
let mut video_encoder = Encoder::new(encode_ctx).unwrap();
let mut video_decoder = Decoder::new(decode_ctx).unwrap();
let mut yuv_file = File::open("input/1920_1080_decoded.yuv").unwrap();
let mut encode_file = File::create("output/1920_1080.265").unwrap();
let mut decode_file = File::create("output/1920_1080_decode.yuv").unwrap();
let mut buf = vec![0; size + 64];
let mut encode_sum = 0;
let mut decode_sum = 0;
let mut encode_size = 0;
let mut counter = 0;
let mut f = |data: &[u8]| {
let now = std::time::Instant::now();
if let Ok(encode_frames) = video_encoder.encode(data, 0) {
log::info!("encode:{:?}", now.elapsed());
encode_sum += now.elapsed().as_micros();
for encode_frame in encode_frames.iter() {
encode_size += encode_frame.data.len();
encode_file.write_all(&encode_frame.data).unwrap();
encode_file.flush().unwrap();
let now = std::time::Instant::now();
if let Ok(docode_frames) = video_decoder.decode(&encode_frame.data) {
log::info!("decode:{:?}", now.elapsed());
decode_sum += now.elapsed().as_micros();
counter += 1;
for decode_frame in docode_frames {
log::info!("decode_frame:{}", decode_frame);
for data in decode_frame.data.iter() {
decode_file.write_all(data).unwrap();
decode_file.flush().unwrap();
}
}
}
}
}
};
loop {
match yuv_file.read(&mut buf[..size]) {
Ok(n) => {
if n > 0 {
f(&buf[..n]);
} else {
break;
}
}
Err(e) => {
log::info!("{:?}", e);
break;
}
}
}
log::info!(
"counter:{}, encode_avg:{}us, decode_avg:{}us, size_avg:{}",
counter,
encode_sum / counter,
decode_sum / counter,
encode_size / counter as usize,
);
}

View File

@@ -0,0 +1,78 @@
use capture::dxgi;
use env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV};
use hwcodec::common::{DataFormat, Driver, MAX_GOP};
use hwcodec::vram::{
decode::Decoder, encode::Encoder, DecodeContext, DynamicContext, EncodeContext, FeatureContext,
};
use render::Render;
use std::{
io::Write,
path::PathBuf,
time::{Duration, Instant},
};
fn main() {
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "trace"));
let luid = 69524; // 63444; // 59677
unsafe {
// one luid create render failed on my pc, wouldn't happen in rustdesk
let data_format = DataFormat::H265;
let mut capturer = dxgi::Capturer::new(luid).unwrap();
let mut render = Render::new(luid, false).unwrap();
let en_ctx = EncodeContext {
f: FeatureContext {
driver: Driver::FFMPEG,
vendor: Driver::NV,
data_format,
luid,
},
d: DynamicContext {
device: Some(capturer.device()),
width: capturer.width(),
height: capturer.height(),
kbitrate: 5000,
framerate: 30,
gop: MAX_GOP as _,
},
};
let de_ctx = DecodeContext {
device: Some(render.device()),
driver: Driver::FFMPEG,
vendor: Driver::NV,
data_format,
luid,
};
let mut dec = Decoder::new(de_ctx).unwrap();
let mut enc = Encoder::new(en_ctx).unwrap();
let filename = PathBuf::from(".\\1.264");
let mut file = std::fs::File::create(filename).unwrap();
let mut dup_sum = Duration::ZERO;
let mut enc_sum = Duration::ZERO;
let mut dec_sum = Duration::ZERO;
let mut pts_instant = Instant::now();
loop {
let start = Instant::now();
let texture = capturer.capture(100);
if texture.is_null() {
continue;
}
dup_sum += start.elapsed();
let start = Instant::now();
let frame = enc
.encode(texture, pts_instant.elapsed().as_millis() as _)
.unwrap();
enc_sum += start.elapsed();
for f in frame {
file.write_all(&mut f.data).unwrap();
let start = Instant::now();
let frames = dec.decode(&f.data).unwrap();
dec_sum += start.elapsed();
for f in frames {
render.render(f.texture).unwrap();
}
}
}
}
}

View File

@@ -0,0 +1,128 @@
use env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV};
use hwcodec::{
common::{Quality::*, RateControl::*, MAX_GOP},
ffmpeg::{
AVHWDeviceType::{self, *},
AVPixelFormat::*,
},
ffmpeg_ram::{
decode::{DecodeContext, Decoder},
encode::{EncodeContext, Encoder},
},
};
use std::{
fs::File,
io::{Read, Write},
};
fn main() {
let gpu = true;
let h264 = true;
let hw_type = if gpu { "gpu" } else { "hw" };
let file_type = if h264 { "h264" } else { "h265" };
let codec = if h264 { "h264" } else { "hevc" };
init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info"));
let device_type = AV_HWDEVICE_TYPE_CUDA;
let decode_ctx = DecodeContext {
name: String::from(codec),
device_type,
thread_count: 4,
};
let mut video_decoder = Decoder::new(decode_ctx).unwrap();
decode_encode(
&mut video_decoder,
0,
hw_type,
file_type,
1600,
900,
h264,
device_type,
);
decode_encode(
&mut video_decoder,
1,
hw_type,
file_type,
1440,
900,
h264,
device_type,
);
}
fn decode_encode(
video_decoder: &mut Decoder,
index: usize,
hw_type: &str,
file_type: &str,
width: usize,
height: usize,
h264: bool,
device_type: AVHWDeviceType,
) {
let input_enc_filename = format!("input/data_and_line/{hw_type}_{width}_{height}.{file_type}");
let len_filename = format!("input/data_and_line/{hw_type}_{width}_{height}_{file_type}.txt");
let enc_ctx = EncodeContext {
name: if h264 {
"h264_nvenc".to_owned()
} else {
"hevc_nvenc".to_owned()
},
mc_name: None,
width: width as _,
height: height as _,
pixfmt: if device_type == AV_HWDEVICE_TYPE_NONE {
AV_PIX_FMT_YUV420P
} else {
AV_PIX_FMT_NV12
},
align: 0,
kbs: 1_000,
fps: 30,
gop: MAX_GOP as _,
quality: Quality_Default,
rc: RC_DEFAULT,
thread_count: 4,
q: -1,
};
let mut video_encoder = Encoder::new(enc_ctx).unwrap();
let mut encode_file =
File::create(format!("output/{hw_type}_{width}_{height}.{file_type}")).unwrap();
let mut yuv_file =
File::create(format!("output/{hw_type}_{width}_{height}_decode.yuv")).unwrap();
let mut file_lens = File::open(len_filename).unwrap();
let mut file = File::open(input_enc_filename).unwrap();
let mut file_lens_buf = Vec::new();
file_lens.read_to_end(&mut file_lens_buf).unwrap();
let file_lens_str = String::from_utf8_lossy(&file_lens_buf).to_string();
let lens: Vec<usize> = file_lens_str
.split(",")
.filter(|e| !e.is_empty())
.map(|e| e.parse().unwrap())
.collect();
for i in 0..lens.len() {
let mut buf = vec![0; lens[i]];
file.read(&mut buf).unwrap();
let frames = video_decoder.decode(&buf).unwrap();
println!(
"file{}, w:{}, h:{}, fmt:{:?}, linesize:{:?}",
index, frames[0].width, frames[0].height, frames[0].pixfmt, frames[0].linesize
);
assert!(frames.len() == 1);
let mut encode_buf = Vec::new();
for d in &mut frames[0].data {
encode_buf.append(d);
}
yuv_file.write_all(&encode_buf).unwrap();
let frames = video_encoder.encode(&encode_buf, 0).unwrap();
assert_eq!(frames.len(), 1);
for f in frames {
encode_file.write_all(&f.data).unwrap();
}
}
}

View File

@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.15)
project(amf)
set(CMAKE_CXX_STANDARD 11)
cmake_policy(SET CMP0091 NEW)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set(AMF_DIR ${CMAKE_SOURCE_DIR})
set(AMF_COMMON_DIR ${AMF_DIR}/amf/public/common)
# Source files
set(SOURCES
${AMF_COMMON_DIR}/AMFFactory.cpp
${AMF_COMMON_DIR}/AMFSTL.cpp
${AMF_COMMON_DIR}/Thread.cpp
${AMF_COMMON_DIR}/TraceAdapter.cpp
${AMF_COMMON_DIR}/Windows/ThreadWindows.cpp
)
# Include directories
set(AMF_INCLUDE_DIRS
${AMF_DIR}/amf
${AMF_COMMON_DIR}
)
include_directories(${AMF_INCLUDE_DIRS})
# Build target
add_library(amf STATIC ${SOURCES})
target_link_libraries(amf
ole32
)

View File

@@ -0,0 +1,262 @@
//
// Notice Regarding Standards. AMD does not provide a license or sublicense to
// any Intellectual Property Rights relating to any standards, including but not
// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;
// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3
// (collectively, the "Media Technologies"). For clarity, you will pay any
// royalties due for such third party technologies, which may include the Media
// Technologies that are owed as a result of AMD providing the Software to you.
//
// MIT license
//
//
// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "AMFFactory.h"
#include "Thread.h"
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
AMFFactoryHelper g_AMFFactory;
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#ifdef AMF_CORE_STATIC
extern "C"
{
extern AMF_CORE_LINK AMF_RESULT AMF_CDECL_CALL AMFInit(amf_uint64 version, amf::AMFFactory **ppFactory);
}
#endif
//-------------------------------------------------------------------------------------------------
AMFFactoryHelper::AMFFactoryHelper() :
m_hDLLHandle(NULL),
m_pFactory(NULL),
m_pDebug(NULL),
m_pTrace(NULL),
m_AMFRuntimeVersion(0),
m_iRefCount(0)
{
}
//-------------------------------------------------------------------------------------------------
AMFFactoryHelper::~AMFFactoryHelper()
{
Terminate();
}
//-------------------------------------------------------------------------------------------------
AMF_RESULT AMFFactoryHelper::Init(const wchar_t* dllName)
{
dllName;
#ifndef AMF_CORE_STATIC
if (m_hDLLHandle != NULL)
{
amf_atomic_inc(&m_iRefCount);
return AMF_OK;
}
const wchar_t* dllName_ = dllName == NULL ? AMF_DLL_NAME : dllName;
#if defined (_WIN32) || defined (__APPLE__)
m_hDLLHandle = amf_load_library(dllName_);
#else
m_hDLLHandle = amf_load_library1(dllName_, false); //load with local flags
#endif
if(m_hDLLHandle == NULL)
{
return AMF_FAIL;
}
AMFInit_Fn initFun = (AMFInit_Fn)::amf_get_proc_address(m_hDLLHandle, AMF_INIT_FUNCTION_NAME);
if(initFun == NULL)
{
return AMF_FAIL;
}
AMF_RESULT res = initFun(AMF_FULL_VERSION, &m_pFactory);
if(res != AMF_OK)
{
return res;
}
AMFQueryVersion_Fn versionFun = (AMFQueryVersion_Fn)::amf_get_proc_address(m_hDLLHandle, AMF_QUERY_VERSION_FUNCTION_NAME);
if(versionFun == NULL)
{
return AMF_FAIL;
}
res = versionFun(&m_AMFRuntimeVersion);
if(res != AMF_OK)
{
return res;
}
#else
AMF_RESULT res = AMFInit(AMF_FULL_VERSION, &m_pFactory);
if (res != AMF_OK)
{
return res;
}
m_AMFRuntimeVersion = AMF_FULL_VERSION;
#endif
m_pFactory->GetTrace(&m_pTrace);
m_pFactory->GetDebug(&m_pDebug);
amf_atomic_inc(&m_iRefCount);
return AMF_OK;
}
//-------------------------------------------------------------------------------------------------
AMF_RESULT AMFFactoryHelper::Terminate()
{
if(m_hDLLHandle != NULL)
{
amf_atomic_dec(&m_iRefCount);
if(m_iRefCount == 0)
{
amf_free_library(m_hDLLHandle);
m_hDLLHandle = NULL;
m_pFactory= NULL;
m_pDebug = NULL;
m_pTrace = NULL;
}
}
return AMF_OK;
}
//-------------------------------------------------------------------------------------------------
amf::AMFFactory* AMFFactoryHelper::GetFactory()
{
return m_pFactory;
}
//-------------------------------------------------------------------------------------------------
amf::AMFDebug* AMFFactoryHelper::GetDebug()
{
return m_pDebug;
}
//-------------------------------------------------------------------------------------------------
amf::AMFTrace* AMFFactoryHelper::GetTrace()
{
return m_pTrace;
}
//-------------------------------------------------------------------------------------------------
amf_uint64 AMFFactoryHelper::AMFQueryVersion()
{
return m_AMFRuntimeVersion;
}
//-------------------------------------------------------------------------------------------------
AMF_RESULT AMFFactoryHelper::LoadExternalComponent(amf::AMFContext* pContext, const wchar_t* dll, const char* function, void* reserved, amf::AMFComponent** ppComponent)
{
// check passed in parameters
if (!pContext || !dll || !function)
{
return AMF_INVALID_ARG;
}
// check if DLL has already been loaded
amf_handle hDll = NULL;
for (std::vector<ComponentHolder>::iterator it = m_extComponents.begin(); it != m_extComponents.end(); ++it)
{
#if defined(_WIN32)
if (wcsicmp(it->m_DLL.c_str(), dll) == 0) // ignore case on Windows
#elif defined(__linux) // Linux
if (wcscmp(it->m_DLL.c_str(), dll) == 0) // case sensitive on Linux
#endif
{
if (it->m_hDLLHandle != NULL)
{
hDll = it->m_hDLLHandle;
amf_atomic_inc(&it->m_iRefCount);
break;
}
return AMF_UNEXPECTED;
}
}
// DLL wasn't loaded before so load it now and
// add it to the internal list
if (hDll == NULL)
{
ComponentHolder component;
component.m_iRefCount = 0;
component.m_hDLLHandle = NULL;
component.m_DLL = dll;
#if defined(_WIN32) || defined(__APPLE__)
hDll = amf_load_library(dll);
#else
hDll = amf_load_library1(dll, false); //global flag set to true
#endif
if (hDll == NULL)
return AMF_FAIL;
// since LoadLibrary succeeded add the information
// into the internal list so we can properly free
// the DLL later on, even if we fail to get the
// required information from it...
component.m_hDLLHandle = hDll;
amf_atomic_inc(&component.m_iRefCount);
m_extComponents.push_back(component);
}
// look for function we want in the dll we just loaded
typedef AMF_RESULT(AMF_CDECL_CALL *AMFCreateComponentFunc)(amf::AMFContext*, void* reserved, amf::AMFComponent**);
AMFCreateComponentFunc initFn = (AMFCreateComponentFunc)::amf_get_proc_address(hDll, function);
if (initFn == NULL)
return AMF_FAIL;
return initFn(pContext, reserved, ppComponent);
}
//-------------------------------------------------------------------------------------------------
AMF_RESULT AMFFactoryHelper::UnLoadExternalComponent(const wchar_t* dll)
{
if (!dll)
{
return AMF_INVALID_ARG;
}
for (std::vector<ComponentHolder>::iterator it = m_extComponents.begin(); it != m_extComponents.end(); ++it)
{
#if defined(_WIN32)
if (wcsicmp(it->m_DLL.c_str(), dll) == 0) // ignore case on Windows
#elif defined(__linux) // Linux
if (wcscmp(it->m_DLL.c_str(), dll) == 0) // case sensitive on Linux
#endif
{
if (it->m_hDLLHandle == NULL)
{
return AMF_UNEXPECTED;
}
amf_atomic_dec(&it->m_iRefCount);
if (it->m_iRefCount == 0)
{
amf_free_library(it->m_hDLLHandle);
m_extComponents.erase(it);
}
break;
}
}
return AMF_OK;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,89 @@
//
// Notice Regarding Standards. AMD does not provide a license or sublicense to
// any Intellectual Property Rights relating to any standards, including but not
// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;
// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3
// (collectively, the "Media Technologies"). For clarity, you will pay any
// royalties due for such third party technologies, which may include the Media
// Technologies that are owed as a result of AMD providing the Software to you.
//
// MIT license
//
//
// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#ifndef AMF_AMFFactory_h
#define AMF_AMFFactory_h
#pragma once
#include "../include/core/Factory.h"
#include <string>
#include <vector>
class AMFFactoryHelper
{
public:
AMFFactoryHelper();
virtual ~AMFFactoryHelper();
AMF_RESULT Init(const wchar_t* dllName = NULL);
AMF_RESULT Terminate();
AMF_RESULT LoadExternalComponent(amf::AMFContext* pContext, const wchar_t* dll, const char* function, void* reserved, amf::AMFComponent** ppComponent);
AMF_RESULT UnLoadExternalComponent(const wchar_t* dll);
amf::AMFFactory* GetFactory();
amf::AMFDebug* GetDebug();
amf::AMFTrace* GetTrace();
amf_uint64 AMFQueryVersion();
amf_handle GetAMFDLLHandle() { return m_hDLLHandle; }
protected:
struct ComponentHolder
{
amf_handle m_hDLLHandle;
amf_long m_iRefCount;
std::wstring m_DLL;
ComponentHolder()
{
m_hDLLHandle = NULL;
m_iRefCount = 0;
}
};
amf_handle m_hDLLHandle;
amf::AMFFactory* m_pFactory;
amf::AMFDebug* m_pDebug;
amf::AMFTrace* m_pTrace;
amf_uint64 m_AMFRuntimeVersion;
amf_long m_iRefCount;
std::vector<ComponentHolder> m_extComponents;
};
extern ::AMFFactoryHelper g_AMFFactory;
#endif // AMF_AMFFactory_h

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,362 @@
//
// Notice Regarding Standards. AMD does not provide a license or sublicense to
// any Intellectual Property Rights relating to any standards, including but not
// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;
// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3
// (collectively, the "Media Technologies"). For clarity, you will pay any
// royalties due for such third party technologies, which may include the Media
// Technologies that are owed as a result of AMD providing the Software to you.
//
// MIT license
//
//
// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#ifndef AMF_AMFSTL_h
#define AMF_AMFSTL_h
#pragma once
#if defined(__GNUC__)
//disable gcc warinings on STL code
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include <memory> //default stl allocator
#else
#include <xmemory> //default stl allocator
#endif
#include <algorithm>
#include <string>
#include <vector>
#include <list>
#include <deque>
#include <queue>
#include <map>
#include <set>
#include "../include/core/Interface.h"
#if defined(__cplusplus)
extern "C"
{
#endif
// allocator
void* AMF_STD_CALL amf_alloc(amf_size count);
void AMF_STD_CALL amf_free(void* ptr);
void* AMF_STD_CALL amf_aligned_alloc(size_t count, size_t alignment);
void AMF_STD_CALL amf_aligned_free(void* ptr);
#if defined(__cplusplus)
}
#endif
namespace amf
{
#pragma warning(push)
#pragma warning(disable: 4996) // was declared deprecated
//-------------------------------------------------------------------------------------------------
// STL allocator redefined - will allocate all memory in "C" runtime of Common.DLL
//-------------------------------------------------------------------------------------------------
template<class _Ty>
class amf_allocator : public std::allocator<_Ty>
{
public:
amf_allocator() : std::allocator<_Ty>()
{}
amf_allocator(const amf_allocator<_Ty>& rhs) : std::allocator<_Ty>(rhs)
{}
template<class _Other> amf_allocator(const amf_allocator<_Other>& rhs) : std::allocator<_Ty>(rhs)
{}
template<class _Other> struct rebind // convert an allocator<_Ty> to an allocator <_Other>
{
typedef amf_allocator<_Other> other;
};
void deallocate(_Ty* const _Ptr, const size_t _Count)
{
_Count;
amf_free((void*)_Ptr);
}
_Ty* allocate(const size_t _Count, const void* = static_cast<const void*>(0))
{ // allocate array of _Count el ements
return static_cast<_Ty*>(amf_alloc(_Count * sizeof(_Ty)));
}
};
//-------------------------------------------------------------------------------------------------
// STL container templates with changed memory allocation
//-------------------------------------------------------------------------------------------------
template<class _Ty>
class amf_vector
: public std::vector<_Ty, amf_allocator<_Ty> >
{
public:
typedef std::vector<_Ty, amf_allocator<_Ty> > _base;
amf_vector() : _base() {}
explicit amf_vector(size_t _Count) : _base(_Count) {} //MM GCC has strange compile error. to get around replaced size_type with size_t
amf_vector(size_t _Count, const _Ty& _Val) : _base(_Count,_Val) {}
};
template<class _Ty>
class amf_list
: public std::list<_Ty, amf_allocator<_Ty> >
{};
template<class _Ty>
class amf_deque
: public std::deque<_Ty, amf_allocator<_Ty> >
{};
template<class _Ty>
class amf_queue
: public std::queue<_Ty, amf_deque<_Ty> >
{};
template<class _Kty, class _Ty, class _Pr = std::less<_Kty> >
class amf_map
: public std::map<_Kty, _Ty, _Pr, amf_allocator<std::pair<const _Kty, _Ty>> >
{};
template<class _Kty, class _Pr = std::less<_Kty> >
class amf_set
: public std::set<_Kty, _Pr, amf_allocator<_Kty> >
{};
template<class _Ty>
class amf_limited_deque
: public amf_deque<_Ty> // circular queue of pointers to blocks
{
public:
typedef amf_deque<_Ty> _base;
amf_limited_deque(size_t size_limit) : _base(), _size_limit(size_limit)
{ // construct empty deque
}
size_t size_limit()
{
return _size_limit;
}
void set_size_limit(size_t size_limit)
{
_size_limit = size_limit;
while(_base::size() > _size_limit)
{
_base::pop_front();
}
}
_Ty push_front(const _Ty& _Val)
{ // insert element at beginning
_Ty ret;
if(_size_limit > 0)
{
_base::push_front(_Val);
if(_base::size() > _size_limit)
{
ret = _base::back();
_base::pop_back();
}
}
return ret;
}
void push_front_ex(const _Ty& _Val)
{ // insert element at beginning
_base::push_front(_Val);
}
_Ty push_back(const _Ty& _Val)
{ // insert element at beginning
_Ty ret;
if(_size_limit > 0)
{
_base::push_back(_Val);
if(_base::size() > _size_limit)
{
ret = _base::front();
_base::pop_front();
}
}
return ret;
}
protected:
size_t _size_limit;
};
#pragma warning(pop)
//---------------------------------------------------------------
#if defined(__GNUC__)
//disable gcc warinings on STL code
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#endif
template<class _Interf>
class AMFInterfacePtr_TAdapted : public AMFInterfacePtr_T<_Interf>
{
public:
AMFInterfacePtr_TAdapted* operator&()
{
return this;
}
AMFInterfacePtr_TAdapted()
: AMFInterfacePtr_T<_Interf>()
{}
AMFInterfacePtr_TAdapted(_Interf* pOther)
: AMFInterfacePtr_T<_Interf>(pOther)
{}
AMFInterfacePtr_TAdapted(const AMFInterfacePtr_T<_Interf>& other)
: AMFInterfacePtr_T<_Interf>(other)
{}
};
template<class _Interf>
class amf_vector<AMFInterfacePtr_T<_Interf> >
: public std::vector<AMFInterfacePtr_TAdapted<_Interf>, amf_allocator<AMFInterfacePtr_TAdapted<_Interf> > >
{
public:
typedef AMFInterfacePtr_T<_Interf>& reference;
typedef std::vector<AMFInterfacePtr_TAdapted<_Interf>, amf_allocator<AMFInterfacePtr_TAdapted<_Interf> > > baseclass;
reference operator[](size_t n)
{
return baseclass::operator[](n);
}
};
template<class _Interf>
class amf_deque<AMFInterfacePtr_T<_Interf> >
: public std::deque<AMFInterfacePtr_TAdapted<_Interf>, amf_allocator<AMFInterfacePtr_TAdapted<_Interf> > >
{};
template<class _Interf>
class amf_list<AMFInterfacePtr_T<_Interf> >
: public std::list<AMFInterfacePtr_TAdapted<_Interf>, amf_allocator<AMFInterfacePtr_TAdapted<_Interf> > >
{};
#if defined(__GNUC__)
// restore gcc warnings
#pragma GCC diagnostic pop
#endif
}
//-------------------------------------------------------------------------------------------------
// string classes
//-------------------------------------------------------------------------------------------------
typedef std::basic_string<char, std::char_traits<char>, amf::amf_allocator<char> > amf_string;
typedef std::basic_string<wchar_t, std::char_traits<wchar_t>, amf::amf_allocator<wchar_t> > amf_wstring;
template <class TAmfString>
std::size_t amf_string_hash(TAmfString const& s) noexcept
{
#if defined(_WIN64) || defined(__x86_64__)
constexpr size_t fnvOffsetBasis = 14695981039346656037ULL;
constexpr size_t fnvPrime = 1099511628211ULL;
#else // defined(_WIN64) || defined(__x86_64__)
constexpr size_t fnvOffsetBasis = 2166136261U;
constexpr size_t fnvPrime = 16777619U;
#endif // defined(_WIN64) || defined(__x86_64__)
const unsigned char* const pStr = reinterpret_cast<const unsigned char*>(s.c_str());
const size_t count = s.size() * sizeof(typename TAmfString::value_type);
size_t value = fnvOffsetBasis;
for (size_t i = 0; i < count; ++i)
{
value ^= static_cast<size_t>(pStr[i]);
value *= fnvPrime;
}
return value;
}
template<>
struct std::hash<amf_wstring>
{
std::size_t operator()(amf_wstring const& s) const noexcept
{
return amf_string_hash<amf_wstring>(s);
}
};
template<>
struct std::hash<amf_string>
{
std::size_t operator()(amf_string const& s) const noexcept
{
return amf_string_hash<amf_string>(s);
}
};
namespace amf
{
//-------------------------------------------------------------------------------------------------
// string conversion
//-------------------------------------------------------------------------------------------------
amf_string AMF_STD_CALL amf_from_unicode_to_utf8(const amf_wstring& str);
amf_wstring AMF_STD_CALL amf_from_utf8_to_unicode(const amf_string& str);
amf_string AMF_STD_CALL amf_from_unicode_to_multibyte(const amf_wstring& str);
amf_wstring AMF_STD_CALL amf_from_multibyte_to_unicode(const amf_string& str);
amf_string AMF_STD_CALL amf_from_string_to_hex_string(const amf_string& str);
amf_string AMF_STD_CALL amf_from_hex_string_to_string(const amf_string& str);
amf_string AMF_STD_CALL amf_string_to_lower(const amf_string& str);
amf_wstring AMF_STD_CALL amf_string_to_lower(const amf_wstring& str);
amf_string AMF_STD_CALL amf_string_to_upper(const amf_string& str);
amf_wstring AMF_STD_CALL amf_string_to_upper(const amf_wstring& str);
amf_string AMF_STD_CALL amf_from_unicode_to_url_utf8(const amf_wstring& data, bool bQuery = false); // converts to UTF8 and replace fobidden symbols
amf_wstring AMF_STD_CALL amf_from_url_utf8_to_unicode(const amf_string& data);
amf_wstring AMF_STD_CALL amf_convert_path_to_os_accepted_path(const amf_wstring& path);
amf_wstring AMF_STD_CALL amf_convert_path_to_url_accepted_path(const amf_wstring& path);
//-------------------------------------------------------------------------------------------------
// string helpers
//-------------------------------------------------------------------------------------------------
amf_wstring AMF_STD_CALL amf_string_format(const wchar_t* format, ...);
amf_string AMF_STD_CALL amf_string_format(const char* format, ...);
amf_wstring AMF_STD_CALL amf_string_formatVA(const wchar_t* format, va_list args);
amf_string AMF_STD_CALL amf_string_formatVA(const char* format, va_list args);
amf_int AMF_STD_CALL amf_string_ci_compare(const amf_wstring& left, const amf_wstring& right);
amf_int AMF_STD_CALL amf_string_ci_compare(const amf_string& left, const amf_string& right);
amf_size AMF_STD_CALL amf_string_ci_find(const amf_wstring& left, const amf_wstring& right, amf_size off = 0);
amf_size AMF_STD_CALL amf_string_ci_rfind(const amf_wstring& left, const amf_wstring& right, amf_size off = amf_wstring::npos);
//-------------------------------------------------------------------------------------------------
} // namespace amf
#if defined(__GNUC__)
// restore gcc warnings
#pragma GCC diagnostic pop
#endif
#endif // AMF_AMFSTL_h

View File

@@ -0,0 +1,136 @@
//
// Notice Regarding Standards. AMD does not provide a license or sublicense to
// any Intellectual Property Rights relating to any standards, including but not
// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;
// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3
// (collectively, the "Media Technologies"). For clarity, you will pay any
// royalties due for such third party technologies, which may include the Media
// Technologies that are owed as a result of AMD providing the Software to you.
//
// MIT license
//
//
// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#ifndef AMF_ByteArray_h
#define AMF_ByteArray_h
#pragma once
#include "../include/core/Platform.h"
#define INIT_ARRAY_SIZE 1024
#define ARRAY_MAX_SIZE (1LL << 60LL) // extremely large maximum size
//------------------------------------------------------------------------
class AMFByteArray
{
protected:
amf_uint8 *m_pData;
amf_size m_iSize;
amf_size m_iMaxSize;
public:
AMFByteArray() : m_pData(0), m_iSize(0), m_iMaxSize(0)
{
}
AMFByteArray(const AMFByteArray &other) : m_pData(0), m_iSize(0), m_iMaxSize(0)
{
*this = other;
}
AMFByteArray(amf_size num) : m_pData(0), m_iSize(0), m_iMaxSize(0)
{
SetSize(num);
}
virtual ~AMFByteArray()
{
if (m_pData != 0)
{
delete[] m_pData;
}
}
void SetSize(amf_size num)
{
if (num == m_iSize)
{
return;
}
if (num < m_iSize)
{
memset(m_pData + num, 0, m_iMaxSize - num);
}
else if (num > m_iMaxSize)
{
// This is done to prevent the following error from surfacing
// for the pNewData allocation on some compilers:
// -Werror=alloc-size-larger-than=
amf_size newSize = (num / INIT_ARRAY_SIZE) * INIT_ARRAY_SIZE + INIT_ARRAY_SIZE;
if (newSize > ARRAY_MAX_SIZE)
{
return;
}
m_iMaxSize = newSize;
amf_uint8 *pNewData = new amf_uint8[m_iMaxSize];
memset(pNewData, 0, m_iMaxSize);
if (m_pData != NULL)
{
memcpy(pNewData, m_pData, m_iSize);
delete[] m_pData;
}
m_pData = pNewData;
}
m_iSize = num;
}
void Copy(const AMFByteArray &old)
{
if (m_iMaxSize < old.m_iSize)
{
m_iMaxSize = old.m_iMaxSize;
if (m_pData != NULL)
{
delete[] m_pData;
}
m_pData = new amf_uint8[m_iMaxSize];
memset(m_pData, 0, m_iMaxSize);
}
memcpy(m_pData, old.m_pData, old.m_iSize);
m_iSize = old.m_iSize;
}
amf_uint8 operator[] (amf_size iPos) const
{
return m_pData[iPos];
}
amf_uint8& operator[] (amf_size iPos)
{
return m_pData[iPos];
}
AMFByteArray& operator=(const AMFByteArray &other)
{
SetSize(other.GetSize());
if (GetSize() > 0)
{
memcpy(GetData(), other.GetData(), GetSize());
}
return *this;
}
amf_uint8 *GetData() const { return m_pData; }
amf_size GetSize() const { return m_iSize; }
};
#endif // AMF_ByteArray_h

View File

@@ -0,0 +1,275 @@
//
// Notice Regarding Standards. AMD does not provide a license or sublicense to
// any Intellectual Property Rights relating to any standards, including but not
// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;
// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3
// (collectively, the "Media Technologies"). For clarity, you will pay any
// royalties due for such third party technologies, which may include the Media
// Technologies that are owed as a result of AMD providing the Software to you.
//
// MIT license
//
// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include <iostream>
#include <vector>
#include <bitset>
#include <array>
#include <string>
#include <cstring>
#if defined(_WIN32)
#include <intrin.h>
#else
#include <stdint.h>
#endif
class InstructionSet
{
// forward declarations
class InstructionSet_Internal;
public:
// getters
static std::string Vendor(void) { return CPU_Rep.vendor_; }
static std::string Brand(void) { return CPU_Rep.brand_; }
static bool SSE3(void) { return CPU_Rep.f_1_ECX_[0]; }
static bool PCLMULQDQ(void) { return CPU_Rep.f_1_ECX_[1]; }
static bool MONITOR(void) { return CPU_Rep.f_1_ECX_[3]; }
static bool SSSE3(void) { return CPU_Rep.f_1_ECX_[9]; }
static bool FMA(void) { return CPU_Rep.f_1_ECX_[12]; }
static bool CMPXCHG16B(void) { return CPU_Rep.f_1_ECX_[13]; }
static bool SSE41(void) { return CPU_Rep.f_1_ECX_[19]; }
static bool SSE42(void) { return CPU_Rep.f_1_ECX_[20]; }
static bool MOVBE(void) { return CPU_Rep.f_1_ECX_[22]; }
static bool POPCNT(void) { return CPU_Rep.f_1_ECX_[23]; }
static bool AES(void) { return CPU_Rep.f_1_ECX_[25]; }
static bool XSAVE(void) { return CPU_Rep.f_1_ECX_[26]; }
static bool OSXSAVE(void) { return CPU_Rep.f_1_ECX_[27]; }
static bool AVX(void) { return CPU_Rep.f_1_ECX_[28]; }
static bool F16C(void) { return CPU_Rep.f_1_ECX_[29]; }
static bool RDRAND(void) { return CPU_Rep.f_1_ECX_[30]; }
static bool MSR(void) { return CPU_Rep.f_1_EDX_[5]; }
static bool CX8(void) { return CPU_Rep.f_1_EDX_[8]; }
static bool SEP(void) { return CPU_Rep.f_1_EDX_[11]; }
static bool CMOV(void) { return CPU_Rep.f_1_EDX_[15]; }
static bool CLFSH(void) { return CPU_Rep.f_1_EDX_[19]; }
static bool MMX(void) { return CPU_Rep.f_1_EDX_[23]; }
static bool FXSR(void) { return CPU_Rep.f_1_EDX_[24]; }
static bool SSE(void) { return CPU_Rep.f_1_EDX_[25]; }
static bool SSE2(void) { return CPU_Rep.f_1_EDX_[26]; }
static bool FSGSBASE(void) { return CPU_Rep.f_7_EBX_[0]; }
static bool BMI1(void) { return CPU_Rep.f_7_EBX_[3]; }
static bool HLE(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[4]; }
static bool AVX2(void) { return CPU_Rep.f_7_EBX_[5]; }
static bool BMI2(void) { return CPU_Rep.f_7_EBX_[8]; }
static bool ERMS(void) { return CPU_Rep.f_7_EBX_[9]; }
static bool INVPCID(void) { return CPU_Rep.f_7_EBX_[10]; }
static bool RTM(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[11]; }
static bool AVX512F(void) { return CPU_Rep.f_7_EBX_[16]; }
static bool RDSEED(void) { return CPU_Rep.f_7_EBX_[18]; }
static bool ADX(void) { return CPU_Rep.f_7_EBX_[19]; }
static bool AVX512PF(void) { return CPU_Rep.f_7_EBX_[26]; }
static bool AVX512ER(void) { return CPU_Rep.f_7_EBX_[27]; }
static bool AVX512CD(void) { return CPU_Rep.f_7_EBX_[28]; }
static bool SHA(void) { return CPU_Rep.f_7_EBX_[29]; }
static bool AVX512BW(void) { return CPU_Rep.f_7_EBX_[30]; }
static bool AVX512VL(void) { return CPU_Rep.f_7_EBX_[31]; }
static bool PREFETCHWT1(void) { return CPU_Rep.f_7_ECX_[0]; }
static bool LAHF(void) { return CPU_Rep.f_81_ECX_[0]; }
static bool LZCNT(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_ECX_[5]; }
static bool ABM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[5]; }
static bool SSE4a(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[6]; }
static bool XOP(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[11]; }
static bool TBM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[21]; }
static bool SYSCALL(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[11]; }
static bool MMXEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[22]; }
static bool RDTSCP(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[27]; }
static bool _3DNOWEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[30]; }
static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; }
private:
static const InstructionSet_Internal CPU_Rep;
class InstructionSet_Internal
{
protected:
void GetCpuID
(
int32_t registers[4], //out
int32_t functionID,
int32_t subfunctionID = 0
)
{
#ifdef _WIN32
if(!subfunctionID)
{
__cpuid((int *)registers, (int)functionID);
}
else
{
__cpuidex((int *)registers, (int)functionID, subfunctionID);
}
#else
asm volatile
(
"cpuid":
"=a" (registers[0]),
"=b" (registers[1]),
"=c" (registers[2]),
"=d" (registers[3]):
"a" (functionID),
"c" (subfunctionID)
);
#endif
}
public:
InstructionSet_Internal()
: nIds_( 0 ),
nExIds_( 0 ),
isIntel_( false ),
isAMD_( false ),
f_1_ECX_( 0 ),
f_1_EDX_( 0 ),
f_7_EBX_( 0 ),
f_7_ECX_( 0 ),
f_81_ECX_( 0 ),
f_81_EDX_( 0 )
{
//int cpuInfo[4] = {-1};
std::array<int, 4> cpui;
// Calling __cpuid with 0x0 as the function_id argument
// gets the number of the highest valid function ID.
//todo: verify
//__cpuid(cpui.data(), 0);
GetCpuID(cpui.data(), 0);
nIds_ = cpui[0];
for (int i = 0; i <= nIds_; ++i)
{
//todo: verify
//__cpuidex(cpui.data(), i, 0);
GetCpuID(cpui.data(), i, 0);
data_.push_back(cpui);
}
// Capture vendor string
char vendor[0x20];
std::memset(vendor, 0, sizeof(vendor));
*reinterpret_cast<int*>(vendor) = data_[0][1];
*reinterpret_cast<int*>(vendor + 4) = data_[0][3];
*reinterpret_cast<int*>(vendor + 8) = data_[0][2];
vendor_ = vendor;
if (vendor_ == "GenuineIntel")
{
isIntel_ = true;
}
else if (vendor_ == "AuthenticAMD")
{
isAMD_ = true;
}
// load bitset with flags for function 0x00000001
if (nIds_ >= 1)
{
f_1_ECX_ = data_[1][2];
f_1_EDX_ = data_[1][3];
}
// load bitset with flags for function 0x00000007
if (nIds_ >= 7)
{
f_7_EBX_ = data_[7][1];
f_7_ECX_ = data_[7][2];
}
// Calling __cpuid with 0x80000000 as the function_id argument
// gets the number of the highest valid extended ID.
//todo: verify
//__cpuid(cpui.data(), 0x80000000);
GetCpuID(cpui.data(), 0x80000000);
nExIds_ = cpui[0];
char brand[0x40];
memset(brand, 0, sizeof(brand));
for (int i = 0x80000000; i <= nExIds_; ++i)
{
//todo: verify
//__cpuidex(cpui.data(), i, 0);
GetCpuID(cpui.data(), i, 0);
extdata_.push_back(cpui);
}
// load bitset with flags for function 0x80000001
if (nExIds_ >= 0x80000001)
{
f_81_ECX_ = extdata_[1][2];
f_81_EDX_ = extdata_[1][3];
}
// Interpret CPU brand string if reported
if (nExIds_ >= 0x80000004)
{
memcpy(brand, extdata_[2].data(), sizeof(cpui));
memcpy(brand + 16, extdata_[3].data(), sizeof(cpui));
memcpy(brand + 32, extdata_[4].data(), sizeof(cpui));
brand_ = brand;
}
};
virtual ~InstructionSet_Internal()
{
int i = 0;
++i;
}
int nIds_;
int nExIds_;
std::string vendor_;
std::string brand_;
bool isIntel_;
bool isAMD_;
std::bitset<32> f_1_ECX_;
std::bitset<32> f_1_EDX_;
std::bitset<32> f_7_EBX_;
std::bitset<32> f_7_ECX_;
std::bitset<32> f_81_ECX_;
std::bitset<32> f_81_EDX_;
std::vector<std::array<int, 4>> data_;
std::vector<std::array<int, 4>> extdata_;
};
};

Some files were not shown because too many files have changed in this diff Show More