Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/test/src/input/decoder/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 24 kB image not shown  

Quelle  input_decoder_scenarios.c   Sprache: C

 

/*****************************************************************************
 * input_decoder_scenario.c: testflight for input_decoder state machine
 *****************************************************************************
 * Copyright (C) 2022-2023 VideoLabs
 *
 * Author: Alexandre Janniaux <ajanni@videolabs.io>
 *
 * 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.
 *****************************************************************************/


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

#define MODULE_STRING "test_input_decoder_mock"

#include <vlc_common.h>
#include <vlc_messages.h>
#include <vlc_player.h>
#include <vlc_interface.h>
#include <vlc_frame.h>
#include <vlc_codec.h>
#include <vlc_filter.h>
#include <vlc_vout_display.h>

#include "input_decoder.h"

static struct scenario_data
{
    vlc_sem_t wait_stop;
    vlc_sem_t wait_ready_to_flush;
    struct vlc_video_context *decoder_vctx;
    bool skip_decoder;
    bool has_reload;
    bool stream_out_sent;
    size_t decoder_image_sent;
    size_t cc_track_idx;
} scenario_data;

static void decoder_fixed_size(decoder_t *dec, vlc_fourcc_t chroma,
        unsigned width, unsigned height)
{
    dec->fmt_out.video.i_chroma
        = dec->fmt_out.i_codec
        = chroma;
    dec->fmt_out.video.i_visible_width
        = dec->fmt_out.video.i_width
        = width;
    dec->fmt_out.video.i_visible_height
        = dec->fmt_out.video.i_height
        = height;
}

static void decoder_i420_800_600(decoder_t *dec)
    { decoder_fixed_size(dec, VLC_CODEC_I420, 800, 600); }

static void decoder_i420_800_600_update(decoder_t *dec)
{
    decoder_fixed_size(dec, VLC_CODEC_I420, 800, 600);
    int ret = decoder_UpdateVideoOutput(dec, NULL);
    assert(ret == VLC_SUCCESS);
}

static void decoder_i420_800_600_stop(decoder_t *dec)
{
    decoder_i420_800_600(dec);
    vlc_sem_post(&scenario_data.wait_stop);
}

static const char cc_block_input[] = "cc01_input";
static vlc_frame_t *create_cc_frame(vlc_tick_t ts)
{
    vlc_frame_t *f = vlc_frame_Alloc(sizeof(cc_block_input));
    assert(f != NULL);
    memcpy(f->p_buffer, cc_block_input, sizeof(cc_block_input));
    f->i_dts = f->i_pts = ts;
    f->i_length = VLC_TICK_FROM_MS(20);
    return f;
}

static void decoder_decode_check_cc_common(decoder_t *dec, picture_t *pic)
{
    vlc_tick_t date = pic->date;

    vlc_frame_t *cc = create_cc_frame(date);

    decoder_cc_desc_t desc = {
        .i_608_channels = 1,
        .i_reorder_depth = 4,
    };
    decoder_QueueCc(dec, cc, &desc);
}

static int decoder_decode_check_cc(decoder_t *dec, picture_t *pic)
{
    decoder_decode_check_cc_common(dec, pic);
    picture_Release(pic);

    vlc_sem_post(&scenario_data.wait_ready_to_flush);

    return VLC_SUCCESS;
}

static int decoder_decode_check_cc_queue_video(decoder_t *dec, picture_t *pic)
{
    decoder_decode_check_cc_common(dec, pic);
    decoder_QueueVideo(dec, pic);

    return VLC_SUCCESS;
}

struct picture_watcher_context {
    picture_context_t context;
    vlc_sem_t wait_picture;
    vlc_sem_t wait_prepare;
    vlc_atomic_rc_t rc;
};

static void context_destroy(picture_context_t *context)
{
    struct picture_watcher_context *watcher =
        container_of(context, struct picture_watcher_context, context);

    if (vlc_atomic_rc_dec(&watcher->rc))
        vlc_sem_post(&watcher->wait_picture);
}

static picture_context_t * context_copy(picture_context_t *context)
{
    struct picture_watcher_context *watcher =
        container_of(context, struct picture_watcher_context, context);
    vlc_atomic_rc_inc(&watcher->rc);
    return context;
}

static int decoder_decode_drop(decoder_t *dec, picture_t *pic)
{
    (void)dec;
    picture_Release(pic);
    return VLC_SUCCESS;
}

static int decoder_decode(decoder_t *dec, picture_t *pic)
{
    decoder_QueueVideo(dec, pic);
    return VLC_SUCCESS;
}

static int decoder_decode_check_flush_video(decoder_t *dec, picture_t *pic)
{
    if (scenario_data.skip_decoder)
    {
        picture_Release(pic);
        return VLC_SUCCESS;
    }

    /* Only update the output format the first time. */
    if (scenario_data.decoder_image_sent == 0)
    {
        int ret = decoder_UpdateVideoOutput(dec, NULL);
        assert(ret == VLC_SUCCESS);
    }

    /* Workaround: the input decoder needs multiple frame (at least 2
     * currently) to start correctly. We're not testing this, but we're
     * testing the flush here, so provide additional picture at the
     * beginning of the test to avoid issues with this.
     * They must not be linked to any picture context. */

    if (scenario_data.decoder_image_sent < 3)
    {
        msg_Info(dec, "Queueing workaround picture number %zu", scenario_data.decoder_image_sent);
        decoder_QueueVideo(dec, pic);
        scenario_data.decoder_image_sent++;
        return VLC_SUCCESS;
    }

    /* Now the input decoder should be completely started. */

    struct picture_watcher_context context1 = {
        .context.destroy = context_destroy,
        .context.copy = context_copy,
    };
    vlc_sem_init(&context1.wait_picture, 0);
    vlc_atomic_rc_init(&context1.rc);

    picture_t *second_pic = picture_Clone(pic);
    second_pic->b_force = true;
    second_pic->date = VLC_TICK_0;
    second_pic->b_progressive = true;
    second_pic->context = &context1.context;

    msg_Info(dec, "Send first frame from decoder to video output");
    decoder_QueueVideo(dec, second_pic);

    msg_Info(dec, "Wait for the display to prepare the frame");
    vlc_sem_wait(&context1.wait_prepare);

    msg_Info(dec, "Trigger decoder and vout flush");
    vlc_sem_post(&scenario_data.wait_ready_to_flush);

    /* Wait for the picture to be flushed by the vout. */
    msg_Info(dec, "Wait for the picture to be flushed from the vout");
    vlc_sem_wait(&context1.wait_picture);

    /* Reinit the picture context waiter. */

    struct picture_watcher_context context2 = {
        .context.destroy = context_destroy,
        .context.copy = context_copy,
    };
    vlc_sem_init(&context2.wait_picture, 0);
    vlc_atomic_rc_init(&context2.rc);

    picture_t *third_pic = picture_Clone(pic);
    third_pic->b_force = true;
    third_pic->date = VLC_TICK_0 + 1;
    third_pic->b_progressive = true;
    third_pic->context = &context2.context;

    msg_Info(dec, "Re-queue the picture to the vout before decoder::pf_flush is called");
    decoder_QueueVideo(dec, third_pic);

    /* Wait for the picture to be flushed by the input decoder. */
    msg_Info(dec, "Ensure the picture has been discarded by the input decoder");
    vlc_sem_wait(&context2.wait_picture);

    /* Ok, since the picture has been released, we should have decode succeed
     * and we can now check that flush is called. */


    picture_Release(pic);

    msg_Info(dec, "Nothing to do from pf_decode, let pf_flush be called by the input decoder");
    scenario_data.skip_decoder = true;
    return VLC_SUCCESS;
}

static void decoder_flush_signal(decoder_t *dec)
{
    (void)dec;
    vlc_sem_post(&scenario_data.wait_stop);
}

static void* SendUpdateOutput(void *opaque)
{
    decoder_t *dec = opaque;
    decoder_UpdateVideoOutput(dec, NULL);
    return NULL;
}

static void decoder_destroy_trigger_update(decoder_t *dec)
{
    /* Use another thread to ensure we don't double-lock, but
     * at most deadlock instead. */

    vlc_thread_t thread;
    int ret = vlc_clone(&thread, SendUpdateOutput, dec);
    assert(ret == VLC_SUCCESS);
    vlc_join(thread, NULL);
}

static int decoder_decode_trigger_reload(decoder_t *dec, picture_t *pic)
{
    (void)dec;
    picture_Release(pic);

    if (!scenario_data.has_reload)
    {
        vlc_sem_post(&scenario_data.wait_stop);
        return VLCDEC_RELOAD;
    }
    return VLCDEC_SUCCESS;
}

static void display_prepare_signal(vout_display_t *vd, picture_t *pic)
{
    (void)vd;

    if (pic->context == NULL)
        return;

    msg_Info(vd, "Signal that the frame has been prepared from display");
    struct picture_watcher_context *watcher =
        container_of(pic->context, struct picture_watcher_context, context);
    vlc_sem_post(&watcher->wait_prepare);
}

static void PlayerOnTrackListChanged(vlc_player_t *player,
        enum vlc_player_list_action action,
        const struct vlc_player_track *track,
        void *data)
{
    (void)data;

    if (action != VLC_PLAYER_LIST_ADDED ||
        track->fmt.i_cat != SPU_ES)
        return;

    vlc_player_SelectTrack(player, track, VLC_PLAYER_SELECT_EXCLUSIVE);
}

static void interface_setup_select_cc(intf_thread_t *intf)
{
    vlc_player_t *player = (vlc_player_t *)intf->p_sys;

    static const struct vlc_player_cbs player_cbs =
    {
        .on_track_list_changed = PlayerOnTrackListChanged,
    };

    vlc_player_Lock(player);
    vlc_player_listener_id *listener_id =
        vlc_player_AddListener(player, &player_cbs, NULL);
    vlc_player_Unlock(player);

    vlc_sem_wait(&scenario_data.wait_ready_to_flush);
    vlc_player_Lock(player);
    vlc_player_SetPosition(player, 0);
    vlc_player_Unlock(player);

    vlc_sem_wait(&scenario_data.wait_ready_to_flush);
    vlc_player_Lock(player);
    vlc_player_RemoveListener(player, listener_id);
    vlc_player_Unlock(player);
}

static void interface_setup_check_flush(intf_thread_t *intf)
{
    vlc_player_t *player = (vlc_player_t *)intf->p_sys;
    vlc_sem_wait(&scenario_data.wait_ready_to_flush);

    vlc_player_Lock(player);
    vlc_player_SetPosition(player, 0);
    vlc_player_Unlock(player);
}

static int sout_filter_send(sout_stream_t *stream, void *id, block_t *block)
{
    (void)stream; (void)id;
    block_Release(block);
    scenario_data.stream_out_sent = true;
    vlc_sem_post(&scenario_data.wait_ready_to_flush);
    return VLC_SUCCESS;
}

static int sout_filter_wait_cc(sout_stream_t *stream, void *id, block_t *block)
{
    (void)stream; (void)id;
    block_Release(block);
    vlc_fourcc_t *codec = id;
    if (*codec == VLC_CODEC_CEA608)
    {
        scenario_data.stream_out_sent = true;
        vlc_sem_post(&scenario_data.wait_ready_to_flush);
    }
    return VLC_SUCCESS;
}

static void sout_filter_flush(sout_stream_t *stream, void *id)
{
    (void)stream; (void)id;
    assert(scenario_data.stream_out_sent);
    vlc_sem_post(&scenario_data.wait_stop);
}

static vlc_frame_t *packetizer_getcc(decoder_t *dec, decoder_cc_desc_t *cc_desc)
{
    (void)dec;

    cc_desc->i_608_channels = 1;
    cc_desc->i_708_channels = 0;
    cc_desc->i_reorder_depth = 4;

    return create_cc_frame(VLC_TICK_0);
}

static vlc_frame_t *packetizer_getcc_cea608_max(decoder_t *dec, decoder_cc_desc_t *cc_desc)
{
    (void)dec;

    cc_desc->i_608_channels = 0xF;
    cc_desc->i_708_channels = 0;
    cc_desc->i_reorder_depth = 4;

    return create_cc_frame(VLC_TICK_0);
}

static vlc_frame_t *packetizer_getcc_cea708_max(decoder_t *dec, decoder_cc_desc_t *cc_desc)
{
    (void)dec;

    cc_desc->i_608_channels = 0;
    cc_desc->i_708_channels = UINT64_MAX;
    cc_desc->i_reorder_depth = 4;

    return create_cc_frame(VLC_TICK_0);
}

static void on_track_list_changed_check_cea608(
        enum vlc_player_list_action action,
        const struct vlc_player_track *track)
{
    if (action != VLC_PLAYER_LIST_ADDED)
        return;

    if (strcmp(vlc_es_id_GetStrId(track->es_id), "video/0/cc/spu/1") != 0)
        return;

    assert(track->fmt.i_codec == VLC_CODEC_CEA608);
    vlc_sem_post(&scenario_data.wait_stop);
}

static void on_track_list_changed_check_count(
        enum vlc_player_list_action action,
        const struct vlc_player_track *track,
        vlc_fourcc_t codec, unsigned track_max)
{
    if (action != VLC_PLAYER_LIST_ADDED)
        return;

    char buffer[] = "video/0/cc/spu/xx";
    assert(scenario_data.cc_track_idx < 100);
    sprintf(buffer, "video/0/cc/spu/%zu", scenario_data.cc_track_idx);

    if (strcmp(vlc_es_id_GetStrId(track->es_id), buffer) != 0)
        return;

    assert(track->fmt.i_codec == codec);

    scenario_data.cc_track_idx++;

    if (scenario_data.cc_track_idx == track_max)
        vlc_sem_post(&scenario_data.wait_stop);
}

static void on_track_list_changed_check_cea608_count(
        enum vlc_player_list_action action,
        const struct vlc_player_track *track)
{
    on_track_list_changed_check_count(action, track, VLC_CODEC_CEA608, 4);
}

static void on_track_list_changed_check_cea708_count(
        enum vlc_player_list_action action,
        const struct vlc_player_track *track)
{
    on_track_list_changed_check_count(action, track, VLC_CODEC_CEA708, 64);
}

static void player_setup_select_cc(vlc_player_t *player)
{
    vlc_player_SelectTracksByStringIds(player, SPU_ES, "video/0/cc/spu/1");
}

static void cc_decoder_setup_01(decoder_t *dec)
{
    assert(dec->fmt_in->i_codec == VLC_CODEC_CEA608);
    assert(dec->fmt_in->subs.cc.i_channel == 0);
    assert(dec->fmt_in->subs.cc.i_reorder_depth == 4);

    dec->fmt_out.i_codec = VLC_CODEC_TEXT;
}

static int cc_decoder_decode_common(decoder_t *dec, vlc_frame_t *in,
                                    const char *text)
{
    assert(memcmp(in->p_buffer, cc_block_input, sizeof(cc_block_input)) == 0);

    subpicture_t *subpic = decoder_NewSubpicture(dec, NULL);
    assert(subpic != NULL);

    subpic->i_start = in->i_pts;
    subpic->i_stop = subpic->i_start + in->i_length;

    subpicture_region_t *p_region = subpicture_region_NewText();;
    assert(p_region != NULL);
    vlc_spu_regions_push( &subpic->regions, p_region );

    p_region->b_absolute = true; p_region->b_in_window = false;
    p_region->i_x = 0;
    p_region->i_y = 0;
    p_region->p_text = text_segment_New(text);
    assert(p_region->p_text != NULL);

    decoder_QueueSub(dec, subpic);

    return VLC_SUCCESS;
}

static const char cc_block_decoded[] = "cc01_dec";
static int cc_decoder_decode(decoder_t *dec, vlc_frame_t *in)
{
    return cc_decoder_decode_common(dec, in, cc_block_decoded);
}

static void display_prepare_noop(vout_display_t *vd, picture_t *pic)
{
    (void)vd; (void) pic;
}

static void cc_text_renderer_render(filter_t *filter, const subpicture_region_t *region_in)
{
    (void) filter;
    assert(strcmp(region_in->p_text->psz_text, cc_block_decoded) == 0);
    vlc_sem_post(&scenario_data.wait_stop);
}

static vlc_frame_t *packetizer_getcc_cea708_1064(decoder_t *dec, decoder_cc_desc_t *cc_desc)
{
    (void)dec;

    cc_desc->i_608_channels = 0;
    cc_desc->i_708_channels = (1ULL << 9) | (1ULL << 63);
    cc_desc->i_reorder_depth = 4;

    return create_cc_frame(VLC_TICK_0);
}

static void cc_decoder_setup_708_1064(decoder_t *dec)
{
    assert(dec->fmt_in->i_codec == VLC_CODEC_CEA708);
    assert(dec->fmt_in->subs.cc.i_channel == 9
        || dec->fmt_in->subs.cc.i_channel == 63);
    assert(dec->fmt_in->subs.cc.i_reorder_depth == 4);

    dec->fmt_out.i_codec = VLC_CODEC_TEXT;
}

static int cc_decoder_decode_channel(decoder_t *dec, vlc_frame_t *in)
{
    char buf[] = "ccxxx_dec";
    assert(dec->fmt_in->subs.cc.i_channel < 64);
    sprintf(buf, "cc%02u_dec", dec->fmt_in->subs.cc.i_channel + 1);
    return cc_decoder_decode_common(dec, in, buf);
}

static void cc_text_renderer_render_708_1064(filter_t *filter,
                                             const subpicture_region_t *region_in)
{
    (void) filter;
    /* Make sure each tracks are rendered once */
    static unsigned chans[] = { 10, 64 };
    static bool rendered[] = { false, false };

    char buf[] = "ccxx_dec";
    for (size_t i = 0; i < ARRAY_SIZE(chans); ++i)
    {
        unsigned chan = chans[i];
        sprintf(buf, "cc%u_dec", chan);

        if (strcmp(region_in->p_text->psz_text, buf) == 0)
        {
            if (rendered[i])
                return;
            rendered[i] = true;
        }
    }

    for (size_t i = 0; i < ARRAY_SIZE(chans); ++i)
    {
        if (!rendered[i])
            return;
    }

    vlc_sem_post(&scenario_data.wait_stop);
}

static void player_setup_select_ccdec_708_1064(vlc_player_t *player)
{
    vlc_player_SelectTracksByStringIds(player, SPU_ES,
        "video/0/cc/spu/10,video/0/cc/spu/64");
}

static vlc_frame_t *packetizer_getcc_cea608_02(decoder_t *dec, decoder_cc_desc_t *cc_desc)
{
    (void)dec;

    cc_desc->i_608_channels = 1 << 1;
    cc_desc->i_708_channels = 0;
    cc_desc->i_reorder_depth = 4;

    return create_cc_frame(VLC_TICK_0);
}

static void cc_decoder_setup_608(decoder_t *dec)
{
    assert(dec->fmt_in->i_codec == VLC_CODEC_CEA608);

    dec->fmt_out.i_codec = VLC_CODEC_TEXT;
}

static void cc_text_renderer_render_608_02(filter_t *filter,
                                           const subpicture_region_t *region_in)
{
    (void) filter;
    assert(strcmp(region_in->p_text->psz_text, "cc02_dec") == 0);
    vlc_sem_post(&scenario_data.wait_stop);
}

static const vlc_fourcc_t subpicture_chromas[] = {
    VLC_CODEC_RGBA, 0
};

#define source_800_600 "mock://video_track_count=1;length=100000000000;video_width=800;video_height=600"
struct input_decoder_scenario input_decoder_scenarios[] =
{{
    .name = "decoder is flushed",
    .source = source_800_600,
    .decoder_setup = decoder_i420_800_600,
    .decoder_flush = decoder_flush_signal,
    .decoder_decode = decoder_decode_check_cc,
    .interface_setup = interface_setup_select_cc,
},
{
    .name = "video output is flushed",
    .source = source_800_600,
    .decoder_setup = decoder_i420_800_600,
    .decoder_decode = decoder_decode_check_flush_video,
    .decoder_flush = decoder_flush_signal,
    .display_prepare = display_prepare_signal,
    .interface_setup = interface_setup_check_flush,
},
{
    /* Check that stream output is also flushed:
      - the test cannot work if the stream_out filter is not added
      - sout_filter_send(), signal the interface that it can flush
      - the interface change player position to trigger a flush
      - the flush is signaled to the stream_out filter
      - the stream_out filter signal the end of the test */

    .name = "stream output is also flushed",
    .source = source_800_600,
    .item_option = ":sout=#" MODULE_STRING,
    .sout_filter_send = sout_filter_send,
    .sout_filter_flush = sout_filter_flush,
    .interface_setup = interface_setup_check_flush,
},
{
    /* Check that releasing a decoder while it is triggering an update
     * of the video output format doesn't lead to a crash. Non-regression
     * test from issue #27532. */

    .name = "releasing a decoder while updating the vout doesn't lead to a crash",
    .source = source_800_600,
    .decoder_setup = decoder_i420_800_600_stop,
    .decoder_decode = decoder_decode_drop,
    .decoder_destroy = decoder_destroy_trigger_update,
},
{
    /* Check that reloading a decoder while it is triggering an update
     * of the video output format doesn't lead to a crash. Non-regression
     * test from issue #27532. */

    .name = "reloading a decoder while updating the vout doesn't lead to a crash",
    .source = source_800_600,
    .decoder_setup = decoder_i420_800_600,
    .decoder_decode = decoder_decode_trigger_reload,
    .decoder_destroy = decoder_destroy_trigger_update,
},
{
    .name = "CC frames are sent to the sout",
    .source = source_800_600,
    .item_option = ":sout=#" MODULE_STRING,
    .packetizer_getcc = packetizer_getcc,
    .sout_filter_send = sout_filter_wait_cc,
    .sout_filter_flush = sout_filter_flush,
    .interface_setup = interface_setup_check_flush,
},
{
    .name = "CC tracks are added",
    .source = source_800_600 ";video_packetized=false",
    .packetizer_getcc = packetizer_getcc,
    .decoder_setup = decoder_i420_800_600,
    .decoder_decode = decoder_decode_drop,
    .on_track_list_changed = on_track_list_changed_check_cea608,
},
{
    /* Check that the CC coming from the packetizer is decoded and rendered:
     * - A valid vout is needed (to blend subtitles into)
     * - Text vlc_frames are passed from the packetizer to the cc decoder to
     *   the text renderer and are checked at each step
     *  - A text renderer is used, this is the last place a text can be checked
     *  before it is rendered (and converted to RGB/YUV)
     */

    .name = "CC coming from the packetizer is decoded and rendered",
    .source = source_800_600 ";video_packetized=false",
    .subpicture_chromas = subpicture_chromas,
    .packetizer_getcc = packetizer_getcc,
    .decoder_setup = decoder_i420_800_600_update,
    .decoder_decode = decoder_decode,
    .cc_decoder_setup = cc_decoder_setup_01,
    .cc_decoder_decode = cc_decoder_decode,
    .display_prepare = display_prepare_noop,
    .text_renderer_render = cc_text_renderer_render,
    .player_setup_before_start = player_setup_select_cc,
},
{
    /* Check that the CC coming from the video decoder is decoded and rendered,
     * cf. the previous scenario for more details. */

    .name = "CC coming from the video decoder is decoded and rendered",
    .source = source_800_600,
    .subpicture_chromas = subpicture_chromas,
    .decoder_setup = decoder_i420_800_600_update,
    .decoder_decode = decoder_decode_check_cc_queue_video,
    .cc_decoder_setup = cc_decoder_setup_01,
    .cc_decoder_decode = cc_decoder_decode,
    .display_prepare = display_prepare_noop,
    .text_renderer_render = cc_text_renderer_render,
    .player_setup_before_start = player_setup_select_cc,
},
{
    .name = "we can create 4 cea608 tracks",
    .source = source_800_600 ";video_packetized=false",
    .packetizer_getcc = packetizer_getcc_cea608_max,
    .decoder_setup = decoder_i420_800_600,
    .decoder_decode = decoder_decode_drop,
    .on_track_list_changed = on_track_list_changed_check_cea608_count,
},
{
    .name = "we can create 64 cea708 tracks",
    .source = source_800_600 ";video_packetized=false",
    .item_option = ":captions=708",
    .packetizer_getcc = packetizer_getcc_cea708_max,
    .decoder_setup = decoder_i420_800_600,
    .decoder_decode = decoder_decode_drop,
    .on_track_list_changed = on_track_list_changed_check_cea708_count,
},
{
    .name = "cea708 10 and 63 tracks can be rendered simultaneously",
    .source = source_800_600 ";video_packetized=false",
    .item_option = ":captions=708",
    .packetizer_getcc = packetizer_getcc_cea708_1064,
    .decoder_setup = decoder_i420_800_600_update,
    .decoder_decode = decoder_decode,
    .cc_decoder_setup = cc_decoder_setup_708_1064,
    .cc_decoder_decode = cc_decoder_decode_channel,
    .display_prepare = display_prepare_noop,
    .text_renderer_render = cc_text_renderer_render_708_1064,
    .player_setup_before_start = player_setup_select_ccdec_708_1064,
},
{
    /* The "--sub-track" option use channel starting with 0 whereas CC tracks
     * indexes start with 1. Ensure channel 1 match with the CC track 2. */

    .name = "A cea608 track can be selected via command line",
    .source = source_800_600 ";video_packetized=false",
    .item_option = ":sub-track=1",
    .packetizer_getcc = packetizer_getcc_cea608_02,
    .decoder_setup = decoder_i420_800_600_update,
    .decoder_decode = decoder_decode,
    .cc_decoder_setup = cc_decoder_setup_608,
    .cc_decoder_decode = cc_decoder_decode_channel,
    .display_prepare = display_prepare_noop,
    .text_renderer_render = cc_text_renderer_render_608_02,
},
};

size_t input_decoder_scenarios_count = ARRAY_SIZE(input_decoder_scenarios);

void input_decoder_scenario_init(void)
{
    scenario_data.decoder_vctx = NULL;
    scenario_data.skip_decoder = false;
    scenario_data.has_reload = false;
    scenario_data.stream_out_sent = false;
    scenario_data.decoder_image_sent = 0;
    scenario_data.cc_track_idx = 1;
    vlc_sem_init(&scenario_data.wait_stop, 0);
    vlc_sem_init(&scenario_data.wait_ready_to_flush, 0);
}

void input_decoder_scenario_wait(intf_thread_t *intf, struct input_decoder_scenario *scenario)
{
    if (scenario->interface_setup)
        scenario->interface_setup(intf);

    vlc_sem_wait(&scenario_data.wait_stop);
}

void input_decoder_scenario_check(struct input_decoder_scenario *scenario)
{
    (void)scenario;
}

Messung
CHZG
81999993

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© 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.