[xiph-commits] r3751 - libannodex/trunk/src/importers

conrad at svn.annodex.net conrad at svn.annodex.net
Wed Oct 29 04:06:27 PDT 2008


Author: conrad
Date: 2008-10-29 04:06:26 -0700 (Wed, 29 Oct 2008)
New Revision: 3751

Modified:
   libannodex/trunk/src/importers/anx_import_ogg.c
Log:
anx_import_ogg: replace scan/seek algorithm with one-pass accumulation,
like oggz-chop but on a packet basis


Modified: libannodex/trunk/src/importers/anx_import_ogg.c
===================================================================
--- libannodex/trunk/src/importers/anx_import_ogg.c	2008-10-29 11:06:21 UTC (rev 3750)
+++ libannodex/trunk/src/importers/anx_import_ogg.c	2008-10-29 11:06:26 UTC (rev 3751)
@@ -85,8 +85,11 @@
 
 typedef enum {
   STATE_HEADERS,
+  STATE_GLUE,
+#if 0
   STATE_GRANULEINFO,
   STATE_FILTER,
+#endif
   STATE_DATA
 } state_t;
 
@@ -111,11 +114,16 @@
 
 struct _AnxOggTrack {
   AnxSourceTrack source_track;
+  int nr_headers_remaining;
+
+  /* Queue of packets corresponding to the last GOP of the
+   * glue section for this track. */
+  AnxList * glue_packets;
+
   int need_keygranule;
   ogg_int64_t keygranule;
   double keygranule_time;
   int filter_got_keygranule;
-  int nr_headers_remaining;
 };
 
 struct _AnxOggData {
@@ -136,8 +144,8 @@
   long skeleton_serialno;
   int got_skeleton_eos;
 
-  long nr_headers_remaining;
-  long headers_unread;
+  int nr_headers_remaining;
+  int headers_unread;
 
   int use_granule_seek;
   double min_granule_seek;
@@ -146,9 +154,12 @@
   int got_end;
 
   /* Maintain a list of AnxOggTracks */
-  OggzTable * logicals;
+  OggzTable * tracks;
 
+  /* Packet queues */
+  AnxList * header_queue;
   AnxList * delivery_queue;
+
   long current_offset; /* offset into current packet */
 
   AnxImportCMML import_cmml;
@@ -287,6 +298,8 @@
   aop->source_track = source_track;
   aop->eos = op->e_o_s;
 
+  memcpy (aop->data, op->packet, op->bytes);
+
   return aop;
 }
 
@@ -311,22 +324,23 @@
 read_packet_headers (OGGZ * oggz, ogg_packet * op, long serialno,
 		     void * user_data)
 {
-  unsigned char * header = op->packet;
   AnxOggData * aod = (AnxOggData *)user_data;
   AnxOggTrack * aot = NULL;
+  AnxOggPacket * aop = NULL;
   AnxSource * m = aod->anx_source;
   AnxSourceTrack * track = NULL;
   OggzStreamContent content;
   int need_insert = 1;
   int got_headers = 0;
 
-  aot = (AnxOggTrack *) oggz_table_lookup (aod->logicals, serialno);
+  aot = (AnxOggTrack *) oggz_table_lookup (aod->tracks, serialno);
   if (aot != NULL) {
     need_insert = 0;
   }
 
+  content = oggz_stream_get_content (oggz, serialno);
+
   if (op->b_o_s) {
-    content = oggz_stream_get_content (oggz, serialno);
 
     if (content == OGGZ_CONTENT_CMML) {
       /* Don't insert this into the media */
@@ -397,7 +411,7 @@
       }
 
       if (need_insert) {
-	oggz_table_insert (aod->logicals, serialno, aot);
+	oggz_table_insert (aod->tracks, serialno, aot);
       }
 
       /* XXX: fix this for theora */
@@ -431,6 +445,11 @@
       aod->got_skeleton_eos = 1;
     }
 
+    if (content != OGGZ_CONTENT_CMML) {
+      aot = (AnxOggTrack *) oggz_table_lookup (aod->tracks, serialno);
+      track = &(aot->source_track);
+    }
+
     aod->got_non_bos = 1;
   }
 
@@ -444,43 +463,202 @@
     if (aod->got_skeleton_eos) got_headers = 1;
   }
   
+  /* Add this packet to the headers_queue */
+  if (track) {
+    aop = anxogg_packet_new (op, track, 0.0);
+    aod->header_queue = anx_list_append (aod->header_queue, aop);
+  }
+
   if (got_headers) {
+#if 0
     if (m->start_time != 0.0) {
       if (oggz_seek_units (oggz, 0, SEEK_CUR) >= 0) {
 	oggz_set_data_start (oggz, oggz_tell (oggz));
-	if (aod->use_granule_seek) aod->state = STATE_GRANULEINFO;
+	if (aod->use_granule_seek)
+          aod->state = STATE_GRANULEINFO;
       }
     } else {
       aod->state = STATE_DATA;
     }
+#else
+    aod->state = STATE_GLUE;
+    aod->headers_unread = anx_list_length (aod->header_queue);
+#endif
     return OGGZ_STOP_OK;
   }
 
   return OGGZ_CONTINUE;
 }
 
+/*
+ * OggzReadPacket read_packet_glue
+ *
+ * Parse Ogg packets up to the start point.
+ */
 static int
+read_packet_glue (OGGZ * oggz, ogg_packet * op, long serialno,
+                  void * user_data)
+{
+  AnxOggData * aod = (AnxOggData *)user_data;
+  AnxOggTrack * aot = NULL;
+  AnxSourceTrack * track = NULL;
+  AnxOggPacket * aop;
+  int granuleshift, iframe;
+  double start_time, at_time;
+
+  aot = (AnxOggTrack *) oggz_table_lookup (aod->tracks, serialno);
+
+  /* If this track is not in the table, ignore it. */
+  if (aot == NULL) return OGGZ_CONTINUE;
+
+  track = &(aot->source_track);
+
+  /* If we are past the start time, then queue this packet for delivery
+   * and set state to STATE_DATA */
+  start_time = aod->anx_source->start_time;
+  at_time = gp_to_time (aod->oggz, serialno, op->granulepos);
+  if (at_time >= start_time) {
+    aop = anxogg_packet_new (op, track, at_time);
+    aod->delivery_queue = anx_list_append (aod->delivery_queue, aop);
+    aod->state = STATE_DATA;
+    return OGGZ_STOP_OK;
+  }
+
+  granuleshift = oggz_get_granuleshift(oggz, serialno);
+  /* No need to cache packets of tracks without granuleshift */
+  if (granuleshift == 0) return OGGZ_CONTINUE;
+
+  /* If this is a keyframe, clear the glue_packets queue for this track */
+  iframe = op->granulepos >> granuleshift;
+  if (op->granulepos != -1 && (iframe << granuleshift) == op->granulepos) {
+    anx_list_free_with (aot->glue_packets, anxogg_packet_free);
+    aot->glue_packets = NULL;
+  }
+
+  /* Append this packet to this track's list of glue_packets */
+  aop = anxogg_packet_new (op, track, at_time);
+  aot->glue_packets = anx_list_append (aot->glue_packets, aop);
+
+  return OGGZ_CONTINUE;
+}
+
+/*
+ * Create a single list containing all the glue packets from all
+ * tracks, in time order.
+ */
+static AnxList *
+merge_glue (AnxOggData * aod)
+{
+  AnxList * glued = NULL, * candidates = NULL, * c, * min_aot_l, * min_aop_l;
+  AnxOggTrack * aot = NULL, * min_aot;
+  AnxOggPacket * aop = NULL;
+  int i, ntracks, ncandidates=0;
+  long serialno;
+  double at_time, min_time;
+
+  /* Prime candidates */
+  ntracks = oggz_table_size (aod->tracks);
+  for (i=0; i < ntracks; i++) {
+    aot = oggz_table_nth (aod->tracks, i, &serialno);
+    if (aot != NULL && aot->glue_packets != NULL) {
+      candidates = anx_list_append (candidates, aot);
+    }
+  }
+
+  /* Merge candidates */
+  while (candidates != NULL) {
+    /* Find minimum time packet in all glue buffers */
+    min_time = 10e100;
+    min_aot_l = NULL;
+    for (c = candidates; c; c = c->next) {
+      aot = (AnxOggTrack *)c->data;
+      aop = (AnxOggPacket *)aot->glue_packets->data;
+      if (aop->current_time < min_time) {
+        /* This is the min so far ... */
+        min_time = aop->current_time;
+        min_aot_l = c;
+      }
+    }
+
+    /* Got min_aot */
+    min_aot = min_aot_l->data;
+
+    /* Stash the AnxList* containing the minimum glue packet */
+    min_aop_l = min_aot->glue_packets;
+
+    /* Set that track's glue_packets to it's own tail */
+    min_aot->glue_packets = min_aop_l->next;
+    if (min_aot->glue_packets) {
+      /* If packets are remaining, clear the head's prev pointer */
+      min_aot->glue_packets->prev = NULL;
+    } else {
+      /* Otherwise, we can remove this track from future candidates */
+      candidates = anx_list_remove (candidates, min_aot_l);
+      /* Free the min_aot_l container (but not its AnxOggTrack data) */
+      anx_free (min_aot_l);
+    }
+    
+    /* Append the min_aop to the list of glued packets we are building */
+    glued = anx_list_append (glued, min_aop_l->data);
+
+    /* Free the min_aop_l container (but not it's AnxOggPacket data) */
+    anx_free (min_aop_l);
+  }
+
+  return glued;
+}
+
+static int
+init_delivery_queue (AnxOggData * aod, AnxList * glued)
+{
+  AnxList * queue = NULL, * tail;
+
+  queue = aod->header_queue;
+  tail = anx_list_tail (queue);
+
+  /* If there packets glued, join them to the header queue */
+  if (glued != NULL) {
+    tail->next = glued;
+    glued->prev = tail;
+
+    /* Advance tail to the tail of (glued)queue */
+    tail = anx_list_tail (glued);
+  }
+
+  /* If there's any packets already queued for delivery during gluing,
+   * join them too. */
+  if (aod->delivery_queue != NULL) {
+    tail->next = aod->delivery_queue;
+    aod->delivery_queue->prev = tail;
+  }
+
+  /* Now queue points to the original headers++glued++delivery, so
+   * mark that for delivery */
+  aod->delivery_queue = queue;
+
+  /* And clear the header_queue pointer as it is no longer needed */
+  aod->header_queue = NULL;
+
+  return 0;
+}
+
+#if 0
+static int
 granuleinfo_update_state (AnxOggData * aod)
 {
   AnxOggTrack * aot;
   int i, n;
 
-#ifdef DEBUG
-  //fprintf (aod->df, "anxogg::granuleinfo_update_state cmml %010ld %s\n",
-	//   aod->cmml_serialno,
-	//   aod->cmml_need_keygranule ? "needs keygranule" : "no keygranule");
-#endif
-
   if (aod->cmml_serialno != -1 && aod->cmml_need_keygranule) return 0;
 
-  n = oggz_table_size (aod->logicals);
+  n = oggz_table_size (aod->tracks);
 
 #ifdef DEBUG
-  fprintf (aod->df, "anxogg::granuleinfo_update_state %d logicals\n", n);
+  fprintf (aod->df, "anxogg::granuleinfo_update_state %d tracks\n", n);
 #endif
 
   for (i = 0; i < n; i++) {
-    aot = (AnxOggTrack *)oggz_table_nth (aod->logicals, i, NULL);
+    aot = (AnxOggTrack *)oggz_table_nth (aod->tracks, i, NULL);
     if (aot->need_keygranule) {
 #ifdef DEBUG
       fprintf (aod->df, "anxogg::granuleinfo_update_state logical %d needs keygranule\n", i);
@@ -559,7 +737,7 @@
       aod->min_granule_seek = offset;    
   } else {
 
-    aot = (AnxOggTrack *) oggz_table_lookup (aod->logicals, serialno);
+    aot = (AnxOggTrack *) oggz_table_lookup (aod->tracks, serialno);
     if (aot == NULL) {
       /* If this track is not in the table, ignore it. */
       return OGGZ_STOP_OK;
@@ -617,10 +795,12 @@
 
   return OGGZ_STOP_OK;
 }
+#endif
 
 static int
 anxogg_setup (AnxOggData * aod)
 {
+  AnxList * glued;
   long n;
   double start_time = 0.0, end_time = -1.0;
   off_t start_offset = 0, end_offset = -1;
@@ -642,6 +822,16 @@
 	  aod->nr_headers_remaining);
 #endif
 
+  /* Slurp glue section */
+  oggz_set_read_callback (aod->oggz, -1, read_packet_glue, aod);
+  while (aod->state == STATE_GLUE &&
+	 (n = oggz_read (aod->oggz, 1024)) != 0);
+
+  /* Now merge the glue section, and build the initial delivery queue */
+  glued = merge_glue (aod);
+  init_delivery_queue (aod, glued);
+
+#if 0
   /* Find bitrate info */
   start_time = aod->anx_source->start_time;
   end_time = aod->anx_source->end_time;
@@ -723,6 +913,7 @@
   /* Reset to beginning */
   oggz_seek (aod->oggz, 0, SEEK_SET);
   aod->nr_headers_remaining = aod->headers_unread;
+#endif
 
   return 0;
 }
@@ -731,6 +922,7 @@
  * Read: Pass data packets to the writer
  */
 
+#if 0
 static double
 anxogg_seek_update (AnxSource * source)
 {
@@ -784,24 +976,28 @@
 
   return offset;
 }
+#endif
 
 static long
 anxogg_read_update (AnxSource * media)
 {
   AnxOggData * aod = (AnxOggData *)media->custom_data;
 
- do_read:
+ //do_read:
 
   while ((aod->delivery_queue == NULL) && (oggz_read (aod->oggz, 1024)) != 0);
 
+#if 0
   if (aod->need_seek == NEED_SEEK && aod->nr_headers_remaining == 0) {
     anxogg_seek_update (media);
     goto do_read;
   }
+#endif
 
   return 0;
 }
 
+#if 0
 /***********************************************************
  * A filter predicate, used between the min seek granule
  * and the start time (ie. the bit between the prior keyframe
@@ -889,6 +1085,7 @@
   return 1;
 #endif
 }
+#endif
 
 /*
  * OggzReadPacket read_packet_data
@@ -914,7 +1111,7 @@
 #endif
 
   if (!(aod->cmml_serialno != -1 && serialno == aod->cmml_serialno)) {
-    aot = (AnxOggTrack *) oggz_table_lookup (aod->logicals, serialno);
+    aot = (AnxOggTrack *) oggz_table_lookup (aod->tracks, serialno);
     if (aot == NULL) {
 #ifdef DEBUG
       fprintf (aod->df,
@@ -990,22 +1187,22 @@
   }
 
 #ifdef DEBUG
-  fprintf (aod->df, "anxogg::read_packet: Filter? (%s)\n", 
-           aod->state == STATE_FILTER ? "USE_FN" : "NO_FN");
+  //fprintf (aod->df, "anxogg::read_packet: Filter? (%s)\n", 
+   //        aod->state == STATE_FILTER ? "USE_FN" : "NO_FN");
 #endif
 
-  if (!aod->ignore_media && !aod->got_end &&
-      (aod->state != STATE_FILTER || filter (aod, aot, serialno, op))) {
+  if (!aod->ignore_media && !aod->got_end) {
+      // &&
+      //(aod->state != STATE_FILTER || filter (aod, aot, serialno, op))) {
 #ifdef DEBUG
     fprintf (aod->df, "anxogg::read_packet_data: no, copy out\n");
 #endif
 
     aop = anxogg_packet_new (op, track,
                              ((double)oggz_tell_units(oggz)) / SUBSECONDS);
-    memcpy (aop->data, op->packet, op->bytes);
-    
     aod->delivery_queue = anx_list_append (aod->delivery_queue, aop);
     
+#if 0
     if (aot->nr_headers_remaining > 0) {
       aod->nr_headers_remaining--;
       aot->nr_headers_remaining--;
@@ -1027,6 +1224,7 @@
         return OGGZ_CONTINUE;
       }
     }
+#endif
   }
 
   return OGGZ_STOP_OK;
@@ -1072,6 +1270,7 @@
 
   /* If that's finished this media packet, advance to the next one */
   if (aod->current_offset >= aop->length) {
+
     aod->delivery_queue = anx_list_remove (aod->delivery_queue, head);
 
     aop = anxogg_packet_free (aop);
@@ -1153,8 +1352,10 @@
 
   aod->need_seek = NEED_SEEK_PENDING;
   aod->got_end = 0;
-  aod->logicals = oggz_table_new ();
+  aod->tracks = oggz_table_new ();
 
+  aod->header_queue = NULL;
+
   aod->delivery_queue = NULL;
   aod->current_offset = 0;
 
@@ -1242,9 +1443,9 @@
 
   anx_list_free_with (aod->delivery_queue, (AnxFreeFunc)anxogg_packet_free);
 
-  n = oggz_table_size (aod->logicals);
+  n = oggz_table_size (aod->tracks);
   for (i = 0; i < n; i++) {
-    aot = (AnxOggTrack *)oggz_table_nth (aod->logicals, i, NULL);
+    aot = (AnxOggTrack *)oggz_table_nth (aod->tracks, i, NULL);
     if (aot) {
       track = &(aot->source_track);
       anx_free (track->id);
@@ -1253,7 +1454,7 @@
     }
   }
 
-  oggz_table_delete (aod->logicals);
+  oggz_table_delete (aod->tracks);
 
 #ifdef DEBUG
 #ifdef DEBUG_FILE



More information about the commits mailing list