Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/codec/arib/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 18 kB image not shown  

Quelle  libaribcaption.c   Sprache: C

 

/*****************************************************************************
 * ARIB STD-B24 caption decoder/renderer using libaribcaption.
 *****************************************************************************
 * Copyright (C) 2022 magicxqq
 *
 * Authors:  magicxqq <xqq@xqq.im>
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


/*****************************************************************************
 * Preamble
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
#   include "config.h"
#endif

#include <stdio.h>
#include <string.h>
#include <limits.h>

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_atomic.h>
#include <vlc_codec.h>

#include <aribcaption/aribcaption.h>


/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

typedef struct
{
    /* The following fields of decoder_sys_t are shared between decoder and spu units */
    vlc_atomic_rc_t    rc;

    bool               b_cfg_fadeout;

    aribcc_context_t  *p_context;
    aribcc_decoder_t  *p_decoder;
    aribcc_renderer_t *p_renderer;

    vlc_mutex_t        dec_lock;    // protect p_dec pointer
    decoder_t*         p_dec;       // pointer to decoder_t for logcat callback, NULL if decoder closed
} decoder_sys_t;

typedef struct
{
    decoder_sys_t         *p_dec_sys;
    vlc_tick_t             i_pts;
    aribcc_render_result_t render_result;
} libaribcaption_spu_updater_sys_t;


static void DecSysRetain(decoder_sys_t *p_sys)
{
    vlc_atomic_rc_inc(&p_sys->rc);
}

static void DecSysRelease(decoder_sys_t *p_sys)
{
    if (!vlc_atomic_rc_dec(&p_sys->rc))
        return;

    if (p_sys->p_renderer)
        aribcc_renderer_free(p_sys->p_renderer);
    if (p_sys->p_decoder)
        aribcc_decoder_free(p_sys->p_decoder);
    if (p_sys->p_context)
        aribcc_context_free(p_sys->p_context);

    free(p_sys);
}


/****************************************************************************
 *
 ****************************************************************************/

static void CopyImageToRegion(picture_t *dst_pic, const aribcc_image_t *image)
{
    if(image->pixel_format != ARIBCC_PIXELFORMAT_RGBA8888)
        return;

    plane_t *p_dstplane = &dst_pic->p[0];
    plane_t srcplane;
    srcplane.i_lines = image->height;
    srcplane.i_pitch = image->stride;
    srcplane.i_pixel_pitch = p_dstplane->i_pixel_pitch;
    srcplane.i_visible_lines = image->height;
    srcplane.i_visible_pitch = image->width /* in pixels */ * p_dstplane->i_pixel_pitch;
    srcplane.p_pixels = image->bitmap;
    plane_CopyPixels( p_dstplane, &srcplane );
}

static void SubpictureUpdate(subpicture_t *p_subpic,
                             const struct vlc_spu_updater_configuration *cfg)
{
    libaribcaption_spu_updater_sys_t *p_spusys = p_subpic->updater.sys;
    decoder_sys_t *p_sys = p_spusys->p_dec_sys;
    const video_format_t *p_src_format = cfg->current.video_src;
    const video_format_t *p_dst_format = cfg->current.video_dst;

    bool b_src_changed = p_src_format->i_visible_width  != cfg->previous.video_src->i_visible_width ||
                         p_src_format->i_visible_height != cfg->previous.video_src->i_visible_height;
    bool b_dst_changed = !video_format_IsSimilar(cfg->previous.video_dst, p_dst_format);

    unsigned i_render_area_width  = p_dst_format->i_visible_width;
    unsigned i_render_area_height = p_src_format->i_visible_height * p_dst_format->i_visible_width /
                                    p_src_format->i_visible_width;
    if (b_src_changed || b_dst_changed) {
        /* don't let library freely scale using either the min of width or height ratio */
        aribcc_renderer_set_frame_size(p_sys->p_renderer, i_render_area_width,
                                                          i_render_area_height);
    }

    const vlc_tick_t i_stream_date = p_spusys->i_pts + (cfg->pts - p_subpic->i_start);

    /* Retrieve the expected render status for detecting whether the subtitle image changed */
    aribcc_render_status_t status = aribcc_renderer_try_render(p_sys->p_renderer,
                                                               MS_FROM_VLC_TICK(i_stream_date));
    if (status == ARIBCC_RENDER_STATUS_GOT_IMAGE_UNCHANGED) {
        /* Skip rendering since images were not changed */
        if (!b_src_changed && !b_dst_changed) {
            return;
        }
    }

    status = aribcc_renderer_render(p_sys->p_renderer,
                                    MS_FROM_VLC_TICK(i_stream_date),
                                    &p_spusys->render_result);
    if (status == ARIBCC_RENDER_STATUS_ERROR) {
        return;
    }

    vlc_spu_regions_Clear( &p_subpic->regions );

    aribcc_image_t *p_images = p_spusys->render_result.images;
    uint32_t        i_image_count = p_spusys->render_result.image_count;

    if (!p_images || i_image_count == 0) {
        return;
    }

    p_subpic->i_original_picture_width = i_render_area_width;
    p_subpic->i_original_picture_height = i_render_area_height;

    video_format_t fmt_region = *p_dst_format;
    fmt_region.i_chroma       = VLC_CODEC_RGBA;
    fmt_region.i_x_offset     = 0;
    fmt_region.i_y_offset     = 0;
    fmt_region.transfer       = TRANSFER_FUNC_SRGB;
    fmt_region.primaries      = COLOR_PRIMARIES_SRGB;
    fmt_region.space          = COLOR_SPACE_SRGB;
    fmt_region.color_range    = COLOR_RANGE_FULL;

    /* Allocate the regions and draw them */
    for (uint32_t i = 0; i < i_image_count; i++) {
        aribcc_image_t *image = &p_images[i];

        fmt_region.i_width =
        fmt_region.i_visible_width  = image->width;
        fmt_region.i_height =
        fmt_region.i_visible_height = image->height;
        fmt_region.i_sar_num = 1;
        fmt_region.i_sar_den = 1;

        subpicture_region_t *region = subpicture_region_New(&fmt_region);
        if (!region)
            break;

        region->b_absolute = true; region->b_in_window = false;
        region->i_x = image->dst_x;
        region->i_y = image->dst_y;
        region->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;

        CopyImageToRegion(region->p_picture, image);

        vlc_spu_regions_push(&p_subpic->regions, region);
    }

    aribcc_render_result_cleanup(&p_spusys->render_result);
}

static void SubpictureDestroy(subpicture_t *p_subpic)
{
    libaribcaption_spu_updater_sys_t *p_spusys = p_subpic->updater.sys;
    DecSysRelease(p_spusys->p_dec_sys);
    free(p_spusys);
}

/****************************************************************************
 * Decode:
 ****************************************************************************/

static int Decode(decoder_t *p_dec, block_t *p_block)
{
    decoder_sys_t *p_sys = p_dec->p_sys;

    if (p_block == NULL) /* No Drain */
        return VLCDEC_SUCCESS;

    if (p_block->i_flags & BLOCK_FLAG_CORRUPTED) {
        block_Release(p_block);
        return VLCDEC_SUCCESS;
    }

    aribcc_caption_t caption;
    aribcc_decode_status_t status = aribcc_decoder_decode(p_sys->p_decoder,
                                                          p_block->p_buffer,
                                                          p_block->i_buffer,
                                                          MS_FROM_VLC_TICK(p_block->i_pts),
                                                          &caption);
    if (status == ARIBCC_DECODE_STATUS_ERROR) {
        msg_Err(p_dec, "aribcc_decoder_decode() returned with error");
    }

    if (status == ARIBCC_DECODE_STATUS_ERROR || status == ARIBCC_DECODE_STATUS_NO_CAPTION) {
        block_Release(p_block);
        return VLCDEC_SUCCESS;
    }

    aribcc_renderer_append_caption(p_sys->p_renderer, &caption);
    aribcc_caption_cleanup(&caption);


    libaribcaption_spu_updater_sys_t *p_spusys = malloc(sizeof(libaribcaption_spu_updater_sys_t));
    if (!p_spusys) {
        block_Release(p_block);
        return VLCDEC_SUCCESS;
    }
    p_spusys->p_dec_sys = p_sys;
    p_spusys->i_pts = p_block->i_pts;
    memset(&p_spusys->render_result, 0, sizeof(p_spusys->render_result));

    static const struct vlc_spu_updater_ops spu_ops =
    {
        .update   = SubpictureUpdate,
        .destroy  = SubpictureDestroy,
    };

    subpicture_updater_t updater = {
        .sys = p_spusys,
        .ops = &spu_ops,
    };

    subpicture_t *p_spu = decoder_NewSubpicture(p_dec, &updater);
    if (!p_spu) {
        msg_Warn(p_dec, "can't get spu buffer");
        free(p_spusys);
        block_Release(p_block);
        return VLCDEC_SUCCESS;
    }
    p_spu->i_start = p_block->i_pts;
    p_spu->i_stop = p_block->i_pts;
    p_spu->b_fade = p_sys->b_cfg_fadeout;

    if (caption.wait_duration == ARIBCC_DURATION_INDEFINITE) {
        p_spu->b_ephemer = true;
    } else {
        p_spu->i_stop = p_block->i_pts + VLC_TICK_FROM_MS(caption.wait_duration);
    }


    DecSysRetain(p_sys); /* Keep a reference for the returned subpicture */

    block_Release(p_block);

    decoder_QueueSub(p_dec, p_spu);

    return VLCDEC_SUCCESS;
}

/*****************************************************************************
 * Flush:
 *****************************************************************************/

static void Flush(decoder_t *p_dec)
{
    decoder_sys_t *p_sys = p_dec->p_sys;
    aribcc_decoder_flush(p_sys->p_decoder);
    aribcc_renderer_flush(p_sys->p_renderer);
}

/*****************************************************************************
 * libaribcaption logcat callback function
 *****************************************************************************/

static void LogcatCallback(aribcc_loglevel_t level, const char *message, void *userdata)
{
    decoder_sys_t *p_sys = userdata;

    if (p_sys->p_dec != NULL) {
        vlc_mutex_lock(&p_sys->dec_lock);

        if (p_sys->p_dec != NULL) {
            decoder_t* p_dec = p_sys->p_dec;

            if (level == ARIBCC_LOGLEVEL_ERROR) {
                msg_Err(p_dec, "%s", message);
            } else if (level == ARIBCC_LOGLEVEL_WARNING) {
                msg_Warn(p_dec, "%s", message);
            } else {
                msg_Dbg(p_dec, "%s", message);
            }
        }

        vlc_mutex_unlock(&p_sys->dec_lock);
    }
}

#define ARIBCAPTION_CFG_PREFIX "aribcaption-"

/*****************************************************************************
 * Open: Create libaribcaption context/decoder/renderer.
 *****************************************************************************/

static int Open(vlc_object_t *p_this)
{
    decoder_t     *p_dec = (decoder_t *)p_this;
    decoder_sys_t *p_sys;

    if (p_dec->fmt_in->i_codec != VLC_CODEC_ARIB_A &&
        p_dec->fmt_in->i_codec != VLC_CODEC_ARIB_C) {
        return VLC_ENOTSUP;
    }

    p_sys = malloc(sizeof(decoder_sys_t));
    if (unlikely(p_sys == NULL))
        return VLC_ENOMEM;

    vlc_atomic_rc_init(&p_sys->rc);

    p_sys->b_cfg_fadeout = var_InheritBool(p_this, ARIBCAPTION_CFG_PREFIX "fadeout");

    vlc_mutex_init(&p_sys->dec_lock);
    p_sys->p_dec = p_dec;

    /* Create libaribcaption context */
    aribcc_context_t *p_ctx = p_sys->p_context = aribcc_context_alloc();
    if (!p_ctx) {
        msg_Err(p_dec, "libaribcaption context allocation failed");
        DecSysRelease(p_sys);
        return VLC_EGENERIC;
    }

    aribcc_context_set_logcat_callback(p_ctx, LogcatCallback, p_sys);


    /* Create the decoder */
    aribcc_decoder_t* p_decoder = p_sys->p_decoder = aribcc_decoder_alloc(p_ctx);
    if (!p_decoder) {
        msg_Err(p_dec, "libaribcaption decoder creation failed");
        DecSysRelease(p_sys);
        return VLC_EGENERIC;
    }

    aribcc_profile_t i_profile = ARIBCC_PROFILE_A;
    if (p_dec->fmt_in->i_codec == VLC_CODEC_ARIB_C) {
        i_profile = ARIBCC_PROFILE_C;
    }

    bool b_succ = aribcc_decoder_initialize(p_decoder,
                                            ARIBCC_ENCODING_SCHEME_AUTO,
                                            ARIBCC_CAPTIONTYPE_CAPTION,
                                            i_profile,
                                            ARIBCC_LANGUAGEID_FIRST);
    if (!b_succ) {
        msg_Err(p_dec, "libaribcaption decoder initialization failed");
        DecSysRelease(p_sys);
        return VLC_EGENERIC;
    }


    /* Create the renderer */
    aribcc_renderer_t* p_renderer = p_sys->p_renderer = aribcc_renderer_alloc(p_ctx);
    if (!p_renderer) {
        msg_Err(p_dec, "libaribcaption renderer creation failed");
        DecSysRelease(p_sys);
        return VLC_EGENERIC;
    }

    int i_cfg_rendering_backend =
        var_InheritInteger(p_this, ARIBCAPTION_CFG_PREFIX "rendering-backend");
    b_succ = aribcc_renderer_initialize(p_renderer,
                                        ARIBCC_CAPTIONTYPE_CAPTION,
                                        ARIBCC_FONTPROVIDER_TYPE_AUTO,
                                        (aribcc_textrenderer_type_t)i_cfg_rendering_backend);
    if (!b_succ) {
        msg_Err(p_dec, "libaribcaption renderer initialization failed");
        DecSysRelease(p_sys);
        return VLC_EGENERIC;
    }

    aribcc_renderer_set_storage_policy(p_renderer, ARIBCC_CAPTION_STORAGE_POLICY_MINIMUM, 0);

    bool b_cfg_replace_drcs = var_InheritBool(p_this, ARIBCAPTION_CFG_PREFIX "replace-drcs");
    bool b_cfg_force_stroke_text = var_InheritBool(p_this, ARIBCAPTION_CFG_PREFIX "force-stroke-text");
    bool b_cfg_ignore_background = var_InheritBool(p_this, ARIBCAPTION_CFG_PREFIX "ignore-background");
    bool b_cfg_ignore_ruby = var_InheritBool(p_this, ARIBCAPTION_CFG_PREFIX "ignore-ruby");
    float f_cfg_stroke_width = var_InheritFloat(p_this, ARIBCAPTION_CFG_PREFIX "stroke-width");
    aribcc_renderer_set_replace_drcs(p_renderer, b_cfg_replace_drcs);
    aribcc_renderer_set_force_stroke_text(p_renderer, b_cfg_force_stroke_text);
    aribcc_renderer_set_force_no_background(p_renderer, b_cfg_ignore_background);
    aribcc_renderer_set_force_no_ruby(p_renderer, b_cfg_ignore_ruby);
    aribcc_renderer_set_stroke_width(p_renderer, f_cfg_stroke_width);

    char *psz_cfg_font_name = var_InheritString(p_this, ARIBCAPTION_CFG_PREFIX "font");
    if (psz_cfg_font_name) {
        const char* font_families[] = { psz_cfg_font_name };
        aribcc_renderer_set_default_font_family(p_renderer,
                                                font_families,
                                                ARRAY_SIZE(font_families),
                                                true);
        free(psz_cfg_font_name);
    }

    p_dec->p_sys = p_sys;
    p_dec->pf_decode = Decode;
    p_dec->pf_flush  = Flush;
    p_dec->fmt_out.i_codec = VLC_CODEC_RGBA;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close: finish
 *****************************************************************************/

static void Close(vlc_object_t *p_this)
{
    decoder_t *p_dec = (decoder_t *)p_this;
    decoder_sys_t *p_sys = p_dec->p_sys;

    vlc_mutex_lock(&p_sys->dec_lock);
    p_sys->p_dec = NULL;
    vlc_mutex_unlock(&p_sys->dec_lock);

    DecSysRelease(p_sys);
}


#define CFG_TEXT_RENDERING_BACKEND N_("Text rendering backend")
#define CFG_LONGTEXT_RENDERING_BACKEND N_("Select text rendering backend")

#define CFG_TEXT_FONT N_("Font")
#define CFG_LONGTEXT_FONT N_("Select font")

#define CFG_TEXT_REPLACE_DRCS N_("Replace known DRCS")
#define CFG_LONGTEXT_REPLACE_DRCS N_("Replace known DRCS in the caption")

#define CFG_TEXT_FORCE_STROKE_TEXT N_("Force stroke text")
#define CFG_LONGTEXT_FORCE_STROKE_TEXT N_("Always render characters with stroke")

#define CFG_TEXT_IGNORE_BACKGROUND N_("Ignore background")
#define CFG_LONGTEXT_IGNORE_BACKGROUND N_("Ignore rendering caption background")

#define CFG_TEXT_IGNORE_RUBY N_("Ignore ruby (furigana)")
#define CFG_LONGTEXT_IGNORE_RUBY N_("Ignore ruby-like characters, like furigana")

#define CFG_TEXT_FADEOUT N_("Fadeout")
#define CFG_LONGTEXT_FADEOUT N_("Enable Fadeout")

#define CFG_TEXT_STROKE_WIDTH N_("Stroke width")
#define CFG_LONGTEXT_STROKE_WIDTH N_("Indicate stroke width for stroke text")


static const int rendering_backend_values[] = {
    ARIBCC_TEXTRENDERER_TYPE_AUTO,

#if defined(ARIBCC_USE_CORETEXT)
    ARIBCC_TEXTRENDERER_TYPE_CORETEXT,
#endif

#if defined(ARIBCC_USE_DIRECTWRITE)
    ARIBCC_TEXTRENDERER_TYPE_DIRECTWRITE,
#endif

#if defined(ARIBCC_USE_FREETYPE)
    ARIBCC_TEXTRENDERER_TYPE_FREETYPE
#endif
};

static const char* const ppsz_rendering_backend_descriptions[] = {
    N_("Auto"),

#if defined(ARIBCC_USE_CORETEXT)
    N_("CoreText"),
#endif

#if defined(ARIBCC_USE_DIRECTWRITE)
    N_("DirectWrite"),
#endif

#if defined(ARIBCC_USE_FREETYPE)
    N_("FreeType")
#endif
};

#ifdef __APPLE__
# define DEFAULT_FAMILY "Hiragino Maru Gothic ProN"
#elif defined(_WIN32)
# define DEFAULT_FAMILY "MS Gothic"
#else
# define DEFAULT_FAMILY "sans-serif"
#endif

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

vlc_module_begin ()
    set_shortname(N_("ARIB caption"))
    set_description(N_("ARIB caption renderer"))
    set_capability("spu decoder", 60)
    set_subcategory(SUBCAT_INPUT_SCODEC)
    set_callbacks(Open, Close)

    add_integer(ARIBCAPTION_CFG_PREFIX "rendering-backend", 0, CFG_TEXT_RENDERING_BACKEND, CFG_LONGTEXT_RENDERING_BACKEND)
        change_integer_list(rendering_backend_values, ppsz_rendering_backend_descriptions)
    add_font(ARIBCAPTION_CFG_PREFIX "font", DEFAULT_FAMILY, CFG_TEXT_FONT, CFG_LONGTEXT_FONT)
    add_bool(ARIBCAPTION_CFG_PREFIX "replace-drcs", true, CFG_TEXT_REPLACE_DRCS, CFG_LONGTEXT_REPLACE_DRCS)
    add_bool(ARIBCAPTION_CFG_PREFIX "force-stroke-text", false, CFG_TEXT_FORCE_STROKE_TEXT, CFG_LONGTEXT_FORCE_STROKE_TEXT)
    add_bool(ARIBCAPTION_CFG_PREFIX "ignore-background", false, CFG_TEXT_IGNORE_BACKGROUND, CFG_LONGTEXT_IGNORE_BACKGROUND)
    add_bool(ARIBCAPTION_CFG_PREFIX "ignore-ruby", false, CFG_TEXT_IGNORE_RUBY, CFG_LONGTEXT_IGNORE_RUBY)
    add_bool(ARIBCAPTION_CFG_PREFIX "fadeout", false, CFG_TEXT_FADEOUT, CFG_LONGTEXT_FADEOUT)
    add_float_with_range(ARIBCAPTION_CFG_PREFIX "stroke-width", 1.5f, 0.0f, 3.0f, CFG_TEXT_STROKE_WIDTH, CFG_LONGTEXT_STROKE_WIDTH)
vlc_module_end ()

Messung
CHZG
831009291

¤ Dauer der Verarbeitung: 0.8 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.