Vula kohore në kohë reale
Grafikët llogaritës MediaPipe përdoren shpesh për të përpunuar transmetimet e kornizave video ose audio për aplikacione interaktive. Korniza MediaPipe kërkon vetëm që paketave të njëpasnjëshme të caktohen stampa kohore në mënyrë monotonike në rritje. Sipas marrëveshjes, kalkulatorët dhe grafikët në kohë reale përdorin kohën e regjistrimit ose kohën e prezantimit të çdo kuadri si vulën e tij kohore, me çdo vulë kohore që tregon mikrosekonda që nga Jan/1/1970:00:00:00
. Kjo lejon që paketat nga burime të ndryshme të përpunohen në një sekuencë të qëndrueshme globalisht.
Planifikimi në kohë reale
Normalisht, çdo Llogaritësi funksionon sapo të gjitha paketat e tij hyrëse për një vulë kohore të caktuar bëhen të disponueshme. Normalisht, kjo ndodh kur kalkulatori ka përfunduar përpunimin e kornizës së mëparshme dhe secila prej kalkulatorëve që prodhon hyrjet e saj ka përfunduar përpunimin e kornizës aktuale. Planifikuesi MediaPipe thërret çdo kalkulator sapo të plotësohen këto kushte. Shikoni Sinkronizimi për më shumë detaje.
Kufijtë e vulës kohore
Kur një kalkulator nuk prodhon asnjë pako dalëse për një vulë kohore të caktuar, në vend të kësaj mund të nxjerrë një "vulë kohore të lidhur" që tregon se asnjë paketë nuk do të prodhohet për atë vulë kohore. Ky tregues është i nevojshëm për të lejuar që llogaritësit e rrjedhës së poshtme të funksionojnë në atë vulë kohore, edhe pse asnjë paketë nuk ka mbërritur për transmetime të caktuara për atë vulë kohore. Kjo është veçanërisht e rëndësishme për grafikët në kohë reale në aplikacionet interaktive, ku është thelbësore që çdo kalkulator të fillojë përpunimin sa më shpejt të jetë e mundur.
Konsideroni një grafik si më poshtë:
node {
calculator: "A"
input_stream: "alpha_in"
output_stream: "alpha"
}
node {
calculator: "B"
input_stream: "alpha"
input_stream: "foo"
output_stream: "beta"
}
Supozoni: në vulën kohore T
, nyja A
nuk dërgon një paketë në rrjedhën e saj dalëse alpha
. Nyja B
merr një paketë në foo
në vulën kohore T
dhe është duke pritur për një paketë në alpha
në vulën kohore T
Nëse A
nuk i dërgon B
një përditësim të lidhur me vulën kohore për alpha
, B
do të vazhdojë të presë që një paketë të arrijë në alpha
. Ndërkohë, radha e paketave të foo
do të grumbullojë paketa në T
, T+1
e kështu me radhë.
Për të nxjerrë një paketë në një transmetim, një kalkulator përdor funksionet API CalculatorContext::Outputs
dhe OutputStream::Add
. Për të nxjerrë një vulë kohore të lidhur në një transmetim, një makinë llogaritëse mund të përdorë funksionet API CalculatorContext::Outputs
dhe CalculatorContext::SetNextTimestampBound
. Kufiri i specifikuar është vula kohore më e ulët e lejueshme për paketën e ardhshme në rrjedhën e specifikuar të daljes. Kur nuk del asnjë paketë, një kalkulator zakonisht do të bëjë diçka si:
cc->Outputs().Tag("output_frame").SetNextTimestampBound(
cc->InputTimestamp().NextAllowedInStream());
Funksioni Timestamp::NextAllowedInStream
kthen vulën kohore të njëpasnjëshme. Për shembull, Timestamp(1).NextAllowedInStream() == Timestamp(2)
.
Përhapja e kufijve të vulave kohore
Llogaritësit që do të përdoren në grafikët në kohë reale duhet të përcaktojnë kufijtë e vulës kohore të daljes bazuar në kufijtë e vulës kohore të hyrjes në mënyrë që të lejojnë që llogaritësit e rrjedhës së poshtme të planifikohen menjëherë. Një model i zakonshëm është që kalkulatorët të nxjerrin paketa me të njëjtat vula kohore si paketat e tyre hyrëse. Në këtë rast, thjesht nxjerrja e një pakete në çdo thirrje në Calculator::Process
është e mjaftueshme për të përcaktuar kufijtë e stampës kohore të daljes.
Megjithatë, llogaritësve nuk u kërkohet të ndjekin këtë model të zakonshëm për vulat kohore të daljes, atyre u kërkohet vetëm të zgjedhin vulat kohore të prodhimit në mënyrë monotonike. Si rezultat, kalkulatorë të caktuar duhet të llogarisin kufijtë e stampës kohore në mënyrë eksplicite. MediaPipe ofron disa mjete për llogaritjen e vulës kohore të përshtatshme për çdo kalkulator.
1. SetNextTimestampBound() mund të përdoret për të specifikuar vulën kohore të lidhur, t + 1
, për një rrjedhë dalëse.
cc->Outputs.Tag("OUT").SetNextTimestampBound(t.NextAllowedInStream());
Përndryshe, një paketë boshe me vulën kohore t
mund të prodhohet për të specifikuar vulën kohore të lidhur t + 1
.
cc->Outputs.Tag("OUT").Add(Packet(), t);
Vula kohore e kufizuar e një rryme hyrëse tregohet nga paketa ose paketa bosh në rrjedhën hyrëse.
Timestamp bound = cc->Inputs().Tag("IN").Value().Timestamp();
2. TimestampOffset() mund të specifikohet për të kopjuar automatikisht vulën kohore të lidhur nga rrjedhat hyrëse në rrjedhat dalëse.
cc->SetTimestampOffset(0);
Ky cilësim ka avantazhin e përhapjes së kufijve të vulave kohore automatikisht, edhe kur mbërrijnë vetëm kufijtë e vulës kohore dhe Calculator::Process nuk thirret.
3. ProcessTimestampBounds() mund të specifikohet në mënyrë që të thirret Calculator::Process
për çdo "vulë kohore të rregulluar" të re, ku "vula kohore e vendosur" është vula kohore më e re më e lartë nën kufijtë aktual të vulës kohore. Pa ProcessTimestampBounds()
, Calculator::Process
thirret vetëm me një ose më shumë paketa që vijnë.
cc->SetProcessTimestampBounds(true);
Ky cilësim lejon një kalkulator të kryejë llogaritjen dhe përhapjen e kufijve të tij të vulës kohore, edhe kur përditësohen vetëm vulat kohore të hyrjes. Mund të përdoret për të përsëritur efektin e TimestampOffset()
, por mund të përdoret gjithashtu për të llogaritur një kufi të vulës kohore që merr parasysh faktorë shtesë.
Për shembull, për të përsëritur SetTimestampOffset(0)
, një kalkulator mund të bëjë sa më poshtë:
absl::Status Open(CalculatorContext* cc) {
cc->SetProcessTimestampBounds(true);
}
absl::Status Process(CalculatorContext* cc) {
cc->Outputs.Tag("OUT").SetNextTimestampBound(
cc->InputTimestamp().NextAllowedInStream());
}
Planifikimi i Llogaritësit::Open dhe Llogaritësi::Mbyll
Calculator::Open
thirret kur janë prodhuar të gjitha paketat anësore të nevojshme hyrëse. Paketat anësore hyrëse mund të sigurohen nga aplikacioni mbyllës ose nga "llogaritësit e paketave anësore" brenda grafikut. Paketat anësore mund të specifikohen nga jashtë grafikut duke përdorur CalculatorGraph::Initialize
dhe CalculatorGraph::StartRun
të API-së. Paketat anësore mund të specifikohen nga kalkulatorët brenda grafikut duke përdorur CalculatorGraphConfig::OutputSidePackets
dhe OutputSidePacket::Set
.
Llogaritësi::Mbyll thirret kur të gjitha transmetimet hyrëse janë Done
duke u mbyllur ose duke arritur vulën Timestamp::Done
të lidhur.
Shënim: Nëse grafiku përfundon të gjithë ekzekutimin në pritje të kalkulatorit dhe bëhet Done
, përpara se disa transmetime të bëhen Done
, atëherë MediaPipe do të thërrasë thirrjet e mbetura në Calculator::Close
, në mënyrë që çdo kalkulator të mund të prodhojë rezultatet e tij përfundimtare.
Përdorimi i TimestampOffset
ka disa implikime për Calculator::Close
. Një kalkulator që specifikon SetTimestampOffset(0)
do të sinjalizojë nga dizajni se të gjitha transmetimet e tij dalëse kanë arritur në Timestamp::Done
kur të gjitha transmetimet e tij hyrëse kanë arritur në Timestamp::Done
, dhe për këtë arsye nuk ka dalje të mëtejshme. Kjo parandalon që një kalkulator i tillë të emetojë ndonjë paketë gjatë Calculator::Close
. Nëse një makinë llogaritëse duhet të prodhojë një paketë përmbledhëse gjatë Calculator::Close
, Calculator::Process
duhet të specifikojë kufijtë e vulës kohore në mënyrë që të paktën një vulë kohore (si p.sh. Timestamp::Max
) të mbetet e disponueshme gjatë Calculator::Close
. Kjo do të thotë që një kalkulator i tillë normalisht nuk mund të mbështetet në SetTimestampOffset(0)
dhe në vend të kësaj duhet të specifikojë kufijtë e vulës kohore në mënyrë eksplicite duke përdorur SetNextTimestampBounds()
.