Skip to content

Commit 1044e3b

Browse files
committed
upipe_sync: Separate frame duplication into a "frame_sync" mode.
This allows for a mode which receives audio and video and outputs the correct audio and video but doesn't retime to a local clock
1 parent b653b0f commit 1044e3b

File tree

1 file changed

+175
-109
lines changed

1 file changed

+175
-109
lines changed

lib/upipe-modules/upipe_sync.c

Lines changed: 175 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/*
2-
* Copyright (C) 2017 Open Broadcast Systems Ltd
2+
* Copyright (C) 2017-2021 Open Broadcast Systems Ltd
33
*
44
* Authors: Rafaël Carré
5+
* Kieran Kunhya
56
*
67
* Permission is hereby granted, free of charge, to any person obtaining
78
* a copy of this software and associated documentation files (the
@@ -96,6 +97,9 @@ struct upipe_sync {
9697
/** ntsc */
9798
uint8_t frame_idx;
9899

100+
/* framesync */
101+
int frame_sync;
102+
99103
/** public upipe structure */
100104
struct upipe upipe;
101105
};
@@ -469,7 +473,7 @@ static bool sync_channel(struct upipe *upipe)
469473
} else {
470474
float f = (float)((int64_t)pts - (int64_t)video_pts) * 1000 / UCLOCK_FREQ;
471475
upipe_notice_va(upipe_sync_sub_to_upipe(upipe_sync_sub),
472-
"DROP %.2f, duration in CLOCK %" PRIu64 "", f, duration);
476+
"DROP %.2f, duration in CLOCK %" PRIu64 " samples %zu", f, duration, samples);
473477
ulist_delete(uchain_uref);
474478
uref_free(uref);
475479
upipe_sync_sub->samples -= samples;
@@ -713,7 +717,8 @@ static void output_sound(struct upipe *upipe, const struct urational *fps,
713717

714718
src_samples -= uref_samples;
715719
samples -= uref_samples;
716-
upipe_sync_sub->samples -= uref_samples;
720+
if (upipe_sync_sub->samples >= uref_samples)
721+
upipe_sync_sub->samples -= uref_samples;
717722
//upipe_notice_va(upipe_sub, "pop, samples %" PRIu64, upipe_sync_sub->samples);
718723

719724
if (src_samples == 0) {
@@ -747,44 +752,52 @@ static void cb(struct upump *upump)
747752
struct upipe_sync *upipe_sync = upipe_sync_from_upipe(upipe);
748753

749754
uint64_t now = uclock_now(upipe_sync->uclock);
750-
if (now - upipe_sync->ticks_per_frame > upipe_sync->pts)
751-
upipe_dbg_va(upipe, "cb after %" PRId64 "ms",
752-
(int64_t)((int64_t)now - (int64_t)upipe_sync->pts) / 27000);
755+
756+
if (upipe_sync->frame_sync) {
757+
if (now - upipe_sync->ticks_per_frame > upipe_sync->pts)
758+
upipe_dbg_va(upipe, "cb after %" PRId64 "ms",
759+
(int64_t)((int64_t)now - (int64_t)upipe_sync->pts) / 27000);
760+
}
753761

754762
now = upipe_sync->pts; // the upump was scheduled for now
755763
struct uchain *uchain = NULL;
756-
for (;;) {
757-
uchain = ulist_peek(&upipe_sync->urefs);
758-
upipe_throw(upipe, UPROBE_SYNC_PICTURE, UPIPE_SYNC_SIGNATURE, !!uchain);
759-
if (!uchain)
760-
break;
761-
762-
struct uref *uref = uref_from_uchain(uchain);
763-
uint64_t pts = 0;
764-
uref_clock_get_pts_sys(uref, &pts);
765-
pts += upipe_sync->latency;
764+
if (upipe_sync->frame_sync) {
765+
for (;;) {
766+
uchain = ulist_peek(&upipe_sync->urefs);
767+
upipe_throw(upipe, UPROBE_SYNC_PICTURE, UPIPE_SYNC_SIGNATURE, !!uchain);
768+
if (!uchain)
769+
break;
770+
771+
struct uref *uref = uref_from_uchain(uchain);
772+
uint64_t pts = 0;
773+
uref_clock_get_pts_sys(uref, &pts);
774+
pts += upipe_sync->latency;
775+
776+
/* frame duration */
777+
const uint64_t ticks = upipe_sync->ticks_per_frame;
778+
779+
if (pts < now - ticks / 2) {
780+
/* frame pts too much in the past */
781+
upipe_warn_va(upipe, "too late");
782+
} else if (pts > now + ticks / 2) {
783+
upipe_warn_va(upipe, "video too early: %.2f > %.2f",
784+
pts_to_time(pts), pts_to_time(now + ticks / 2)
785+
);
786+
uchain = NULL; /* do not drop */
787+
break;
788+
} else {
789+
break; // ok
790+
}
766791

767-
/* frame duration */
768-
const uint64_t ticks = upipe_sync->ticks_per_frame;
769-
770-
if (pts < now - ticks / 2) {
771-
/* frame pts too much in the past */
772-
upipe_warn_va(upipe, "too late");
773-
} else if (pts > now + ticks / 2) {
774-
upipe_warn_va(upipe, "video too early: %.2f > %.2f",
775-
pts_to_time(pts), pts_to_time(now + ticks / 2)
776-
);
777-
uchain = NULL; /* do not drop */
778-
break;
779-
} else {
780-
break; // ok
792+
ulist_pop(&upipe_sync->urefs);
793+
uref_free(uref);
794+
upipe_sync->buffered_frames--;
795+
int64_t u = pts - now;
796+
upipe_err_va(upipe, "Drop pic (pts-now == %" PRId64 "ms)", u / 27000);
781797
}
782-
783-
ulist_pop(&upipe_sync->urefs);
784-
uref_free(uref);
785-
upipe_sync->buffered_frames--;
786-
int64_t u = pts - now;
787-
upipe_err_va(upipe, "Drop pic (pts-now == %" PRId64 "ms)", u / 27000);
798+
}
799+
else {
800+
upipe_dbg_va(upipe, "queued %zu", ulist_depth(&upipe_sync->urefs));
788801
}
789802

790803
/* sync audio */
@@ -795,56 +808,65 @@ static void cb(struct upump *upump)
795808
/* output audio */
796809
output_sound(upipe_sync_to_upipe(upipe_sync), &upipe_sync->fps, NULL);
797810

798-
/* output pic */
799-
if (uchain) {
800-
ulist_pop(&upipe_sync->urefs);
801-
/* buffer picture */
802-
uref_free(upipe_sync->uref);
803-
upipe_sync->buffered_frames--;
804-
upipe_sync->uref = uref_from_uchain(uchain);
805-
} else {
806-
upipe_dbg_va(upipe, "no picture, repeating last one");
807-
}
808-
809811
struct uref *uref = NULL;
810-
if (upipe_sync->uref) {
811-
uref = uref_dup(upipe_sync->uref);
812-
uref_clock_set_pts_sys(uref, upipe_sync->pts - upipe_sync->latency);
813-
}
812+
if (upipe_sync->frame_sync) {
813+
/* output pic */
814+
if (uchain) {
815+
ulist_pop(&upipe_sync->urefs);
816+
/* buffer picture */
817+
uref_free(upipe_sync->uref);
818+
upipe_sync->buffered_frames--;
819+
upipe_sync->uref = uref_from_uchain(uchain);
820+
} else {
821+
upipe_dbg_va(upipe, "no picture, repeating last one");
822+
}
814823

815-
if (0) {
816-
now = uclock_now(upipe_sync->uclock);
817-
upipe_notice_va(upipe,
818-
"output %.2f now %.2f latency %" PRIu64,
819-
pts_to_time(upipe_sync->pts - upipe_sync->latency),
820-
pts_to_time(now),
821-
upipe_sync->latency / 27000
822-
);
824+
if (upipe_sync->uref) {
825+
uref = uref_dup(upipe_sync->uref);
826+
uref_clock_set_pts_sys(uref, upipe_sync->pts - upipe_sync->latency);
827+
}
828+
829+
if (0) {
830+
now = uclock_now(upipe_sync->uclock);
831+
upipe_notice_va(upipe,
832+
"output %.2f now %.2f latency %" PRIu64,
833+
pts_to_time(upipe_sync->pts - upipe_sync->latency),
834+
pts_to_time(now),
835+
upipe_sync->latency / 27000
836+
);
837+
}
838+
}
839+
else {
840+
uchain = ulist_pop(&upipe_sync->urefs);
841+
uref = uref_from_uchain(uchain);
823842
}
824843

825844
if (uref)
826845
upipe_sync_output(upipe, uref, NULL);
827846

828-
/* increment pts */
829-
upipe_sync->pts += upipe_sync->ticks_per_frame;
830-
831-
/* schedule next pic */
832-
now = uclock_now(upipe_sync->uclock);
833-
if (now != UINT64_MAX && now > upipe_sync->pts) {
834-
uint64_t diff = now - upipe_sync->pts;
835-
diff += upipe_sync->ticks_per_frame - 1;
836-
diff /= upipe_sync->ticks_per_frame;
837-
upipe_err_va(upipe, "skipping %"PRIu64" beats", diff);
838-
upipe_sync->pts += diff * upipe_sync->ticks_per_frame;
839-
}
847+
/* In non framesync mode scheduling is based on when video frame arrives */
848+
if (upipe_sync->frame_sync) {
849+
/* increment pts */
850+
upipe_sync->pts += upipe_sync->ticks_per_frame;
840851

841-
uint64_t wait;
842-
if (now == UINT64_MAX)
843-
wait = upipe_sync->ticks_per_frame;
844-
else
845-
wait = upipe_sync->pts - now;
852+
/* schedule next pic */
853+
now = uclock_now(upipe_sync->uclock);
854+
if (now != UINT64_MAX && now > upipe_sync->pts) {
855+
uint64_t diff = now - upipe_sync->pts;
856+
diff += upipe_sync->ticks_per_frame - 1;
857+
diff /= upipe_sync->ticks_per_frame;
858+
upipe_err_va(upipe, "skipping %"PRIu64" beats", diff);
859+
upipe_sync->pts += diff * upipe_sync->ticks_per_frame;
860+
}
861+
862+
uint64_t wait;
863+
if (now == UINT64_MAX)
864+
wait = upipe_sync->ticks_per_frame;
865+
else
866+
wait = upipe_sync->pts - now;
846867

847-
upipe_sync_wait_upump(upipe, wait, cb);
868+
upipe_sync_wait_upump(upipe, wait, cb);
869+
}
848870
}
849871

850872
/** @internal @This receives data.
@@ -883,7 +905,7 @@ static void upipe_sync_sub_input(struct upipe *upipe, struct uref *uref,
883905
size_t samples = 0;
884906
uref_sound_size(uref, &samples, NULL);
885907
upipe_sync_sub->samples += samples;
886-
//upipe_notice_va(upipe, "push, samples %" PRIu64, upipe_sync_sub->samples);
908+
upipe_notice_va(upipe, "push in samples %zu, queued samples %" PRIu64, samples, upipe_sync_sub->samples);
887909

888910
ulist_add(&upipe_sync_sub->urefs, uref_to_uchain(uref));
889911

@@ -928,46 +950,63 @@ static void upipe_sync_input(struct upipe *upipe, struct uref *uref,
928950
return;
929951
}
930952
pts += upipe_sync->latency;
931-
932953
uint64_t now = uclock_now(upipe_sync->uclock);
933954

934-
/* reject late pics */
935-
if (now != UINT64_MAX && now > pts) {
936-
uint64_t cr = 0;
937-
uref_clock_get_cr_sys(uref, &cr);
938-
upipe_err_va(upipe, "%s() picture too late by %" PRIu64 "ms, drop pic, recept %" PRIu64 "",
939-
__func__, (now - pts) / 27000, (now - cr) / 27000);
940-
uref_free(uref);
941-
return;
942-
}
955+
uint64_t wait;
956+
if (upipe_sync->frame_sync) {
957+
/* reject late pics */
958+
if (now != UINT64_MAX && now > pts) {
959+
uint64_t cr = 0;
960+
uref_clock_get_cr_sys(uref, &cr);
961+
upipe_err_va(upipe, "%s() picture too late by %" PRIu64 "ms, drop pic, recept %" PRIu64 "",
962+
__func__, (now - pts) / 27000, (now - cr) / 27000);
963+
uref_free(uref);
964+
return;
965+
}
943966

944-
//upipe_dbg_va(upipe, "push PTS in %" PRIu64 " ms", (pts - now) / 27000);
967+
//upipe_dbg_va(upipe, "push PTS in %" PRIu64 " ms", (pts - now) / 27000);
945968

946-
/* buffer pic */
947-
ulist_add(&upipe_sync->urefs, uref_to_uchain(uref));
948-
upipe_sync->buffered_frames++;
969+
/* buffer pic */
970+
ulist_add(&upipe_sync->urefs, uref_to_uchain(uref));
971+
upipe_sync->buffered_frames++;
949972

950-
/* limit buffered frames */
951-
if (unlikely(upipe_sync->buffered_frames >= MAX_VIDEO_FRAMES)) {
952-
ulist_uref_flush(&upipe_sync->urefs);
953-
upipe_sync->buffered_frames = 0;
954-
}
973+
/* limit buffered frames */
974+
if (unlikely(upipe_sync->buffered_frames >= MAX_VIDEO_FRAMES)) {
975+
ulist_uref_flush(&upipe_sync->urefs);
976+
upipe_sync->buffered_frames = 0;
977+
}
955978

956-
/* timer already active */
957-
if (upipe_sync->upump)
958-
return;
979+
/* timer already active */
980+
if (upipe_sync->upump)
981+
return;
959982

960-
/* need upump mgr */
961-
if (!ubase_check(upipe_sync_check_upump_mgr(upipe_sync_to_upipe(upipe_sync))))
962-
return;
983+
/* need upump mgr */
984+
if (!ubase_check(upipe_sync_check_upump_mgr(upipe_sync_to_upipe(upipe_sync))))
985+
return;
963986

964-
/* start timer */
965-
uint64_t wait;
966-
if (now == UINT64_MAX)
967-
wait = upipe_sync->ticks_per_frame;
968-
else
987+
/* start timer */
988+
if (now == UINT64_MAX)
989+
wait = upipe_sync->ticks_per_frame;
990+
else
991+
wait = pts - now;
992+
}
993+
else {
969994
wait = pts - now;
970995

996+
/* too old frames */
997+
if (now > pts + upipe_sync->ticks_per_frame) {
998+
uref_free(uref);
999+
return;
1000+
}
1001+
1002+
/* buffer pic */
1003+
ulist_add(&upipe_sync->urefs, uref_to_uchain(uref));
1004+
1005+
/* need upump mgr */
1006+
if (!ubase_check(upipe_sync_check_upump_mgr(upipe_sync_to_upipe(upipe_sync))))
1007+
return;
1008+
}
1009+
9711010
upipe_sync->pts = pts;
9721011
upipe_sync_wait_upump(upipe_sync_to_upipe(upipe_sync), wait, cb);
9731012
}
@@ -997,6 +1036,7 @@ static struct upipe *upipe_sync_alloc(struct upipe_mgr *mgr,
9971036
upipe_sync->buffered_frames = 0;
9981037
upipe_sync->uref = NULL;
9991038
ulist_init(&upipe_sync->urefs);
1039+
upipe_sync->frame_sync = 1; /* Old frame sync behaviour by default */
10001040

10011041
upipe_sync_init_urefcount(upipe);
10021042
upipe_sync_init_uclock(upipe);
@@ -1059,6 +1099,28 @@ static int upipe_sync_set_flow_def(struct upipe *upipe, struct uref *flow_def)
10591099
return UBASE_ERR_NONE;
10601100
}
10611101

1102+
/** @internal @This sets the content of an sync option.
1103+
*
1104+
* @param upipe description structure of the pipe
1105+
* @param option name of the option
1106+
* @param content content of the option
1107+
* @return an error code
1108+
*/
1109+
static int upipe_sync_set_option(struct upipe *upipe,
1110+
const char *option, const char *content)
1111+
{
1112+
struct upipe_sync *upipe_sync = upipe_sync_from_upipe(upipe);
1113+
if (!option || !content)
1114+
return UBASE_ERR_INVALID;
1115+
1116+
if (!strcmp(option, "frame-sync"))
1117+
upipe_sync->frame_sync = atoi(content);
1118+
else
1119+
return UBASE_ERR_INVALID;
1120+
1121+
return UBASE_ERR_NONE;
1122+
}
1123+
10621124
/** @internal @This processes control commands.
10631125
*
10641126
* @param upipe description structure of the pipe
@@ -1081,7 +1143,11 @@ static int upipe_sync_control(struct upipe *upipe, int command, va_list args)
10811143
return UBASE_ERR_NONE;
10821144
case UPIPE_ATTACH_UPUMP_MGR:
10831145
return upipe_sync_attach_upump_mgr(upipe);
1084-
1146+
case UPIPE_SET_OPTION: {
1147+
const char *option = va_arg(args, const char *);
1148+
const char *content = va_arg(args, const char *);
1149+
return upipe_sync_set_option(upipe, option, content);
1150+
}
10851151
default:
10861152
return UBASE_ERR_UNHANDLED;
10871153
}

0 commit comments

Comments
 (0)