Skip to content

Commit

Permalink
added file display module
Browse files Browse the repository at this point in the history
just a simple, video only, display at the moment
  • Loading branch information
MartinPulec committed Sep 1, 2023
1 parent f2e5350 commit 00ddaec
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 11 deletions.
4 changes: 3 additions & 1 deletion .github/scripts/Linux/arm/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ APPNAME=UltraGrid-latest-${ARCH}.AppImage

set -- --enable-plugins --enable-openssl --enable-soxr --enable-speexdsp # general
set -- "$@" --enable-alsa --enable-jack --enable-jack-transport # audio
set -- "$@" --enable-decklink --enable-ndi --enable-rtsp --enable-screen=x11 --enable-swmix --enable-v4l2 --enable-vidcap-file --enable-ximea # vcap
# vidcap (+ duplex video)
set -- "$@" --enable-decklink --enable-file --enable-ndi --enable-rtsp \
--enable-screen=x11 --enable-swmix --enable-v4l2 --enable-ximea
set -- "$@" --enable-caca --enable-gl-display --enable-panogl_disp --enable-sdl # display
set -- "$@" --enable-libavcodec --enable-rtdxt --enable-libswscale --enable-uyvy # compression
set -- "$@" --enable-blank --enable-holepunch --enable-natpmp --enable-pcp --enable-resize --enable-scale --enable-sdp-http --enable-testcard-extras --enable-text --enable-video-mixer --enable-zfec # extras (pp. etc)
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/environment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export FEATURES="\
--enable-caca\
--enable-cineform\
--enable-decklink\
--enable-file\
--enable-gl\
--enable-gl-display\
--enable-holepunch\
Expand Down Expand Up @@ -57,7 +58,6 @@ export FEATURES="\
--enable-libswscale\
--enable-testcard-extras=all\
--enable-text\
--enable-vidcap-file\
--enable-video-mixer\
--enable-vulkan\
--enable-ximea\
Expand Down
20 changes: 11 additions & 9 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1708,17 +1708,17 @@ fi
ENSURE_FEATURE_PRESENT([$libavcodec_req], [$libavcodec], [Libavcodec not found])

# -------------------------------------------------------------------------------------------------
# File input
# File vidcap/display
# -------------------------------------------------------------------------------------------------
vidcap_file=no
AC_ARG_ENABLE(vidcap-file,
AS_HELP_STRING([--disable-vidcap-file], [disable file input support (default is auto)]
file=no
AC_ARG_ENABLE(file,
AS_HELP_STRING([--disable-file], [disable file IO support (default is auto)]
[Requires: libavcodec libavformat libavutil libswscale]),
[vidcap_file_req=$enableval],
[vidcap_file_req=$build_default]
[file_req=$enableval],
[file_req=$build_default]
)

if test $vidcap_file_req != no && test $libavcodec = yes && test $libswscale = yes; then
if test $file_req != no && test $libavcodec = yes && test $libswscale = yes; then
PKG_CHECK_MODULES([LIBAVFORMAT], [libavformat], [found_libavformat=yes], [found_libavformat=no])
if test "$found_libavformat" != yes; then
SAVED_LIBS=$LIBS
Expand All @@ -1736,9 +1736,11 @@ if test $vidcap_file_req != no && test $libavcodec = yes && test $libswscale = y
fi
if test $found_libavformat = yes; then
COMMON_FLAGS="$COMMON_FLAGS $LIBAVFORMAT_CFLAGS"
vidcap_file=yes
file=yes
add_module vidcap_file "src/libavcodec/lavc_common.o "\
src/video_capture/file.o "$LIBAVCODEC_LIBS $LIBAVFORMAT_LIBS $LIBSWSCALE_LIBS"
add_module display_file "src/libavcodec/lavc_common.o "\
src/video_display/file.o "$LIBAVCODEC_LIBS $LIBAVFORMAT_LIBS"
fi
fi

Expand Down Expand Up @@ -3411,7 +3413,7 @@ RESULT=`add_column "$RESULT" "DeckLink" $decklink $?`
RESULT=`add_column "$RESULT" "DELTACAST" $deltacast $?`
RESULT=`add_column "$RESULT" "DirectShow" $dshow $?`
RESULT=`add_column "$RESULT" "DVS" $dvs $?`
RESULT=`add_column "$RESULT" "File input" $vidcap_file $?`
RESULT=`add_column "$RESULT" "File" $file $?`
RESULT=`add_column "$RESULT" "Gpustitch" $gpustitch $?`
RESULT=`add_column "$RESULT" "NDI" $ndi $?`
RESULT=`add_column "$RESULT" "OpenGL" $gl_display $?`
Expand Down
284 changes: 284 additions & 0 deletions src/video_display/file.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
/**
* @file video_display/file.c
* @author Martin Pulec <[email protected]>
*/
/*
* Copyright (c) 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 <assert.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

#include "debug.h"
#include "lib_common.h"
#include "libavcodec/utils.h"
#include "types.h"
#include "utils/color_out.h"
#include "utils/fs.h"
#include "utils/macros.h"
#include "utils/misc.h"
#include "video.h"
#include "video_display.h"

#define DEFAULT_FILENAME "out.nut"
#define MOD_NAME "[File disp.] "

struct output_stream {
AVStream *st;
AVCodecContext *enc;
AVPacket *pkt;
AVFrame *frame;
};

struct state_file {
AVFormatContext *format_ctx;
struct output_stream video;
struct video_desc video_desc;
char filename[MAX_PATH_SIZE];
};

static void
display_file_probe(struct device_info **available_cards, int *count,
void (**deleter)(void *))
{
*deleter = free;
*count = 1;
*available_cards = calloc(*count, sizeof **available_cards);
snprintf((*available_cards)[0].name, sizeof(*available_cards)[0].name,
"file");
}

static void
display_file_done(void *state)
{
struct state_file *s = state;
av_write_trailer(s->format_ctx);
avcodec_free_context(&s->video.enc);
if (!(s->format_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&s->format_ctx->pb);
}
av_frame_free(&s->video.frame);
av_packet_free(&s->video.pkt);
free(s);
}

static void
usage(void)
{
color_printf("Display " TBOLD("file") " syntax:\n");
color_printf("\t" TBOLD(TRED("file") "[:file=<name>]") "\n");
}

static void *
display_file_init(struct module *parent, const char *fmt, unsigned int flags)
{
const char *filename = DEFAULT_FILENAME;
UNUSED(flags);
UNUSED(parent);
if (strlen(fmt) > 0) {
if (IS_KEY_PREFIX(fmt, "file")) {
filename = strchr(fmt, '=') + 1;
} else {
usage();
return strcmp(fmt, "help") == 0 ? INIT_NOERR : NULL;
}
}
struct state_file *s = calloc(1, sizeof *s);
strncat(s->filename, filename, sizeof s->filename - 1);
avformat_alloc_output_context2(&s->format_ctx, NULL, NULL, filename);
if (s->format_ctx == NULL) {
log_msg(LOG_LEVEL_WARNING, "Could not deduce output format "
"from file extension, using NUT.\n");
avformat_alloc_output_context2(&s->format_ctx, NULL, "nut",
filename);
assert(s->format_ctx != NULL);
}
s->video.st = avformat_new_stream(s->format_ctx, NULL);
s->video.st->id = 0;

if (!(s->format_ctx->oformat->flags & AVFMT_NOFILE)) {
int ret =
avio_open(&s->format_ctx->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
error_msg(MOD_NAME "avio_open: %s\n", av_err2str(ret));
display_file_done(s);
return NULL;
}
}
s->video.frame = av_frame_alloc();
s->video.pkt = av_packet_alloc();

return s;
}

static struct video_frame *
display_file_getf(void *state)
{
struct state_file *s = state;
struct video_frame *out = vf_alloc_desc(s->video_desc);
out->tiles[0].data = (char *) s->video.frame->data[0];
return out;
}

static bool
display_file_putf(void *state, struct video_frame *frame, long long timeout_ns)
{
vf_free(frame); // not needed
if (timeout_ns == PUTF_DISCARD) {
return true;
}
struct state_file *s = state;

int ret = avcodec_send_frame(s->video.enc, s->video.frame);
s->video.frame->pts += 1;
if (ret < 0) {
error_msg(MOD_NAME "avcodec_send_frame: %s\n", av_err2str(ret));
return false;
}
while (ret >= 0) {
ret = avcodec_receive_packet(s->video.enc, s->video.pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
if (ret < 0) {
error_msg(MOD_NAME "avcodec_receive_frame: %s\n",
av_err2str(ret));
return false;
}
av_packet_rescale_ts(s->video.pkt, s->video.enc->time_base,
s->video.st->time_base);
s->video.pkt->stream_index = s->video.st->index;
ret = av_interleaved_write_frame(s->format_ctx, s->video.pkt);
if (ret < 0) {
error_msg(MOD_NAME "error writting packet: %s\n",
av_err2str(ret));
}
}
return true;
}

static bool
display_file_get_property(void *state, int property, void *val, size_t *len)
{
UNUSED(state);

codec_t codecs[VIDEO_CODEC_COUNT] = { 0 };
int count = 0;
for (int i = 0; i < VIDEO_CODEC_COUNT; ++i) {
if (get_ug_to_av_pixfmt(i)) {
codecs[count++] = i;
}
}
const size_t c_len = count * sizeof codecs[0];
if (property == DISPLAY_PROPERTY_CODECS) {
assert(c_len <= *len);
memcpy(val, codecs, c_len);
*len = c_len;
return true;
}
return false;
}

static bool
display_file_reconfigure(void *state, struct video_desc desc)
{
struct state_file *s = state;

s->video_desc = desc;
s->video.st->time_base = (AVRational){ get_framerate_d(desc.fps),
get_framerate_n(desc.fps) };
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_RAWVIDEO);
avcodec_free_context(&s->video.enc);
s->video.enc = avcodec_alloc_context3(codec);
s->video.enc->width = (int) desc.width;
s->video.enc->height = (int) desc.height;
s->video.enc->time_base = s->video.st->time_base;
s->video.enc->pix_fmt = get_ug_to_av_pixfmt(desc.color_spec);
int ret = avcodec_open2(s->video.enc, codec, NULL);
if (ret < 0) {
error_msg(MOD_NAME "avcodec_open2: %s\n", av_err2str(ret));
return false;
}
ret = avcodec_parameters_from_context(s->video.st->codecpar,
s->video.enc);
if (ret < 0) {
error_msg(MOD_NAME "Could not copy the stream parameters: %s\n",
av_err2str(ret));
return false;
}
s->video.frame->format = s->video.enc->pix_fmt;
s->video.frame->width = (int) desc.width;
s->video.frame->height = (int) desc.height;
s->video.frame->pts = 0;
ret = av_frame_get_buffer(s->video.frame, 0);
if (ret < 0) {
error_msg(MOD_NAME "Could not allocate frame data: %s.\n",
av_err2str(ret));
return false;
}

av_dump_format(s->format_ctx, 0, s->filename, 1);

ret = avformat_write_header(s->format_ctx, NULL);
if (ret < 0) {
error_msg(MOD_NAME
"Error occurred when opening output file: %s\n",
av_err2str(ret));
return false;
}

return true;
}

static const void *
display_file_info_get()
{
static const struct video_display_info display_file_info = {
display_file_probe,
display_file_init,
NULL, // _run
display_file_done,
display_file_getf,
display_file_putf,
display_file_reconfigure,
display_file_get_property,
NULL,
NULL,
MOD_NAME,
};
return &display_file_info;
};

REGISTER_MODULE_WITH_FUNC(file, display_file_info_get,
LIBRARY_CLASS_VIDEO_DISPLAY,
VIDEO_DISPLAY_ABI_VERSION);

0 comments on commit 00ddaec

Please sign in to comment.