रीयल-टाइम स्ट्रीम

रीयल-टाइम टाइमस्टैंप

MediaPipe Calculator ग्राफ़ का इस्तेमाल अक्सर इंटरैक्टिव ऐप्लिकेशन के लिए, वीडियो या ऑडियो फ़्रेम की स्ट्रीम को प्रोसेस करने में किया जाता है. MediaPipe फ़्रेमवर्क के लिए सिर्फ़ यह ज़रूरी है कि क्रमिक पैकेट को एक-दूसरे के हिसाब से बढ़ते टाइमस्टैंप दिए जाएं. कन्वेंशन के मुताबिक, रीयल-टाइम कैलकुलेटर और ग्राफ़, रिकॉर्डिंग के समय या हर फ़्रेम के दिखाए जाने के समय को टाइमस्टैंप के तौर पर इस्तेमाल करते हैं. हर टाइमस्टैंप में, Jan/1/1970:00:00:00 से अब तक के माइक्रोसेकंड दिखाए जाते हैं. इससे अलग-अलग सोर्स के पैकेट को दुनिया भर में एक जैसे क्रम में प्रोसेस किया जा सकता है.

रीयल-टाइम में शेड्यूल करना

आम तौर पर, हर कैलकुलेटर वैसे ही काम करता है जैसे किसी दिए गए टाइमस्टैंप के लिए, उसके सभी इनपुट पैकेट उपलब्ध होते हैं. आम तौर पर, ऐसा तब होता है, जब कैलकुलेटर पिछले फ़्रेम की प्रोसेसिंग पूरी कर लेता है और इनपुट देने वाला हर कैलकुलेटर मौजूदा फ़्रेम को प्रोसेस कर चुका होता है. इन शर्तों के पूरा होते ही, MediaPipe शेड्यूलर हर कैलकुलेटर को शुरू करता है. ज़्यादा जानकारी के लिए, सिंक करने की सुविधा देखें.

टाइमस्टैंप की सीमाएं

जब कैलकुलेटर किसी दिए गए टाइमस्टैंप के लिए कोई आउटपुट पैकेट नहीं बनाता, तो उसके बजाय "टाइमस्टैंप की सीमा" जनरेट होती है. इससे पता चलता है कि उस टाइमस्टैंप के लिए कोई भी पैकेट नहीं बनाया जाएगा. डाउनस्ट्रीम कैलकुलेटर को उस टाइमस्टैंप पर चलाने के लिए यह जानकारी देना ज़रूरी है. भले ही, उस टाइमस्टैंप की कुछ स्ट्रीम के लिए कोई पैकेट नहीं मिला हो. यह खास तौर पर इंटरैक्टिव ऐप्लिकेशन में रीयल-टाइम ग्राफ़ के लिए ज़रूरी है, जहां यह ज़रूरी है कि हर कैलकुलेटर जल्द से जल्द प्रोसेस करना शुरू कर दे.

नीचे दिए गए ग्राफ़ जैसे ग्राफ़ पर विचार करें:

node {
   calculator: "A"
   input_stream: "alpha_in"
   output_stream: "alpha"
}
node {
   calculator: "B"
   input_stream: "alpha"
   input_stream: "foo"
   output_stream: "beta"
}

मान लें: टाइमस्टैंप T पर, नोड A अपनी आउटपुट स्ट्रीम alpha में कोई पैकेट नहीं भेजता. नोड B को foo में, T टाइमस्टैंप पर एक पैकेट मिलता है और वह alpha में, T टाइमस्टैंप पर पैकेट का इंतज़ार कर रहा है. अगर A, B को alpha के लिए टाइमस्टैंप बाउंड अपडेट नहीं भेजता है, तो B alpha में पैकेट के आने का इंतज़ार करेगा. इस दौरान, foo की पैकेट सूची में T, T+1 वगैरह पर पैकेट इकट्ठा होंगे.

किसी स्ट्रीम पर पैकेट जनरेट करने के लिए, कैलकुलेटर एपीआई फ़ंक्शन CalculatorContext::Outputs और OutputStream::Add का इस्तेमाल करता है. इसके बजाय, स्ट्रीम पर बाउंड टाइमस्टैंप को आउटपुट करने के लिए, कैलकुलेटर एपीआई फ़ंक्शन CalculatorContext::Outputs और CalculatorContext::SetNextTimestampBound का इस्तेमाल कर सकता है. तय की गई सीमा, तय की गई आउटपुट स्ट्रीम पर अगले पैकेट के लिए सबसे कम मान्य टाइमस्टैंप है. जब कोई पैकेट आउटपुट नहीं होता है, तो आम तौर पर कैलकुलेटर कुछ इस तरह का काम करेगा:

cc->Outputs().Tag("output_frame").SetNextTimestampBound(
  cc->InputTimestamp().NextAllowedInStream());

फ़ंक्शन Timestamp::NextAllowedInStream, क्रम के हिसाब से टाइमस्टैंप दिखाता है. उदाहरण के लिए, Timestamp(1).NextAllowedInStream() == Timestamp(2).

टाइमस्टैंप की सीमाएं लागू की जा रही हैं

रीयल-टाइम ग्राफ़ में इस्तेमाल किए जाने वाले कैलकुलेटर को, इनपुट टाइमस्टैंप की सीमाओं के आधार पर आउटपुट टाइमस्टैंप की सीमाएं तय करनी चाहिए. इससे डाउनस्ट्रीम कैलकुलेटर को तुरंत शेड्यूल करने में मदद मिलती है. एक सामान्य पैटर्न यह है कि कैलकुलेटर ऐसे आउटपुट पैकेट उनके इनपुट पैकेट के टाइमस्टैंप से मेल खाते हैं या नहीं. इस मामले में, आउटपुट टाइमस्टैंप की सीमाओं को तय करने के लिए, हर कॉल के लिए Calculator::Process पर पैकेट आउटपुट करना काफ़ी है.

हालांकि, कैलकुलेटर को आउटपुट टाइमस्टैंप के लिए इस सामान्य पैटर्न को अपनाना ज़रूरी नहीं है. उन्हें सिर्फ़ एक-एक करके बढ़ते आउटपुट टाइमस्टैंप चुनने की ज़रूरत होती है. इसलिए, कुछ कैलकुलेटर को टाइमस्टैंप की सीमाओं का सटीक हिसाब लगाना होगा. MediaPipe हर कैलकुलेटर के लिए सही टाइमस्टैंप की गणना करने के लिए कई टूल उपलब्ध कराता है.

1. SetNextTimestampBound() का इस्तेमाल, आउटपुट स्ट्रीम के लिए टाइमस्टैंप बाउंड, t + 1 की जानकारी देने के लिए किया जा सकता है.

cc->Outputs.Tag("OUT").SetNextTimestampBound(t.NextAllowedInStream());

इसके अलावा, t टाइमस्टैंप वाला एक खाली पैकेट भी बनाया जा सकता है, ताकि बाउंड t + 1 टाइमस्टैंप की जानकारी दी जा सके.

cc->Outputs.Tag("OUT").Add(Packet(), t);

इनपुट स्ट्रीम की टाइमस्टैंप बाउंड, इनपुट स्ट्रीम पर पैकेट या खाली पैकेट के ज़रिए दी गई है.

Timestamp bound = cc->Inputs().Tag("IN").Value().Timestamp();

2. TimestampOffset() को इसलिए तय किया जा सकता है, ताकि इनपुट स्ट्रीम से आउटपुट स्ट्रीम में बाउंड टाइमस्टैंप को अपने-आप कॉपी किया जा सके.

cc->SetTimestampOffset(0);

इस सेटिंग का फ़ायदा यह है कि टाइमस्टैंप की सीमा अपने-आप लागू होती है. भले ही, सिर्फ़ टाइमस्टैंप की सीमा आ रही हो और Calculator::Process शुरू नहीं किया गया हो.

3. हर नए "सेट किए गए टाइमस्टैंप" के लिए Calculator::Process को शुरू करने के लिए, ProcessTimestampBounds() को तय किया जा सकता है. यहां "सेट किए गए टाइमस्टैंप", मौजूदा टाइमस्टैंप की सीमाओं के नीचे नया सबसे ज़्यादा टाइमस्टैंप होता है. ProcessTimestampBounds() के बिना, Calculator::Process को सिर्फ़ एक या उससे ज़्यादा आने वाले पैकेट के साथ शुरू किया जाता है.

cc->SetProcessTimestampBounds(true);

इस सेटिंग की मदद से कैलकुलेटर अपने टाइमस्टैंप की सीमाओं का हिसाब लगा सकता है और उसे लागू कर सकता है. भले ही, सिर्फ़ इनपुट टाइमस्टैंप अपडेट किए गए हों. इसका इस्तेमाल TimestampOffset() के असर को दोहराने के लिए किया जा सकता है. हालांकि, इसका इस्तेमाल टाइमस्टैंप बाउंड का पता लगाने के लिए भी किया जा सकता है. ऐसा करने के लिए, अन्य फ़ैक्टर को ध्यान में रखा जाता है.

उदाहरण के लिए, SetTimestampOffset(0) को कॉपी करने के लिए, कैलकुलेटर ये काम कर सकता है:

absl::Status Open(CalculatorContext* cc) {
  cc->SetProcessTimestampBounds(true);
}

absl::Status Process(CalculatorContext* cc) {
  cc->Outputs.Tag("OUT").SetNextTimestampBound(
      cc->InputTimestamp().NextAllowedInStream());
}

Calculator::खोलें और Calculator::बंद करें

जब सभी ज़रूरी इनपुट साइड-पैकेट बनाए जाते हैं, तो Calculator::Open को शुरू कर दिया जाता है. इनपुट साइड-पैकेट, एनक्लोज़िंग ऐप्लिकेशन या ग्राफ़ के अंदर "साइड-पैकेट कैलकुलेटर" की मदद से दिए जा सकते हैं. साइड-पैकेट को ग्राफ़ के बाहर से तय किया जा सकता है. इसके लिए, एपीआई के CalculatorGraph::Initialize और CalculatorGraph::StartRun का इस्तेमाल किया जा सकता है. साइड पैकेट को कैलकुलेटर की मदद से, ग्राफ़ में CalculatorGraphConfig::OutputSidePackets और OutputSidePacket::Set का इस्तेमाल करके तय किया जा सकता है.

कैलकुलेटर::बंद होने की सुविधा तब चालू होती है, जब सभी इनपुट स्ट्रीम Done बंद हो जाती हैं या टाइमस्टैंप की सीमा Timestamp::Done पर पहुंच जाती है.

ध्यान दें: अगर ग्राफ़ में सभी बचे हुए कैलकुलेटर एक्ज़ीक्यूशन के बाद Done हो जाते हैं और कुछ स्ट्रीम Done होने से पहले, तो MediaPipe, बचे हुए कॉल Calculator::Close पर शुरू कर देगा, ताकि हर कैलकुलेटर अपने फ़ाइनल आउटपुट दे सके.

Calculator::Close के लिए, TimestampOffset के इस्तेमाल पर कुछ असर पड़ सकता है. SetTimestampOffset(0) के बारे में जानकारी देने वाला एक कैलकुलेटर डिज़ाइन सिग्नल से यह पता लगाएगा कि इसकी सभी आउटपुट स्ट्रीम Timestamp::Done तक पहुंच गई हैं, जबकि इसकी सभी इनपुट स्ट्रीम Timestamp::Done तक पहुंच गई हैं. इसलिए, आगे कोई आउटपुट संभव नहीं होगा. यह इस तरह के कैलकुलेटर को Calculator::Close के दौरान कोई भी पैकेट उत्सर्जित करने से रोकता है. अगर कैलकुलेटर को Calculator::Close के दौरान समरी पैकेट जनरेट करना हो, तो Calculator::Process को टाइमस्टैंप की सीमाएं बतानी होंगी, ताकि Calculator::Close के दौरान कम से कम एक टाइमस्टैंप (जैसे, Timestamp::Max) उपलब्ध रहे. इसका मतलब है कि इस तरह के कैलकुलेटर के लिए आम तौर पर SetTimestampOffset(0) पर भरोसा नहीं किया जा सकता. इसके बजाय, इसे SetNextTimestampBounds() का इस्तेमाल करके टाइमस्टैंप की सीमाएं तय करनी चाहिए.