[FFmpeg-devel] [PATCH] libcaca output device

Stefano Sabatini stefasab at gmail.com
Thu Jul 19 20:29:54 CEST 2012


On date Thursday 2012-07-19 15:10:22 +0000, Paul B Mahol encoded:
> 
> Signed-off-by: Paul B Mahol <onemda at gmail.com>
> ---
>  configure                |   12 +++
>  libavdevice/Makefile     |    1 +
>  libavdevice/alldevices.c |    1 +
>  libavdevice/caca.c       |  191 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 205 insertions(+), 0 deletions(-)
>  create mode 100644 libavdevice/caca.c
> 
> diff --git a/configure b/configure
> index 7b25877..2198e45 100755
> --- a/configure
> +++ b/configure
> @@ -1177,6 +1177,7 @@ HAVE_LIST="
>      $ARCH_EXT_LIST
>      $HAVE_LIST_PUB
>      $THREADS_LIST
> +    caca

libcaca? (that's what we do for all libraries but libsdl).

alphabetical order

>      aligned_malloc
>      aligned_stack
>      alsa_asoundlib_h
> @@ -1705,6 +1706,7 @@ oss_indev_deps_any="soundcard_h sys_soundcard_h"
>  oss_outdev_deps_any="soundcard_h sys_soundcard_h"
>  pulse_indev_deps="libpulse"
>  sdl_outdev_deps="sdl"
> +caca_outdev_deps="caca"

alphabetical order

>  sndio_indev_deps="sndio_h"
>  sndio_outdev_deps="sndio_h"
>  v4l_indev_deps="linux_videodev_h"
> @@ -3377,6 +3379,14 @@ if enabled libdc1394; then
>      die "ERROR: No version of libdc1394 found "
>  fi
>  
> +CACA_CONFIG="${cross_prefix}caca-config"
> +if "${CACA_CONFIG}" --version > /dev/null 2>&1; then
> +    caca_cflags=$("${CACA_CONFIG}" --cflags)
> +    caca_libs=$("${CACA_CONFIG}" --libs)
> +    enable caca
> +fi
> +enabled caca && add_cflags $caca_cflags && add_extralibs $caca_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 &&
> @@ -3831,6 +3841,8 @@ HOSTLDFLAGS=$host_ldflags
>  HOSTLIBS=$host_libs
>  TARGET_EXEC=$target_exec
>  TARGET_PATH=$target_path
> +CACA_LIBS=$caca_libs
> +CACA_CFLAGS=$caca_cflags
>  SDL_LIBS=$sdl_libs
>  SDL_CFLAGS=$sdl_cflags
>  LIB_INSTALL_EXTRA_CMD=$LIB_INSTALL_EXTRA_CMD
> 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..d5e6edf
> --- /dev/null
> +++ b/libavdevice/caca.c
> @@ -0,0 +1,191 @@
> +/*
> + * 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;
> +    char           *window_title;
> +
> +    caca_canvas_t  *canvas;
> +    caca_display_t *display;
> +    caca_dither_t  *dither;
> +
> +    int            width;
> +    int            height;
> +
> +    char           *antialias;
> +    char           *charset;
> +    char           *colors;
> +    char           *algorithm;
> +    char           *driver;
> +} CACAContext;
> +
> +static int caca_write_trailer(AVFormatContext *s)
> +{
> +    CACAContext *c= s->priv_data;

Nit: c =

> +
> +    av_freep(&c->window_title);
> +
> +    caca_free_dither(c->dither);
> +    caca_free_display(c->display);
> +    caca_free_canvas(c->canvas);
> +    return 0;
> +}
> +
> +static int caca_write_header(AVFormatContext *s)
> +{
> +    CACAContext *c= s->priv_data;
> +    AVStream *st = s->streams[0];
> +    AVCodecContext *encctx = st->codec;
> +    int ret, i, bpp;
> +
> +    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);
> +    }
> +
> +    c->canvas = caca_create_canvas(encctx->width, encctx->height);
> +    if (!c->canvas) {
> +        av_log(s, AV_LOG_ERROR, "Failed to create canvas.\n");
> +        return AVERROR(ENOMEM);

Don't know if libcaca supports error messages, could be nice to add
them to improve the feedback.

> +    }
> +
> +    c->display = caca_create_display_with_driver(c->canvas, c->driver);
> +    if (!c->display) {
> +        const char *const *drivers = caca_get_display_driver_list();
> +
> +        av_log(s, AV_LOG_ERROR, "Failed to create display.\n");

Nit: no need for the final dot (consistency reasons), here and below

> +        av_log(s, AV_LOG_INFO, "Possible drivers:\n");
> +        for (i = 0; drivers[i]; i += 2)
> +            av_log(s, AV_LOG_INFO, "%s : %s\n", drivers[i], drivers[i + 1]);
> +        caca_free_canvas(c->canvas);

Note: we could create a list driver|algorithm|antialias|color|... option
showing the supported stuff.

> +        return AVERROR(EINVAL);
> +    }
> +
> +    c->width  = caca_get_canvas_width(c->canvas);
> +    c->height = caca_get_canvas_height(c->canvas);
> +
> +    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(ENOMEM);
> +        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) {
> +        const char *const *algorithm = caca_get_dither_algorithm_list(c->dither);
> +        const char *const *antialias = caca_get_dither_antialias_list(c->dither);
> +        const char *const *charset = caca_get_dither_charset_list(c->dither);
> +        const char *const *color = caca_get_dither_color_list(c->dither);
> +
> +        av_log(s, AV_LOG_ERROR, "Invalid value given to one of options.\n");
> +        av_log(s, AV_LOG_INFO, "Possible dithering algorithms:\n");
> +        for (i = 0; algorithm[i]; i += 2)
> +            av_log(s, AV_LOG_INFO, "%s : %s\n", algorithm[i], algorithm[i + 1]);
> +        av_log(s, AV_LOG_INFO, "\n");
> +        av_log(s, AV_LOG_INFO, "Possible antialias:\n");
> +        for (i = 0; antialias[i]; i += 2)
> +            av_log(s, AV_LOG_INFO, "%s : %s\n", antialias[i], antialias[i + 1]);
> +        av_log(s, AV_LOG_INFO, "\n");
> +        av_log(s, AV_LOG_INFO, "Possible charsets:\n");
> +        for (i = 0; charset[i]; i += 2)
> +            av_log(s, AV_LOG_INFO, "%s : %s\n", charset[i], charset[i + 1]);
> +        av_log(s, AV_LOG_INFO, "\n");
> +        av_log(s, AV_LOG_INFO, "Possible colors:\n");
> +        for (i = 0; color[i]; i += 2)
> +            av_log(s, AV_LOG_INFO, "%s : %s\n", color[i], color[i + 1]);
> +        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;

Nit: c =

> +
> +    caca_dither_bitmap(c->canvas, 0, 0, c->width, c->height, c->dither, pkt->data);
> +    caca_refresh_display(c->display);
> +
> +    return 0;
> +}
> +
> +#define OFFSET(x) offsetof(CACAContext,x)
> +
> +static const AVOption options[] = {

> +    { "window_title", "window title", OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
> +    { "driver",       "display driver", OFFSET(driver), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
> +    { "algorithm",    "dithering algorithm", OFFSET(algorithm), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
> +    { "antialias",    "antialias output", OFFSET(antialias), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
> +    { "charset",      "charset used to render output", OFFSET(charset), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
> +    { "colors",       "colors used to render output", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },

prepend "set " in all options help message (I know sdl is not like that)

> +    { 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,
> +};

What about adding some docs in outdevs.texi?
-- 
FFmpeg = Frightening Fierce Multipurpose Political Experimenting Goblin


More information about the ffmpeg-devel mailing list