Skip to content

Commit 2275745

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 d73bcb4 commit 2275745

1 file changed

Lines changed: 188 additions & 109 deletions

File tree

lib/upipe-modules/upipe_sync.c

Lines changed: 188 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 %"PRIu64"", ulist_depth(&upipe_sync->urefs));
788801
}
789802

790803
/* sync audio */
@@ -795,56 +808,78 @@ 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+
} else {
870+
uchain = ulist_peek(&upipe_sync->urefs);
871+
if (uchain) {
872+
uref = uref_from_uchain(uchain);
873+
if (uref) {
874+
uint64_t pts = 0;
875+
uref_clock_get_pts_sys(uref, &pts);
876+
upipe_sync->pts = pts + upipe_sync->latency;
877+
878+
uint64_t wait = pts - now;
879+
upipe_sync_wait_upump(upipe, wait, cb);
880+
}
881+
}
882+
}
848883
}
849884

850885
/** @internal @This receives data.
@@ -883,7 +918,7 @@ static void upipe_sync_sub_input(struct upipe *upipe, struct uref *uref,
883918
size_t samples = 0;
884919
uref_sound_size(uref, &samples, NULL);
885920
upipe_sync_sub->samples += samples;
886-
//upipe_notice_va(upipe, "push, samples %" PRIu64, upipe_sync_sub->samples);
921+
upipe_notice_va(upipe, "push in samples %zu, queued samples %" PRIu64, samples, upipe_sync_sub->samples);
887922

888923
ulist_add(&upipe_sync_sub->urefs, uref_to_uchain(uref));
889924

@@ -928,46 +963,63 @@ static void upipe_sync_input(struct upipe *upipe, struct uref *uref,
928963
return;
929964
}
930965
pts += upipe_sync->latency;
931-
932966
uint64_t now = uclock_now(upipe_sync->uclock);
933967

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-
}
968+
uint64_t wait;
969+
if (upipe_sync->frame_sync) {
970+
/* reject late pics */
971+
if (now != UINT64_MAX && now > pts) {
972+
uint64_t cr = 0;
973+
uref_clock_get_cr_sys(uref, &cr);
974+
upipe_err_va(upipe, "%s() picture too late by %" PRIu64 "ms, drop pic, recept %" PRIu64 "",
975+
__func__, (now - pts) / 27000, (now - cr) / 27000);
976+
uref_free(uref);
977+
return;
978+
}
943979

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

946-
/* buffer pic */
947-
ulist_add(&upipe_sync->urefs, uref_to_uchain(uref));
948-
upipe_sync->buffered_frames++;
982+
/* buffer pic */
983+
ulist_add(&upipe_sync->urefs, uref_to_uchain(uref));
984+
upipe_sync->buffered_frames++;
949985

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-
}
986+
/* limit buffered frames */
987+
if (unlikely(upipe_sync->buffered_frames >= MAX_VIDEO_FRAMES)) {
988+
ulist_uref_flush(&upipe_sync->urefs);
989+
upipe_sync->buffered_frames = 0;
990+
}
955991

956-
/* timer already active */
957-
if (upipe_sync->upump)
958-
return;
992+
/* timer already active */
993+
if (upipe_sync->upump)
994+
return;
959995

960-
/* need upump mgr */
961-
if (!ubase_check(upipe_sync_check_upump_mgr(upipe_sync_to_upipe(upipe_sync))))
962-
return;
996+
/* need upump mgr */
997+
if (!ubase_check(upipe_sync_check_upump_mgr(upipe_sync_to_upipe(upipe_sync))))
998+
return;
963999

964-
/* start timer */
965-
uint64_t wait;
966-
if (now == UINT64_MAX)
967-
wait = upipe_sync->ticks_per_frame;
968-
else
1000+
/* start timer */
1001+
if (now == UINT64_MAX)
1002+
wait = upipe_sync->ticks_per_frame;
1003+
else
1004+
wait = pts - now;
1005+
}
1006+
else {
9691007
wait = pts - now;
9701008

1009+
/* too old frames */
1010+
if (now > pts + upipe_sync->ticks_per_frame) {
1011+
uref_free(uref);
1012+
return;
1013+
}
1014+
1015+
/* buffer pic */
1016+
ulist_add(&upipe_sync->urefs, uref_to_uchain(uref));
1017+
1018+
/* need upump mgr */
1019+
if (!ubase_check(upipe_sync_check_upump_mgr(upipe_sync_to_upipe(upipe_sync))))
1020+
return;
1021+
}
1022+
9711023
upipe_sync->pts = pts;
9721024
upipe_sync_wait_upump(upipe_sync_to_upipe(upipe_sync), wait, cb);
9731025
}
@@ -997,6 +1049,7 @@ static struct upipe *upipe_sync_alloc(struct upipe_mgr *mgr,
9971049
upipe_sync->buffered_frames = 0;
9981050
upipe_sync->uref = NULL;
9991051
ulist_init(&upipe_sync->urefs);
1052+
upipe_sync->frame_sync = 1; /* Old frame sync behaviour by default */
10001053

10011054
upipe_sync_init_urefcount(upipe);
10021055
upipe_sync_init_uclock(upipe);
@@ -1059,6 +1112,28 @@ static int upipe_sync_set_flow_def(struct upipe *upipe, struct uref *flow_def)
10591112
return UBASE_ERR_NONE;
10601113
}
10611114

1115+
/** @internal @This sets the content of an sync option.
1116+
*
1117+
* @param upipe description structure of the pipe
1118+
* @param option name of the option
1119+
* @param content content of the option
1120+
* @return an error code
1121+
*/
1122+
static int upipe_sync_set_option(struct upipe *upipe,
1123+
const char *option, const char *content)
1124+
{
1125+
struct upipe_sync *upipe_sync = upipe_sync_from_upipe(upipe);
1126+
if (!option || !content)
1127+
return UBASE_ERR_INVALID;
1128+
1129+
if (!strcmp(option, "frame-sync"))
1130+
upipe_sync->frame_sync = atoi(content);
1131+
else
1132+
return UBASE_ERR_INVALID;
1133+
1134+
return UBASE_ERR_NONE;
1135+
}
1136+
10621137
/** @internal @This processes control commands.
10631138
*
10641139
* @param upipe description structure of the pipe
@@ -1081,7 +1156,11 @@ static int upipe_sync_control(struct upipe *upipe, int command, va_list args)
10811156
return UBASE_ERR_NONE;
10821157
case UPIPE_ATTACH_UPUMP_MGR:
10831158
return upipe_sync_attach_upump_mgr(upipe);
1084-
1159+
case UPIPE_SET_OPTION: {
1160+
const char *option = va_arg(args, const char *);
1161+
const char *content = va_arg(args, const char *);
1162+
return upipe_sync_set_option(upipe, option, content);
1163+
}
10851164
default:
10861165
return UBASE_ERR_UNHANDLED;
10871166
}

0 commit comments

Comments
 (0)