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

Quelle  pcr_sync.c   Sprache: C

 

/*****************************************************************************
 * pcr_sync.c: PCR_Sync unit tests
 *****************************************************************************
 * Copyright (C) 2022 VLC authors and VideoLAN
 *
 * 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

#undef NDEBUG

#include <assert.h>

#include <vlc_common.h>

#include <vlc_arrays.h>
#include <vlc_frame.h>
#include <vlc_tick.h>
#include <vlc_vector.h>

#include "../modules/stream_out/transcode/pcr_sync.h"
#include "../modules/stream_out/transcode/pcr_helper.h"

typedef struct
{
    enum test_element_type
    {
        TEST_ELEM_TYPE_FRAME,
        TEST_ELEM_TYPE_PCR,
    } type;
    union
    {
        struct
        {
            vlc_tick_t dts;
            unsigned int track_number;
            bool discontinuity;
        };
        vlc_tick_t pcr;
    };
} test_element;

typedef struct
{
    size_t input_count;
    const test_element *input;
    size_t output_count;
    const test_element *output;

    size_t track_count;
} test_context;

#undef FRAME
#define FRAME(id, val)                                                                             \
    ((test_element){.type = TEST_ELEM_TYPE_FRAME, .dts = val, .track_number = id})
#undef DISCONTINUITY
#define DISCONTINUITY(id, val)                                                                     \
    ((test_element){                                                                               \
        .type = TEST_ELEM_TYPE_FRAME, .dts = val, .track_number = id, .discontinuity = true})
#undef PCR
#define PCR(val) ((test_element){.type = TEST_ELEM_TYPE_PCR, .pcr = val})

static void test_Sync(vlc_pcr_sync_t *sync, const test_context context)
{
    // Add all tracks to the synchronizer and keep their id.
    struct VLC_VECTOR(unsigned int) track_ids_vec = VLC_VECTOR_INITIALIZER;
    bool alloc_success = vlc_vector_reserve(&track_ids_vec, context.track_count);
    assert(alloc_success);
    for (size_t i = 0; i < context.track_count; ++i)
    {
        unsigned int id;
        alloc_success = vlc_pcr_sync_NewESID(sync, &id) == VLC_SUCCESS;
        assert(alloc_success);
        alloc_success = vlc_vector_push(&track_ids_vec, id);
        assert(alloc_success);
    }

    // Queue PCR events in the synchronizer and signal all the frames.
    for (size_t i = 0; i < context.input_count; ++i)
    {
        const test_element *elem = &context.input[i];
        if (elem->type == TEST_ELEM_TYPE_PCR)
        {
            assert(vlc_pcr_sync_SignalPCR(sync, elem->pcr) == VLC_SUCCESS);
        }
        else
        {
            const vlc_frame_t mock = {.i_dts = elem->dts,
                                      .i_flags =
                                          elem->discontinuity ? VLC_FRAME_FLAG_DISCONTINUITY : 0};
            vlc_pcr_sync_SignalFrame(sync, track_ids_vec.data[elem->track_number], &mock);
        }
    }

    // Signal all the frame leaving and check that PCR events are properly aligned.
    vlc_tick_t last_pcr;
    for (size_t i = 0; i < context.output_count; ++i)
    {
        const test_element *elem = &context.output[i];
        if (elem->type == TEST_ELEM_TYPE_FRAME)
        {
            const vlc_frame_t mock = {.i_dts = elem->dts};
            last_pcr =
                vlc_pcr_sync_SignalFrameOutput(sync, track_ids_vec.data[elem->track_number], &mock);
        }
        else
        {
            assert(elem->pcr == last_pcr);
        }
    }

    vlc_vector_destroy(&track_ids_vec);
}

static void test_Run(void (*test)(vlc_pcr_sync_t *))
{
    vlc_pcr_sync_t *sync = vlc_pcr_sync_New();
    assert(sync != NULL);

    test(sync);

    vlc_pcr_sync_Delete(sync);
}

typedef enum
{
    VIDEO_TRACK,
    AUDIO_TRACK,
    SUB_TRACK,

    MAX_TRACKS
} track_ids;

static void test_Simple(vlc_pcr_sync_t *sync)
{
    const test_element input[] = {FRAME(VIDEO_TRACK, 1), PCR(10), FRAME(VIDEO_TRACK, 20)};
    test_Sync(sync, (test_context){
                        .input = input,
                        .input_count = ARRAY_SIZE(input),
                        .output = input,
                        .output_count = ARRAY_SIZE(input),
                        .track_count = 1,
                    });
}

static void test_MultipleTracks(vlc_pcr_sync_t *sync)
{
    const test_element input[] = {
        FRAME(VIDEO_TRACK, 1),  FRAME(AUDIO_TRACK, 1),  PCR(10),
        FRAME(VIDEO_TRACK, 15), FRAME(AUDIO_TRACK, 15), FRAME(SUB_TRACK, 20)};
    test_Sync(sync, (test_context){.input = input,
                                   .input_count = ARRAY_SIZE(input),
                                   .output = input,
                                   .output_count = ARRAY_SIZE(input),
                                   .track_count = MAX_TRACKS});
}

static void test_MultipleTracksButOnlyOneSends(vlc_pcr_sync_t *sync)
{
    const test_element input[] = {FRAME(VIDEO_TRACK, 1), PCR(10), FRAME(VIDEO_TRACK, 15)};
    test_Sync(sync, (test_context){.input = input,
                                   .input_count = ARRAY_SIZE(input),
                                   .output = input,
                                   .output_count = ARRAY_SIZE(input),
                                   .track_count = MAX_TRACKS});
}

static void test_InvalidDTS(vlc_pcr_sync_t *sync)
{
    const test_element input[] = {FRAME(VIDEO_TRACK, 1), FRAME(VIDEO_TRACK, VLC_TICK_INVALID),
                                  FRAME(VIDEO_TRACK, VLC_TICK_INVALID), PCR(5),
                                  FRAME(VIDEO_TRACK, 10)};
    const test_element output[] = {FRAME(VIDEO_TRACK, 1), PCR(5), FRAME(VIDEO_TRACK, 10)};
    test_Sync(sync, (test_context){.input = input,
                                   .input_count = ARRAY_SIZE(input),
                                   .output = output,
                                   .output_count = ARRAY_SIZE(output),
                                   .track_count = MAX_TRACKS});
}

static void test_LowDiscontinuity(vlc_pcr_sync_t *sync)
{
    const test_element input[] = {FRAME(VIDEO_TRACK, 200), DISCONTINUITY(VIDEO_TRACK, 1), PCR(5),
                                  FRAME(VIDEO_TRACK, 10)};
    test_Sync(sync, (test_context){.input = input,
                                   .input_count = ARRAY_SIZE(input),
                                   .output = input,
                                   .output_count = ARRAY_SIZE(input),
                                   .track_count = MAX_TRACKS});
}

static void test_HighDiscontinuity(vlc_pcr_sync_t *sync)
{
    const test_element input[] = {FRAME(VIDEO_TRACK, 1), DISCONTINUITY(VIDEO_TRACK, 200), PCR(210),
                                  FRAME(VIDEO_TRACK, 220)};
    test_Sync(sync, (test_context){.input = input,
                                   .input_count = ARRAY_SIZE(input),
                                   .output = input,
                                   .output_count = ARRAY_SIZE(input),
                                   .track_count = MAX_TRACKS});
}
static void test_TwoDiscontinuities(vlc_pcr_sync_t *sync)
{
    const test_element input[] = {FRAME(VIDEO_TRACK, 200), DISCONTINUITY(VIDEO_TRACK, 1),
                                  DISCONTINUITY(VIDEO_TRACK, 210), PCR(210),
                                  FRAME(VIDEO_TRACK, 220)};
    test_Sync(sync, (test_context){.input = input,
                                   .input_count = ARRAY_SIZE(input),
                                   .output = input,
                                   .output_count = ARRAY_SIZE(input),
                                   .track_count = MAX_TRACKS});
}

static void test_FastForwardAtBeginning(vlc_pcr_sync_t *sync)
{
    assert(vlc_pcr_sync_SignalPCR(sync, 1u) == VLC_PCR_SYNC_FORWARD_PCR);
}

static void test_FastForwardMiddleOfStream(vlc_pcr_sync_t *sync)
{
    // Launch a basic test scenario to mimic an usual stream flow.
    test_MultipleTracks(sync);

    // Ensure PCRs sent in the middle of the stream without any data in between
    // can be immediately forwarded.
    assert(vlc_pcr_sync_SignalPCR(sync, 30u) == VLC_PCR_SYNC_FORWARD_PCR);
    assert(vlc_pcr_sync_SignalPCR(sync, 40u) == VLC_PCR_SYNC_FORWARD_PCR);
    assert(vlc_pcr_sync_SignalPCR(sync, 50u) == VLC_PCR_SYNC_FORWARD_PCR);
}

static void test_OneTrackWithDelay(vlc_pcr_sync_t *sync)
{
    unsigned id;
    assert(vlc_pcr_sync_NewESID(sync, &id) == VLC_SUCCESS);

    const vlc_frame_t frame1 = {.i_dts = 1u};
    vlc_pcr_sync_SignalFrame(sync, id, &frame1);
    assert(vlc_pcr_sync_SignalPCR(sync, 10u) == VLC_SUCCESS);

    const vlc_frame_t frame2 = {.i_dts = 11u};
    vlc_pcr_sync_SignalFrame(sync, id, &frame2);
    assert(vlc_pcr_sync_SignalFrameOutput(sync, id, &frame1) == 10u);
    assert(vlc_pcr_sync_SignalPCR(sync, 20u) == VLC_SUCCESS);
}

static void test_SubsequentPCR(vlc_pcr_sync_t *sync)
{
    unsigned id;
    assert(vlc_pcr_sync_NewESID(sync, &id) == VLC_SUCCESS);

    const vlc_frame_t frame = {.i_dts = 1u};
    vlc_pcr_sync_SignalFrame(sync, id, &frame);
    assert(vlc_pcr_sync_SignalPCR(sync, 10u) == VLC_SUCCESS);
    assert(vlc_pcr_sync_SignalPCR(sync, 20u) == VLC_SUCCESS);
    assert(vlc_pcr_sync_SignalPCR(sync, 30u) == VLC_SUCCESS);

    // Output the same frame until no PCR is returned.
    assert(vlc_pcr_sync_SignalFrameOutput(sync, id, &frame) == 10u);
    assert(vlc_pcr_sync_SignalFrameOutput(sync, id, &frame) == 20u);
    assert(vlc_pcr_sync_SignalFrameOutput(sync, id, &frame) == 30u);
    assert(vlc_pcr_sync_SignalFrameOutput(sync, id, &frame) == VLC_TICK_INVALID);
}

static void test_LateTrack(vlc_pcr_sync_t *sync)
{
    unsigned audio, video;
    assert(vlc_pcr_sync_NewESID(sync, &audio) == VLC_SUCCESS);
    assert(vlc_pcr_sync_NewESID(sync, &video) == VLC_SUCCESS);

    for (vlc_tick_t dts = 0; dts < 100; ++dts )
    {
        if (dts % 2 == 0)
        {
            const vlc_frame_t v_frame = {.i_dts = dts + 1};
            vlc_pcr_sync_SignalFrame(sync, video, &v_frame);
        }
        const vlc_frame_t a_frame = {.i_dts = dts + 1};
        vlc_pcr_sync_SignalFrame(sync, audio, &a_frame);
        if (dts % 10 == 0)
            assert(vlc_pcr_sync_SignalPCR(sync, dts + 1) == VLC_SUCCESS);
    }

    // Unqueue the audio frames first to mimic fast decoding
    for (vlc_tick_t dts = 0; dts < 100; ++dts)
    {
        const vlc_frame_t a_frame = {.i_dts = dts + 1};
        const vlc_tick_t pcr =
            vlc_pcr_sync_SignalFrameOutput(sync, audio, &a_frame);
        assert(pcr == VLC_TICK_INVALID);
    }

    for (vlc_tick_t dts = 0; dts < 100; ++dts )
    {
        if (dts % 2 != 0)
            continue;

        const vlc_frame_t v_frame = {.i_dts = dts + 1};
        const vlc_tick_t pcr =
            vlc_pcr_sync_SignalFrameOutput(sync, video, &v_frame);
        if (dts % 10 == 0)
            assert(pcr == dts + 1);
        else
            assert(pcr == VLC_TICK_INVALID);
    }

    vlc_pcr_sync_DelESID(sync, audio);
    vlc_pcr_sync_DelESID(sync, video);
}

static void test_PCRHelper(void (*test)(vlc_pcr_sync_t *,
                                        transcode_track_pcr_helper_t *))
{
    vlc_pcr_sync_t *sync = vlc_pcr_sync_New();
    assert(sync != NULL);

    transcode_track_pcr_helper_t *track_helper =
        transcode_track_pcr_helper_New(sync, VLC_TICK_FROM_MS(200));
    assert(track_helper != NULL);

    test(sync, track_helper);

    transcode_track_pcr_helper_Delete(track_helper);
    vlc_pcr_sync_Delete(sync);
}

static void test_PCRHelperSimple(vlc_pcr_sync_t *sync,
                                 transcode_track_pcr_helper_t *pcr_helper)
{
    const vlc_frame_t in_frame = {.i_dts = 1u, .i_length = 10u};
    vlc_tick_t dropped_frame_pcr;
    transcode_track_pcr_helper_SignalEnteringFrame(
        pcr_helper, &in_frame, &dropped_frame_pcr);
    assert(dropped_frame_pcr == VLC_TICK_INVALID);

    assert(vlc_pcr_sync_SignalPCR(sync, 10u) == VLC_SUCCESS);

    const vlc_frame_t out_frame = {.i_dts = 2000u, .i_length = 10u};
    vlc_tick_t pcr;
    const int status = transcode_track_pcr_helper_SignalLeavingFrame(
        pcr_helper, &out_frame, &pcr);
    assert(status == VLC_SUCCESS);
    assert(pcr == 10u);
}

static void
test_PCRHelperMultipleTracks(vlc_pcr_sync_t *sync,
                             transcode_track_pcr_helper_t *video)
{
    transcode_track_pcr_helper_t *audio =
        transcode_track_pcr_helper_New(sync, VLC_TICK_FROM_MS(200));
    vlc_tick_t dropped_frame_pcr;

    for (vlc_tick_t dts = 0; dts < 200; dts += 10)
    {
        if (dts % 20 == 0)
        {
            const vlc_frame_t v_frame = {.i_dts = dts + 1, .i_length = 20u};
            transcode_track_pcr_helper_SignalEnteringFrame(
                video, &v_frame, &dropped_frame_pcr);
            assert(dropped_frame_pcr == VLC_TICK_INVALID);
        }
        const vlc_frame_t a_frame = {.i_dts = dts + 1, .i_length = 10u};
        transcode_track_pcr_helper_SignalEnteringFrame(
            audio, &a_frame, &dropped_frame_pcr);
        assert(dropped_frame_pcr == VLC_TICK_INVALID);
        if (dts == 100)
            assert(vlc_pcr_sync_SignalPCR(sync, 101) == VLC_SUCCESS);
    }

    vlc_tick_t pcr;
    // Unqueue the audio frames first to mimic fast decoding
    for (vlc_tick_t dts = 0; dts < 200; dts += 10)
    {
        const vlc_frame_t a_frame = {.i_dts = dts + 1, .i_length = 10u};
        const int status = transcode_track_pcr_helper_SignalLeavingFrame(
            audio, &a_frame, &pcr);
        assert(status == VLC_SUCCESS);
        assert(pcr == VLC_TICK_INVALID);
    }

    for (vlc_tick_t dts = 0; dts < 100; dts += 10)
    {
        const vlc_frame_t v_frame = {.i_dts = (dts * 2) - 10 + 1,
                                     .i_length = 20u};
        const int status = transcode_track_pcr_helper_SignalLeavingFrame(
            video, &v_frame, &pcr);
        assert(status == VLC_SUCCESS);
        if (dts != 50)
            assert(pcr == VLC_TICK_INVALID);
        else
            assert(pcr == 91);
    }

    transcode_track_pcr_helper_Delete(audio);
}

static void
test_PCRHelperSplitFrameOutput(vlc_pcr_sync_t *sync,
                               transcode_track_pcr_helper_t *pcr_helper)
{
    const vlc_frame_t in_frame = {.i_dts = 1u, .i_length = 10u};
    vlc_tick_t dropped_frame_pcr;
    transcode_track_pcr_helper_SignalEnteringFrame(
        pcr_helper, &in_frame, &dropped_frame_pcr);
    assert(dropped_frame_pcr == VLC_TICK_INVALID);

    assert(vlc_pcr_sync_SignalPCR(sync, 10u) == VLC_SUCCESS);

    const vlc_frame_t out_frame1 = {.i_dts = 2000u, .i_length = 5u};
    const vlc_frame_t out_frame2 = {.i_dts = 2005u, .i_length = 5u};
    vlc_tick_t pcr;
    int status = transcode_track_pcr_helper_SignalLeavingFrame(
        pcr_helper, &out_frame1, &pcr);
    assert(status == VLC_SUCCESS);
    assert(pcr == VLC_TICK_INVALID);

    status = transcode_track_pcr_helper_SignalLeavingFrame(
        pcr_helper, &out_frame2, &pcr);
    assert(status == VLC_SUCCESS);
    assert(pcr == 10u);
}

static void
test_PCRHelperSplitFrameInput(vlc_pcr_sync_t *sync,
                              transcode_track_pcr_helper_t *pcr_helper)
{
    const vlc_frame_t in_frame1 = {.i_dts = 2000u, .i_length = 10u};
    const vlc_frame_t in_frame2 = {.i_dts = 2010u, .i_length = 10u};
    vlc_tick_t dropped_frame_pcr;
    transcode_track_pcr_helper_SignalEnteringFrame(
        pcr_helper, &in_frame1, &dropped_frame_pcr);
    assert(dropped_frame_pcr == VLC_TICK_INVALID);
    transcode_track_pcr_helper_SignalEnteringFrame(
        pcr_helper, &in_frame2, &dropped_frame_pcr);
    assert(dropped_frame_pcr == VLC_TICK_INVALID);

    assert(vlc_pcr_sync_SignalPCR(sync, 2010u) == VLC_SUCCESS);

    const vlc_frame_t in_frame3 = {.i_dts = 2020u, .i_length = 10u};
    transcode_track_pcr_helper_SignalEnteringFrame(
        pcr_helper, &in_frame3, &dropped_frame_pcr);
    assert(dropped_frame_pcr == VLC_TICK_INVALID);

    const vlc_frame_t out_frame1 = {.i_dts = 1u, .i_length = 20u};
    vlc_tick_t pcr;
    int status = transcode_track_pcr_helper_SignalLeavingFrame(
        pcr_helper, &out_frame1, &pcr);
    assert(status == VLC_SUCCESS);
    // PCR should be re-synthetized.
    assert(pcr == 1u);
    const vlc_frame_t out_frame2 = {.i_dts = 21u, .i_length = 10u};
    status = transcode_track_pcr_helper_SignalLeavingFrame(
        pcr_helper, &out_frame2, &pcr);
    assert(status == VLC_SUCCESS);
    assert(pcr == VLC_TICK_INVALID);
}

static void test_PCRHelperIOMismatch(vlc_pcr_sync_t *sync,
                                     transcode_track_pcr_helper_t *pcr_helper)
{
    const vlc_frame_t in_frame = {.i_dts = 1u, .i_length = 10u};
    vlc_tick_t dropped_frame_pcr;
    transcode_track_pcr_helper_SignalEnteringFrame(
        pcr_helper, &in_frame, &dropped_frame_pcr);
    assert(dropped_frame_pcr == VLC_TICK_INVALID);

    assert(vlc_pcr_sync_SignalPCR(sync, 10u) == VLC_SUCCESS);

    const vlc_frame_t out_frame1 = {.i_dts = 2000u, .i_length = 10u};
    const vlc_frame_t out_frame2 = {.i_dts = 2010u, .i_length = 10u};
    vlc_tick_t pcr;
    int status = transcode_track_pcr_helper_SignalLeavingFrame(
        pcr_helper, &out_frame1, &pcr);
    assert(status == VLC_SUCCESS);
    assert(pcr == 10u);

    status = transcode_track_pcr_helper_SignalLeavingFrame(
        pcr_helper, &out_frame2, &pcr);
    assert(status == VLC_EGENERIC);
    assert(pcr == VLC_TICK_INVALID);
}

int main(void)
{
    test_Run(test_Simple);
    test_Run(test_MultipleTracks);
    test_Run(test_MultipleTracksButOnlyOneSends);
    test_Run(test_InvalidDTS);
    test_Run(test_LowDiscontinuity);
    test_Run(test_HighDiscontinuity);
    test_Run(test_TwoDiscontinuities);
    test_Run(test_FastForwardAtBeginning);
    test_Run(test_FastForwardMiddleOfStream);
    test_Run(test_OneTrackWithDelay);
    test_Run(test_SubsequentPCR);
    test_Run(test_LateTrack);
    test_PCRHelper(test_PCRHelperSimple);
    test_PCRHelper(test_PCRHelperMultipleTracks);
    test_PCRHelper(test_PCRHelperSplitFrameOutput);
    test_PCRHelper(test_PCRHelperSplitFrameInput);
    test_PCRHelper(test_PCRHelperIOMismatch);
}

Messung
CHZG
829610092

¤ 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.