mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-21 00:40:13 +00:00
Remove old RPI4 hw accel
This was based around the /dev/rpi_hevcmem interface, which is no longer supported or working on the current versions of raspbian
This commit is contained in:
30
configure.ac
30
configure.ac
@@ -1571,7 +1571,6 @@ fi
|
||||
lavc_hwacc_common=no
|
||||
lavc_hwacc_vdpau=no
|
||||
lavc_hwacc_vaapi=no
|
||||
lavc_hwacc_rpi4=no
|
||||
|
||||
define(lavc_hwacc_common_dep, libavcodec libavutil >= 55.22.1)
|
||||
define(lavc_hwacc_vdpau_dep, lavc_hwacc_common_dep vdpau)
|
||||
@@ -1591,13 +1590,6 @@ AC_ARG_ENABLE(lavc-hw-accel-vaapi,
|
||||
[lavc_hwacc_vaapi_req=$build_default]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(lavc-hw-accel-rpi4,
|
||||
[ --disable-lavc-hw-accel-rpi4 disable lavc-hw-accel-rpi4 (default is auto)]
|
||||
[ Requires: Raspbian-patched ffmpeg libmmal],
|
||||
[lavc_hwacc_rpi4_req=$enableval],
|
||||
[lavc_hwacc_rpi4_req=$build_default]
|
||||
)
|
||||
|
||||
LAVC_HWACC_FLAGS=-DHWACC_COMMON_IMPL
|
||||
LAVC_HWACC_LIBS=
|
||||
HW_ACC_OBJ="src/hwaccel_libav_common.o"
|
||||
@@ -1622,25 +1614,6 @@ if test $lavc_hwacc_vaapi_req != no; then
|
||||
lavc_hwacc_common=yes
|
||||
fi
|
||||
fi
|
||||
if test $lavc_hwacc_rpi4_req != no; then
|
||||
SAVED_PKG_CONFIG_PATH=$PKG_CONFIG_PATH
|
||||
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/opt/vc/lib/pkgconfig/"
|
||||
PKG_CHECK_MODULES([MMAL], [mmal], [ FOUND_HWACC_RPI4_DEP=yes ], [ FOUND_HWACC_RPI4_DEP=no ])
|
||||
PKG_CHECK_MODULES([BCM_HOST], [bcm_host], [ ], [ FOUND_HWACC_RPI4_DEP=no ])
|
||||
PKG_CONFIG_PATH=$SAVED_PKG_CONFIG_PATH
|
||||
AC_CHECK_HEADER([libavcodec/rpi_zc.h], [ ], [FOUND_HWACC_RPI4_DEP=no], [#include <libavcodec/avcodec.h>])
|
||||
SAVED_CFLAGS=$CFLAGS
|
||||
CFLAGS="$CFLAGS ${MMAL_CFLAGS}"
|
||||
AC_CHECK_HEADER([interface/mmal/mmal.h], [], [FOUND_HWACC_RPI4_DEP=no])
|
||||
CFLAGS=$SAVED_CFLAGS
|
||||
if test "$FOUND_HWACC_RPI4_DEP" = yes; then
|
||||
LAVC_HWACC_FLAGS="${LAVC_HWACC_FLAGS} -DHWACC_RPI4 ${MMAL_CFLAGS} ${BCM_HOST_CFLAGS}"
|
||||
LAVC_HWACC_LIBS="${LAVC_HWACC_LIBS} ${MMAL_LIBS} ${BCM_HOST_LIBS}"
|
||||
add_module rpi4_hw_accel src/video_display/rpi4_out.o "${MMAL_LIBS} ${BCM_HOST_LIBS}"
|
||||
lavc_hwacc_rpi4=yes
|
||||
lavc_hwacc_common=yes
|
||||
fi
|
||||
fi
|
||||
if test $system = MacOSX; then
|
||||
lavc_hwacc_common=yes
|
||||
fi
|
||||
@@ -1652,7 +1625,6 @@ fi
|
||||
|
||||
ENSURE_FEATURE_PRESENT([$lavc_hwacc_vdpau_req], [$lavc_hwacc_vdpau], [Could not find hwacc vdpau dependencies])
|
||||
ENSURE_FEATURE_PRESENT([$lavc_hwacc_vaapi_req], [$lavc_hwacc_vaapi], [Could not find hwacc vaapi dependencies!])
|
||||
ENSURE_FEATURE_PRESENT([$lavc_hwacc_rpi4_req], [$lavc_hwacc_rpi4], [Could not find hwacc rpi4 dependencies!])
|
||||
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
# Libswscale
|
||||
@@ -3515,7 +3487,7 @@ RESULT=`add_column "$RESULT" "Comprimato J2K" $cmpto_j2k $?`
|
||||
RESULT=`add_column "$RESULT" "CUDA DXT" $cuda_dxt $?`
|
||||
RESULT=`add_column "$RESULT" "GPUJPEG" $gpujpeg $?`
|
||||
RESULT=`add_column "$RESULT" "GPUJPEG transcode to DXT" $gpujpeg_to_dxt $?`
|
||||
RESULT=`add_column "$RESULT" "Lavc (VDP $lavc_hwacc_vdpau, VA $lavc_hwacc_vaapi, RPI4 $lavc_hwacc_rpi4)" $libavcodec $?`
|
||||
RESULT=`add_column "$RESULT" "Lavc (VDP $lavc_hwacc_vdpau, VA $lavc_hwacc_vaapi)" $libavcodec $?`
|
||||
RESULT=`add_column "$RESULT" "Realtime DXT" $rtdxt $?`
|
||||
RESULT=`add_column "$RESULT" "UYVY dummy compression" $uyvy $?`
|
||||
RESULT=`end_section "$RESULT"`
|
||||
|
||||
@@ -64,7 +64,6 @@ static const struct {
|
||||
{ "vdpau-copy", HWACCEL_VDPAU, AV_PIX_FMT_VDPAU, HW_VDPAU},
|
||||
{ "vaapi", HWACCEL_VAAPI, AV_PIX_FMT_VAAPI, 0 },
|
||||
{ "videotoolbox", HWACCEL_VIDEOTOOLBOX, AV_PIX_FMT_VIDEOTOOLBOX, 0 },
|
||||
{ "rpi4", HWACCEL_RPI4, AV_PIX_FMT_RPI4_8, RPI4_8 },
|
||||
{ "cuda", HWACCEL_CUDA, AV_PIX_FMT_CUDA, 0 },
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 39, 100)
|
||||
{ "vulkan", HWACCEL_VULKAN, AV_PIX_FMT_VULKAN, 0 },
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* @file hwaccel_rpi4.h
|
||||
* @author Martin Piatka <piatka@cesnet.cz>
|
||||
*
|
||||
* @brief This file contains functions related to hw acceleration
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2021 CESNET z.s.p.o.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, is permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
*
|
||||
* This product includes software developed by CESNET z.s.p.o.
|
||||
*
|
||||
* 4. Neither the name of the CESNET nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HWACCEL_RPI4_H
|
||||
#define HWACCEL_RPI4_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif // defind HAVE_CONFIG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct AVFrame;
|
||||
|
||||
typedef struct av_frame_wrapper{
|
||||
struct AVFrame *av_frame;
|
||||
} av_frame_wrapper;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -58,7 +58,6 @@
|
||||
#include "compat/qsort_s.h"
|
||||
#include "debug.h"
|
||||
#include "host.h"
|
||||
#include "hwaccel_rpi4.h"
|
||||
#include "hwaccel_vdpau.h"
|
||||
#include "libavcodec/from_lavc_vid_conv.h"
|
||||
#include "libavcodec/lavc_common.h"
|
||||
@@ -2040,20 +2039,6 @@ static void av_vdpau_to_ug_vdpau(char * __restrict dst_buffer, AVFrame * __restr
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HWACC_RPI4
|
||||
static void av_rpi4_8_to_ug(char * __restrict dst_buffer, AVFrame * __restrict in_frame,
|
||||
int width, int height, int pitch, const int * __restrict rgb_shift)
|
||||
{
|
||||
UNUSED(width);
|
||||
UNUSED(height);
|
||||
UNUSED(pitch);
|
||||
UNUSED(rgb_shift);
|
||||
|
||||
av_frame_wrapper *out = (av_frame_wrapper *)(void *) dst_buffer;
|
||||
av_frame_ref(out->av_frame, in_frame);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ayuv64_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict in_frame,
|
||||
int width, int height, int pitch, const int * __restrict rgb_shift)
|
||||
{
|
||||
|
||||
@@ -116,7 +116,6 @@ enum hw_accel_type {
|
||||
HWACCEL_VDPAU,
|
||||
HWACCEL_VAAPI,
|
||||
HWACCEL_VIDEOTOOLBOX,
|
||||
HWACCEL_RPI4,
|
||||
HWACCEL_CUDA,
|
||||
HWACCEL_VULKAN,
|
||||
HWACCEL_COUNT
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
#include "debug.h"
|
||||
#include "host.h"
|
||||
#include "hwaccel_vdpau.h"
|
||||
#include "hwaccel_rpi4.h"
|
||||
#include "utils/macros.h" // to_fourcc, OPTIMEZED_FOR
|
||||
#include "video_codec.h"
|
||||
|
||||
@@ -165,8 +164,6 @@ static const struct codec_info_t codec_info[] = {
|
||||
to_fourcc('M','J','2','R'), 1, 1, 0, 8, FALSE, TRUE, FALSE, FALSE, 0, "j2k"},
|
||||
[HW_VDPAU] = {"HW_VDPAU", "VDPAU hardware surface",
|
||||
to_fourcc('V', 'D', 'P', 'S'), HW_VDPAU_FRAME_SZ, 1, 0, 8, FALSE, TRUE, FALSE, TRUE, 4440, "vdpau"},
|
||||
[RPI4_8] = {"RPI4_8", "Raspberry pi 4 hw. decoded (SAND)",
|
||||
to_fourcc('S', 'A', 'N', 'D'), sizeof(av_frame_wrapper), 1, 0, 8, FALSE, TRUE, FALSE, TRUE, 4200, "sand"},
|
||||
[HFYU] = {"HFYU", "HuffYUV",
|
||||
to_fourcc('H','F','Y','U'), 1, 1, 0, 8, FALSE, TRUE, FALSE, FALSE, 0, "hfyu"},
|
||||
[FFV1] = {"FFV1", "FFV1",
|
||||
|
||||
@@ -485,19 +485,6 @@ static int libavcodec_decompress_reconfigure(void *state, struct video_desc desc
|
||||
return configure_with(s, desc, NULL, 0);
|
||||
}
|
||||
|
||||
#ifdef HWACC_RPI4
|
||||
static int rpi4_hwacc_init(struct AVCodecContext *s,
|
||||
struct hw_accel_state *state,
|
||||
codec_t out_codec)
|
||||
{
|
||||
UNUSED(s), UNUSED(out_codec);
|
||||
state->type = HWACCEL_RPI4;
|
||||
state->copy = false;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined HWACC_COMMON_IMPL && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56, 39, 100)
|
||||
static int vulkan_init(struct AVCodecContext *s,
|
||||
struct hw_accel_state *state,
|
||||
@@ -609,9 +596,6 @@ static enum AVPixelFormat get_format_callback(struct AVCodecContext *s, const en
|
||||
#endif
|
||||
#ifdef HAVE_MACOSX
|
||||
{AV_PIX_FMT_VIDEOTOOLBOX, videotoolbox_init},
|
||||
#endif
|
||||
#ifdef HWACC_RPI4
|
||||
{AV_PIX_FMT_RPI4_8, rpi4_hwacc_init},
|
||||
#endif
|
||||
{AV_PIX_FMT_NONE, NULL}
|
||||
};
|
||||
|
||||
@@ -1,662 +0,0 @@
|
||||
/**
|
||||
* @file video_display/rpi4_out.cpp
|
||||
* @author Martin Piatka <piatka@cesnet.cz>
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2021-2023 CESNET, z. s. p. o.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, is permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of CESNET nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
#include "debug.h"
|
||||
#include "lib_common.h"
|
||||
#include "video.h"
|
||||
#include "video_codec.h"
|
||||
#include "video_display.h"
|
||||
#include "hwaccel_rpi4.h"
|
||||
#include "utils/string_view_utils.hpp"
|
||||
#include "utils/color_out.h"
|
||||
#include "utils/misc.h"
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <charconv>
|
||||
|
||||
#include <bcm_host.h>
|
||||
#include <interface/mmal/mmal.h>
|
||||
#include <interface/mmal/mmal_component.h>
|
||||
#include <interface/mmal/util/mmal_default_components.h>
|
||||
#include <interface/mmal/util/mmal_util_params.h>
|
||||
|
||||
extern "C" { //needed for rpi_zc.h and rpi_sand_fns.h
|
||||
#include <libavcodec/avcodec.h> //needed for rpi_zc.h
|
||||
#include <libavcodec/rpi_zc.h>
|
||||
#include <libavutil/rpi_sand_fns.h>
|
||||
}
|
||||
|
||||
#define MAX_BUFFER_SIZE 4
|
||||
|
||||
namespace{
|
||||
|
||||
struct frame_deleter{
|
||||
void operator()(struct video_frame *f){ vf_free(f); }
|
||||
};
|
||||
|
||||
using unique_frame = std::unique_ptr<struct video_frame, frame_deleter>;
|
||||
|
||||
struct mmal_component_deleter{
|
||||
void operator()(MMAL_COMPONENT_T *c){ mmal_component_destroy(c); }
|
||||
};
|
||||
|
||||
using mmal_component_unique = std::unique_ptr<MMAL_COMPONENT_T, mmal_component_deleter>;
|
||||
|
||||
struct mmal_pool_deleter{
|
||||
void operator()(MMAL_POOL_T *p){ mmal_pool_destroy(p); }
|
||||
};
|
||||
|
||||
using mmal_pool_unique = std::unique_ptr<MMAL_POOL_T, mmal_pool_deleter>;
|
||||
|
||||
struct av_zc_deleter{
|
||||
void operator()(AVZcEnvPtr env) { av_rpi_zc_int_env_freep(&env); }
|
||||
};
|
||||
|
||||
using av_zc_env_unique = std::unique_ptr<std::remove_pointer_t<AVZcEnvPtr>, av_zc_deleter>;
|
||||
|
||||
struct av_zc_frame_deleter{
|
||||
void operator()(AVRpiZcRefPtr f){ av_rpi_zc_unref(f); }
|
||||
};
|
||||
|
||||
using av_zero_copy_frame_unique = std::unique_ptr<std::remove_pointer_t<AVRpiZcRefPtr>, av_zc_frame_deleter>;
|
||||
|
||||
struct mmal_buf_header_deleter{
|
||||
void operator()(MMAL_BUFFER_HEADER_T *buf){ mmal_buffer_header_release(buf); }
|
||||
};
|
||||
|
||||
using mmal_buf_header_unique = std::unique_ptr<MMAL_BUFFER_HEADER_T, mmal_buf_header_deleter>;
|
||||
|
||||
class Rpi4_video_out{
|
||||
public:
|
||||
Rpi4_video_out() = default;
|
||||
Rpi4_video_out(int x, int y, int width, int height, bool fs, int layer);
|
||||
|
||||
void display(AVFrame *f);
|
||||
|
||||
void resize(int width, int height);
|
||||
void move(){
|
||||
const int max_x = 1920 - out_width;
|
||||
const int max_y = 1080 - out_height;
|
||||
|
||||
out_pos_x += x_dir;
|
||||
out_pos_y += y_dir;
|
||||
|
||||
if(out_pos_x >= max_x){
|
||||
out_pos_x = max_x;
|
||||
x_dir = -x_dir;
|
||||
}
|
||||
|
||||
if(out_pos_x <= 0){
|
||||
out_pos_x = 0;
|
||||
x_dir = -x_dir;
|
||||
}
|
||||
|
||||
if(out_pos_y >= max_y){
|
||||
out_pos_y = max_y;
|
||||
y_dir = -y_dir;
|
||||
}
|
||||
|
||||
if(out_pos_y <= 0){
|
||||
out_pos_y = 0;
|
||||
y_dir = -y_dir;
|
||||
}
|
||||
|
||||
set_output_params();
|
||||
}
|
||||
private:
|
||||
void set_output_params();
|
||||
void stream_fmt_from_frame(MMAL_ES_FORMAT_T *fmt, const AVFrame *f, const AVRpiZcRefPtr zc_frame);
|
||||
void set_output_format(MMAL_ES_FORMAT_T *fmt);
|
||||
|
||||
int out_pos_x;
|
||||
int out_pos_y;
|
||||
int out_width;
|
||||
int out_height;
|
||||
int x_dir = 2;
|
||||
int y_dir = 2;
|
||||
bool fullscreen;
|
||||
int layer;
|
||||
|
||||
MMAL_ES_FORMAT_T curr_stream_format = {};
|
||||
|
||||
mmal_component_unique renderer_component;
|
||||
mmal_pool_unique pool;
|
||||
av_zc_env_unique zero_copy_env;
|
||||
};
|
||||
|
||||
void Rpi4_video_out::resize(int width, int height){
|
||||
if(width == out_width && height == out_height)
|
||||
return;
|
||||
|
||||
out_width = width;
|
||||
out_height = height;
|
||||
|
||||
set_output_params();
|
||||
}
|
||||
|
||||
void Rpi4_video_out::set_output_params(){
|
||||
MMAL_DISPLAYREGION_T region = {};
|
||||
region.hdr = {MMAL_PARAMETER_DISPLAYREGION, sizeof(region)};
|
||||
region.set = MMAL_DISPLAY_SET_DEST_RECT
|
||||
| MMAL_DISPLAY_SET_FULLSCREEN
|
||||
| MMAL_DISPLAY_SET_LAYER
|
||||
| MMAL_DISPLAY_SET_ALPHA;
|
||||
region.dest_rect = {out_pos_x, out_pos_y, out_width, out_height};
|
||||
region.fullscreen = fullscreen;
|
||||
region.layer = layer;
|
||||
region.alpha = 0xff;
|
||||
|
||||
mmal_port_parameter_set(renderer_component->input[0], ®ion.hdr);
|
||||
}
|
||||
|
||||
static void release_buf_cb(MMAL_PORT_T *, MMAL_BUFFER_HEADER_T *buf){
|
||||
mmal_buffer_header_release(buf);
|
||||
}
|
||||
|
||||
Rpi4_video_out::Rpi4_video_out(int x, int y, int width, int height, bool fs, int layer):
|
||||
out_pos_x(x),
|
||||
out_pos_y(y),
|
||||
out_width(width),
|
||||
out_height(height),
|
||||
fullscreen(fs),
|
||||
layer(layer)
|
||||
{
|
||||
bcm_host_init();
|
||||
|
||||
if(mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, out_ptr(renderer_component)) != MMAL_SUCCESS){
|
||||
throw std::runtime_error("Failed to create renderer component");
|
||||
}
|
||||
|
||||
set_output_params();
|
||||
|
||||
|
||||
if(mmal_component_enable(renderer_component.get()) != MMAL_SUCCESS){
|
||||
throw std::runtime_error("Failed to enable renderer component");
|
||||
}
|
||||
|
||||
if(mmal_port_enable(renderer_component->control, release_buf_cb) != MMAL_SUCCESS){
|
||||
throw std::runtime_error("Failed to enable control port");
|
||||
}
|
||||
|
||||
|
||||
pool.reset(mmal_pool_create(MAX_BUFFER_SIZE, 0));
|
||||
if(!pool){
|
||||
throw std::runtime_error("Failed to create pool");
|
||||
}
|
||||
|
||||
zero_copy_env.reset(av_rpi_zc_int_env_alloc(nullptr));
|
||||
}
|
||||
|
||||
void Rpi4_video_out::stream_fmt_from_frame(MMAL_ES_FORMAT_T *stream_fmt, const AVFrame *f, const AVRpiZcRefPtr zc_frame){
|
||||
MMAL_VIDEO_FORMAT_T *v_fmt = &stream_fmt->es->video;
|
||||
const AVRpiZcFrameGeometry *geo = av_rpi_zc_geometry(zc_frame);
|
||||
|
||||
stream_fmt->flags = 0;
|
||||
|
||||
if(av_rpi_is_sand_format(geo->format)){
|
||||
v_fmt->width = geo->height_y;
|
||||
v_fmt->height = geo->height_y;
|
||||
|
||||
if(geo->stripe_is_yc)
|
||||
v_fmt->width += geo->height_c;
|
||||
|
||||
stream_fmt->flags |= MMAL_ES_FORMAT_FLAG_COL_FMTS_WIDTH_IS_COL_STRIDE;
|
||||
} else {
|
||||
v_fmt->width = geo->stride_y / geo->bytes_per_pel;
|
||||
v_fmt->height = geo->height_y;
|
||||
}
|
||||
|
||||
stream_fmt->type = MMAL_ES_TYPE_VIDEO;
|
||||
assert(geo->format == AV_PIX_FMT_RPI4_8
|
||||
|| geo->format == AV_PIX_FMT_SAND128);
|
||||
stream_fmt->encoding = MMAL_ENCODING_YUVUV128;
|
||||
|
||||
v_fmt->crop.x = f->crop_left;
|
||||
v_fmt->crop.y = f->crop_top;
|
||||
v_fmt->crop.width = av_frame_cropped_width(f);
|
||||
v_fmt->crop.height = av_frame_cropped_height(f);
|
||||
|
||||
v_fmt->frame_rate.den = 30;
|
||||
v_fmt->frame_rate.num = 1;
|
||||
|
||||
v_fmt->par.den = f->sample_aspect_ratio.den;
|
||||
v_fmt->par.num = f->sample_aspect_ratio.num;
|
||||
}
|
||||
|
||||
void Rpi4_video_out::set_output_format(MMAL_ES_FORMAT_T *fmt){
|
||||
if(mmal_format_compare(fmt, &curr_stream_format) == 0)
|
||||
return;
|
||||
|
||||
mmal_format_copy(renderer_component->input[0]->format, fmt);
|
||||
|
||||
if(mmal_port_format_commit(renderer_component->input[0]) != MMAL_SUCCESS){
|
||||
throw std::runtime_error("Failed to commit port format");
|
||||
}
|
||||
}
|
||||
|
||||
static MMAL_BOOL_T buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *){
|
||||
if(buf->user_data){
|
||||
av_zc_frame_deleter()(static_cast<AVRpiZcRefPtr>(buf->user_data));
|
||||
buf->user_data = nullptr;
|
||||
}
|
||||
|
||||
return MMAL_FALSE;
|
||||
}
|
||||
|
||||
void Rpi4_video_out::display(AVFrame *f){
|
||||
auto zc_frame = av_zero_copy_frame_unique(
|
||||
av_rpi_zc_ref(nullptr,
|
||||
zero_copy_env.get(),
|
||||
f,
|
||||
static_cast<AVPixelFormat>(f->format),
|
||||
true)
|
||||
);
|
||||
if(!zc_frame)
|
||||
return;
|
||||
|
||||
|
||||
auto buf = mmal_buf_header_unique(mmal_queue_get(pool->queue));
|
||||
if(!buf)
|
||||
return;
|
||||
|
||||
MMAL_ES_SPECIFIC_FORMAT_T sfmt = {};
|
||||
MMAL_ES_FORMAT_T stream_fmt = {};
|
||||
|
||||
stream_fmt.es = &sfmt;
|
||||
|
||||
stream_fmt_from_frame(&stream_fmt, f, zc_frame.get());
|
||||
set_output_format(&stream_fmt);
|
||||
|
||||
renderer_component->input[0]->buffer_num = MAX_BUFFER_SIZE;
|
||||
renderer_component->input[0]->buffer_size = av_rpi_zc_numbytes(zc_frame.get());
|
||||
|
||||
if(!renderer_component->input[0]->is_enabled){
|
||||
mmal_port_parameter_set_boolean(renderer_component->input[0],
|
||||
MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);
|
||||
|
||||
if(mmal_port_enable(renderer_component->input[0], release_buf_cb) != MMAL_SUCCESS){
|
||||
throw std::runtime_error("Failed to enable port");
|
||||
}
|
||||
}
|
||||
|
||||
mmal_buffer_header_reset(buf.get());
|
||||
|
||||
buf->data = reinterpret_cast<uint8_t *>(av_rpi_zc_vc_handle(zc_frame.get()));
|
||||
buf->length = av_rpi_zc_length(zc_frame.get());
|
||||
buf->offset = av_rpi_zc_offset(zc_frame.get());
|
||||
buf->alloc_size = av_rpi_zc_numbytes(zc_frame.get());
|
||||
|
||||
mmal_buffer_header_pre_release_cb_set(buf.get(), buf_pre_release_cb, nullptr);
|
||||
buf->user_data = zc_frame.get();
|
||||
if(mmal_port_send_buffer(renderer_component->input[0], buf.get()) != MMAL_SUCCESS){
|
||||
throw std::runtime_error("Failed to send buffer");
|
||||
}
|
||||
|
||||
//Frame was successfully submitted, we don't own the data anymore
|
||||
zc_frame.release();
|
||||
buf.release();
|
||||
}
|
||||
|
||||
} //anonymous namespace
|
||||
|
||||
static void display_rpi4_run(void *state);
|
||||
|
||||
struct rpi4_display_state{
|
||||
struct video_desc current_desc;
|
||||
|
||||
std::mutex frame_queue_mut;
|
||||
std::condition_variable new_frame_ready_cv;
|
||||
std::condition_variable frame_consumed_cv;
|
||||
std::queue<unique_frame> frame_queue;
|
||||
|
||||
std::mutex free_frames_mut;
|
||||
std::stack<unique_frame> free_frames;
|
||||
|
||||
std::thread thread_id;
|
||||
|
||||
int requested_pos_x = 0;
|
||||
int requested_pos_y = 0;
|
||||
int force_w = 0;
|
||||
int force_h = 0;
|
||||
bool fullscreen = false;
|
||||
|
||||
Rpi4_video_out video_out;
|
||||
};
|
||||
|
||||
static void print_rpi4_out_help(){
|
||||
col() << "usage:\n";
|
||||
col() << TBOLD(TRED("\t-d rpi4") << "[:force-size=<w>x<h>|:position=<x>x<y>|:fs]* | help\n\n");
|
||||
col() << "options:\n";
|
||||
col() << TBOLD("\tfs") << "\t\tfullscreen\n";
|
||||
col() << TBOLD("\tforce-size") << "\t\tspecifies desired size of output\n";
|
||||
col() << TBOLD("\tposition") << "\t\tspecifies the desired position of output (coordinates of top left corner)\n";
|
||||
}
|
||||
|
||||
static void *display_rpi4_init(struct module *parent, const char *cfg, unsigned int flags)
|
||||
{
|
||||
UNUSED(parent), UNUSED(cfg), UNUSED(flags);
|
||||
auto s = std::make_unique<rpi4_display_state>();
|
||||
|
||||
std::string_view conf(cfg);
|
||||
while(!conf.empty()){
|
||||
auto token = tokenize(conf, ':');
|
||||
|
||||
auto key = tokenize(token, '=');
|
||||
if(key == "help"){
|
||||
print_rpi4_out_help();
|
||||
return nullptr;
|
||||
} else if(key == "force-size"){
|
||||
auto val = tokenize(token, '=');
|
||||
|
||||
auto width = tokenize(val, 'x');
|
||||
auto height = tokenize(val, 'x');
|
||||
|
||||
if(width.empty() || height.empty())
|
||||
return nullptr;
|
||||
|
||||
if(std::from_chars(width.data(), width.data() + width.size(), s->force_w).ec != std::errc()
|
||||
|| std::from_chars(height.data(), height.data() + height.size(), s->force_h).ec != std::errc())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
} else if(key == "fs"){
|
||||
s->fullscreen = true;
|
||||
} else if(key == "position"){
|
||||
auto val = tokenize(token, '=');
|
||||
|
||||
auto x_str = tokenize(val, 'x');
|
||||
auto y_str = tokenize(val, 'x');
|
||||
|
||||
if(x_str.empty() || y_str.empty())
|
||||
return nullptr;
|
||||
|
||||
if(std::from_chars(x_str.data(), x_str.data() + x_str.size(), s->requested_pos_x).ec != std::errc()
|
||||
|| std::from_chars(y_str.data(), y_str.data() + y_str.size(), s->requested_pos_y).ec != std::errc())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int width = s->force_w ? s->force_w : 640;
|
||||
int height = s->force_h ? s->force_h : 480;
|
||||
|
||||
s->video_out = Rpi4_video_out(s->requested_pos_x, s->requested_pos_y,
|
||||
width, height,
|
||||
s->fullscreen, 2);
|
||||
|
||||
s->thread_id = std::thread(display_rpi4_run, s.get());
|
||||
return s.release();
|
||||
}
|
||||
|
||||
static void display_rpi4_done(void *state) {
|
||||
auto *s = static_cast<rpi4_display_state *>(state);
|
||||
s->thread_id.join();
|
||||
|
||||
delete s;
|
||||
}
|
||||
|
||||
static void frame_data_deleter(struct video_frame *buf){
|
||||
auto wrapper = reinterpret_cast<av_frame_wrapper *>(buf->tiles[0].data);
|
||||
|
||||
av_frame_free(&wrapper->av_frame);
|
||||
|
||||
delete wrapper;
|
||||
}
|
||||
|
||||
static inline void av_frame_wrapper_recycle(struct video_frame *f){
|
||||
for(unsigned i = 0; i < f->tile_count; i++){
|
||||
av_frame_wrapper *wrapper = (av_frame_wrapper *)(void *) f->tiles[i].data;
|
||||
|
||||
av_frame_unref(wrapper->av_frame);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void av_frame_wrapper_copy(struct video_frame *f){
|
||||
for(unsigned i = 0; i < f->tile_count; i++){
|
||||
av_frame_wrapper *wrapper = (av_frame_wrapper *)(void *) f->tiles[i].data;
|
||||
|
||||
wrapper->av_frame = av_frame_clone(wrapper->av_frame);
|
||||
}
|
||||
}
|
||||
|
||||
static struct video_frame *alloc_new_frame(rpi4_display_state *s){
|
||||
auto new_frame = vf_alloc_desc(s->current_desc);
|
||||
|
||||
assert(new_frame->tile_count == 1);
|
||||
|
||||
auto wrapper = new av_frame_wrapper();
|
||||
|
||||
wrapper->av_frame = av_frame_alloc();
|
||||
|
||||
new_frame->tiles[0].data_len = sizeof(av_frame_wrapper);
|
||||
new_frame->tiles[0].data = reinterpret_cast<char *>(wrapper);
|
||||
new_frame->callbacks.recycle = av_frame_wrapper_recycle;
|
||||
new_frame->callbacks.copy = av_frame_wrapper_copy;
|
||||
new_frame->callbacks.data_deleter = frame_data_deleter;
|
||||
|
||||
return new_frame;
|
||||
}
|
||||
|
||||
static struct video_frame *display_rpi4_getf(void *state) {
|
||||
auto *s = static_cast<rpi4_display_state *>(state);
|
||||
|
||||
{//lock
|
||||
std::lock_guard lk(s->free_frames_mut);
|
||||
|
||||
while(!s->free_frames.empty()){
|
||||
unique_frame frame = std::move(s->free_frames.top());
|
||||
s->free_frames.pop();
|
||||
if(video_desc_eq(video_desc_from_frame(frame.get()), s->current_desc))
|
||||
{
|
||||
return frame.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto new_frame = alloc_new_frame(s);
|
||||
return new_frame;
|
||||
}
|
||||
|
||||
static bool display_rpi4_putf(void *state, struct video_frame *frame, long long flags)
|
||||
{
|
||||
auto *s = static_cast<rpi4_display_state *>(state);
|
||||
|
||||
if(!frame){
|
||||
std::unique_lock lk(s->frame_queue_mut);
|
||||
s->frame_queue.emplace(frame);
|
||||
lk.unlock();
|
||||
s->new_frame_ready_cv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flags == PUTF_DISCARD) {
|
||||
vf_recycle(frame);
|
||||
std::lock_guard(s->free_frames_mut);
|
||||
s->free_frames.emplace(frame);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s->frame_queue.size() >= MAX_BUFFER_SIZE && flags != PUTF_BLOCKING) {
|
||||
log_msg(LOG_LEVEL_VERBOSE, "nonblock putf drop\n");
|
||||
vf_recycle(frame);
|
||||
std::lock_guard(s->free_frames_mut);
|
||||
s->free_frames.emplace(frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock lk(s->frame_queue_mut);
|
||||
s->frame_consumed_cv.wait(lk, [s]{return s->frame_queue.size() < MAX_BUFFER_SIZE;});
|
||||
s->frame_queue.emplace(frame);
|
||||
lk.unlock();
|
||||
s->new_frame_ready_cv.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void display_rpi4_run(void *state)
|
||||
{
|
||||
auto *s = static_cast<rpi4_display_state *>(state);
|
||||
|
||||
bool run = true;
|
||||
while(run){
|
||||
std::unique_lock lk(s->frame_queue_mut);
|
||||
s->new_frame_ready_cv.wait(lk, [s] {return s->frame_queue.size() > 0;});
|
||||
|
||||
if (s->frame_queue.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unique_frame frame = std::move(s->frame_queue.front());
|
||||
s->frame_queue.pop();
|
||||
lk.unlock();
|
||||
s->frame_consumed_cv.notify_one();
|
||||
|
||||
if(!frame){
|
||||
run = false;
|
||||
break;
|
||||
}
|
||||
|
||||
auto av_wrap = reinterpret_cast<struct av_frame_wrapper *>(
|
||||
reinterpret_cast<void *>(frame->tiles[0].data));
|
||||
|
||||
s->video_out.display(av_wrap->av_frame);
|
||||
|
||||
vf_recycle(frame.get());
|
||||
std::lock_guard(s->free_frames_mut);
|
||||
s->free_frames.push(std::move(frame));
|
||||
}
|
||||
}
|
||||
|
||||
static bool display_rpi4_reconfigure(void *state, struct video_desc desc)
|
||||
{
|
||||
auto *s = static_cast<rpi4_display_state *>(state);
|
||||
|
||||
assert(desc.color_spec == RPI4_8);
|
||||
s->current_desc = desc;
|
||||
|
||||
if(s->force_w == 0 && s->force_h == 0)
|
||||
s->video_out.resize(desc.width, desc.height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static auto display_rpi4_get_property(void *state, int property, void *val, size_t *len)
|
||||
{
|
||||
UNUSED(state);
|
||||
codec_t codecs[] = {
|
||||
RPI4_8,
|
||||
};
|
||||
enum interlacing_t supported_il_modes[] = {PROGRESSIVE};
|
||||
int rgb_shift[] = {0, 8, 16};
|
||||
|
||||
switch (property) {
|
||||
case DISPLAY_PROPERTY_CODECS:
|
||||
if(sizeof(codecs) <= *len) {
|
||||
memcpy(val, codecs, sizeof(codecs));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
*len = sizeof(codecs);
|
||||
break;
|
||||
case DISPLAY_PROPERTY_RGB_SHIFT:
|
||||
if(sizeof(rgb_shift) > *len) {
|
||||
return false;
|
||||
}
|
||||
memcpy(val, rgb_shift, sizeof(rgb_shift));
|
||||
*len = sizeof(rgb_shift);
|
||||
break;
|
||||
case DISPLAY_PROPERTY_BUF_PITCH:
|
||||
*(int *) val = PITCH_DEFAULT;
|
||||
*len = sizeof(int);
|
||||
break;
|
||||
case DISPLAY_PROPERTY_SUPPORTED_IL_MODES:
|
||||
if(sizeof(supported_il_modes) <= *len) {
|
||||
memcpy(val, supported_il_modes, sizeof(supported_il_modes));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
*len = sizeof(supported_il_modes);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void display_rpi4_put_audio_frame(void *, const struct audio_frame *)
|
||||
{
|
||||
}
|
||||
|
||||
static bool display_rpi4_reconfigure_audio(void *, int, int, int)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void display_rpi4_probe(struct device_info **available_cards, int *count, void (**deleter)(void *)) {
|
||||
UNUSED(deleter);
|
||||
*available_cards = nullptr;
|
||||
*count = 0;
|
||||
};
|
||||
|
||||
static const struct video_display_info display_rpi4_info = {
|
||||
display_rpi4_probe,
|
||||
display_rpi4_init,
|
||||
NULL, // _run
|
||||
display_rpi4_done,
|
||||
display_rpi4_getf,
|
||||
display_rpi4_putf,
|
||||
display_rpi4_reconfigure,
|
||||
display_rpi4_get_property,
|
||||
display_rpi4_put_audio_frame,
|
||||
display_rpi4_reconfigure_audio,
|
||||
"[RPi display] ",
|
||||
};
|
||||
|
||||
REGISTER_MODULE(rpi4, &display_rpi4_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION);
|
||||
Reference in New Issue
Block a user