[FFmpeg-devel] [PATCH] libcaca output device
Paul B Mahol
onemda at gmail.com
Fri Jul 20 04:08:21 CEST 2012
Signed-off-by: Paul B Mahol <onemda at gmail.com>
---
configure | 12 ++
doc/outdevs.texi | 52 ++++++++++
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/caca.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 318 insertions(+), 0 deletions(-)
create mode 100644 libavdevice/caca.c
diff --git a/configure b/configure
index 300f128..2c5d62a 100755
--- a/configure
+++ b/configure
@@ -1230,6 +1230,7 @@ HAVE_LIST="
jack_port_get_latency_range
kbhit
ldbrx
+ libcaca
libdc1394_1
libdc1394_2
llrint
@@ -1697,6 +1698,7 @@ fbdev_indev_deps="linux_fb_h"
iec61883_indev_deps="libiec61883"
jack_indev_deps="jack_jack_h sem_timedwait"
lavfi_indev_deps="avfilter"
+libcaca_outdev_deps="libcaca"
libcdio_indev_deps="libcdio"
libdc1394_indev_deps="libdc1394"
libv4l2_indev_deps="libv4l2"
@@ -3363,6 +3365,14 @@ if enabled libdc1394; then
die "ERROR: No version of libdc1394 found "
fi
+LIBCACA_CONFIG="${cross_prefix}caca-config"
+if "${LIBCACA_CONFIG}" --version > /dev/null 2>&1; then
+ libcaca_cflags=$("${LIBCACA_CONFIG}" --cflags)
+ libcaca_libs=$("${LIBCACA_CONFIG}" --libs)
+ enable libcaca
+fi
+enabled libcaca && add_cflags $libcaca_cflags && add_extralibs $libcaca_libs
+
SDL_CONFIG="${cross_prefix}sdl-config"
if check_pkg_config sdl SDL_events.h SDL_PollEvent; then
check_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) >= 0x010201" $sdl_cflags &&
@@ -3817,6 +3827,8 @@ HOSTLDFLAGS=$host_ldflags
HOSTLIBS=$host_libs
TARGET_EXEC=$target_exec
TARGET_PATH=$target_path
+CACA_LIBS=$libcaca_libs
+CACA_CFLAGS=$libcaca_cflags
SDL_LIBS=$sdl_libs
SDL_CFLAGS=$sdl_cflags
LIB_INSTALL_EXTRA_CMD=$LIB_INSTALL_EXTRA_CMD
diff --git a/doc/outdevs.texi b/doc/outdevs.texi
index 8034a22..71e4986 100644
--- a/doc/outdevs.texi
+++ b/doc/outdevs.texi
@@ -22,6 +22,58 @@ A description of the currently available output devices follows.
ALSA (Advanced Linux Sound Architecture) output device.
+ at section caca
+
+CACA output device.
+
+This output devices allows to show a video stream in CACA window.
+Only one CACA window is allowed per application, so you can
+have only one instance of this output device in an application.
+
+To enable this output device you need libcaca installed on your system
+when configuring your build.
+libcaca is a graphics library that outputs text instead of pixels.
+
+ at subsection Options
+
+ at table @option
+
+ at item window_title
+Set the CACA window title, if not specified default to the filename
+specified for the output device.
+
+ at item window_size
+Set the CACA window size, can be a string of the form
+ at var{width}x at var{height} or a video size abbreviation.
+If not specified it defaults to the size of the input video.
+
+ at item driver
+Set display driver.
+
+ at item algorithm
+Set dithering algorithm. Dithering is necessary
+because the picture being rendered has usually far more colours than
+the available palette.
+
+ at item antialias
+Set antialias method. Antialiasing smoothens the rendered
+image and avoids the commonly seen staircase effect.
+
+ at item charset
+Set which characters are going to be used when rendering text.
+
+ at item colors
+Set colors to be used when rendering text.
+ at end table
+
+ at subsection Examples
+
+The following command shows the @command{ffmpeg} output is an
+CACA window, forcing its size to 80x25:
+ at example
+ffmpeg -i INPUT -vcodec rawvideo -pix_fmt rgb24 -window_size 80x25 -f caca -
+ at end example
+
@section oss
OSS (Open Sound System) output device.
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index 4759a82..8f6f843 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -16,6 +16,7 @@ OBJS-$(CONFIG_ALSA_INDEV) += alsa-audio-common.o \
OBJS-$(CONFIG_ALSA_OUTDEV) += alsa-audio-common.o \
alsa-audio-enc.o
OBJS-$(CONFIG_BKTR_INDEV) += bktr.o
+OBJS-$(CONFIG_CACA_OUTDEV) += caca.o
OBJS-$(CONFIG_DSHOW_INDEV) += dshow.o dshow_enummediatypes.o \
dshow_enumpins.o dshow_filter.o \
dshow_pin.o dshow_common.o
diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
index 2a0bffb..092e6c5 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -40,6 +40,7 @@ void avdevice_register_all(void)
/* devices */
REGISTER_INOUTDEV (ALSA, alsa);
REGISTER_INDEV (BKTR, bktr);
+ REGISTER_OUTDEV (CACA, caca);
REGISTER_INDEV (DSHOW, dshow);
REGISTER_INDEV (DV1394, dv1394);
REGISTER_INDEV (FBDEV, fbdev);
diff --git a/libavdevice/caca.c b/libavdevice/caca.c
new file mode 100644
index 0000000..09fe192
--- /dev/null
+++ b/libavdevice/caca.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2011 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <caca.h>
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "avdevice.h"
+
+typedef struct {
+ AVClass *class;
+ AVFormatContext *ctx;
+ char *window_title;
+ int window_width, window_height;
+
+ caca_canvas_t *canvas;
+ caca_display_t *display;
+ caca_dither_t *dither;
+
+ char *algorithm, *antialias;
+ char *charset, *colors;
+ char *driver;
+
+ int list_antialias, list_algorithms;
+ int list_charsets, list_colors;
+ int list_drivers;
+
+} CACAContext;
+
+static int caca_write_trailer(AVFormatContext *s)
+{
+ CACAContext *c = s->priv_data;
+
+ av_freep(&c->window_title);
+
+ caca_free_dither(c->dither);
+ caca_free_display(c->display);
+ caca_free_canvas(c->canvas);
+ return 0;
+}
+
+static void available_drivers(CACAContext *c)
+{
+ const char *const *drivers = caca_get_display_driver_list();
+ int i;
+
+ av_log(c->ctx, AV_LOG_INFO, "Available drivers:\n");
+ for (i = 0; drivers[i]; i += 2)
+ av_log(c->ctx, AV_LOG_INFO, "%s : %s\n", drivers[i], drivers[i + 1]);
+}
+
+static void available_colors(CACAContext *c)
+{
+ const char *const *color = caca_get_dither_color_list(c->dither);
+ int i;
+
+ av_log(c->ctx, AV_LOG_INFO, "Available colors:\n");
+ for (i = 0; color[i]; i += 2)
+ av_log(c->ctx, AV_LOG_INFO, "%s : %s\n", color[i], color[i + 1]);
+}
+
+static void available_charsets(CACAContext *c)
+{
+ const char *const *charset = caca_get_dither_charset_list(c->dither);
+ int i;
+
+ av_log(c->ctx, AV_LOG_INFO, "Available charsets:\n");
+ for (i = 0; charset[i]; i += 2)
+ av_log(c->ctx, AV_LOG_INFO, "%s : %s\n", charset[i], charset[i + 1]);
+}
+
+static void available_algorithms(CACAContext *c)
+{
+ const char *const *algorithm = caca_get_dither_algorithm_list(c->dither);
+ int i;
+
+ av_log(c->ctx, AV_LOG_INFO, "Available dithering algorithms:\n");
+ for (i = 0; algorithm[i]; i += 2)
+ av_log(c->ctx, AV_LOG_INFO, "%s : %s\n", algorithm[i], algorithm[i + 1]);
+}
+
+static void available_antialias(CACAContext *c)
+{
+ const char *const *antialias = caca_get_dither_antialias_list(c->dither);
+ int i;
+
+ av_log(c->ctx, AV_LOG_INFO, "Available antialias methods:\n");
+ for (i = 0; antialias[i]; i += 2)
+ av_log(c->ctx, AV_LOG_INFO, "%s : %s\n", antialias[i], antialias[i + 1]);
+}
+
+static int caca_write_header(AVFormatContext *s)
+{
+ CACAContext *c = s->priv_data;
+ AVStream *st = s->streams[0];
+ AVCodecContext *encctx = st->codec;
+ int ret, bpp;
+
+ c->ctx = s;
+
+ if (c->list_drivers)
+ available_drivers(c);
+ if (c->list_colors)
+ available_colors(c);
+ if (c->list_charsets)
+ available_charsets(c);
+ if (c->list_algorithms)
+ available_algorithms(c);
+ if (c->list_antialias)
+ available_antialias(c);
+
+ if (c->list_drivers || c->list_colors || c->list_charsets || c->list_algorithms || c->list_antialias)
+ return AVERROR_EXIT;
+
+ if ( s->nb_streams > 1
+ || encctx->codec_type != AVMEDIA_TYPE_VIDEO
+ || encctx->codec_id != CODEC_ID_RAWVIDEO) {
+ av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
+ return AVERROR(EINVAL);
+ }
+
+ if (encctx->pix_fmt != PIX_FMT_RGB24) {
+ av_log(s, AV_LOG_ERROR,
+ "Unsupported pixel format '%s', choose rgb24\n",
+ av_get_pix_fmt_name(encctx->pix_fmt));
+ return AVERROR(EINVAL);
+ }
+
+ if (!c->window_width || !c->window_height) {
+ c->window_width = encctx->width;
+ c->window_height = encctx->height;
+ }
+ c->canvas = caca_create_canvas(c->window_width, c->window_height);
+ if (!c->canvas) {
+ av_log(s, AV_LOG_ERROR, "Failed to create canvas\n");
+ return AVERROR(errno);
+ }
+
+ c->display = caca_create_display_with_driver(c->canvas, c->driver);
+ if (!c->display) {
+ av_log(s, AV_LOG_ERROR, "Failed to create display\n");
+ available_drivers(c);
+ caca_free_canvas(c->canvas);
+ return AVERROR(errno);
+ }
+
+ bpp = av_get_bits_per_pixel(&av_pix_fmt_descriptors[encctx->pix_fmt]);
+ c->dither = caca_create_dither(bpp, encctx->width, encctx->height,
+ bpp * encctx->width / 8,
+ 0x0000ff, 0x00ff00, 0xff0000, 0);
+ if (!c->dither) {
+ av_log(s, AV_LOG_ERROR, "Failed to create dither\n");
+ ret = AVERROR(errno);
+ goto fail;
+ }
+
+ ret = caca_set_dither_algorithm(c->dither, c->algorithm);
+ ret += caca_set_dither_antialias(c->dither, c->antialias);
+ ret += caca_set_dither_charset(c->dither, c->charset);
+ ret += caca_set_dither_color(c->dither, c->colors);
+ if (ret) {
+ av_log(s, AV_LOG_ERROR, "Invalid value given to one of options\n");
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ if (!c->window_title)
+ c->window_title = av_strdup(s->filename);
+ caca_set_display_title(c->display, c->window_title);
+ caca_set_display_time(c->display, av_rescale_q(1, st->codec->time_base, AV_TIME_BASE_Q));
+
+ return 0;
+
+fail:
+ caca_write_trailer(s);
+ return ret;
+}
+
+static int caca_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ CACAContext *c = s->priv_data;
+
+ caca_dither_bitmap(c->canvas, 0, 0, c->window_width, c->window_height, c->dither, pkt->data);
+ caca_refresh_display(c->display);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(CACAContext,x)
+#define ENC AV_OPT_FLAG_ENCODING_PARAM
+
+static const AVOption options[] = {
+ { "window_size", "set window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, ENC},
+ { "window_title", "set window title", OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, ENC },
+ { "driver", "set display driver", OFFSET(driver), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, ENC },
+ { "algorithm", "set dithering algorithm", OFFSET(algorithm), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, ENC },
+ { "antialias", "set antialias method", OFFSET(antialias), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, ENC },
+ { "charset", "set charset used to render output", OFFSET(charset), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, ENC },
+ { "colors", "set colors used to render output", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, ENC },
+ { "list_drivers", "list available drivers", OFFSET(list_drivers), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1, ENC, "list_drivers" },
+ { "true", "", 0, AV_OPT_TYPE_CONST, {.dbl=1}, 0, 0, ENC, "list_drivers" },
+ { "false", "", 0, AV_OPT_TYPE_CONST, {.dbl=0}, 0, 0, ENC, "list_drivers" },
+ { "list_colors", "list available colors", OFFSET(list_colors), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1, ENC, "list_colors" },
+ { "true", "", 0, AV_OPT_TYPE_CONST, {.dbl=1}, 0, 0, ENC, "list_colors" },
+ { "false", "", 0, AV_OPT_TYPE_CONST, {.dbl=0}, 0, 0, ENC, "list_colors" },
+ { "list_charsets", "list available charsets", OFFSET(list_charsets), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1, ENC, "list_charsets" },
+ { "true", "", 0, AV_OPT_TYPE_CONST, {.dbl=1}, 0, 0, ENC, "list_charsets" },
+ { "false", "", 0, AV_OPT_TYPE_CONST, {.dbl=0}, 0, 0, ENC, "list_charsets" },
+ { "list_algorithms", "list available dithering algorithms", OFFSET(list_algorithms), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1, ENC, "list_algorithms" },
+ { "true", "", 0, AV_OPT_TYPE_CONST, {.dbl=1}, 0, 0, ENC, "list_algorithms" },
+ { "false", "", 0, AV_OPT_TYPE_CONST, {.dbl=0}, 0, 0, ENC, "list_algorithms" },
+ { "list_antialias", "list available antialias methods", OFFSET(list_antialias), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1, ENC, "list_antialias" },
+ { "true", "", 0, AV_OPT_TYPE_CONST, {.dbl=1}, 0, 0, ENC, "list_antialias" },
+ { "false", "", 0, AV_OPT_TYPE_CONST, {.dbl=0}, 0, 0, ENC, "list_antialias" },
+ { NULL },
+};
+
+static const AVClass caca_class = {
+ .class_name = "caca_outdev",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVOutputFormat ff_caca_muxer = {
+ .name = "caca",
+ .long_name = NULL_IF_CONFIG_SMALL("CACA output device"),
+ .priv_data_size = sizeof(CACAContext),
+ .audio_codec = CODEC_ID_NONE,
+ .video_codec = CODEC_ID_RAWVIDEO,
+ .write_header = caca_write_header,
+ .write_packet = caca_write_packet,
+ .write_trailer = caca_write_trailer,
+ .flags = AVFMT_NOFILE,
+ .priv_class = &caca_class,
+};
--
1.7.7
More information about the ffmpeg-devel
mailing list