From b6066bfbcfcddbdc684665e612439f950b51bbed Mon Sep 17 00:00:00 2001 From: Arnaud de Turckheim Date: Tue, 30 Dec 2025 11:37:28 +0100 Subject: [PATCH 1/2] Make progressive and tff attribute a boolean Change uref_pic_progressive and uref_pic_tff attributes from void to boolean. With void attribute, there is no possibility to explicitly set interlaced or bottom field first on a flow definition packet. --- doc/tutorials.mkdoc | 4 +-- examples/extract_pic.c | 4 +-- include/upipe/uref_attr.h | 25 +++++++++++++++++++ include/upipe/uref_pic.h | 5 ++-- lib/upipe-av/upipe_av.c | 8 +++--- lib/upipe-av/upipe_avcodec_decode.c | 8 +++--- lib/upipe-av/upipe_avcodec_encode.c | 4 +-- lib/upipe-av/upipe_avfilter.c | 10 ++++---- .../upipe_blackmagic_sink.cpp | 2 +- .../upipe_blackmagic_source.cpp | 8 +++--- lib/upipe-filters/upipe_audio_bar.c | 2 +- lib/upipe-filters/upipe_audio_graph.c | 4 +-- lib/upipe-filters/upipe_filter_blend.c | 4 +-- lib/upipe-filters/upipe_filter_format.c | 4 +-- lib/upipe-framers/upipe_h264_framer.c | 8 +++--- lib/upipe-framers/upipe_h265_framer.c | 8 +++--- lib/upipe-framers/upipe_mpgv_framer.c | 8 +++--- lib/upipe-gl/upipe_glx_sink.c | 2 +- lib/upipe-modules/upipe_rtp_anc_unpack.c | 2 +- lib/upipe-modules/upipe_separate_fields.c | 6 ++--- lib/upipe-modules/upipe_video_blank.c | 4 +-- lib/upipe-modules/upipe_videocont.c | 4 +-- lib/upipe-swscale/upipe_sws.c | 2 +- lib/upipe-x264/upipe_x264.c | 2 +- lib/upipe-x265/upipe_x265.c | 2 +- lib/upipe/udict_inline.c | 5 ++-- tests/upipe_blank_source_test.c | 2 +- tests/upipe_blit_test.c | 6 ++--- tests/upipe_separate_fields_test.c | 2 +- tests/upipe_sws_test.c | 4 +-- tests/upipe_x265_test.c | 2 +- 31 files changed, 94 insertions(+), 67 deletions(-) diff --git a/doc/tutorials.mkdoc b/doc/tutorials.mkdoc index f26b1b96e..1647032f7 100644 --- a/doc/tutorials.mkdoc +++ b/doc/tutorials.mkdoc @@ -154,13 +154,13 @@ static int avcdec_catch(struct uprobe *uprobe, struct upipe *upipe, return UBASE_ERR_UNHANDLED; } wanted_hsize = (hsize * sar.num / sar.den / 2) * 2; - progressive = ubase_check(uref_pic_get_progressive(flow_def)); + progressive = ubase_check(uref_pic_check_progressive(flow_def)); struct uref *flow_def2 = uref_dup(flow_def); upipe_use(upipe); if (!progressive) { - uref_pic_set_progressive(flow_def2); + uref_pic_set_progressive(flow_def2, true); struct upipe *deint = upipe_void_alloc_output(upipe, upipe_filter_blend_mgr, uprobe_pfx_alloc(uprobe_use(logger), diff --git a/examples/extract_pic.c b/examples/extract_pic.c index 8eb35d559..42425c3ad 100644 --- a/examples/extract_pic.c +++ b/examples/extract_pic.c @@ -153,7 +153,7 @@ static int avcdec_catch(struct uprobe *uprobe, struct upipe *upipe, return UBASE_ERR_UNHANDLED; } wanted_hsize = (hsize * sar.num / sar.den / 2) * 2; - progressive = ubase_check(uref_pic_get_progressive(flow_def)); + progressive = ubase_check(uref_pic_check_progressive(flow_def)); /* supported format of the jpeg encoder */ const struct uref_pic_flow_format *supported_formats[] = { @@ -173,7 +173,7 @@ static int avcdec_catch(struct uprobe *uprobe, struct upipe *upipe, struct uref *flow_def2 = uref_dup(flow_def); upipe_use(upipe); - uref_pic_set_progressive(flow_def2); + uref_pic_set_progressive(flow_def2, true); uref_pic_flow_set_hsize(flow_def2, wanted_hsize); if (!supported) uref_pic_flow_set_format(flow_def2, diff --git a/include/upipe/uref_attr.h b/include/upipe/uref_attr.h index 561b4c099..6d44e713e 100644 --- a/include/upipe/uref_attr.h +++ b/include/upipe/uref_attr.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2012-2016 OpenHeadend S.A.R.L. + * Copyright (C) 2026 EasyTools * * Authors: Christophe Massiot * @@ -1016,6 +1017,18 @@ static inline int uref_##group##_get_##attr(struct uref *uref, bool *p) \ { \ return uref_attr_get_bool(uref, p, UDICT_TYPE_BOOL, name); \ } \ +/** @This checks if the desc attribute of a uref is set and true. \ + * \ + * @param uref pointer to the uref \ + * @return an error code \ + */ \ +static inline int uref_##group##_check_##attr(struct uref *uref) \ +{ \ + bool v = false; \ + int ret = uref_##group##_get_##attr(uref, &v); \ + return ubase_check(ret) ? \ + (v == true ? UBASE_ERR_NONE : UBASE_ERR_INVALID) : ret; \ +} \ /** @This sets the desc attribute of a uref. \ * \ * @param uref pointer to the uref \ @@ -1083,6 +1096,18 @@ static inline int uref_##group##_get_##attr(struct uref *uref, bool *p) \ { \ return uref_attr_get_bool(uref, p, type, NULL); \ } \ +/** @This checks if the desc attribute of a uref is set and true. \ + * \ + * @param uref pointer to the uref \ + * @return an error code \ + */ \ +static inline int uref_##group##_check_##attr(struct uref *uref) \ +{ \ + bool v = false; \ + int ret = uref_##group##_get_##attr(uref, &v); \ + return ubase_check(ret) ? \ + (v == true ? UBASE_ERR_NONE : UBASE_ERR_INVALID) : ret; \ +} \ /** @This sets the desc attribute of a uref. \ * \ * @param uref pointer to the uref \ diff --git a/include/upipe/uref_pic.h b/include/upipe/uref_pic.h index b7129d24e..29d40d239 100644 --- a/include/upipe/uref_pic.h +++ b/include/upipe/uref_pic.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2012-2016 OpenHeadend S.A.R.L. + * Copyright (C) 2026 EasyTools * * Authors: Christophe Massiot * @@ -34,10 +35,10 @@ UREF_ATTR_UNSIGNED_SH(pic, lpadding, UDICT_TYPE_PIC_LPADDING, left padding) UREF_ATTR_UNSIGNED_SH(pic, rpadding, UDICT_TYPE_PIC_RPADDING, right padding) UREF_ATTR_UNSIGNED_SH(pic, tpadding, UDICT_TYPE_PIC_TPADDING, top padding) UREF_ATTR_UNSIGNED_SH(pic, bpadding, UDICT_TYPE_PIC_BPADDING, bottom padding) -UREF_ATTR_VOID_SH(pic, progressive, UDICT_TYPE_PIC_PROGRESSIVE, progressive) +UREF_ATTR_BOOL_SH(pic, progressive, UDICT_TYPE_PIC_PROGRESSIVE, progressive) UREF_ATTR_VOID_SH(pic, tf, UDICT_TYPE_PIC_TF, top field present) UREF_ATTR_VOID_SH(pic, bf, UDICT_TYPE_PIC_BF, bottom field present) -UREF_ATTR_VOID_SH(pic, tff, UDICT_TYPE_PIC_TFF, top field first) +UREF_ATTR_BOOL_SH(pic, tff, UDICT_TYPE_PIC_TFF, top field first) UREF_ATTR_SMALL_UNSIGNED_SH(pic, afd, UDICT_TYPE_PIC_AFD, active format description) UREF_ATTR_OPAQUE_SH(pic, cea_708, UDICT_TYPE_PIC_CEA_708, cea-708 captions) UREF_ATTR_OPAQUE_SH(pic, bar_data, UDICT_TYPE_PIC_BAR_DATA, afd bar data) diff --git a/lib/upipe-av/upipe_av.c b/lib/upipe-av/upipe_av.c index 3fa17ab36..c534d3230 100644 --- a/lib/upipe-av/upipe_av.c +++ b/lib/upipe-av/upipe_av.c @@ -122,18 +122,18 @@ int upipe_av_set_frame_properties(struct upipe *upipe, { #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(58, 7, 100) frame->key_frame = ubase_check(uref_pic_get_key(uref)); - frame->interlaced_frame = !ubase_check(uref_pic_get_progressive(uref)); - frame->top_field_first = ubase_check(uref_pic_get_tff(uref)); + frame->interlaced_frame = !ubase_check(uref_pic_check_progressive(uref)); + frame->top_field_first = ubase_check(uref_pic_check_tff(uref)); #else if (ubase_check(uref_pic_get_key(uref))) frame->flags |= AV_FRAME_FLAG_KEY; else frame->flags &= ~AV_FRAME_FLAG_KEY; - if (!ubase_check(uref_pic_get_progressive(uref))) + if (!ubase_check(uref_pic_check_progressive(uref))) frame->flags |= AV_FRAME_FLAG_INTERLACED; else frame->flags &= ~AV_FRAME_FLAG_INTERLACED; - if (ubase_check(uref_pic_get_tff(uref))) + if (ubase_check(uref_pic_check_tff(uref))) frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; else frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; diff --git a/lib/upipe-av/upipe_avcodec_decode.c b/lib/upipe-av/upipe_avcodec_decode.c index f3d0a6c9d..b7eed2b33 100644 --- a/lib/upipe-av/upipe_avcodec_decode.c +++ b/lib/upipe-av/upipe_avcodec_decode.c @@ -1110,7 +1110,7 @@ static void upipe_avcdec_output_sub(struct upipe *upipe, AVSubtitle *sub, !ubase_check(uref_pic_flow_set_bgra(flow_def_attr)) || #endif !ubase_check(uref_flow_set_def(flow_def_attr, UREF_PIC_SUB_FLOW_DEF)) || - !ubase_check(uref_pic_set_progressive(flow_def_attr)) || + !ubase_check(uref_pic_set_progressive(flow_def_attr, true)) || !ubase_check(uref_pic_flow_set_full_range(flow_def_attr)))) { uref_free(flow_def_attr); @@ -1191,7 +1191,7 @@ static void upipe_avcdec_output_sub(struct upipe *upipe, AVSubtitle *sub, return; } - uref_pic_set_progressive(uref); + uref_pic_set_progressive(uref, true); ubuf_pic_clear(ubuf, 0, 0, -1, -1, 1); uref_attach_ubuf(uref, ubuf); @@ -1352,9 +1352,9 @@ static void upipe_avcdec_output_pic(struct upipe *upipe, struct upump **upump_p) UBASE_FATAL(upipe, uref_pic_set_tf(uref)) UBASE_FATAL(upipe, uref_pic_set_bf(uref)) if (!interlaced_frame) - UBASE_FATAL(upipe, uref_pic_set_progressive(uref)) + UBASE_FATAL(upipe, uref_pic_set_progressive(uref, true)) else if (top_field_first) - UBASE_FATAL(upipe, uref_pic_set_tff(uref)) + UBASE_FATAL(upipe, uref_pic_set_tff(uref, true)) uint64_t duration = 0; AVRational uclock_time_base = av_make_q(1, UCLOCK_FREQ); diff --git a/lib/upipe-av/upipe_avcodec_encode.c b/lib/upipe-av/upipe_avcodec_encode.c index d40d52d8c..5871153d5 100644 --- a/lib/upipe-av/upipe_avcodec_encode.c +++ b/lib/upipe-av/upipe_avcodec_encode.c @@ -1538,10 +1538,10 @@ static int upipe_avcenc_set_flow_def(struct upipe *upipe, struct uref *flow_def) uref_pic_flow_get_matrix_coefficients_val(flow_def, &val))) context->colorspace = val; - if (!ubase_check(uref_pic_get_progressive(flow_def))) { + if (!ubase_check(uref_pic_check_progressive(flow_def))) { context->flags |= AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME; - if (ubase_check(uref_pic_get_tff(flow_def))) + if (ubase_check(uref_pic_check_tff(flow_def))) context->field_order = AV_FIELD_TT; else context->field_order = AV_FIELD_BB; diff --git a/lib/upipe-av/upipe_avfilter.c b/lib/upipe-av/upipe_avfilter.c index 498586a23..9b87946dc 100644 --- a/lib/upipe-av/upipe_avfilter.c +++ b/lib/upipe-av/upipe_avfilter.c @@ -310,7 +310,7 @@ static int build_video_flow_def(struct uref *flow_def, #endif if (!interlaced_frame) - UBASE_RETURN(uref_pic_set_progressive(flow_def)) + UBASE_RETURN(uref_pic_set_progressive(flow_def, true)) if (color_range == AVCOL_RANGE_JPEG) UBASE_RETURN(uref_pic_flow_set_full_range(flow_def)) @@ -578,9 +578,9 @@ upipe_avfilt_sub_frame_to_uref(struct upipe *upipe, AVFrame *frame) #endif if (!interlaced_frame) - UBASE_ERROR(upipe, uref_pic_set_progressive(uref)) + UBASE_ERROR(upipe, uref_pic_set_progressive(uref, true)) else if (top_field_first) - UBASE_ERROR(upipe, uref_pic_set_tff(uref)) + UBASE_ERROR(upipe, uref_pic_set_tff(uref, true)) if (key_frame) UBASE_ERROR(upipe, uref_pic_set_key(uref)) @@ -1993,9 +1993,9 @@ static void upipe_avfilt_output_frame(struct upipe *upipe, #endif if (!interlaced_frame) - UBASE_ERROR(upipe, uref_pic_set_progressive(uref)) + UBASE_ERROR(upipe, uref_pic_set_progressive(uref, true)) else if (top_field_first) - UBASE_ERROR(upipe, uref_pic_set_tff(uref)) + UBASE_ERROR(upipe, uref_pic_set_tff(uref, true)) if (key_frame) UBASE_ERROR(upipe, uref_pic_set_key(uref)) diff --git a/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp b/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp index 66bb9c611..cf120cd54 100644 --- a/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp +++ b/lib/upipe-blackmagic/upipe_blackmagic_sink.cpp @@ -1223,7 +1223,7 @@ uint32_t upipe_bmd_mode_from_flow_def(struct upipe *upipe, struct uref *flow_def return bmdModeUnknown; } - bool interlaced = !ubase_check(uref_pic_get_progressive(flow_def)); + bool interlaced = !ubase_check(uref_pic_check_progressive(flow_def)); upipe_notice_va(upipe, "%" PRIu64"x%" PRIu64" %" PRId64"/%" PRIu64" interlaced %d", hsize, vsize, fps.num, fps.den, interlaced); diff --git a/lib/upipe-blackmagic/upipe_blackmagic_source.cpp b/lib/upipe-blackmagic/upipe_blackmagic_source.cpp index ecc905e00..26dbe3de0 100644 --- a/lib/upipe-blackmagic/upipe_blackmagic_source.cpp +++ b/lib/upipe-blackmagic/upipe_blackmagic_source.cpp @@ -360,7 +360,7 @@ static int upipe_bmd_src_build_video(struct upipe *upipe, case bmdUnknownFieldDominance: /* sensible defaults */ case bmdUpperFieldFirst: - UBASE_RETURN(uref_pic_set_tff(flow_def)); + UBASE_RETURN(uref_pic_set_tff(flow_def, true)); uref_pic_delete_progressive(flow_def); upipe_bmd_src->tff = true; upipe_bmd_src->progressive = false; @@ -368,7 +368,7 @@ static int upipe_bmd_src_build_video(struct upipe *upipe, case bmdProgressiveFrame: case bmdProgressiveSegmentedFrame: uref_pic_delete_tff(flow_def); - UBASE_RETURN(uref_pic_set_progressive(flow_def)); + UBASE_RETURN(uref_pic_set_progressive(flow_def, true)); upipe_bmd_src->tff = false; upipe_bmd_src->progressive = true; break; @@ -529,9 +529,9 @@ HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived( uref_clock_set_duration(uref, FrameDuration); if (upipe_bmd_src->progressive) - uref_pic_set_progressive(uref); + uref_pic_set_progressive(uref, true); else if (upipe_bmd_src->tff) - uref_pic_set_tff(uref); + uref_pic_set_tff(uref, true); if (!uqueue_push(&upipe_bmd_src->uqueue, uref)) uref_free(uref); diff --git a/lib/upipe-filters/upipe_audio_bar.c b/lib/upipe-filters/upipe_audio_bar.c index 303cc220d..31f6346a4 100644 --- a/lib/upipe-filters/upipe_audio_bar.c +++ b/lib/upipe-filters/upipe_audio_bar.c @@ -248,7 +248,7 @@ static bool upipe_audiobar_handle(struct upipe *upipe, struct uref *uref, UBASE_FATAL(upipe, uref_pic_flow_add_plane(uref, 2, 1, 1, "u8")) UBASE_FATAL(upipe, uref_pic_flow_add_plane(uref, 2, 1, 1, "v8")) UBASE_FATAL(upipe, uref_pic_flow_add_plane(uref, 1, 1, 1, "a8")) - UBASE_FATAL(upipe, uref_pic_set_progressive(uref)) + UBASE_FATAL(upipe, uref_pic_set_progressive(uref, true)) upipe_audiobar->hsize = upipe_audiobar->vsize = upipe_audiobar->sep_width = upipe_audiobar->pad_width = UINT64_MAX; diff --git a/lib/upipe-filters/upipe_audio_graph.c b/lib/upipe-filters/upipe_audio_graph.c index f0a95dbba..f99b48495 100644 --- a/lib/upipe-filters/upipe_audio_graph.c +++ b/lib/upipe-filters/upipe_audio_graph.c @@ -244,7 +244,7 @@ static bool upipe_agraph_handle(struct upipe *upipe, struct uref *uref, uref_attr_import(uref, upipe_agraph->flow_def_config)) uref_pic_flow_clear_format(uref); UBASE_FATAL(upipe, uref_pic_flow_set_yuv420p(uref)); - UBASE_FATAL(upipe, uref_pic_set_progressive(uref)) + UBASE_FATAL(upipe, uref_pic_set_progressive(uref, true)) upipe_agraph->hsize = upipe_agraph->vsize = upipe_agraph->sep_width = upipe_agraph->pad_width = UINT64_MAX; @@ -371,7 +371,7 @@ static bool upipe_agraph_handle(struct upipe *upipe, struct uref *uref, if (dst[i]) ubuf_pic_plane_unmap(ubuf, chroma[i], 0, 0, -1, -1); - uref_pic_set_progressive(uref); + uref_pic_set_progressive(uref, true); upipe_agraph_output(upipe, uref, upump_p); return true; } diff --git a/lib/upipe-filters/upipe_filter_blend.c b/lib/upipe-filters/upipe_filter_blend.c index 1421c27d7..9c3c18bd2 100644 --- a/lib/upipe-filters/upipe_filter_blend.c +++ b/lib/upipe-filters/upipe_filter_blend.c @@ -247,7 +247,7 @@ static bool upipe_filter_blend_handle(struct upipe *upipe, struct uref *uref, // Attach new ubuf and output frame uref_attach_ubuf(uref, ubuf_deint); - uref_pic_set_progressive(uref); + uref_pic_set_progressive(uref, true); uref_pic_delete_tff(uref); upipe_filter_blend_output(upipe, uref, upump_p); @@ -327,7 +327,7 @@ static int upipe_filter_blend_set_flow_def(struct upipe *upipe, upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return UBASE_ERR_ALLOC; } - UBASE_RETURN(uref_pic_set_progressive(flow_def_dup)) + UBASE_RETURN(uref_pic_set_progressive(flow_def_dup, true)) upipe_input(upipe, flow_def_dup, NULL); return UBASE_ERR_NONE; } diff --git a/lib/upipe-filters/upipe_filter_format.c b/lib/upipe-filters/upipe_filter_format.c index df2ff6446..8bde314ff 100644 --- a/lib/upipe-filters/upipe_filter_format.c +++ b/lib/upipe-filters/upipe_filter_format.c @@ -374,8 +374,8 @@ static int upipe_ffmt_check_flow_format(struct upipe *upipe, surface_type_out = ""; bool need_deint = ffmt_mgr->deint_mgr && - !ubase_check(uref_pic_get_progressive(flow_def)) && - ubase_check(uref_pic_get_progressive(flow_def_dup)); + !ubase_check(uref_pic_check_progressive(flow_def)) && + ubase_check(uref_pic_check_progressive(flow_def_dup)); bool need_scale = uref_pic_flow_cmp_hsize(flow_def, flow_def_dup) || uref_pic_flow_cmp_vsize(flow_def, flow_def_dup); diff --git a/lib/upipe-framers/upipe_h264_framer.c b/lib/upipe-framers/upipe_h264_framer.c index 0c9ac0ae3..745baf919 100644 --- a/lib/upipe-framers/upipe_h264_framer.c +++ b/lib/upipe-framers/upipe_h264_framer.c @@ -731,7 +731,7 @@ static bool upipe_h264f_activate_sps(struct upipe *upipe, uint32_t sps_id) vsize = map_height * 16 * 2; ubuf_block_stream_skip_bits(s, 1); /* mb_adaptive_frame_field */ } else { - UBASE_FATAL(upipe, uref_pic_set_progressive(flow_def)) + UBASE_FATAL(upipe, uref_pic_set_progressive(flow_def, true)) vsize = map_height * 16; } ubuf_block_stream_skip_bits(s, 1); /* direct8x8_inference */ @@ -1841,7 +1841,7 @@ static int upipe_h264f_prepare_au(struct upipe *upipe, struct uref *uref) switch (upipe_h264f->pic_struct) { case H264SEI_STRUCT_FRAME: - UBASE_RETURN(uref_pic_set_progressive(uref)) + UBASE_RETURN(uref_pic_set_progressive(uref, true)) duration *= 2; break; case H264SEI_STRUCT_TOP: @@ -1853,7 +1853,7 @@ static int upipe_h264f_prepare_au(struct upipe *upipe, struct uref *uref) case H264SEI_STRUCT_TOP_BOT: UBASE_RETURN(uref_pic_set_tf(uref)) UBASE_RETURN(uref_pic_set_bf(uref)) - UBASE_RETURN(uref_pic_set_tff(uref)) + UBASE_RETURN(uref_pic_set_tff(uref, true)) duration *= 2; break; case H264SEI_STRUCT_BOT_TOP: @@ -1864,7 +1864,7 @@ static int upipe_h264f_prepare_au(struct upipe *upipe, struct uref *uref) case H264SEI_STRUCT_TOP_BOT_TOP: UBASE_RETURN(uref_pic_set_tf(uref)) UBASE_RETURN(uref_pic_set_bf(uref)) - UBASE_RETURN(uref_pic_set_tff(uref)) + UBASE_RETURN(uref_pic_set_tff(uref, true)) duration *= 3; break; case H264SEI_STRUCT_BOT_TOP_BOT: diff --git a/lib/upipe-framers/upipe_h265_framer.c b/lib/upipe-framers/upipe_h265_framer.c index 1a467cda1..b4c71333c 100644 --- a/lib/upipe-framers/upipe_h265_framer.c +++ b/lib/upipe-framers/upipe_h265_framer.c @@ -1225,7 +1225,7 @@ static bool upipe_h265f_activate_sps(struct upipe *upipe, uint32_t sps_id) if (field_seq_flag) frame_rate.den *= 2; else - uref_pic_set_progressive(flow_def); + uref_pic_set_progressive(flow_def, true); urational_simplify(&frame_rate); UBASE_FATAL(upipe, uref_pic_flow_set_fps(flow_def, frame_rate)) @@ -2082,7 +2082,7 @@ static int upipe_h265f_prepare_au(struct upipe *upipe, struct uref *uref) switch (upipe_h265f->pic_struct) { case H265SEI_STRUCT_FRAME: - UBASE_FATAL(upipe, uref_pic_set_progressive(uref)) + UBASE_FATAL(upipe, uref_pic_set_progressive(uref, true)) duration *= 2; break; case H265SEI_STRUCT_TOP: @@ -2098,7 +2098,7 @@ static int upipe_h265f_prepare_au(struct upipe *upipe, struct uref *uref) case H265SEI_STRUCT_TOP_BOT: UBASE_FATAL(upipe, uref_pic_set_tf(uref)) UBASE_FATAL(upipe, uref_pic_set_bf(uref)) - UBASE_FATAL(upipe, uref_pic_set_tff(uref)) + UBASE_FATAL(upipe, uref_pic_set_tff(uref, true)) duration *= 2; break; case H265SEI_STRUCT_BOT_TOP: @@ -2109,7 +2109,7 @@ static int upipe_h265f_prepare_au(struct upipe *upipe, struct uref *uref) case H265SEI_STRUCT_TOP_BOT_TOP: UBASE_FATAL(upipe, uref_pic_set_tf(uref)) UBASE_FATAL(upipe, uref_pic_set_bf(uref)) - UBASE_FATAL(upipe, uref_pic_set_tff(uref)) + UBASE_FATAL(upipe, uref_pic_set_tff(uref, true)) duration *= 3; break; case H265SEI_STRUCT_BOT_TOP_BOT: diff --git a/lib/upipe-framers/upipe_mpgv_framer.c b/lib/upipe-framers/upipe_mpgv_framer.c index 87da35a5d..4e09c4f12 100644 --- a/lib/upipe-framers/upipe_mpgv_framer.c +++ b/lib/upipe-framers/upipe_mpgv_framer.c @@ -456,7 +456,7 @@ static bool upipe_mpgvf_parse_sequence(struct upipe *upipe) UBASE_FATAL(upipe, uref_block_flow_set_max_octetrate(flow_def, max_octetrate)) upipe_mpgvf->progressive_sequence = progressive; if (progressive) - UBASE_FATAL(upipe, uref_pic_set_progressive(flow_def)) + UBASE_FATAL(upipe, uref_pic_set_progressive(flow_def, true)) UBASE_FATAL(upipe, uref_pic_flow_set_macropixel(flow_def, 1)) UBASE_FATAL(upipe, uref_pic_flow_set_planes(flow_def, 0)) UBASE_FATAL(upipe, uref_pic_flow_add_plane(flow_def, 1, 1, 1, "y8")) @@ -862,14 +862,14 @@ static bool upipe_mpgvf_parse_picture(struct upipe *upipe, struct uref *uref, if (structure & MP2VPICX_BOTTOM_FIELD) UBASE_FATAL(upipe, uref_pic_set_bf(uref)) if (tff) - UBASE_FATAL(upipe, uref_pic_set_tff(uref)) + UBASE_FATAL(upipe, uref_pic_set_tff(uref, true)) if (progressive) - UBASE_FATAL(upipe, uref_pic_set_progressive(uref)) + UBASE_FATAL(upipe, uref_pic_set_progressive(uref, true)) } else { upipe_mpgvf->field_number = 0; UBASE_FATAL(upipe, uref_pic_set_tf(uref)) UBASE_FATAL(upipe, uref_pic_set_bf(uref)) - UBASE_FATAL(upipe, uref_pic_set_progressive(uref)) + UBASE_FATAL(upipe, uref_pic_set_progressive(uref, true)) } UBASE_FATAL(upipe, uref_clock_set_duration(uref, *duration_p)) diff --git a/lib/upipe-gl/upipe_glx_sink.c b/lib/upipe-gl/upipe_glx_sink.c index 82e34def0..69df0b7a2 100644 --- a/lib/upipe-gl/upipe_glx_sink.c +++ b/lib/upipe-gl/upipe_glx_sink.c @@ -500,7 +500,7 @@ static int upipe_glx_sink_provide_flow_format(struct upipe *upipe, uref_pic_flow_add_plane(flow_format, 1, 1, 2, "r5g6b5"); else uref_pic_flow_add_plane(flow_format, 1, 1, 3, "r8g8b8"); - uref_pic_set_progressive(flow_format); + uref_pic_set_progressive(flow_format, true); uref_pic_flow_delete_colour_primaries(flow_format); uref_pic_flow_delete_transfer_characteristics(flow_format); uref_pic_flow_delete_matrix_coefficients(flow_format); diff --git a/lib/upipe-modules/upipe_rtp_anc_unpack.c b/lib/upipe-modules/upipe_rtp_anc_unpack.c index 74e947611..b26a86035 100644 --- a/lib/upipe-modules/upipe_rtp_anc_unpack.c +++ b/lib/upipe-modules/upipe_rtp_anc_unpack.c @@ -179,7 +179,7 @@ static bool upipe_rtp_anc_unpack_handle(struct upipe *upipe, struct uref *uref, switch (f) { case RFC_8331_F_PROGRESSIVE: - uref_pic_set_progressive(uref); + uref_pic_set_progressive(uref, true); break; case RFC_8331_F_FIELD_1: uref_pic_set_tf(uref); diff --git a/lib/upipe-modules/upipe_separate_fields.c b/lib/upipe-modules/upipe_separate_fields.c index 383c2a747..d714f810e 100644 --- a/lib/upipe-modules/upipe_separate_fields.c +++ b/lib/upipe-modules/upipe_separate_fields.c @@ -85,7 +85,7 @@ static int upipe_separate_fields_set_flow_def(struct upipe *upipe, ctx->field_duration = UCLOCK_FREQ * fps.den / fps.num; UBASE_RETURN(uref_pic_flow_set_fps(flow_def_dup, fps)); UBASE_RETURN(uref_pic_flow_set_vsize(flow_def_dup, height)); - if (ubase_check(uref_pic_get_progressive(flow_def_dup))) + if (ubase_check(uref_pic_check_progressive(flow_def_dup))) UBASE_RETURN(uref_pic_delete_progressive(flow_def_dup)); upipe_separate_fields_store_flow_def(upipe, flow_def_dup); @@ -152,8 +152,8 @@ static void upipe_separate_fields_input(struct upipe *upipe, struct uref *uref, return; } - bool has_progressive_attr = ubase_check(uref_pic_get_progressive(uref)); - bool has_tff_attr = ubase_check(uref_pic_get_tff(uref)); + bool has_progressive_attr = ubase_check(uref_pic_check_progressive(uref)); + bool has_tff_attr = ubase_check(uref_pic_check_tff(uref)); if (has_progressive_attr) upipe_warn(upipe, "picture marked as progressive, separating fields anyway"); diff --git a/lib/upipe-modules/upipe_video_blank.c b/lib/upipe-modules/upipe_video_blank.c index dc637925c..f021af5ef 100644 --- a/lib/upipe-modules/upipe_video_blank.c +++ b/lib/upipe-modules/upipe_video_blank.c @@ -254,8 +254,8 @@ static bool upipe_vblk_try_output(struct upipe *upipe, uref_attach_ubuf(uref, ubuf); if (upipe_vblk->pic_attr) uref_attr_import(uref, upipe_vblk->pic_attr); - if (ubase_check(uref_pic_get_progressive(upipe_vblk->flow_def))) - uref_pic_set_progressive(uref); + if (ubase_check(uref_pic_check_progressive(upipe_vblk->flow_def))) + uref_pic_set_progressive(uref, true); upipe_vblk_output(upipe, uref, upump_p); return true; diff --git a/lib/upipe-modules/upipe_videocont.c b/lib/upipe-modules/upipe_videocont.c index 2f7fda10d..e469f2cf1 100644 --- a/lib/upipe-modules/upipe_videocont.c +++ b/lib/upipe-modules/upipe_videocont.c @@ -427,8 +427,8 @@ static int upipe_videocont_switch_format(struct upipe *upipe, } else { uref_pic_flow_delete_overscan(out_flow); } - if (likely(ubase_check(uref_pic_get_progressive(in_flow)))) { - uref_pic_set_progressive(out_flow); + if (likely(ubase_check(uref_pic_check_progressive(in_flow)))) { + uref_pic_set_progressive(out_flow, true); } else { uref_pic_delete_progressive(out_flow); } diff --git a/lib/upipe-swscale/upipe_sws.c b/lib/upipe-swscale/upipe_sws.c index c88b79394..60bd0b04f 100644 --- a/lib/upipe-swscale/upipe_sws.c +++ b/lib/upipe-swscale/upipe_sws.c @@ -169,7 +169,7 @@ static bool upipe_sws_handle(struct upipe *upipe, struct uref *uref, return true; } - int progressive = ubase_check(uref_pic_get_progressive(uref)) ? 1 : 0; + int progressive = ubase_check(uref_pic_check_progressive(uref)) ? 1 : 0; if (unlikely(!progressive && input_vsize % 2)) { upipe_warn(upipe, "interlaced picture has odd vertical size"); progressive = 1; diff --git a/lib/upipe-x264/upipe_x264.c b/lib/upipe-x264/upipe_x264.c index fe060ec9f..52d216196 100644 --- a/lib/upipe-x264/upipe_x264.c +++ b/lib/upipe-x264/upipe_x264.c @@ -423,7 +423,7 @@ static bool upipe_x264_open(struct upipe *upipe, int width, int height) params->i_width = width; params->i_height = height; params->b_interlaced = - !ubase_check(uref_pic_get_progressive(upipe_x264->flow_def_input)); + !ubase_check(uref_pic_check_progressive(upipe_x264->flow_def_input)); const char *content; int ret; diff --git a/lib/upipe-x265/upipe_x265.c b/lib/upipe-x265/upipe_x265.c index ddec9b774..167ca92dc 100644 --- a/lib/upipe-x265/upipe_x265.c +++ b/lib/upipe-x265/upipe_x265.c @@ -401,7 +401,7 @@ static void apply_params(struct upipe *upipe, x265_param *params) params->internalCsp = upipe_x265->color_space; params->interlaceMode = - !ubase_check(uref_pic_get_progressive(flow_def)); + !ubase_check(uref_pic_check_progressive(flow_def)); upipe_x265_set_option(upipe, params, "range", ubase_check(uref_pic_flow_get_full_range(flow_def)) ? diff --git a/lib/upipe/udict_inline.c b/lib/upipe/udict_inline.c index 58db72ba8..d7ad37b33 100644 --- a/lib/upipe/udict_inline.c +++ b/lib/upipe/udict_inline.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2012-2017 OpenHeadend S.A.R.L. + * Copyright (C) 2026 EasyTools * * Authors: Christophe Massiot * @@ -79,10 +80,10 @@ static const struct inline_shorthand inline_shorthands[] = { { "p.bpadding", UDICT_TYPE_UNSIGNED }, { "p.sar", UDICT_TYPE_RATIONAL }, { "p.overscan", UDICT_TYPE_BOOL }, - { "p.progressive", UDICT_TYPE_VOID }, + { "p.progressive", UDICT_TYPE_BOOL }, { "p.tf", UDICT_TYPE_VOID }, { "p.bf", UDICT_TYPE_VOID }, - { "p.tff", UDICT_TYPE_VOID }, + { "p.tff", UDICT_TYPE_BOOL }, { "p.afd", UDICT_TYPE_SMALL_UNSIGNED }, { "p.cea_708", UDICT_TYPE_OPAQUE }, { "p.bar_data", UDICT_TYPE_OPAQUE }, diff --git a/tests/upipe_blank_source_test.c b/tests/upipe_blank_source_test.c index 8362b7858..8c8cbe937 100644 --- a/tests/upipe_blank_source_test.c +++ b/tests/upipe_blank_source_test.c @@ -116,7 +116,7 @@ static void test_input(struct upipe *upipe, struct uref *uref, uref_free(flow); struct uref *uref = uref_alloc(uref_mgr); - uref_pic_set_progressive(uref); + uref_pic_set_progressive(uref, true); upipe_input(blksrc, uref, NULL); } diff --git a/tests/upipe_blit_test.c b/tests/upipe_blit_test.c index 683c4bd02..98f83223c 100644 --- a/tests/upipe_blit_test.c +++ b/tests/upipe_blit_test.c @@ -152,7 +152,7 @@ static void setup_sub(struct upipe *sub, struct uref_mgr *uref_mgr, struct uref *uref = uref_pic_alloc(uref_mgr, pic_mgr, SUBSIZE, SUBSIZE); assert(uref != NULL); - uref_pic_set_progressive(uref); + uref_pic_set_progressive(uref, true); fill_in(uref, "y8", val); fill_in(uref, "u8", val); fill_in(uref, "v8", val); @@ -312,7 +312,7 @@ int main(int argc, char **argv) struct uref *uref; uref = uref_pic_alloc(uref_mgr, pic_mgr, BGSIZE, BGSIZE); assert(uref != NULL); - uref_pic_set_progressive(uref); + uref_pic_set_progressive(uref, true); fill_in(uref, "y8", 0); fill_in(uref, "u8", 0); fill_in(uref, "v8", 0); @@ -343,7 +343,7 @@ int main(int argc, char **argv) uref = uref_pic_alloc(uref_mgr, pic_mgr, BGSIZE, BGSIZE); assert(uref != NULL); - uref_pic_set_progressive(uref); + uref_pic_set_progressive(uref, true); fill_in(uref, "y8", 0); fill_in(uref, "u8", 0); fill_in(uref, "v8", 0); diff --git a/tests/upipe_separate_fields_test.c b/tests/upipe_separate_fields_test.c index 6ceb72d82..89545911e 100644 --- a/tests/upipe_separate_fields_test.c +++ b/tests/upipe_separate_fields_test.c @@ -278,7 +278,7 @@ int main(int argc, char **argv) ubase_assert(uref_pic_flow_add_plane(flow, 1, 1, 1, "y8")); ubase_assert(uref_pic_flow_add_plane(flow, 2, 2, 1, "u8")); ubase_assert(uref_pic_flow_add_plane(flow, 2, 2, 1, "v8")); - ubase_assert(uref_pic_set_tff(flow)); + ubase_assert(uref_pic_set_tff(flow, true)); ubase_assert(uref_pic_flow_set_hsize(flow, WIDTH)); ubase_assert(uref_pic_flow_set_vsize(flow, HEIGHT)); ubase_assert(uref_pic_flow_set_fps(flow, fps)); diff --git a/tests/upipe_sws_test.c b/tests/upipe_sws_test.c index 28cfffa52..6af965c80 100644 --- a/tests/upipe_sws_test.c +++ b/tests/upipe_sws_test.c @@ -310,7 +310,7 @@ int main(int argc, char **argv) uref1 = uref_pic_alloc(uref_mgr, ubuf_mgr, SRCSIZE, SRCSIZE); assert(uref1 != NULL); assert(uref1->ubuf != NULL); - ubase_assert(uref_pic_set_progressive(uref1)); + ubase_assert(uref_pic_set_progressive(uref1, true)); /* fill reference picture */ fill_in(uref1, "y8", 1, 1, 1); @@ -323,7 +323,7 @@ int main(int argc, char **argv) uref2 = uref_pic_alloc(uref_mgr, ubuf_mgr, DSTSIZE, DSTSIZE); assert(uref2); assert(uref2->ubuf); - ubase_assert(uref_pic_set_progressive(uref2)); + ubase_assert(uref_pic_set_progressive(uref2, true)); img_convert_ctx = sws_getCachedContext(NULL, SRCSIZE, SRCSIZE, AV_PIX_FMT_YUV420P, diff --git a/tests/upipe_x265_test.c b/tests/upipe_x265_test.c index 49dff409e..bd66d8709 100644 --- a/tests/upipe_x265_test.c +++ b/tests/upipe_x265_test.c @@ -219,7 +219,7 @@ int main(int argc, char **argv) ubase_assert(uref_pic_flow_set_vsize(flow_def, HEIGHT)); struct urational fps = { .num = 25, .den = 1 }; ubase_assert(uref_pic_flow_set_fps(flow_def, fps)); - ubase_assert(uref_pic_set_progressive(flow_def)); + ubase_assert(uref_pic_set_progressive(flow_def, true)); /* x265 pipe */ struct upipe *x265 = upipe_void_alloc(upipe_x265_mgr, From 72dfc5cd557a9d692f24f7044e39f597a62ef6b1 Mon Sep 17 00:00:00 2001 From: Arnaud de Turckheim Date: Tue, 30 Dec 2025 13:51:43 +0100 Subject: [PATCH 2/2] upipe-modules: add interlacing pipe Add upipe_interlace pipe to preform temporal field interlacing. --- include/upipe-modules/upipe_interlace.h | 33 ++ lib/upipe-modules/Build.mk | 2 + lib/upipe-modules/upipe_interlace.c | 563 ++++++++++++++++++++++++ tests/Build.mk | 4 + tests/upipe_interlace_test.c | 474 ++++++++++++++++++++ 5 files changed, 1076 insertions(+) create mode 100644 include/upipe-modules/upipe_interlace.h create mode 100644 lib/upipe-modules/upipe_interlace.c create mode 100644 tests/upipe_interlace_test.c diff --git a/include/upipe-modules/upipe_interlace.h b/include/upipe-modules/upipe_interlace.h new file mode 100644 index 000000000..1294d5973 --- /dev/null +++ b/include/upipe-modules/upipe_interlace.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 EasyTools + * + * Authors: Arnaud de Turckheim + * + * SPDX-License-Identifier: MIT + */ + +/** @file + * @short Upipe interlacing module + */ + +#ifndef _UPIPE_MODULES_UPIPE_INTERLACE_H_ +/** @hidden */ +#define _UPIPE_MODULES_UPIPE_INTERLACE_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#include "upipe/upipe.h" + +#define UPIPE_INTERLACE_SIGNATURE UBASE_FOURCC('i','n','t','l') + +/** @This returns the management structure for all interlace pipes. + * + * @return pointer to manager + */ +struct upipe_mgr *upipe_interlace_mgr_alloc(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/upipe-modules/Build.mk b/lib/upipe-modules/Build.mk index 32b685974..064788cca 100644 --- a/lib/upipe-modules/Build.mk +++ b/lib/upipe-modules/Build.mk @@ -35,6 +35,7 @@ libupipe_modules-includes = \ upipe_htons.h \ upipe_http_source.h \ upipe_idem.h \ + upipe_interlace.h \ upipe_m3u_reader.h \ upipe_match_attr.h \ upipe_multicat_probe.h \ @@ -120,6 +121,7 @@ libupipe_modules-src = \ upipe_htons.c \ upipe_http_source.c \ upipe_idem.c \ + upipe_interlace.c \ upipe_m3u_reader.c \ upipe_match_attr.c \ upipe_multicat_probe.c \ diff --git a/lib/upipe-modules/upipe_interlace.c b/lib/upipe-modules/upipe_interlace.c new file mode 100644 index 000000000..5aa7d3d89 --- /dev/null +++ b/lib/upipe-modules/upipe_interlace.c @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2026 EasyTools + * + * Authors: Arnaud de Turckheim + * + * SPDX-License-Identifier: MIT + */ + +/** @file + * @short Upipe interlacing module + */ + +#include "upipe-modules/upipe_interlace.h" +#include "upipe/ubuf.h" +#include "upipe/upipe.h" +#include "upipe/upipe_helper_input.h" +#include "upipe/upipe_helper_output.h" +#include "upipe/upipe_helper_ubuf_mgr.h" +#include "upipe/upipe_helper_upipe.h" +#include "upipe/upipe_helper_urefcount.h" +#include "upipe/upipe_helper_void.h" +#include "upipe/uref.h" +#include "upipe/uref_clock.h" +#include "upipe/uref_flow.h" +#include "upipe/uref_pic.h" +#include "upipe/uref_pic_flow.h" + +#include +#include + +/** @hidden */ +static bool upipe_interlace_handle(struct upipe *upipe, struct uref *uref, + struct upump **upump_p); +/** @hidden */ +static int upipe_interlace_check(struct upipe *upipe, struct uref *flow_format); + +/** @internal upipe_interlace private structure */ +struct upipe_interlace { + /** refcount management structure */ + struct urefcount urefcount; + + /** ubuf manager */ + struct ubuf_mgr *ubuf_mgr; + /** flow format packet */ + struct uref *flow_format; + /** ubuf manager request */ + struct urequest ubuf_mgr_request; + + /** output pipe */ + struct upipe *output; + /** flow_definition packet */ + struct uref *flow_def; + /** output state */ + enum upipe_helper_output_state output_state; + /** list of output requests */ + struct uchain request_list; + + /** temporary uref storage (used during urequest) */ + struct uchain urefs; + /** nb urefs in storage */ + unsigned int nb_urefs; + /** max urefs in storage */ + unsigned int max_urefs; + /** list of blockers (used during udeal) */ + struct uchain blockers; + + /** input is already interlaced? */ + bool bypass; + /** output top field first */ + bool tff; + /** drop field? */ + bool drop; + /** last input frame */ + struct uref *uref_last; + /** current input width */ + uint64_t width; + /** current input heigth */ + uint64_t height; + + /** public structure */ + struct upipe upipe; +}; + +UPIPE_HELPER_UPIPE(upipe_interlace, upipe, UPIPE_INTERLACE_SIGNATURE); +UPIPE_HELPER_UREFCOUNT(upipe_interlace, urefcount, upipe_interlace_free) +UPIPE_HELPER_VOID(upipe_interlace) +UPIPE_HELPER_OUTPUT(upipe_interlace, output, flow_def, output_state, + request_list) +UPIPE_HELPER_UBUF_MGR(upipe_interlace, ubuf_mgr, flow_format, ubuf_mgr_request, + upipe_interlace_check, + upipe_interlace_register_output_request, + upipe_interlace_unregister_output_request) +UPIPE_HELPER_INPUT(upipe_interlace, urefs, nb_urefs, max_urefs, blockers, + upipe_interlace_handle) + +/** @internal @This allocates a filter pipe. + * + * @param mgr common management structure + * @param uprobe structure used to raise events + * @param signature signature of the pipe allocator + * @param args optional arguments + * @return pointer to upipe or NULL in case of allocation error + */ +static struct upipe *upipe_interlace_alloc(struct upipe_mgr *mgr, + struct uprobe *uprobe, + uint32_t signature, va_list args) +{ + struct upipe *upipe = + upipe_interlace_alloc_void(mgr, uprobe, signature, args); + if (unlikely(upipe == NULL)) + return NULL; + + upipe_interlace_init_urefcount(upipe); + upipe_interlace_init_ubuf_mgr(upipe); + upipe_interlace_init_output(upipe); + upipe_interlace_init_input(upipe); + + struct upipe_interlace *upipe_interlace = upipe_interlace_from_upipe(upipe); + upipe_interlace->bypass = true; + upipe_interlace->uref_last = NULL; + upipe_interlace->tff = true; + upipe_interlace->drop = true; + + upipe_throw_ready(upipe); + + return upipe; +} + +/** @This frees a upipe. + * + * @param upipe description structure of the pipe + */ +static void upipe_interlace_free(struct upipe *upipe) +{ + struct upipe_interlace *upipe_interlace = upipe_interlace_from_upipe(upipe); + + upipe_throw_dead(upipe); + + uref_free(upipe_interlace->uref_last); + + upipe_interlace_clean_input(upipe); + upipe_interlace_clean_ubuf_mgr(upipe); + upipe_interlace_clean_output(upipe); + upipe_interlace_clean_urefcount(upipe); + upipe_interlace_free_void(upipe); +} + +/** @internal @This processes a picture plane + * + * @param upipe description structure of the pipe + * @param top input frame buffer used as top lines + * @param bottom input frame buffer used as bottom lines + * @param ubuf output frame buffer + * @param chroma chroma plane to interlace + * @return an error code + */ +static int upipe_interlace_plane(struct upipe *upipe, struct uref *top, + struct uref *bottom, struct ubuf *ubuf, + const char *chroma) +{ + struct upipe_interlace *upipe_interlace = upipe_interlace_from_upipe(upipe); + const uint8_t *t_in; + const uint8_t *b_in; + uint8_t *out; + uint8_t t_hsub, b_hsub, out_hsub; + uint8_t t_vsub, b_vsub, out_vsub; + uint8_t t_size, b_size, out_size; + size_t t_stride = 0, b_stride = 0, out_stride = 0; + int ret; + + ret = + uref_pic_plane_size(top, chroma, &t_stride, &t_hsub, &t_vsub, &t_size); + if (unlikely(!ubase_check(ret))) { + upipe_err(upipe, "Could not get top input plane size"); + return ret; + } + + ret = uref_pic_plane_size(bottom, chroma, &b_stride, &b_hsub, &b_vsub, + &b_size); + if (unlikely(!ubase_check(ret))) { + upipe_err(upipe, "Could not get bottom input plane size"); + return ret; + } + + ret = ubuf_pic_plane_size(ubuf, chroma, &out_stride, &out_hsub, &out_vsub, + &out_size); + if (unlikely(!ubase_check(ret))) { + upipe_err(upipe, "Could not get output plane size"); + return ret; + } + + if (!t_hsub || !t_vsub || !t_size) { + upipe_err(upipe, "Invalid input frame"); + return UBASE_ERR_INVALID; + } + + if (t_hsub != b_hsub || t_vsub != b_vsub || t_size != b_size) { + upipe_err(upipe, "Incompatible input frames"); + return UBASE_ERR_INVALID; + } + + if (t_hsub != out_hsub || t_vsub != out_vsub || t_size != out_size) { + upipe_err(upipe, "Incompatible output frame"); + return UBASE_ERR_INVALID; + } + + // map + uref_pic_plane_read(top, chroma, 0, 0, -1, -1, &t_in); + uref_pic_plane_read(bottom, chroma, 0, 0, -1, -1, &b_in); + ubuf_pic_plane_write(ubuf, chroma, 0, 0, -1, -1, &out); + + // interlace plane + uint64_t lines = upipe_interlace->height / t_vsub; + uint64_t size = upipe_interlace->width * t_size / t_hsub; + for (uint64_t l = 0; l < lines; l++) { + memcpy(out, (l % 2 ? b_in : t_in), size); + t_in += t_stride; + b_in += b_stride; + out += out_stride; + } + + // unmap + uref_pic_plane_unmap(top, chroma, 0, 0, -1, -1); + uref_pic_plane_unmap(bottom, chroma, 0, 0, -1, -1); + ubuf_pic_plane_unmap(ubuf, chroma, 0, 0, -1, -1); + + return UBASE_ERR_NONE; +} + +/** @internal @This updates the output flow definition. + * + * @param upipe description structure of the pipe + * @param flow_def input flow definition packet + */ +static void upipe_interlace_set_flow_def_real(struct upipe *upipe, + struct uref *flow_def) +{ + struct upipe_interlace *upipe_interlace = upipe_interlace_from_upipe(upipe); + + uref_free(upipe_interlace->uref_last); + upipe_interlace->uref_last = NULL; + + if (ubase_check(uref_pic_check_progressive(flow_def))) { + upipe_interlace->bypass = false; + struct urational fps; + if (upipe_interlace->drop && + ubase_check(uref_pic_flow_get_fps(flow_def, &fps))) { + fps.den *= 2; + urational_simplify(&fps); + uref_pic_flow_set_fps(flow_def, fps); + } + uref_pic_set_progressive(flow_def, false); + uref_pic_set_tff(flow_def, upipe_interlace->tff); + upipe_interlace_store_flow_def(upipe, NULL); + upipe_interlace_require_ubuf_mgr(upipe, flow_def); + } else { + upipe_interlace->bypass = true; + upipe_interlace_store_flow_def(upipe, flow_def); + } +} + +/** @internal @This checks the input frames + * + * @param upipe description structure of the pipe + * @param top input frame used for top field + * @param bottom input frame used for bottom field + * @return an error code + */ +static int upipe_interlace_check_frames(struct upipe *upipe, struct uref *top, + struct uref *bottom) +{ + struct upipe_interlace *upipe_interlace = upipe_interlace_from_upipe(upipe); + + size_t t_width = 0; + size_t t_height = 0; + uint8_t t_macropixel = 0; + uref_pic_size(top, &t_width, &t_height, &t_macropixel); + + size_t b_width = 0; + size_t b_height = 0; + uint8_t b_macropixel = 0; + uref_pic_size(bottom, &b_width, &b_height, &b_macropixel); + + if (t_width != b_width || t_height != b_height || + t_macropixel != b_macropixel) { + upipe_err(upipe, "Incompatible frames received"); + return UBASE_ERR_INVALID; + } + + upipe_interlace->width = t_width; + upipe_interlace->height = t_height; + + return UBASE_ERR_NONE; +} + +/** @internal @This handles input. + * + * @param upipe description structure of the pipe + * @param uref uref structure + * @param upump_p reference to upump structure + * @return always true + */ +static bool upipe_interlace_handle(struct upipe *upipe, struct uref *uref, + struct upump **upump_p) +{ + struct upipe_interlace *upipe_interlace = upipe_interlace_from_upipe(upipe); + const char *def; + if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) { + upipe_interlace_set_flow_def_real(upipe, uref); + return true; + } + + if (!upipe_interlace->flow_def) { + if (urequest_get_opaque(&upipe_interlace->ubuf_mgr_request, + struct upipe *) == NULL) { + upipe_warn(upipe, "no input flow def received, dropping..."); + uref_free(uref); + return true; + } + + return false; + } + + if (upipe_interlace->bypass) { + upipe_interlace_output(upipe, uref, upump_p); + return true; + } + + if (!upipe_interlace->uref_last) { + upipe_interlace->uref_last = uref; + return true; + } + + // Now process input frames + struct uref *last = upipe_interlace->uref_last; + struct uref *top = upipe_interlace->tff ? last : uref; + struct uref *bottom = upipe_interlace->tff ? uref : last; + upipe_interlace->uref_last = upipe_interlace->drop ? NULL : uref_dup(uref); + if (unlikely( + !ubase_check(upipe_interlace_check_frames(upipe, top, bottom)))) { + uref_free(top); + uref_free(bottom); + return true; + } + + // Allocate output frame + struct ubuf *ubuf = NULL; + if (upipe_interlace->ubuf_mgr) + ubuf = ubuf_pic_alloc(upipe_interlace->ubuf_mgr, upipe_interlace->width, + upipe_interlace->height); + if (unlikely(!ubuf)) { + uref_free(top); + uref_free(bottom); + upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); + return true; + } + + // Interlace planes + const char *chroma; + uref_pic_foreach_plane(uref, chroma) { + int ret = upipe_interlace_plane(upipe, top, bottom, ubuf, chroma); + if (unlikely(!ubase_check(ret))) { + uref_free(top); + uref_free(bottom); + ubuf_free(ubuf); + return true; + } + } + + // Compute output frame duration + uint64_t t_duration = UINT64_MAX; + uint64_t b_duration = UINT64_MAX; + uref_clock_get_duration(top, &t_duration); + uref_clock_get_duration(bottom, &b_duration); + + // Free last frame + uref_free(last); + + // Attach new ubuf to current frame + uref_attach_ubuf(uref, ubuf); + + // Update attributes + uref_pic_set_progressive(uref, false); + uref_pic_set_tff(uref, upipe_interlace->tff); + if (upipe_interlace->drop) { + uref_clock_delete_duration(uref); + if (t_duration != UINT64_MAX && b_duration != UINT64_MAX) + uref_clock_set_duration(uref, t_duration + b_duration); + } + + // Output frame + upipe_interlace_output(upipe, uref, upump_p); + + return true; +} + +/** @internal @This inputs data. + * + * @param upipe description structure of the pipe + * @param uref uref structure + * @param upump_p reference to pump that generated the buffer + */ +static void upipe_interlace_input(struct upipe *upipe, struct uref *uref, + struct upump **upump_p) +{ + if (!upipe_interlace_check_input(upipe)) { + upipe_interlace_hold_input(upipe, uref); + upipe_interlace_block_input(upipe, upump_p); + } else if (!upipe_interlace_handle(upipe, uref, upump_p)) { + upipe_interlace_hold_input(upipe, uref); + upipe_interlace_block_input(upipe, upump_p); + /* Increment upipe refcount to avoid disappearing before all packets + * have been sent. */ + upipe_use(upipe); + } +} + +/** @internal @This checks if the input may start. + * + * @param upipe description structure of the pipe + * @param flow_format amended flow format + * @return an error code + */ +static int upipe_interlace_check(struct upipe *upipe, struct uref *flow_format) +{ + struct upipe_interlace *upipe_interlace = upipe_interlace_from_upipe(upipe); + if (flow_format != NULL) + upipe_interlace_store_flow_def(upipe, flow_format); + + if (upipe_interlace->flow_def == NULL) + return UBASE_ERR_NONE; + + bool was_buffered = !upipe_interlace_check_input(upipe); + upipe_interlace_output_input(upipe); + upipe_interlace_unblock_input(upipe); + if (was_buffered && upipe_interlace_check_input(upipe)) { + /* All packets have been output, release again the pipe that has been + * used in @ref upipe_interlace_input. */ + upipe_release(upipe); + } + return UBASE_ERR_NONE; +} + +/** @internal @This sets the input flow definition. + * + * @param upipe description structure of the pipe + * @param flow_def flow definition packet + * @return an error code + */ +static int upipe_interlace_set_flow_def(struct upipe *upipe, + struct uref *flow_def) +{ + if (flow_def == NULL) + return UBASE_ERR_INVALID; + + UBASE_RETURN(uref_flow_match_def(flow_def, "pic.")) + struct uref *flow_def_dup; + if (unlikely((flow_def_dup = uref_dup(flow_def)) == NULL)) { + upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); + return UBASE_ERR_ALLOC; + } + + upipe_input(upipe, flow_def_dup, NULL); + return UBASE_ERR_NONE; +} + +/** @internal @This handles interlace pipe options. + * + * @param upipe description structure of the pipe + * @param option option name + * @param value option value to set + * @return an error code + */ +static int upipe_interlace_set_option(struct upipe *upipe, const char *option, + const char *value) +{ + struct upipe_interlace *upipe_interlace = upipe_interlace_from_upipe(upipe); + + if (!option || !value) + return UBASE_ERR_INVALID; + + bool v; + if (!strcasecmp(value, "true") || !strcmp(value, "1") || + !strcasecmp(value, "enable")) + v = true; + else if (!strcasecmp(value, "false") || !strcmp(value, "0") || + !strcasecmp(value, "disable")) + v = false; + else + return UBASE_ERR_INVALID; + + if (!strcasecmp(option, "drop")) { + upipe_interlace->drop = v; + } else if (!strcasecmp(option, "tff")) { + upipe_interlace->tff = v; + } else + return UBASE_ERR_INVALID; + + return UBASE_ERR_NONE; +} + +/** @internal @This processes control commands on the pipe. + * + * @param upipe description structure of the pipe + * @param command type of command to process + * @param args arguments of the command + * @return an error code + */ +static int upipe_interlace_control(struct upipe *upipe, int command, + va_list args) +{ + switch (command) { + case UPIPE_REGISTER_REQUEST: { + struct urequest *request = va_arg(args, struct urequest *); + if (request->type == UREQUEST_UBUF_MGR || + request->type == UREQUEST_FLOW_FORMAT) + return upipe_throw_provide_request(upipe, request); + return upipe_interlace_alloc_output_proxy(upipe, request); + } + case UPIPE_UNREGISTER_REQUEST: { + struct urequest *request = va_arg(args, struct urequest *); + if (request->type == UREQUEST_UBUF_MGR || + request->type == UREQUEST_FLOW_FORMAT) + return UBASE_ERR_NONE; + return upipe_interlace_free_output_proxy(upipe, request); + } + case UPIPE_SET_FLOW_DEF: { + struct uref *flow_def = va_arg(args, struct uref *); + return upipe_interlace_set_flow_def(upipe, flow_def); + } + case UPIPE_GET_FLOW_DEF: + case UPIPE_GET_OUTPUT: + case UPIPE_SET_OUTPUT: + return upipe_interlace_control_output(upipe, command, args); + + case UPIPE_SET_OPTION: { + const char *option = va_arg(args, const char *); + const char *value = va_arg(args, const char *); + return upipe_interlace_set_option(upipe, option, value); + } + default: + return UBASE_ERR_UNHANDLED; + } +} + +/** module manager static descriptor */ +static struct upipe_mgr upipe_interlace_mgr = { + .refcount = NULL, + .signature = UPIPE_INTERLACE_SIGNATURE, + + .upipe_alloc = upipe_interlace_alloc, + .upipe_input = upipe_interlace_input, + .upipe_control = upipe_interlace_control +}; + +/** @This returns the management structure for interlace pipes + * + * @return pointer to manager + */ +struct upipe_mgr *upipe_interlace_mgr_alloc(void) +{ + return &upipe_interlace_mgr; +} diff --git a/tests/Build.mk b/tests/Build.mk index c0d3efb12..fd4d6bd57 100644 --- a/tests/Build.mk +++ b/tests/Build.mk @@ -240,6 +240,10 @@ upipe_http_src_test-src = upipe_http_src_test.c upipe_http_src_test-libs = libupipe libupipe_modules libupump_ev upipe_http_src_test-opt-libs = libupipe_bearssl libupipe_openssl +tests += upipe_interlace_test +upipe_interlace_test-src = upipe_interlace_test.c +upipe_interlace_test-libs = libupipe libupipe_modules + tests += upipe_m3u_reader_test.sh upipe_m3u_reader_test.sh-deps = upipe_m3u_reader_test diff --git a/tests/upipe_interlace_test.c b/tests/upipe_interlace_test.c new file mode 100644 index 000000000..df80d03e1 --- /dev/null +++ b/tests/upipe_interlace_test.c @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2026 EasyTools + * + * Authors: Arnaud de Turckheim + * + * SPDX-License-Identifier: MIT + */ + +#undef NDEBUG + +#include "upipe/uprobe.h" +#include "upipe/uprobe_prefix.h" +#include "upipe/uprobe_stdio.h" +#include "upipe/uprobe_ubuf_mem.h" +#include "upipe/umem.h" +#include "upipe/umem_alloc.h" +#include "upipe/udict.h" +#include "upipe/udict_inline.h" +#include "upipe/ubuf.h" +#include "upipe/ubuf_pic_mem.h" +#include "upipe/ubuf_mem.h" +#include "upipe/uref.h" +#include "upipe/uref_pic_flow.h" +#include "upipe/uref_pic.h" +#include "upipe/uref_std.h" +#include "upipe/upipe.h" +#include "upipe/uref_dump.h" +#include "upipe/uref_pic_flow_formats.h" +#include "upipe-modules/upipe_interlace.h" + +#include +#include +#include + +#define UDICT_POOL_DEPTH 5 +#define UREF_POOL_DEPTH 5 +#define UBUF_POOL_DEPTH 5 +#define UBUF_PREPEND 0 +#define UBUF_APPEND 0 +#define UBUF_ALIGN 32 +#define UBUF_ALIGN_HOFFSET 0 +#define UPROBE_LOG_LEVEL UPROBE_LOG_DEBUG + +#define WIDTH 4 +#define HEIGHT 8 + +static struct urational fps = { .num = 25, .den = 1 }; +static struct umem_mgr *umem_mgr = NULL; +static struct uref_mgr *uref_mgr; +static struct upipe_mgr output_mgr; +static struct upipe output; +static int output_counter = 0; +static void (*current_test)(struct upipe *) = NULL; + +static void test_no_input_flow_def(struct upipe *); +static void test_rgb_packed(struct upipe *); +static void test_yuv_planar(struct upipe *); +static void test_yuv_interlaced(struct upipe *); + +static void (*tests[])(struct upipe *) = { + test_no_input_flow_def, + test_rgb_packed, + test_yuv_planar, + test_yuv_interlaced, +}; + +/** definition of our uprobe */ +static int catch(struct uprobe *uprobe, struct upipe *upipe, + int event, va_list args) +{ + switch (event) { + default: + assert(0); + break; + case UPROBE_READY: + case UPROBE_DEAD: + case UPROBE_NEW_FLOW_DEF: + break; + } + return UBASE_ERR_NONE; +} + +static void dump_pic_plane(struct uref *uref, const char *chroma) +{ + size_t height; + size_t width; + uint8_t macro; + uint8_t hsub; + uint8_t vsub; + size_t stride; + uint8_t macropixel; + const uint8_t *buf; + + printf(" chroma %s:\n", chroma); + + ubase_assert(uref_pic_size(uref, &width, &height, ¯o)); + ubase_assert( + uref_pic_plane_size(uref, chroma, &stride, &hsub, &vsub, ¯opixel)); + + assert(hsub && vsub && width && height && macro && stride && macropixel); + + ubase_assert(uref_pic_plane_read(uref, chroma, 0, 0, -1, -1, &buf)); + for (uint64_t y = 0; y < (height / vsub); y++) { + for (uint64_t x = 0; x < width * macropixel / hsub; x += macropixel) { + printf(x ? " " : " "); + for (uint8_t o = 0; o < macropixel; o++) + printf("%x", buf[x + o]); + } + printf("\n"); + buf += stride; + } + uref_pic_plane_unmap(uref, chroma, 0, 0, -1, -1); +} + +static void dump_pic(struct uref *uref, const char *name) +{ + printf("%s:\n", name); + const char *chroma; + uref_pic_foreach_plane(uref, chroma) { + dump_pic_plane(uref, chroma); + } +} + +static struct uref *pic_alloc(struct ubuf_mgr *ubuf_mgr, unsigned counter) +{ + struct uref *uref = uref_pic_alloc(uref_mgr, ubuf_mgr, WIDTH, HEIGHT); + assert(uref); + ubase_assert(uref_pic_set_progressive(uref, true)); + + size_t width; + size_t height; + uint8_t macro; + ubase_assert(uref_pic_size(uref, &width, &height, ¯o)); + assert(width && height); + + const char *chroma; + uref_pic_foreach_plane(uref, chroma) { + uint8_t hsub; + uint8_t vsub; + uint8_t macropixel; + size_t stride; + uint8_t *buf; + + ubase_assert(uref_pic_plane_write(uref, chroma, 0, 0, -1, -1, &buf)); + ubase_assert(uref_pic_plane_size(uref, chroma, &stride, &hsub, &vsub, + ¯opixel)); + assert(stride && hsub && vsub); + + unsigned lines = height / vsub; + unsigned size = width * macropixel / hsub; + for (uint64_t y = 0; y < lines; y++) { + for (uint64_t x = 0; x < size; x++) + buf[x] = counter; + buf += stride; + } + uref_pic_plane_unmap(uref, chroma, 0, 0, -1, -1); + } + return uref; +} + +static int output_control(struct upipe *upipe, int command, va_list args) +{ + assert(current_test != test_no_input_flow_def); + + switch (command) { + case UPIPE_SET_FLOW_DEF: { + struct uref *flow_def = va_arg(args, struct uref *); + uref_dump_notice(flow_def, upipe->uprobe); + ubase_nassert(uref_pic_check_progressive(flow_def)); + output_counter = 0; + return UBASE_ERR_NONE; + } + } + return UBASE_ERR_UNHANDLED; +} + +static void output_input(struct upipe *upipe, struct uref *uref, + struct upump **upump_p) +{ + char *name = NULL; + + assert(current_test != test_no_input_flow_def); + if (current_test == test_no_input_flow_def) + + uref_dump(uref, upipe->uprobe); + assert(asprintf(&name, "output %i", output_counter) > 0); + dump_pic(uref, name); + free(name); + ubase_nassert(uref_pic_check_progressive(uref)); + uref_free(uref); + + output_counter++; +} + +static struct ubuf_mgr *test_alloc_ubuf_mgr(struct uref *flow_def) +{ + assert(flow_def); + + struct ubuf_mgr *ubuf_mgr = ubuf_mem_mgr_alloc_from_flow_def( + UBUF_POOL_DEPTH, UBUF_POOL_DEPTH, umem_mgr, flow_def); + assert(ubuf_mgr); + + return ubuf_mgr; +} + +static void test_no_input_flow_def(struct upipe *upipe) +{ + struct uref *flow_def = uref_pic_flow_alloc_rgb24(uref_mgr); + struct ubuf_mgr *ubuf_mgr = test_alloc_ubuf_mgr(flow_def); + uref_free(flow_def); + + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + + printf("Sending pic %d\n", counter); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubuf_mgr_release(ubuf_mgr); +} + +static void test_rgb_packed(struct upipe *upipe) +{ + struct uref *flow_def = uref_pic_flow_alloc_rgb24(uref_mgr); + struct ubuf_mgr *ubuf_mgr = test_alloc_ubuf_mgr(flow_def); + ubase_assert(uref_pic_set_progressive(flow_def, true)); + + ubase_assert(upipe_set_option(upipe, "drop", "true")); + ubase_assert(upipe_set_option(upipe, "tff", "true")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubase_assert(upipe_set_option(upipe, "drop", "false")); + ubase_assert(upipe_set_option(upipe, "tff", "false")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubase_assert(uref_pic_flow_set_fps(flow_def, fps)); + + ubase_assert(upipe_set_option(upipe, "drop", "true")); + ubase_assert(upipe_set_option(upipe, "tff", "false")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubase_assert(upipe_set_option(upipe, "drop", "false")); + ubase_assert(upipe_set_option(upipe, "tff", "true")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubuf_mgr_release(ubuf_mgr); + uref_free(flow_def); +} + +static void test_yuv_planar(struct upipe *upipe) +{ + struct uref *flow_def = uref_pic_flow_alloc_yuv420p(uref_mgr); + struct ubuf_mgr *ubuf_mgr = test_alloc_ubuf_mgr(flow_def); + ubase_assert(uref_pic_set_progressive(flow_def, true)); + + ubase_assert(upipe_set_option(upipe, "drop", "true")); + ubase_assert(upipe_set_option(upipe, "tff", "true")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubase_assert(upipe_set_option(upipe, "drop", "false")); + ubase_assert(upipe_set_option(upipe, "tff", "false")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubase_assert(uref_pic_flow_set_fps(flow_def, fps)); + + ubase_assert(upipe_set_option(upipe, "drop", "true")); + ubase_assert(upipe_set_option(upipe, "tff", "false")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubase_assert(upipe_set_option(upipe, "drop", "false")); + ubase_assert(upipe_set_option(upipe, "tff", "true")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubuf_mgr_release(ubuf_mgr); + uref_free(flow_def); +} + +static void test_yuv_interlaced(struct upipe *upipe) +{ + struct uref *flow_def = uref_pic_flow_alloc_yuv420p(uref_mgr); + struct ubuf_mgr *ubuf_mgr = test_alloc_ubuf_mgr(flow_def); + ubase_assert(uref_pic_set_progressive(flow_def, false)); + + ubase_assert(upipe_set_option(upipe, "drop", "true")); + ubase_assert(upipe_set_option(upipe, "tff", "true")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + uref_pic_set_progressive(uref, false); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubase_assert(upipe_set_option(upipe, "drop", "false")); + ubase_assert(upipe_set_option(upipe, "tff", "false")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + uref_pic_set_progressive(uref, false); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubase_assert(uref_pic_flow_set_fps(flow_def, fps)); + + ubase_assert(upipe_set_option(upipe, "drop", "true")); + ubase_assert(upipe_set_option(upipe, "tff", "false")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + uref_pic_set_progressive(uref, false); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubase_assert(upipe_set_option(upipe, "drop", "false")); + ubase_assert(upipe_set_option(upipe, "tff", "true")); + ubase_assert(upipe_set_flow_def(upipe, flow_def)); + for (int counter = 0; counter < 10; counter++) { + struct uref *uref = pic_alloc(ubuf_mgr, counter); + uref_pic_set_progressive(uref, false); + + char *name; + assert(asprintf(&name, "input %i", counter) > 0); + dump_pic(uref, name); + free(name); + upipe_input(upipe, uref, NULL); + } + + ubuf_mgr_release(ubuf_mgr); + uref_free(flow_def); +} + +int main(int argc, char **argv) +{ + printf("Compiled %s %s (%s)\n", __DATE__, __TIME__, __FILE__); + + /* upipe env */ + umem_mgr = umem_alloc_mgr_alloc(); + assert(umem_mgr != NULL); + struct udict_mgr *udict_mgr = udict_inline_mgr_alloc(UDICT_POOL_DEPTH, umem_mgr, -1, -1); + assert(udict_mgr != NULL); + uref_mgr = uref_std_mgr_alloc(UREF_POOL_DEPTH, udict_mgr, 0); + assert(uref_mgr != NULL); + + struct uprobe uprobe; + uprobe_init(&uprobe, catch, NULL); + struct uprobe *logger = uprobe_stdio_alloc(&uprobe, stdout, + UPROBE_LOG_LEVEL); + assert(logger != NULL); + logger = uprobe_ubuf_mem_alloc(logger, umem_mgr, UBUF_POOL_DEPTH, + UBUF_POOL_DEPTH); + assert(logger != NULL); + + /* output pipe */ + upipe_mgr_init(&output_mgr); + output_mgr.upipe_input = output_input; + output_mgr.upipe_control = output_control; + + upipe_init(&output, upipe_mgr_use(&output_mgr), uprobe_use(logger)); + + /* interlace */ + struct upipe_mgr *upipe_interlace_mgr = upipe_interlace_mgr_alloc(); + struct upipe *upipe_interlace = upipe_void_alloc( + upipe_interlace_mgr, + uprobe_pfx_alloc(uprobe_use(logger), UPROBE_LOG_LEVEL, "interlace")); + assert(upipe_interlace); + ubase_assert(upipe_set_output(upipe_interlace, &output)); + + for (unsigned i = 0; i < UBASE_ARRAY_SIZE(tests); i++) { + current_test = tests[i]; + current_test(upipe_interlace); + } + + upipe_release(upipe_interlace); + upipe_clean(&output); + + upipe_mgr_release(upipe_interlace_mgr); + uref_mgr_release(uref_mgr); + uprobe_release(logger); + uprobe_clean(&uprobe); + udict_mgr_release(udict_mgr); + umem_mgr_release(umem_mgr); + return 0; +}