libdvbv5: reimplement the logic that gets a full section
authorMauro Carvalho Chehab <m.chehab@samsung.com>
Thu, 6 Mar 2014 12:31:27 +0000 (09:31 -0300)
committerMauro Carvalho Chehab <m.chehab@samsung.com>
Thu, 6 Mar 2014 13:49:05 +0000 (10:49 -0300)
The logic that gets a full section is broken: it expects to
receive the sections in order, and not to receive the same
section segment twice.

Reimplement it.

As a side effect, it no longer needs to wait for a section
repeat, with speeds up the code.

On my tests with an ATSC signal, before this patch, scanning
to a single frequency was spending 1.850 secs. After the
patch, dvbv5-scan is now spending 1.432 secs.

That means a 418 ms save per frequency.

Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
lib/libdvbv5/dvb-scan.c

index 69b175a..bc40dab 100644 (file)
@@ -91,44 +91,196 @@ int dvb_read_section(struct dvb_v5_fe_parms *parms, int dmx_fd,
        return dvb_read_section_with_id(parms, dmx_fd, tid, pid, -1, table, timeout);
 }
 
-int dvb_read_section_with_id(struct dvb_v5_fe_parms *parms, int dmx_fd,
-                            unsigned char tid, uint16_t pid, int id,
-                            uint8_t **table, unsigned timeout)
+/*
+ * The code below was inspired on Linux Kernel's bitmask implementation
+ */
+
+#define BITS_PER_LONG          (8 * sizeof(long))
+#define BIT_MASK(nr)           (1UL << ((nr) % BITS_PER_LONG))
+#define BIT_WORD(nr)           ((nr) / BITS_PER_LONG)
+#define BITS_TO_LONGS(nr)      ((nr +BITS_PER_LONG - 1) / BITS_PER_LONG)
+
+static void set_bit(int nr, unsigned long *addr)
 {
-       uint8_t *buf = NULL;
-       uint8_t *tbl = NULL;
-       ssize_t table_length = 0;
-       uint8_t mask = 0xff;
+       unsigned long mask = BIT_MASK(nr);
+       unsigned long *p = addr + BIT_WORD(nr);
 
-       /* variables for section handling */
-       int start_id = -1;
-       int start_section = -1;
-       int first_section = -1;
-       int last_section = -1;
-       int sections = 0;
-       struct dvb_table_header *h;
+       *p  |= mask;
+}
+
+static int test_bit(int nr, unsigned long *addr)
+{
+       unsigned long mask = BIT_MASK(nr);
+       unsigned long *p = addr + BIT_WORD(nr);
+
+       return (*p & mask) ? -1 : 0;
+}
+
+static int is_all_bits_set(int nr, unsigned long *addr)
+{
+       unsigned long mask = BIT_MASK(nr + 1) - 1;
+
+       return (*addr == mask);
+}
+
+
+struct tid_pid_table_priv {
+       int last_section;
+       unsigned long is_read_bits[BITS_TO_LONGS(256)];
+       int done;
+};
+
+struct tid_pid_table {
+       /* Input data */
+       unsigned char tid;
+       uint16_t pid;
+       int ts_id;
+       uint8_t **table;
+
+       /*
+        * Private temp data used by dvb_read_sections().
+        * Should not be filled outside dvb-scan.c, as they'll be
+        * overrided
+        */
+       void *priv;
+};
 
-       if (!table)
+static int dvb_parse_section_alloc(struct dvb_v5_fe_parms *parms,
+                                  struct tid_pid_table *sect)
+{
+       struct tid_pid_table_priv *priv;
+
+       if (!sect->table) {
+               dvb_logerr("table memory pointer not filled");
                return -4;
-       *table = NULL;
+       }
+       *sect->table = NULL;
+       priv = calloc(sizeof(struct tid_pid_table_priv), 1);
+       if (!priv) {
+               dvb_perror("Out of memory");
+               return -1;
+       }
+       priv->last_section = -1;
+       sect->priv = priv;
+
+       return 0;
+}
 
-       // FIXME: verify known table
+static void dvb_parse_section_free(struct tid_pid_table *sect)
+{
+       if (sect->priv)
+               free(sect->priv);
+}
+
+static int dvb_parse_section(struct dvb_v5_fe_parms *parms,
+                            struct tid_pid_table *sect,
+                            uint8_t *buf, ssize_t buf_length)
+{
+       struct dvb_table_header *h;
+       struct tid_pid_table_priv *priv;
 
-       if (dvb_set_section_filter(dmx_fd, pid, 1, &tid, &mask, NULL,
-                                  DMX_IMMEDIATE_START | DMX_CHECK_CRC))
+       uint8_t *tbl = NULL;
+       unsigned char tid;
+       ssize_t table_length = 0;
+
+       h = (struct dvb_table_header *)buf;
+       dvb_table_header_init(h);
+
+       if (parms->verbose)
+               dvb_log("Received table 0x%02x, TS ID 0x%04x, section number 0x%02x, last section 0x%02x",
+                       h->table_id, h->id, h->section_id, h->last_section);
+
+       if (sect->tid != h->table_id) {
+               dvb_logdbg("Something's wrong: couldn't match ID %d at the active section filters",
+                          h->table_id);
                return -1;
+       }
+       priv = sect->priv;
+       tid = h->table_id;
+
+       /* Check if the table was already parsed */
+
+       if (priv->last_section < 0)
+               priv->last_section = h->last_section;
+       else if (test_bit(h->section_id, priv->is_read_bits))
+               return 0;
+
+       /* search for an specific TS ID */
+       if (sect->ts_id != -1) {
+               if (h->id != sect->ts_id)
+                       return 0;
+       }
+
+       /* handle the sections */
+       set_bit(h->section_id, priv->is_read_bits);
 
+       tbl = *sect->table;
+       if (!tbl) {
+               if (!dvb_table_initializers[tid].size) {
+                       dvb_logerr("dvb_read_section: no table size for table %d",
+                                       tid);
+                       return -1;
+               }
+
+               tbl = calloc(dvb_table_initializers[tid].size, 1);
+       }
+
+       if (dvb_table_initializers[tid].init)
+               dvb_table_initializers[tid].init(parms, buf, buf_length,
+                                                tbl, &table_length);
+       else
+               dvb_logerr("dvb_read_section: no initializer for table %d",
+                          tid);
+
+       /* Store the table */
+       *sect->table = tbl;
+
+       if (is_all_bits_set(priv->last_section, priv->is_read_bits))
+               priv->done = 1;
+
+       if (!priv->done)
+               return 0;
+
+       /* Section was fully parsed */
+       return 1;
+}
+
+static int dvb_read_sections(struct dvb_v5_fe_parms *parms, int dmx_fd,
+                            struct tid_pid_table *sect,
+                            unsigned timeout)
+{
+       int ret;
+       uint8_t *buf = NULL;
+       uint8_t mask = 0xff;
+
+       /* FIXME: verify if all requested tables are known */
+
+       ret = dvb_parse_section_alloc(parms, sect);
+       if (ret < 0)
+               return ret;
+
+       if (dvb_set_section_filter(dmx_fd, sect->pid, 1,
+                                  &sect->tid, &mask, NULL,
+                                  DMX_IMMEDIATE_START | DMX_CHECK_CRC)) {
+               dvb_dmx_stop(dmx_fd);
+               return -1;
+       }
        if (parms->verbose)
-               dvb_log("Parsing table ID %d, program ID %d", tid, pid);
+               dvb_log("Waiting for table ID %d, program ID %d",
+                       sect->tid, sect->pid);
 
-       buf = malloc(DVB_MAX_PAYLOAD_PACKET_SIZE);
+       buf = calloc(DVB_MAX_PAYLOAD_PACKET_SIZE, 1);
        if (!buf) {
                dvb_perror("Out of memory");
                dvb_dmx_stop(dmx_fd);
+               dvb_parse_section_free(sect);
                return -1;
        }
-       while (1) {
+
+
+       do {
                int available;
+               uint32_t crc;
                ssize_t buf_length = 0;
 
                do {
@@ -136,110 +288,59 @@ int dvb_read_section_with_id(struct dvb_v5_fe_parms *parms, int dmx_fd,
                } while (available < 0 && errno == EOVERFLOW);
 
                if (parms->abort) {
-                       free(buf);
-                       if (tbl)
-                               free(tbl);
-                       dvb_dmx_stop(dmx_fd);
-                       return 0;
+                       ret = 0;
+                       break;
                }
                if (available <= 0) {
-                       dvb_logerr("dvb_read_section: no data read on pid %x table %x",
-                                  pid, tid);
-                       free(buf);
-                       if (tbl)
-                               free(tbl);
-                       dvb_dmx_stop(dmx_fd);
-                       return -1;
+                       dvb_logerr("dvb_read_section: no data read on section filter");
+                       ret = -1;
+                       break;
                }
                buf_length = read(dmx_fd, buf, DVB_MAX_PAYLOAD_PACKET_SIZE);
 
                if (!buf_length) {
-                       dvb_logerr("dvb_read_section: not enough data to read on pid %x table %x",
-                                  pid, tid);
-                       free(buf);
-                       if (tbl)
-                               free(tbl);
-                       dvb_dmx_stop(dmx_fd);
-                       return -1;
+                       dvb_logerr("dvb_read_section: buf returned an empty buffer");
+                       ret = -1;
+                       break;
                }
                if (buf_length < 0) {
                        dvb_perror("dvb_read_section: read error");
-                       free(buf);
-                       if (tbl)
-                               free(tbl);
-                       dvb_dmx_stop(dmx_fd);
-                       return -2;
+                       ret = -2;
+                       break;
                }
 
-               uint32_t crc = crc32(buf, buf_length, 0xFFFFFFFF);
+               crc = crc32(buf, buf_length, 0xFFFFFFFF);
                if (crc != 0) {
                        dvb_logerr("dvb_read_section: crc error");
-                       free(buf);
-                       if (tbl)
-                               free(tbl);
-                       dvb_dmx_stop(dmx_fd);
-                       return -3;
-               }
-
-               h = (struct dvb_table_header *)buf;
-               dvb_table_header_init(h);
-
-               if (start_id == h->id && start_section == h->section_id) {
-                       dvb_logdbg( "dvb_read_section: section repeated, reading done" );
+                       ret = -3;
                        break;
                }
-               if (start_id == -1)
-                       start_id = h->id;
-               if (start_section == -1)
-                       start_section = h->section_id;
 
-               if (id != -1 && h->id != id) { /* search for a specific table id */
-                       continue;
-               }
+               ret = dvb_parse_section(parms, sect, buf, buf_length);
+       } while (!ret);
+       free(buf);
+       dvb_dmx_stop(dmx_fd);
+       dvb_parse_section_free(sect);
 
-               /* handle the sections */
-               if (first_section == -1)
-                       first_section = h->section_id;
-               else if (start_id == h->id && h->section_id == first_section)
-                       break;
+       if (ret > 0)
+               ret = 0;
 
-               if (last_section == -1 || h->last_section > last_section)
-                       last_section = h->last_section;
-
-               if (!tbl) {
-                       if (dvb_table_initializers[tid].size)
-                               tbl = malloc(dvb_table_initializers[tid].size);
-                       else {
-                               dvb_logerr("dvb_read_section: no table size for table %d",
-                                          tid);
-                               free(buf);
-                               dvb_dmx_stop(dmx_fd);
-                               return -1;
-                       }
-                       if (!tbl) {
-                               dvb_logerr("Out of memory");
-                               free(buf);
-                               dvb_dmx_stop(dmx_fd);
-                               return -4;
-                       }
-               }
-
-               if (dvb_table_initializers[tid].init)
-                       dvb_table_initializers[tid].init(parms, buf, buf_length, tbl, &table_length);
-               else
-                       dvb_logerr("dvb_read_section: no initializer for table %d", tid);
+       return ret;
+}
 
-               if (id != -1 && ++sections == last_section + 1) {
-                       dvb_logerr("dvb_read_section: read more sections than last section id: %d / %d", sections, last_section);
-                       break;
-               }
-       }
-       free(buf);
+int dvb_read_section_with_id(struct dvb_v5_fe_parms *parms, int dmx_fd,
+                            unsigned char tid, uint16_t pid,
+                            int ts_id,
+                            uint8_t **table, unsigned timeout)
+{
+       struct tid_pid_table tab;
 
-       dvb_dmx_stop(dmx_fd);
+       tab.tid = tid;
+       tab.pid = pid;
+       tab.ts_id = ts_id;
+       tab.table = table;
 
-       *table = tbl;
-       return 0;
+       return dvb_read_sections(parms, dmx_fd, &tab, timeout);
 }
 
 struct dvb_v5_descriptors *dvb_scan_alloc_handler_table(uint32_t delivery_system,