Εξερευνώντας τη μηχανή VoIP Mediastreamer2. Μέρος 11

Το υλικό του άρθρου είναι παρμένο από το δικό μου κανάλι ζεν.

Εξερευνώντας τη μηχανή VoIP Mediastreamer2. Μέρος 11

Μηχανισμός κίνησης δεδομένων

  • Μπλοκ δεδομένων dblk_t
  • Μήνυμα mblk_t
  • Λειτουργίες για εργασία με μηνύματα mblk_t
  • Ουρά ουρά_t
  • Λειτουργίες για εργασία με ουρές queue_t
  • Σύνδεση φίλτρων
  • Σημείο σήματος της γραφικής παράστασης επεξεργασίας δεδομένων
  • Παρασκηνιακές δραστηριότητες του ticker
  • Bufferizer (MSBufferizer)
  • Λειτουργίες για εργασία με MSBufferizer

Στο παρελθόν άρθρο έχουμε αναπτύξει το δικό μας φίλτρο. Αυτό το άρθρο θα επικεντρωθεί στον εσωτερικό μηχανισμό για τη μετακίνηση δεδομένων μεταξύ των φίλτρων ροής πολυμέσων. Αυτό θα σας επιτρέψει να γράψετε εξελιγμένα φίλτρα με λιγότερη προσπάθεια στο μέλλον.

Μηχανισμός κίνησης δεδομένων

Η μετακίνηση δεδομένων στη ροή πολυμέσων πραγματοποιείται χρησιμοποιώντας ουρές που περιγράφονται από τη δομή ουρά_t. Σειρές μηνυμάτων όπως mblk_t, τα οποία από μόνα τους δεν περιέχουν δεδομένα σήματος, αλλά μόνο συνδέσμους προς το προηγούμενο, το επόμενο μήνυμα και το μπλοκ δεδομένων. Επιπλέον, θέλω να τονίσω ιδιαίτερα ότι υπάρχει επίσης ένα πεδίο για έναν σύνδεσμο προς ένα μήνυμα του ίδιου τύπου, το οποίο σας επιτρέπει να οργανώσετε μια λίστα μηνυμάτων μεμονωμένα συνδεδεμένα. Θα ονομάσουμε μια ομάδα μηνυμάτων που ενώνονται από μια τέτοια λίστα πλειάδα. Έτσι, οποιοδήποτε στοιχείο της ουράς μπορεί να είναι ένα μόνο μήνυμα mblk_t, και ίσως το κεφάλι μιας πλειάδας μηνυμάτων mblk_t. Κάθε πολλαπλό μήνυμα μπορεί να έχει το δικό του μπλοκ δεδομένων πτέρυγας. Θα συζητήσουμε γιατί χρειάζονται πλειάδες λίγο αργότερα.

Όπως αναφέρθηκε παραπάνω, το ίδιο το μήνυμα δεν περιέχει ένα μπλοκ δεδομένων, αλλά περιέχει μόνο έναν δείκτη στην περιοχή μνήμης όπου είναι αποθηκευμένο το μπλοκ. Σε αυτό το μέρος, η συνολική εικόνα της δουλειάς του media streamer θυμίζει την αποθήκη της πόρτας στο καρτούν «Monsters, Inc.», όπου οι πόρτες (σύνδεσμοι προς δεδομένα - δωμάτια) κινούνται με τρελή ταχύτητα κατά μήκος των εναέριων μεταφορέων, ενώ τα ίδια τα δωμάτια παραμένουν ακίνητοι.

Τώρα, προχωρώντας κατά μήκος της ιεραρχίας από κάτω προς τα πάνω, ας εξετάσουμε λεπτομερώς τις καταχωρημένες οντότητες του μηχανισμού μετάδοσης δεδομένων στη ροή πολυμέσων.

Μπλοκ δεδομένων dblk_t

Το μπλοκ δεδομένων αποτελείται από μια κεφαλίδα και μια προσωρινή μνήμη δεδομένων. Η κεφαλίδα περιγράφεται από την ακόλουθη δομή:

typedef struct datab
{
unsigned char *db_base; // Указатель на начало буфер данных.
unsigned char *db_lim;  // Указатель на конец буфер данных.
void (*db_freefn)(void*); // Функция освобождения памяти при удалении блока.
int db_ref; // Счетчик ссылок.
} dblk_t;

Τα πεδία της δομής περιέχουν δείκτες προς την αρχή του buffer, το τέλος του buffer και τη συνάρτηση για τη διαγραφή του buffer δεδομένων. Τελευταίο στοιχείο στην κεφαλίδα db_ref — μετρητής αναφοράς, εάν φτάσει στο μηδέν, αυτό χρησιμεύει ως σήμα για τη διαγραφή αυτού του μπλοκ από τη μνήμη. Εάν το μπλοκ δεδομένων δημιουργήθηκε από τη συνάρτηση datab_alloc() , τότε η προσωρινή μνήμη δεδομένων θα τοποθετηθεί στη μνήμη αμέσως μετά την κεφαλίδα. Σε όλες τις άλλες περιπτώσεις, το buffer μπορεί να βρίσκεται κάπου χωριστά. Η προσωρινή μνήμη δεδομένων θα περιέχει δείγματα σημάτων ή άλλα δεδομένα που θέλουμε να επεξεργαστούμε με φίλτρα.

Μια νέα παρουσία ενός μπλοκ δεδομένων δημιουργείται χρησιμοποιώντας τη συνάρτηση:

dblk_t *datab_alloc(int size);

Ως παράμετρος εισόδου, δίνεται το μέγεθος των δεδομένων που θα αποθηκεύσει το μπλοκ. Εκχωρείται περισσότερη μνήμη για να τοποθετηθεί μια κεφαλίδα - δομή - στην αρχή της εκχωρημένης μνήμης βάση δεδομένων. Αλλά όταν χρησιμοποιείτε άλλες λειτουργίες, αυτό δεν συμβαίνει πάντα· σε ορισμένες περιπτώσεις, η προσωρινή μνήμη δεδομένων μπορεί να βρίσκεται χωριστά από την κεφαλίδα του μπλοκ δεδομένων. Κατά τη δημιουργία μιας δομής, τα πεδία διαμορφώνονται έτσι ώστε το πεδίο της db_base έδειξε στην αρχή της περιοχής δεδομένων και db_lim μέχρι το τέλος του. Αριθμός συνδέσμων db_ref έχει οριστεί σε ένα. Ο δείκτης λειτουργίας διαγραφής δεδομένων έχει οριστεί στο μηδέν.

Μήνυμα mblk_t

Όπως αναφέρθηκε, τα στοιχεία ουράς είναι τύπου mblk_t, ορίζεται ως εξής:

typedef struct msgb
{
  struct msgb *b_prev;   // Указатель на предыдущий элемент списка.
  struct msgb *b_next;   // Указатель на следующий элемент списка.
  struct msgb *b_cont;   // Указатель для подклейки к сообщению других сообщений, для создания кортежа сообщений.
  struct datab *b_datap; // Указатель на структуру блока данных.
  unsigned char *b_rptr; // Указатель на начало области данных для чтения данных буфера b_datap.
  unsigned char *b_wptr; // Указатель на начало области данных для записи данных буфера b_datap.
  uint32_t reserved1;    // Зарезервированное поле1, медиастример помещает туда служебную информацию. 
  uint32_t reserved2;    // Зарезервированное поле2, медиастример помещает туда служебную информацию.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Δομή mblk_t περιέχει δείκτες στην αρχή β_προηγ, b_next, τα οποία είναι απαραίτητα για την οργάνωση μιας διπλά συνδεδεμένης λίστας (η οποία είναι μια ουρά ουρά_t).

Μετά έρχεται ο δείκτης b_cont, το οποίο χρησιμοποιείται μόνο όταν το μήνυμα είναι μέρος μιας πλειάδας. Για το τελευταίο μήνυμα στην πλειάδα, αυτός ο δείκτης παραμένει μηδενικός.

Στη συνέχεια βλέπουμε έναν δείκτη σε ένα μπλοκ δεδομένων b_datap, για το οποίο υπάρχει το μήνυμα. Ακολουθείται από δείκτες στην περιοχή μέσα στο buffer δεδομένων μπλοκ. Πεδίο b_rptr καθορίζει τη θέση από την οποία θα διαβάζονται τα δεδομένα από το buffer. Πεδίο b_wptr υποδεικνύει τη θέση από την οποία θα εκτελεστούν οι εγγραφές στο buffer.

Τα υπόλοιπα πεδία είναι υπηρεσιακού χαρακτήρα και δεν σχετίζονται με τη λειτουργία του μηχανισμού μεταφοράς δεδομένων.

Παρακάτω είναι ένα μήνυμα με το όνομα m1 και μπλοκ δεδομένων d1.
Εξερευνώντας τη μηχανή VoIP Mediastreamer2. Μέρος 11
Το παρακάτω σχήμα δείχνει μια πλειάδα τριών μηνυμάτων m1, m1_1, m1_2.
Εξερευνώντας τη μηχανή VoIP Mediastreamer2. Μέρος 11

Λειτουργίες ανταλλαγής μηνυμάτων mblk_t

Ένα νέο μήνυμα mblk_t που δημιουργείται από τη συνάρτηση:

mblk_t *allocb(int size, int pri); 

τοποθετεί ένα νέο μήνυμα στη μνήμη mblk_t με ένα μπλοκ δεδομένων του καθορισμένου μεγέθους μέγεθος, δεύτερο επιχείρημα - pri δεν χρησιμοποιείται σε αυτήν την έκδοση της βιβλιοθήκης. Θα πρέπει να παραμείνει μηδέν. Κατά τη λειτουργία της λειτουργίας, θα εκχωρηθεί μνήμη για τη δομή του νέου μηνύματος και η συνάρτηση θα κληθεί mblk_init(), το οποίο θα επαναφέρει όλα τα πεδία του δημιουργημένου στιγμιότυπου της δομής και στη συνέχεια, χρησιμοποιώντας τα παραπάνω datab_alloc(), θα δημιουργήσει ένα buffer δεδομένων. Μετά από αυτό θα διαμορφωθούν τα πεδία στη δομή:

mp->b_datap=datab;
mp->b_rptr=mp->b_wptr=datab->db_base;
mp->b_next=mp->b_prev=mp->b_cont=NULL;

Στην έξοδο λαμβάνουμε ένα νέο μήνυμα με αρχικοποιημένα πεδία και ένα κενό buffer δεδομένων. Για να προσθέσετε δεδομένα σε ένα μήνυμα, πρέπει να το αντιγράψετε στην προσωρινή μνήμη μπλοκ δεδομένων:

memcpy(msg->b_rptr, data, size);

όπου ημερομηνία είναι ένας δείκτης στην πηγή δεδομένων και μέγεθος - το μέγεθός τους.
τότε πρέπει να ενημερώσετε τον δείκτη στο σημείο εγγραφής, έτσι ώστε να δείχνει ξανά στην αρχή της ελεύθερης περιοχής στο buffer:

msg->b_wptr = msg->b_wptr + size

Εάν πρέπει να δημιουργήσετε ένα μήνυμα από ένα υπάρχον buffer, χωρίς αντιγραφή, χρησιμοποιήστε τη συνάρτηση:

mblk_t *esballoc(uint8_t *buf, int size, int pri, void (*freefn)(void*)); 

Η συνάρτηση, αφού δημιουργήσει το μήνυμα και τη δομή του μπλοκ δεδομένων, θα διαμορφώσει τους δείκτες της στα δεδομένα στη διεύθυνση μπουφ. Εκείνοι. σε αυτήν την περίπτωση, το buffer δεδομένων δεν βρίσκεται μετά τα πεδία κεφαλίδας του μπλοκ δεδομένων, όπως συνέβαινε κατά τη δημιουργία ενός μπλοκ δεδομένων με τη συνάρτηση datab_alloc(). Η προσωρινή μνήμη με δεδομένα που μεταβιβάζονται στη συνάρτηση θα παραμείνει εκεί που ήταν, αλλά με τη βοήθεια δεικτών θα προσαρτηθεί στην κεφαλίδα του μπλοκ δεδομένων που δημιουργήθηκε πρόσφατα και, κατά συνέπεια, στο μήνυμα.

Σε ένα μήνυμα mblk_t Πολλά μπλοκ δεδομένων μπορούν να συνδεθούν διαδοχικά. Αυτό γίνεται από τη συνάρτηση:

mblk_t * appendb(mblk_t *mp, const char *data, int size, bool_t pad); 

mp — ένα μήνυμα στο οποίο θα προστεθεί ένα άλλο μπλοκ δεδομένων·
ημερομηνία — δείκτης στο μπλοκ, αντίγραφο του οποίου θα προστεθεί στο μήνυμα.
μέγεθος — μέγεθος δεδομένων·
μπλοκ — μια σημαία ότι το μέγεθος της εκχωρημένης μνήμης πρέπει να ευθυγραμμιστεί κατά μήκος ενός ορίου 4 byte (η συμπλήρωση θα γίνει με μηδενικά).

Εάν υπάρχει αρκετός χώρος στην υπάρχουσα προσωρινή μνήμη δεδομένων μηνυμάτων, τότε τα νέα δεδομένα θα επικολληθούν πίσω από τα δεδομένα που υπάρχουν ήδη. Εάν υπάρχει λιγότερος ελεύθερος χώρος στην προσωρινή μνήμη δεδομένων μηνυμάτων από μέγεθος, τότε δημιουργείται ένα νέο μήνυμα με επαρκές μέγεθος buffer και τα δεδομένα αντιγράφονται στο buffer του. Αυτό είναι ένα νέο μήνυμα, που συνδέεται με το αρχικό χρησιμοποιώντας δείκτη b_cont. Σε αυτή την περίπτωση, το μήνυμα μετατρέπεται σε πλειάδα.

Εάν πρέπει να προσθέσετε ένα άλλο μπλοκ δεδομένων στην πλειάδα, τότε πρέπει να χρησιμοποιήσετε τη συνάρτηση:

void msgappend(mblk_t *mp, const char *data, int size, bool_t pad);

θα βρει το τελευταίο μήνυμα στην πλειάδα (έχει b_cont θα είναι null) και θα καλέσει τη συνάρτηση για αυτό το μήνυμα appendb().

Μπορείτε να μάθετε το μέγεθος των δεδομένων σε ένα μήνυμα ή πλειάδα χρησιμοποιώντας τη συνάρτηση:

int msgdsize(const mblk_t *mp);

Θα κάνει βρόχο μέσω όλων των μηνυμάτων της πλειάδας και θα επιστρέψει τη συνολική ποσότητα δεδομένων στα buffer δεδομένων αυτών των μηνυμάτων. Για κάθε μήνυμα, ο όγκος των δεδομένων υπολογίζεται ως εξής:

 mp->b_wptr - mp->b_rptr

Για να συνδυάσετε δύο πλειάδες, χρησιμοποιήστε τη συνάρτηση:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

αυτή προσαρτά την πλειάδα νέος στην ουρά της πλειάδας mp και επιστρέφει έναν δείκτη στο τελευταίο μήνυμα της πλειάδας που προκύπτει.

Εάν είναι απαραίτητο, μια πλειάδα μπορεί να μετατραπεί σε ένα μήνυμα με ένα μόνο μπλοκ δεδομένων, αυτό γίνεται από τη συνάρτηση:

void msgpullup(mblk_t *mp,int len);

αν επιχείρημα len είναι -1, τότε το μέγεθος του εκχωρημένου buffer καθορίζεται αυτόματα. Αν len είναι ένας θετικός αριθμός, θα δημιουργηθεί ένα buffer αυτού του μεγέθους και τα δεδομένα πολλαπλών μηνυμάτων θα αντιγραφούν σε αυτό. Εάν το buffer εξαντληθεί, η αντιγραφή θα σταματήσει εκεί. Το πρώτο μήνυμα της πλειάδας θα λάβει μια προσωρινή μνήμη νέου μεγέθους με τα αντιγραμμένα δεδομένα. Τα υπόλοιπα μηνύματα θα διαγραφούν και η μνήμη θα επιστρέψει στο σωρό.

Κατά τη διαγραφή μιας δομής mblk_t ο αριθμός αναφοράς του μπλοκ δεδομένων λαμβάνεται υπόψη εάν, κατά την κλήση freeb() αποδεικνύεται ότι είναι μηδέν, τότε το buffer δεδομένων διαγράφεται μαζί με το στιγμιότυπο mblk_t, που το δείχνει.

Εκκίνηση των πεδίων ενός νέου μηνύματος:

void mblk_init(mblk_t *mp);

Προσθήκη άλλου τμήματος δεδομένων στο μήνυμα:

mblk_t * appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);

Εάν τα νέα δεδομένα δεν χωρούν στον ελεύθερο χώρο της προσωρινής μνήμης δεδομένων μηνυμάτων, τότε στο μήνυμα επισυνάπτεται ένα ξεχωριστά δημιουργημένο μήνυμα με προσωρινή μνήμη του απαιτούμενου μεγέθους (ένας δείκτης στο προστιθέμενο μήνυμα ορίζεται στο πρώτο μήνυμα) και το μήνυμα μετατρέπεται σε πλειάδα.

Προσθέτοντας ένα κομμάτι δεδομένων σε μια πλειάδα:

void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad); 

Η συνάρτηση καλεί την appendb() σε έναν βρόχο.

Συνδυάζοντας δύο πλειάδες σε μία:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

Μήνυμα νέος θα επισυναφθεί σε mp.

Δημιουργία αντιγράφου ενός μόνο μηνύματος:

mblk_t *copyb(const mblk_t *mp);

Πλήρης αντιγραφή μιας πλειάδας με όλα τα μπλοκ δεδομένων:

mblk_t *copymsg(const mblk_t *mp);

Τα στοιχεία της πλειάδας αντιγράφονται από τη συνάρτηση copyb().

Δημιουργήστε ένα εύκολο αντίγραφο ενός μηνύματος mblk_t. Σε αυτήν την περίπτωση, το μπλοκ δεδομένων δεν αντιγράφεται, αλλά ο μετρητής αναφοράς του αυξάνεται db_ref:

mblk_t *dupb(mblk_t *mp);

Δημιουργία ενός ελαφρού αντιγράφου μιας πλειάδας. Τα μπλοκ δεδομένων δεν αντιγράφονται, μόνο οι μετρητές αναφοράς τους αυξάνονται db_ref:

mblk_t *dupmsg(mblk_t* m);

Κολλώντας όλα τα μηνύματα μιας πλειάδας σε ένα μήνυμα:

void msgpullup(mblk_t *mp,size_t len);

Αν το επιχείρημα len είναι -1, τότε το μέγεθος του εκχωρημένου buffer καθορίζεται αυτόματα.

Διαγραφή μηνύματος, πολλαπλασιασμός:

void freemsg(mblk_t *mp);

Ο αριθμός αναφοράς του μπλοκ δεδομένων μειώνεται κατά ένα. Εάν φτάσει στο μηδέν, το μπλοκ δεδομένων διαγράφεται επίσης.

Υπολογισμός του συνολικού όγκου δεδομένων σε ένα μήνυμα ή πλειάδα.

size_t msgdsize(const mblk_t *mp);

Ανάκτηση μηνύματος από την ουρά της ουράς:

mblk_t *ms_queue_peek_last (q);

Αντιγραφή των περιεχομένων των δεσμευμένων πεδίων ενός μηνύματος σε ένα άλλο μήνυμα (στην πραγματικότητα, αυτά τα πεδία περιέχουν σημαίες που χρησιμοποιούνται από τη ροή πολυμέσων):

mblk_meta_copy(const mblk_t *source, mblk *dest);

Στροφή ουρά_t

Η ουρά μηνυμάτων στη ροή πολυμέσων υλοποιείται ως μια κυκλική λίστα διπλά συνδεδεμένη. Κάθε στοιχείο λίστας περιέχει έναν δείκτη σε ένα μπλοκ δεδομένων με δείγματα σήματος. Αποδεικνύεται ότι μόνο οι δείκτες προς το μπλοκ δεδομένων κινούνται με τη σειρά τους, ενώ τα ίδια τα δεδομένα παραμένουν ακίνητα. Εκείνοι. μετακινούνται μόνο οι σύνδεσμοι προς αυτούς.
Δομή που περιγράφει την ουρά ουρά_t, Φαίνεται παρακάτω:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Холостой" элемент очереди, не указывает на данные, используется только для управления очередью. При инициализации очереди (qinit()) его указатели настраиваются так, чтобы они указывали на него самого. */
   int q_mcount;        // Количество элементов в очереди.
} queue_t;

Η δομή περιέχει ένα πεδίο - έναν δείκτη _q_στόπερ πληκτρολογήστε *mblk_t, δείχνει στο πρώτο στοιχείο (μήνυμα) στην ουρά. Το δεύτερο πεδίο της δομής είναι ο μετρητής των μηνυμάτων στην ουρά.
Το παρακάτω σχήμα δείχνει μια ουρά με το όνομα q1 που περιέχει 4 μηνύματα m1, m2, m3, m4.
Εξερευνώντας τη μηχανή VoIP Mediastreamer2. Μέρος 11
Το παρακάτω σχήμα δείχνει μια ουρά με το όνομα q1 που περιέχει 4 μηνύματα m1,m2,m3,m4. Το μήνυμα m2 είναι η κεφαλή μιας πλειάδας που περιέχει δύο ακόμη μηνύματα m2_1 και m2_2.

Εξερευνώντας τη μηχανή VoIP Mediastreamer2. Μέρος 11

Λειτουργίες για εργασία με ουρές queue_t

Αρχικοποίηση ουράς:

void qinit(queue_t *q);

Πεδίο _q_στόπερ (εφεξής θα το ονομάζουμε «στόπερ») αρχικοποιείται από τη συνάρτηση mblk_init(), το προηγούμενο στοιχείο του και ο δείκτης του επόμενου στοιχείου προσαρμόζονται ώστε να δείχνουν προς τον εαυτό του. Ο μετρητής του στοιχείου ουράς επαναφέρεται στο μηδέν.

Προσθήκη νέου στοιχείου (μηνύματα):

void putq(queue_t *q, mblk_t *m);

Νέο στοιχείο m προστίθεται στο τέλος της λίστας, οι δείκτες στοιχείων προσαρμόζονται έτσι ώστε το πώμα να γίνεται το επόμενο στοιχείο για αυτό και να γίνεται το προηγούμενο στοιχείο για το πώμα. Ο μετρητής του στοιχείου ουράς αυξάνεται.

Ανάκτηση στοιχείου από την ουρά:

mblk_t * getq(queue_t *q); 

Το μήνυμα που έρχεται μετά το πώμα ανακτάται και ο μετρητής στοιχείων μειώνεται. Εάν δεν υπάρχουν στοιχεία στην ουρά εκτός από το πώμα, τότε επιστρέφεται το 0.

Εισαγωγή μηνύματος σε μια ουρά:

void insq(queue_t *q, mblk_t *emp, mblk_t *mp); 

Στοιχείο mp εισάγεται πριν από το στοιχείο emp. Αν emp=0, τότε το μήνυμα προστίθεται στην ουρά της ουράς.

Ανάκτηση μηνύματος από τον επικεφαλής της ουράς:

void remq(queue_t *q, mblk_t *mp); 

Ο μετρητής στοιχείων μειώνεται.

Ανάγνωση ενός δείκτη στο πρώτο στοιχείο στην ουρά:

mblk_t * peekq(queue_t *q); 

Αφαίρεση όλων των στοιχείων από την ουρά ενώ διαγράφονται τα ίδια τα στοιχεία:

void flushq(queue_t *q, int how);

Επιχείρημα πως δεν χρησιμοποιείται. Ο μετρητής του στοιχείου ουράς έχει οριστεί στο μηδέν.

Μακροεντολή για την ανάγνωση ενός δείκτη στο τελευταίο στοιχείο της ουράς:

mblk_t * qlast(queue_t *q);

Όταν εργάζεστε με ουρές μηνυμάτων, να γνωρίζετε ότι όταν καλείτε ms_queue_put(q, m) με μηδενικό δείκτη στο μήνυμα, η συνάρτηση επαναλαμβάνεται. Το πρόγραμμά σας θα παγώσει. συμπεριφέρεται παρόμοια ms_queue_next(q, m).

Σύνδεση φίλτρων

Η ουρά που περιγράφεται παραπάνω χρησιμοποιείται για τη μετάδοση μηνυμάτων από το ένα φίλτρο στο άλλο ή από ένα σε πολλά φίλτρα. Τα φίλτρα και οι συνδέσεις τους σχηματίζουν ένα κατευθυνόμενο γράφημα. Η είσοδος ή η έξοδος του φίλτρου θα ονομάζεται η γενική λέξη "pin". Για να περιγράψει τη σειρά με την οποία συνδέονται τα φίλτρα μεταξύ τους, ο streamer πολυμέσων χρησιμοποιεί την έννοια του "σημείου σήματος". Το σημείο σήματος είναι δομή _MSCPpoint, το οποίο περιέχει έναν δείκτη στο φίλτρο και τον αριθμό μιας από τις ακίδες του· κατά συνέπεια, περιγράφει τη σύνδεση μιας από τις εισόδους ή εξόδους του φίλτρου.

Σημείο σήματος της γραφικής παράστασης επεξεργασίας δεδομένων

typedef struct _MSCPoint{
struct _MSFilter *filter; // Указатель на фильтр медиастримера.
int pin;                        // Номер одного из входов или выходов фильтра, т.е. пин.
} MSCPoint;

Οι ακίδες φίλτρου αριθμούνται ξεκινώντας από το μηδέν.

Η σύνδεση δύο ακίδων με μια ουρά μηνυμάτων περιγράφεται από τη δομή _MSQueue, το οποίο περιέχει μια ουρά μηνυμάτων και δείκτες στα δύο σημεία σήματος που συνδέει:

typedef struct _MSQueue
{
queue_t q;
MSCPoint prev;
MSCPoint next;
}MSQueue;

Θα ονομάσουμε αυτή τη δομή σύνδεσμο σήματος. Κάθε φίλτρο ροής πολυμέσων περιέχει έναν πίνακα συνδέσμων εισόδου και έναν πίνακα συνδέσμων εξόδου (MSQueue). Το μέγεθος των πινάκων ορίζεται κατά τη δημιουργία ενός φίλτρου· το έχουμε ήδη κάνει χρησιμοποιώντας μια εξαγόμενη μεταβλητή τύπου MSFilterDesc, όταν αναπτύξαμε το δικό μας φίλτρο. Παρακάτω είναι μια δομή που περιγράφει οποιοδήποτε φίλτρο σε μια ροή πολυμέσων, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    /* Указатель на дескриптор фильтра. */
    /* Защищенные атрибуты, их нельзя сдвигать или убирать иначе будет нарушена работа с плагинами. */
    ms_mutex_t lock;      /* Семафор. */
    MSQueue **inputs;     /* Таблица входных линков. */
    MSQueue **outputs;    /* Таблица выходных линков. */
    struct _MSFactory *factory; /* Указатель на фабрику, которая создала данный экземпляр фильтра. */
    void *padding;              /* Не используется, будет задействован если добавятся защищенные поля. */
    void *data;                 /* Указатель на произвольную структуру для хранения данных внутреннего состояния фильтра и промежуточных вычислений. */
    struct _MSTicker *ticker;   /* Указатель на объект тикера, который не должен быть нулевым когда вызывается функция process(). */
    /*private attributes, they can be moved and changed at any time*/
    MSList *notify_callbacks; /* Список обратных вызовов, используемых для обработки событий фильтра. */
    uint32_t last_tick;       /* Номер последнего такта, когда выполнялся вызов process(). */ 
    MSFilterStats *stats;     /* Статистика работы фильтра.*/
    int postponed_task; /*Количество отложенных задач. Некоторые фильтры могут откладывать обработку данных (вызов process()) на несколько тактов.*/
    bool_t seen;  /* Флаг, который использует тикер, чтобы помечать что этот экземпляр фильтра он уже обслужил на данном такте.*/
};
typedef struct _MSFilter MSFilter;

Αφού συνδέσαμε τα φίλτρα στο πρόγραμμα C σύμφωνα με το σχέδιό μας (αλλά δεν συνδέσαμε το ticker), δημιουργήσαμε έτσι ένα κατευθυνόμενο γράφημα, οι κόμβοι του οποίου είναι στιγμιότυπα της δομής MSFilterκαι οι άκρες είναι περιπτώσεις συνδέσμων MSQueue.

Παρασκηνιακές δραστηριότητες του ticker

Όταν σας είπα ότι το ticker είναι ένα φίλτρο για την πηγή των κροτώνων, δεν ήταν όλη η αλήθεια γι 'αυτό. Το ticker είναι ένα αντικείμενο που εκτελεί λειτουργίες στο ρολόι επεξεργάζομαι, διαδικασία() όλα τα φίλτρα του κυκλώματος (γραφήματος) στο οποίο είναι συνδεδεμένο. Όταν συνδέουμε ένα ticker σε ένα φίλτρο γραφήματος σε ένα πρόγραμμα C, δείχνουμε στο ticker το γράφημα που θα ελέγχει από εδώ και στο εξής μέχρι να το απενεργοποιήσουμε. Μετά τη σύνδεση, το ticker αρχίζει να εξετάζει το γράφημα που του έχει ανατεθεί, συντάσσοντας μια λίστα με φίλτρα που το περιλαμβάνουν. Για να μην «μετρήσει» το ίδιο φίλτρο δύο φορές, επισημαίνει τα φίλτρα που έχουν εντοπιστεί τοποθετώντας ένα πλαίσιο ελέγχου σε αυτά δει. Η αναζήτηση πραγματοποιείται χρησιμοποιώντας τους πίνακες συνδέσμων που έχει κάθε φίλτρο.

Κατά την εισαγωγική του περιήγηση στο γράφημα, το ticker ελέγχει εάν μεταξύ των φίλτρων υπάρχει τουλάχιστον ένα που λειτουργεί ως πηγή μπλοκ δεδομένων. Εάν δεν υπάρχει κανένα, τότε το γράφημα θεωρείται λανθασμένο και το ticker κολλάει.

Εάν το γράφημα αποδειχθεί «σωστό», για κάθε φίλτρο που βρέθηκε, η συνάρτηση καλείται για προετοιμασία προεπεξεργασία (). Μόλις φτάσει η στιγμή για τον επόμενο κύκλο επεξεργασίας (κάθε 10 χιλιοστά του δευτερολέπτου από προεπιλογή), ο δείκτης καλεί τη συνάρτηση επεξεργάζομαι, διαδικασία() για όλα τα φίλτρα προέλευσης που βρέθηκαν προηγουμένως και, στη συνέχεια, για τα υπόλοιπα φίλτρα στη λίστα. Εάν το φίλτρο έχει συνδέσμους εισόδου, τότε εκτελέστε τη λειτουργία επεξεργάζομαι, διαδικασία() επαναλαμβάνεται μέχρι να αδειάσουν οι ουρές συνδέσμων εισόδου. Μετά από αυτό, μεταβαίνει στο επόμενο φίλτρο της λίστας και το κάνει "κύλιση" έως ότου οι σύνδεσμοι εισαγωγής δεν περιέχουν μηνύματα. Το ticker μετακινείται από φίλτρο σε φίλτρο μέχρι να τελειώσει η λίστα. Αυτό ολοκληρώνει την επεξεργασία του κύκλου.

Τώρα θα επιστρέψουμε στις πλειάδες και θα μιλήσουμε για το γιατί μια τέτοια οντότητα προστέθηκε στη ροή πολυμέσων. Γενικά, η ποσότητα των δεδομένων που απαιτείται από τον αλγόριθμο που λειτουργεί μέσα στο φίλτρο δεν συμπίπτει και δεν είναι πολλαπλάσιο του μεγέθους των buffer δεδομένων που λαμβάνονται στην είσοδο. Για παράδειγμα, γράφουμε ένα φίλτρο που εκτελεί έναν γρήγορο μετασχηματισμό Fourier, ο οποίος εξ ορισμού μπορεί να επεξεργαστεί μόνο μπλοκ δεδομένων των οποίων το μέγεθος είναι δύναμη δύο. Ας είναι 512 μετρήσεις. Εάν τα δεδομένα παράγονται από ένα τηλεφωνικό κανάλι, τότε το buffer δεδομένων κάθε μηνύματος στην είσοδο θα μας φέρει 160 δείγματα σήματος. Είναι δελεαστικό να μην συλλέγετε δεδομένα από την είσοδο μέχρι να υπάρξει η απαιτούμενη ποσότητα δεδομένων. Αλλά σε αυτήν την περίπτωση, θα συμβεί σύγκρουση με το ticker, το οποίο θα προσπαθήσει ανεπιτυχώς να μετακινήσει το φίλτρο μέχρι να αδειάσει ο σύνδεσμος εισαγωγής. Προηγουμένως, ορίσαμε αυτόν τον κανόνα ως την τρίτη αρχή του φίλτρου. Σύμφωνα με αυτήν την αρχή, η συνάρτηση process() του φίλτρου πρέπει να λαμβάνει όλα τα δεδομένα από τις ουρές εισόδου.

Επιπλέον, δεν θα είναι δυνατή η λήψη μόνο 512 δειγμάτων από την είσοδο, αφού μπορείτε να πάρετε μόνο ολόκληρα μπλοκ, π.χ. το φίλτρο θα πρέπει να λάβει 640 δείγματα και να χρησιμοποιήσει 512 από αυτά, το υπόλοιπο πριν συγκεντρώσει ένα νέο τμήμα δεδομένων. Έτσι, το φίλτρο μας, εκτός από την κύρια εργασία του, πρέπει να παρέχει βοηθητικές ενέργειες για ενδιάμεση αποθήκευση δεδομένων εισόδου. Οι προγραμματιστές της ροής πολυμέσων και η λύση σε αυτό το γενικό πρόβλημα έχουν αναπτύξει ένα ειδικό αντικείμενο - το MSBufferizer (bufferer), το οποίο λύνει αυτό το πρόβλημα χρησιμοποιώντας πλειάδες.

Bufferizer (MSBufferizer)

Αυτό είναι ένα αντικείμενο που θα συγκεντρώσει δεδομένα εισόδου μέσα στο φίλτρο και θα αρχίσει να τα υποβάλλει για επεξεργασία μόλις ο όγκος των πληροφοριών επαρκεί για την εκτέλεση του αλγόριθμου φίλτρου. Ενώ η προσωρινή μνήμη συγκεντρώνει δεδομένα, το φίλτρο θα λειτουργεί σε κατάσταση αναμονής, χωρίς να καταναλώνει την επεξεργαστική ισχύ του επεξεργαστή. Αλλά μόλις η συνάρτηση ανάγνωσης από το bufferer επιστρέψει μια τιμή διαφορετική από το μηδέν, η συνάρτηση process() του φίλτρου αρχίζει να λαμβάνει και να επεξεργάζεται δεδομένα από το bufferer σε τμήματα του απαιτούμενου μεγέθους, μέχρι να εξαντληθεί.
Τα δεδομένα που δεν απαιτούνται ακόμη παραμένουν στην προσωρινή μνήμη ως το πρώτο στοιχείο της πλειάδας, στο οποίο προσαρτώνται τα επόμενα μπλοκ δεδομένων εισόδου.

Η δομή που περιγράφει το buffer:

struct _MSBufferizer{
queue_t q; /* Очередь сообщений. */
int size; /* Суммарный размер данных находящихся в буферизаторе в данный момент. */
};
typedef struct _MSBufferizer MSBufferizer;

Λειτουργίες για εργασία με MSBufferizer

Δημιουργία μιας νέας παρουσίας buffer:

MSBufferizer * ms_bufferizer_new(void);

Η μνήμη εκχωρείται, αρχικοποιείται σε ms_bufferizer_init() και επιστρέφεται ένας δείκτης.

Λειτουργία αρχικοποίησης:

void ms_bufferizer_init(MSBufferizer *obj); 

Η ουρά αρχικοποιείται q, πεδίο μέγεθος έχει οριστεί στο μηδέν.

Προσθήκη μηνύματος:

void ms_bufferizer_put(MSBufferizer *obj, mblk_t *m); 

Το μήνυμα m προστίθεται στην ουρά. Προστίθεται το υπολογισμένο μέγεθος των μπλοκ δεδομένων μέγεθος.

Μεταφορά όλων των μηνυμάτων από την ουρά δεδομένων συνδέσμου στο buffer q:

void ms_bufferizer_put_from_queue(MSBufferizer *obj, MSQueue *q);   

Μεταφορά μηνυμάτων από σύνδεσμο q στο buffer εκτελείται χρησιμοποιώντας τη συνάρτηση ms_bufferizer_put().

Ανάγνωση από το buffer:

int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen); 

Εάν το μέγεθος των δεδομένων που συσσωρεύονται στο buffer είναι μικρότερο από το ζητούμενο (datalen), τότε η συνάρτηση επιστρέφει μηδέν, τα δεδομένα δεν αντιγράφονται στα δεδομένα. Διαφορετικά, εκτελείται διαδοχική αντιγραφή δεδομένων από πλειάδες που βρίσκονται στο buffer. Μετά την αντιγραφή, η πλειάδα διαγράφεται και η μνήμη ελευθερώνεται. Η αντιγραφή τελειώνει τη στιγμή που αντιγράφονται τα byte δεδομένων. Εάν εξαντληθεί ο χώρος στη μέση ενός μπλοκ δεδομένων, τότε σε αυτό το μήνυμα, το μπλοκ δεδομένων θα μειωθεί στο υπόλοιπο μη αντιγραμμένο τμήμα. Την επόμενη φορά που θα καλέσετε, η αντιγραφή θα συνεχιστεί από αυτό το σημείο.

Ανάγνωση του όγκου των δεδομένων που είναι διαθέσιμα αυτήν τη στιγμή στο buffer:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Επιστρέφει το πεδίο μέγεθος ρυθμιστής.

Απόρριψη μέρους των δεδομένων στο buffer:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Ο καθορισμένος αριθμός byte δεδομένων ανακτάται και απορρίπτεται. Τα παλαιότερα δεδομένα απορρίπτονται.

Διαγραφή όλων των μηνυμάτων στο buffer:

void ms_bufferizer_flush(MSBufferizer *obj); 

Ο μετρητής δεδομένων επαναφέρεται στο μηδέν.

Διαγραφή όλων των μηνυμάτων στο buffer:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Δεν γίνεται επαναφορά του μετρητή.

Αφαίρεση του buffer και απελευθέρωση μνήμης:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Παραδείγματα χρήσης του bufferer μπορούν να βρεθούν στον πηγαίο κώδικα πολλών φίλτρων ροής πολυμέσων. Για παράδειγμα, στο φίλτρο MS_L16_ENC, το οποίο αναδιατάσσει τα byte στα δείγματα από τη σειρά δικτύου στη σειρά κεντρικού υπολογιστή: l16.c

Στο επόμενο άρθρο, θα εξετάσουμε το θέμα της εκτίμησης του φορτίου σε ένα ticker και τρόπους καταπολέμησης του υπερβολικού φόρτου υπολογιστών σε μια ροή πολυμέσων.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο