[xiph-commits] r14677 - trunk/ogg-tools/oggmerge

ivo at svn.xiph.org ivo at svn.xiph.org
Tue Apr 8 11:27:57 PDT 2008


Author: ivo
Date: 2008-04-08 11:27:55 -0700 (Tue, 08 Apr 2008)
New Revision: 14677

Added:
   trunk/ogg-tools/oggmerge/config.h.in
   trunk/ogg-tools/oggmerge/kate.c
   trunk/ogg-tools/oggmerge/kate.h
   trunk/ogg-tools/oggmerge/oggmerge-multiplexed-streams.sh
   trunk/ogg-tools/oggmerge/skeleton.c
   trunk/ogg-tools/oggmerge/skeleton.h
   trunk/ogg-tools/oggmerge/speex.c
   trunk/ogg-tools/oggmerge/speex.h
   trunk/ogg-tools/oggmerge/theora.c
   trunk/ogg-tools/oggmerge/theora.h
Modified:
   trunk/ogg-tools/oggmerge/Makefile.am
   trunk/ogg-tools/oggmerge/acinclude.m4
   trunk/ogg-tools/oggmerge/configure.in
   trunk/ogg-tools/oggmerge/midi.c
   trunk/ogg-tools/oggmerge/midi.h
   trunk/ogg-tools/oggmerge/mng.c
   trunk/ogg-tools/oggmerge/mng.h
   trunk/ogg-tools/oggmerge/mngread.c
   trunk/ogg-tools/oggmerge/oggmerge.c
   trunk/ogg-tools/oggmerge/oggmerge.h
   trunk/ogg-tools/oggmerge/vorbis.c
   trunk/ogg-tools/oggmerge/vorbis.h
Log:
Large patch by ogg.k.ogg.k:
* support for Theora, Kate, and Speex streams
* skeleton support for Theora, Kate, Speex, and Vorbis
* checks magic on .og[avxg] files
* fixed pointer bug in Vorbis packetizer
* fixed bug in MNG which prevented building
* shell script to deal with multiplexed streams
* bumped version number

Modified: trunk/ogg-tools/oggmerge/Makefile.am
===================================================================
--- trunk/ogg-tools/oggmerge/Makefile.am	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/Makefile.am	2008-04-08 18:27:55 UTC (rev 14677)
@@ -6,8 +6,9 @@
 
 bin_PROGRAMS = oggmerge
 
-oggmerge_SOURCES = oggmerge.c vorbis.c midi.c mng.c\
-			oggmerge.h vorbis.h midi.h mng.h
+oggmerge_SOURCES = oggmerge.c vorbis.c midi.c mng.c kate.c theora.c speex.c skeleton.c \
+			oggmerge.h vorbis.h midi.h mng.h kate.h theora.h speex.h skeleton.h
+
 EXTRA_oggmerge_SOURCES = getopt.c getopt1.c getopt.h
 
 oggmerge_CFLAGS = $(OGG_CFLAGS) $(VORBIS_CFLAGS)

Modified: trunk/ogg-tools/oggmerge/acinclude.m4
===================================================================
--- trunk/ogg-tools/oggmerge/acinclude.m4	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/acinclude.m4	2008-04-08 18:27:55 UTC (rev 14677)
@@ -56,7 +56,7 @@
 
   if test "x$no_ogg" = "x" ; then
      AC_MSG_RESULT(yes)
-     ifelse([$1], , :, [$1])     
+     ifelse([$1], , :, [$1])
   else
      AC_MSG_RESULT(no)
      if test -f conf.oggtest ; then
@@ -153,7 +153,7 @@
 
   if test "x$no_vorbis" = "x" ; then
      AC_MSG_RESULT(yes)
-     ifelse([$1], , :, [$1])     
+     ifelse([$1], , :, [$1])
   else
      AC_MSG_RESULT(no)
      if test -f conf.vorbistest ; then

Added: trunk/ogg-tools/oggmerge/config.h.in
===================================================================
--- trunk/ogg-tools/oggmerge/config.h.in	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/config.h.in	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,34 @@
+/* config.h.in.  Generated from configure.in by autoheader.  */
+
+/* Define if you have libkate */
+#undef HAVE_KATE
+
+/* Define to 1 if you have the `theora' library (-ltheora). */
+#undef HAVE_LIBTHEORA
+
+/* Define if you have libspeex */
+#undef HAVE_SPEEX
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION

Modified: trunk/ogg-tools/oggmerge/configure.in
===================================================================
--- trunk/ogg-tools/oggmerge/configure.in	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/configure.in	2008-04-08 18:27:55 UTC (rev 14677)
@@ -1,8 +1,10 @@
 dnl process this file with autoconf to generate the configure script
 
 AC_INIT(oggmerge.c)
-AM_INIT_AUTOMAKE(oggmerge,0.9)
+AM_INIT_AUTOMAKE(oggmerge,1.0)
 
+AC_CONFIG_HEADERS(config.h)
+
 AC_PROG_CC
 
 dnl substitue the included getopt if the system doesn't support long options
@@ -16,7 +18,22 @@
 #AC_CHECK_LIB(png, png_check_sig)
 #AC_CHECK_LIB(jpeg, jpeg_set_defaults)
 #AC_CHECK_LIB(mng, mng_readdisplay)
+AC_CHECK_LIB(oggkate, kate_ogg_decode_headerin,[have_kate=yes],[have_kate=no],-lkate -logg)
+AC_CHECK_LIB(speex, speex_packet_to_header,[have_speex=yes],[have_speex=no],-logg)
+AC_CHECK_LIB(theora, theora_decode_init,,,-logg)
 
+if test "x$have_kate" == "xyes"
+then
+  LIBS="$LIBS -loggkate -lkate -logg"
+  AC_DEFINE([HAVE_KATE],[1],[Define if you have libkate])
+fi
+
+if test "x$have_speex" == "xyes"
+then
+  LIBS="$LIBS -lspeex -logg"
+  AC_DEFINE([HAVE_SPEEX],[1],[Define if you have libspeex])
+fi
+
 dnl Check for headers
 AC_HEADER_STDC()
 #AC_CHECK_HEADERS(libmng.h)

Added: trunk/ogg-tools/oggmerge/kate.c
===================================================================
--- trunk/ogg-tools/oggmerge/kate.c	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/kate.c	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,274 @@
+/* kate.c
+**
+** kate dummy packetizing module
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+
+#ifdef HAVE_KATE
+#include <kate/kate.h>
+#include <kate/oggkate.h>
+#endif
+
+#include "oggmerge.h"
+#include "kate.h"
+
+typedef struct kate_page_tag {
+	oggmerge_page_t *page;
+	struct kate_page_tag *next;
+} kate_page_t;
+
+typedef struct {
+	ogg_sync_state oy;
+	int serialno;
+	ogg_int64_t old_granulepos;
+	int first_page;
+        int num_headers;
+        unsigned long int granule_shift;
+	unsigned long granule_numerator;
+	unsigned long granule_denominator;
+	kate_page_t *pages;
+        int old_style;
+} kate_state_t;
+
+static void _add_kate_page(kate_state_t *kstate, ogg_page *og);
+
+static unsigned long read32le(const unsigned char *bytes)
+{
+  unsigned long v=0;
+  v|=*bytes++;
+  v|=((*bytes++)<<8);
+  v|=((*bytes++)<<16);
+  v|=((*bytes++)<<24);
+  return v;
+}
+
+int kate_state_init(oggmerge_state_t *state, int serialno, int old_style)
+{
+	kate_state_t *kstate;
+
+	if (state == NULL) return 0;
+
+	kstate = (kate_state_t *)malloc(sizeof(kate_state_t));
+	if (kstate == NULL) return 0;
+
+	ogg_sync_init(&kstate->oy);
+	kstate->first_page = 1;
+        kstate->num_headers = 0;
+        kstate->granule_shift=0;
+	kstate->granule_numerator = 0;
+	kstate->granule_denominator = 0;
+	kstate->old_granulepos = 0;
+        kstate->pages = NULL;
+        kstate->old_style = old_style;
+
+	// NOTE: we just ignore serialno for now
+	// until a future libogg supports ogg_page_set_serialno
+
+	state->private = (void *)kstate;
+
+	return 1;
+}
+
+int kate_data_in(oggmerge_state_t *state, char *buffer, unsigned long size)
+{
+	int ret;
+	char *buf;
+	kate_state_t *kstate;
+	ogg_page og;
+	ogg_packet op;
+	ogg_stream_state os;
+#ifdef HAVE_KATE
+	kate_info ki;
+	kate_comment kc;
+#endif
+
+	kstate = (kate_state_t *)state->private;
+
+	// stuff data in
+	buf = ogg_sync_buffer(&kstate->oy, size);
+	memcpy(buf, buffer, size);
+	ogg_sync_wrote(&kstate->oy, size);
+
+	// pull pages out
+	while ((ret = ogg_sync_pageout(&kstate->oy, &og)) == 1) {
+		if (kstate->first_page) {
+			kstate->first_page = 0;
+			ogg_stream_init(&os, kstate->serialno=ogg_page_serialno(&og));
+			if (ogg_stream_pagein(&os, &og) < 0) {
+				ogg_stream_clear(&os);
+				return EBADHEADER;
+			}
+			if (ogg_stream_packetout(&os, &op) != 1) {
+				ogg_stream_clear(&os);
+				return EBADHEADER;
+			}
+#ifdef HAVE_KATE
+			kate_info_init(&ki);
+			kate_comment_init(&kc);
+			if (kate_ogg_decode_headerin(&ki, &kc, &op) < 0) {
+				kate_comment_clear(&kc);
+				kate_info_clear(&ki);
+				ogg_stream_clear(&os);
+				return EBADHEADER;
+			}
+                        kstate->num_headers = ki.num_headers;
+                        kstate->granule_shift = kate_granule_shift(&ki);
+			kstate->granule_numerator = ki.gps_numerator;
+			kstate->granule_denominator = ki.gps_denominator;
+			kate_comment_clear(&kc);
+			kate_info_clear(&ki);
+#else
+                        if (op.bytes<32) {
+                                /* just enough for the data we'll extract */
+				ogg_stream_clear(&os);
+                                return EBADHEADER;
+                        }
+                        if (memcmp(op.packet,"\200kate\0\0\0\0",9)) {
+				ogg_stream_clear(&os);
+                                return EBADHEADER;
+                        }
+                        kstate->num_headers = op.packet[11];
+                        kstate->granule_shift = op.packet[15];
+			kstate->granule_numerator = read32le(op.packet+24);
+			kstate->granule_denominator = read32le(op.packet+28);
+#endif
+			ogg_stream_clear(&os);
+			
+		}
+		_add_kate_page(kstate, &og);
+	}
+
+	if (ret == 0) return EMOREDATA;
+	return EOTHER;
+}
+
+oggmerge_page_t *kate_page_out(oggmerge_state_t *state)
+{
+	kate_state_t *kstate, *temp;
+	oggmerge_page_t *page;
+
+	kstate = (kate_state_t *)state->private;
+
+	if (kstate->pages == NULL) return NULL;
+
+        /* here, we don't want to make available pages with -1 granulepos,
+           instead we wait till we have the next page with an set granulepos.
+           returning NULL will force a further read */
+        if (kstate->pages->page->timestamp<0) return NULL;
+
+	page = kstate->pages->page;
+	kstate->pages = kstate->pages->next;
+
+	return page;
+}
+
+static ogg_page *_copy_ogg_page(ogg_page *og)
+{
+	ogg_page *page;
+
+	page = (ogg_page *)malloc(sizeof(ogg_page));
+	if (page == NULL) return NULL;
+
+	page->header_len = og->header_len;
+	page->body_len = og->body_len;
+	page->header = (unsigned char *)malloc(page->header_len);
+	if (page->header == NULL) {
+		free(page);
+		return NULL;
+	}
+	memcpy(page->header, og->header, page->header_len);
+	page->body = (unsigned char *)malloc(page->body_len);
+	if (page->body == NULL) {
+		free(page->header);
+		free(page);
+		return NULL;
+	}
+	memcpy(page->body, og->body, page->body_len);
+
+	return page;
+}
+
+static u_int64_t _make_timestamp(kate_state_t *kstate, ogg_int64_t granulepos)
+{
+	u_int64_t base, offset, stamp;
+	
+	if (kstate->granule_denominator == 0) return 0;
+	
+        base = granulepos>>kstate->granule_shift;
+        offset = granulepos-(base<<kstate->granule_shift);
+        stamp = (double)(base+offset) *1000000.0*kstate->granule_denominator/(double)kstate->granule_numerator;
+
+        if (granulepos>=0) {
+          kstate->old_granulepos = granulepos;
+        }
+
+	return stamp;
+}
+
+static void _add_kate_page(kate_state_t *kstate, ogg_page *og)
+{
+	oggmerge_page_t *page;
+	kate_page_t *kpage, *temp;
+
+        if (ogg_page_serialno(og)!=kstate->serialno) {
+          fprintf(stderr,"Error: oggmerge does not support merging multiplexed streams (%x %x)\n",ogg_page_serialno(og),kstate->serialno);
+          return;
+        }
+
+	// build oggmerge page
+	page = (oggmerge_page_t *)malloc(sizeof(oggmerge_page_t));
+	page->og = _copy_ogg_page(og);
+	page->timestamp = -1;
+        if (ogg_page_granulepos(og)>=0) {
+            page->timestamp = _make_timestamp(kstate, ogg_page_granulepos(og));
+            /* if we had queued pages with -1 timestamp (eg, we didn't know yet),
+               we want to update them now to the newly known timestamp so they'll
+               automatically be selected when appropriate */
+            temp = kstate->pages;
+            while (temp && temp->page->timestamp<0) {
+                temp->page->timestamp = page->timestamp;
+                temp=temp->next;
+            }
+        }
+	
+	// build kate page
+	kpage = (kate_page_t *)malloc(sizeof(kate_page_t));
+	if (kpage == NULL) {
+		free(page->og->header);
+		free(page->og->body);
+		free(page->og);
+		free(page);
+		return;
+	}
+
+	kpage->page = page;
+	kpage->next = NULL;
+
+	// add page to state
+	temp = kstate->pages;
+	if (temp == NULL) {
+		kstate->pages = kpage;
+	} else {
+		while (temp->next) temp = temp->next;
+		temp->next = kpage;
+	}
+}
+
+int kate_fisbone_out(oggmerge_state_t *state, ogg_packet *op)
+{
+    kate_state_t *kstate = (kate_state_t *)state->private;
+
+    int gshift = kstate->granule_shift;
+    ogg_int64_t gnum = kstate->granule_numerator;
+    ogg_int64_t gden = kstate->granule_denominator;
+    int nheaders = kstate->num_headers;
+    add_fisbone_packet(op, kstate->serialno, "application/x-kate", nheaders, 0, gshift, gnum, gden);
+
+    return 0;
+}

Added: trunk/ogg-tools/oggmerge/kate.h
===================================================================
--- trunk/ogg-tools/oggmerge/kate.h	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/kate.h	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,19 @@
+/* kate.h
+**
+** oggmerge kate file module
+**
+*/
+
+#ifndef __KATE_H__
+#define __KATE_H__
+
+#include "config.h"
+
+#include "oggmerge.h"
+
+int kate_state_init(oggmerge_state_t *state, int serialno, int old_style);
+int kate_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
+oggmerge_page_t *kate_page_out(oggmerge_state_t *state);
+int kate_fisbone_out(oggmerge_state_t *state, ogg_packet *op);
+
+#endif  /* __KATE_H__*/

Modified: trunk/ogg-tools/oggmerge/midi.c
===================================================================
--- trunk/ogg-tools/oggmerge/midi.c	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/midi.c	2008-04-08 18:27:55 UTC (rev 14677)
@@ -5,6 +5,8 @@
 */
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <ogg/ogg.h>
 
 #include "oggmerge.h"
@@ -49,6 +51,7 @@
 	midi_page_t *pages;
 	int serialno;
 	ogg_int64_t old_granulepos;
+	int old_style;
 	// timing information
 	int ticks;
 	long tempo;
@@ -63,7 +66,7 @@
 
 static int _process_data(midi_state_t *midistate);
 
-int midi_state_init(oggmerge_state_t *state, int serialno)
+int midi_state_init(oggmerge_state_t *state, int serialno, int old_style)
 {
 	midi_state_t *midistate;
 
@@ -84,6 +87,7 @@
 		midistate->frames = 0;
 		midistate->smtpe = 0;
 		midistate->current = 0;
+		midistate->old_style = old_style;
 		state->private = (void *)midistate;
 		return 1;
 	}
@@ -326,12 +330,13 @@
 static u_int64_t _make_timestamp(midi_state_t *midistate, ogg_int64_t granulepos)
 {
 	u_int64_t timestamp;
+	ogg_int64_t gp = midistate->old_style?midistate->old_granulepos:granulepos;
 
 	if (midistate->smtpe) {
-		timestamp = (double)midistate->old_granulepos * ((double)(midistate->frames * midistate->ticks) / (double)1000000);
+		timestamp = (double)gp * ((double)(midistate->frames * midistate->ticks) / (double)1000000);
 	} else {
 		// tempo is in us/quartnernote
-		timestamp = (double)midistate->old_granulepos * (double)midistate->tempo / (double)midistate->ticks;
+		timestamp = (double)gp * (double)midistate->tempo / (double)midistate->ticks;
 	}
 
 	midistate->old_granulepos = granulepos;
@@ -725,3 +730,10 @@
 	return EMOREDATA;
 }
 
+int midi_fisbone_out(oggmerge_state_t *state, ogg_packet *op)
+{
+    fprintf(stderr, "Skeleton unsupported for MIDI stream\n");
+    exit(-1);
+
+    return 0;
+}

Modified: trunk/ogg-tools/oggmerge/midi.h
===================================================================
--- trunk/ogg-tools/oggmerge/midi.h	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/midi.h	2008-04-08 18:27:55 UTC (rev 14677)
@@ -9,8 +9,9 @@
 
 #include "oggmerge.h"
 
-int midi_state_init(oggmerge_state_t *state, int serialno);
+int midi_state_init(oggmerge_state_t *state, int serialno, int old_style);
 int midi_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
 oggmerge_page_t *midi_page_out(oggmerge_state_t *state);
+int midi_fisbone_out(oggmerge_state_t *state, ogg_packet *op);
 
 #endif  /* __MIDI_H__*/

Modified: trunk/ogg-tools/oggmerge/mng.c
===================================================================
--- trunk/ogg-tools/oggmerge/mng.c	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/mng.c	2008-04-08 18:27:55 UTC (rev 14677)
@@ -64,6 +64,7 @@
 	u_int64_t		packetno;
 	unsigned char		*chunk;
 	unsigned long		length;
+	int                     old_style;
 } mng_state_t;
 
 /* allocates and initializes local storage for a particular
@@ -71,7 +72,7 @@
  *
  * returns 1 on success, 0 on failure
  */
-int mng_state_init(oggmerge_state_t *state, int serialno)
+int mng_state_init(oggmerge_state_t *state, int serialno, int old_style)
 {
 	mng_state_t	*local;
 	ogg_stream_state *os;
@@ -89,6 +90,7 @@
 		local->packetno = 0;	/* number of 'next' packet */
 		local->chunk = NULL;
 		local->length = 0;
+		local->old_style = old_style;
 		
 		/* save our local data inside the oggmerge state */
 		state->private = local;
@@ -112,8 +114,8 @@
         if (state == NULL) return 0;	/* nothing to do */
 
         local = (mng_state_t *)state->private;
-                                                      
-        if (local != NULL) {                          
+
+        if (local != NULL) {
                 ogg_stream_destroy(local->os);
 		state->private = NULL;
 		free(local);
@@ -334,3 +336,11 @@
 	
 	return om;
 }
+
+int mng_fisbone_out(oggmerge_state_t *state, ogg_packet *op)
+{
+    fprintf(stderr, "Skeleton unsupported for MNG stream\n");
+    exit(-1);
+
+    return 0;
+}

Modified: trunk/ogg-tools/oggmerge/mng.h
===================================================================
--- trunk/ogg-tools/oggmerge/mng.h	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/mng.h	2008-04-08 18:27:55 UTC (rev 14677)
@@ -9,8 +9,9 @@
 
 #include "oggmerge.h"
 
-int mng_state_init(oggmerge_state_t *state, int serialno);
+int mng_state_init(oggmerge_state_t *state, int serialno, int old_style);
 int mng_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
 oggmerge_page_t *mng_page_out(oggmerge_state_t *state);
+int mng_fisbone_out(oggmerge_state_t *state, ogg_packet *op);
 
 #endif  /* __MNG_H__*/

Modified: trunk/ogg-tools/oggmerge/mngread.c
===================================================================
--- trunk/ogg-tools/oggmerge/mngread.c	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/mngread.c	2008-04-08 18:27:55 UTC (rev 14677)
@@ -68,7 +68,7 @@
 	int	err = 0;
 
 	/* check the signature */
-	err = fread(sig, sizeof(*sig), 8, param->in);
+	err = fread(sig, sizeof(*sig), 8, param->input);
 	sig[8] = '\0';
 	if (strncmp((const char *)sig, MNG_SIGNATURE, 8) != 0) {
 		fprintf(stderr, "not a mng file!");

Added: trunk/ogg-tools/oggmerge/oggmerge-multiplexed-streams.sh
===================================================================
--- trunk/ogg-tools/oggmerge/oggmerge-multiplexed-streams.sh	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/oggmerge-multiplexed-streams.sh	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+dir=`mktemp -d`
+if [ ! -d "$dir" ]; then
+  echo "failed to create temporary directory"
+  exit 1
+fi
+
+merged_file="merged.ogx"
+
+files=$(oggsplit -o "$dir" "$@" | grep "writing stream to" | sed -e "s^.*\`\(.*\)'.*^\1^")
+echo $files
+
+for ((c=1;;++c)); do
+  chain=`printf ".c%02d." $c`
+  files_in_this_chain=
+  for f in $files
+  do
+    echo "$f" | grep -q "$chain"
+    if [ $? -eq 0 ]; then
+      files_in_this_chain="$files_in_this_chain $f"
+    fi
+  done
+  if [ -z "$files_in_this_chain" ]; then break; fi
+  tmpfile=`mktemp`
+  oggmerge -o "$tmpfile" $files_in_this_chain
+  cat "$tmpfile" >> "$merged_file"
+  rm -f "$tmpfile"
+done
+
+exit 0
+
+# don't rm -fr $dir, just in case... :)
+rm -f $files
+rmdir "$dir"
\ No newline at end of file


Property changes on: trunk/ogg-tools/oggmerge/oggmerge-multiplexed-streams.sh
___________________________________________________________________
Name: svn:executable
   + *

Modified: trunk/ogg-tools/oggmerge/oggmerge.c
===================================================================
--- trunk/ogg-tools/oggmerge/oggmerge.c	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/oggmerge.c	2008-04-08 18:27:55 UTC (rev 14677)
@@ -21,10 +21,15 @@
 
 #include <ogg/ogg.h>
 
+#include "config.h"
 #include "oggmerge.h"
 #include "vorbis.h"
 #include "midi.h"
 #include "mng.h"
+#include "kate.h"
+#include "theora.h"
+#include "speex.h"
+#include "skeleton.h"
 
 #define VERSIONINFO "oggmerge v" VERSION "\n"
 
@@ -40,10 +45,12 @@
 		"oggmerge [-o <outfile> | --output=<outfile>] <file1> <file2> [<file3> ...]\n"
 		"  Any of the file arguments can be '-' for stdin/out\n"
 		"  Other options:\n"
-		"         -h, --help     this help\n"
-		"         -v, --verbose  verbose status\n"
-		"         -q, --quiet    suppress status output\n"
-		"         --version      print version information\n"
+		"         -h, --help          this help\n"
+		"         -v, --verbose       verbose status\n"
+		"         -q, --quiet         suppress status output\n"
+		"         -O, --old-style     use old style sorting method (use previous packet's granulepos)\n"
+		"         -s, --skeleton      adds a Skeleton track to the output\n"
+		"         --version           print version information\n"
 	);
 }
 
@@ -54,6 +61,8 @@
 	params.out = NULL;
 	params.input = NULL;
 	params.verbose = 1;
+        params.old_style = 0;
+        params.skeleton = 0;
 }
 
 struct option long_options[] = {
@@ -63,6 +72,8 @@
 	{"help", 0, NULL, '?'},
 	{"version", 0, NULL, 'V'},
 	{"output", 1, NULL, 'o'},
+	{"old-style", 0, NULL, 'O'},
+	{"skeleton", 0, NULL, 's'},
 	{NULL, 0, NULL, 0}
 };
 
@@ -71,7 +82,7 @@
 	int ret;
 	int option_index = 1;
 
-	while ((ret = getopt_long(argc, argv, "qvh?Vo:", long_options, &option_index)) != -1) {
+	while ((ret = getopt_long(argc, argv, "qvh?VOso:", long_options, &option_index)) != -1) {
 		switch (ret) {
 		case 0:
 			fprintf(stderr, "Internal error parsing command line options.\n");
@@ -100,6 +111,12 @@
 			}
 			params.outfile = (char *)strdup(optarg);
 			break;
+                case 'O':
+                        params.old_style = 1;
+                        break;
+                case 's':
+                        params.skeleton = 1;
+                        break;
 		default:
 			_usage();
 			exit(0);
@@ -115,16 +132,47 @@
 	if (ext == NULL) return TYPEUNKNOWN;
 
 	/* should be smarter and check file magic */
+	/* yes, it would, wouldn't it ? let's do the bare minimum though :) */
 
-	if (strcasecmp(ext, ".ogg") == 0)
-		return TYPEVORBIS;
+        /* simpler than adding the list of full extensions, esp if others in og[^gvax] start to be used too */
+	if (strncasecmp(ext, ".og",3) == 0 && !ext[4] && strchr("gGvVaAxX",ext[3])) {
+                int offset=28; /* 27 for header, 1 for small packet lacing */
+                char buf[64];
+                FILE *f=fopen(filename,"r");
+                if (!f) return TYPEUNKNOWN;
+                memset(buf,0,sizeof(buf)); /* avoid looking at stale data for small files */
+                fread(buf,1,sizeof(buf),f);
+                fclose(f);
+                if (!memcmp(buf+offset,"\001vorbis",7)) return TYPEVORBIS;
+                if (!memcmp(buf+offset,"\200kate\0\0\0\0",9)) return TYPEKATE;
+                if (!memcmp(buf+offset,"\200theora",7)) return TYPETHEORA;
+                if (!memcmp(buf+offset,"Speex   ",8)) return TYPESPEEX;
+                return TYPEUNKNOWN;
+        }
 	else if (strcasecmp(ext, ".mid") == 0)
 		return TYPEMIDI;
 	else if (strcasecmp(ext, ".mng") == 0)
 		return TYPEMNG;
+	else if (strcasecmp(ext, ".spx") == 0)
+		return TYPESPEEX;
 	return TYPEUNKNOWN;
 }
 
+static const char *_get_file_type_name(int type)
+{
+  switch (type) {
+    default: return "Er, not sure, code's broken";
+    case TYPESKELETON: return "Skeleton";
+    case TYPEUNKNOWN: return "Unknown";
+    case TYPEVORBIS: return "Vorbis";
+    case TYPEMIDI: return "MIDI";
+    case TYPEMNG: return "MNG";
+    case TYPEKATE: return "Kate";
+    case TYPETHEORA: return "Theora";
+    case TYPESPEEX: return "Speex";
+  }
+}
+
 static void _add_file(filelist_t *file)
 {
 	filelist_t *temp;
@@ -138,6 +186,12 @@
 	}
 }
 
+static void _prepend_file(filelist_t *file)
+{
+        file->next = params.input;
+	params.input = file;
+}
+
 static int _unique_serialno(int serialno)
 {
 	filelist_t *file;
@@ -150,6 +204,172 @@
 	return 1;
 }
 
+/* copied and adapted from ffmpeg2theora (GPL too) */
+#define SKELETON_VERSION_MAJOR 3
+#define SKELETON_VERSION_MINOR 0
+#define FISHEAD_IDENTIFIER "fishead\0"
+#define FISBONE_IDENTIFIER "fisbone\0"
+#define FISBONE_SIZE 52
+#define FISBONE_MESSAGE_HEADER_OFFSET 44
+
+static void write16le(unsigned char *ptr,ogg_uint16_t v)
+{
+  ptr[0]=v&0xff;
+  ptr[1]=(v>>8)&0xff;
+}
+
+static void write32le(unsigned char *ptr,ogg_uint32_t v)
+{
+  ptr[0]=v&0xff;
+  ptr[1]=(v>>8)&0xff;
+  ptr[2]=(v>>16)&0xff;
+  ptr[3]=(v>>24)&0xff;
+}
+
+static void write64le(unsigned char *ptr,ogg_int64_t v)
+{
+  ogg_uint32_t hi=v>>32;
+  ptr[0]=v&0xff;
+  ptr[1]=(v>>8)&0xff;
+  ptr[2]=(v>>16)&0xff;
+  ptr[3]=(v>>24)&0xff;
+  ptr[4]=hi&0xff;
+  ptr[5]=(hi>>8)&0xff;
+  ptr[6]=(hi>>16)&0xff;
+  ptr[7]=(hi>>24)&0xff;
+}
+
+static void _add_fishead_packet (ogg_packet *op) {
+
+    if (!op) return;
+    memset (op, 0, sizeof (*op));
+
+    op->packet = _ogg_calloc (64, sizeof(unsigned char));
+    if (op->packet == NULL) return;
+
+    memset (op->packet, 0, 64);
+    memcpy (op->packet, FISHEAD_IDENTIFIER, 8); /* identifier */
+    write16le(op->packet+8, SKELETON_VERSION_MAJOR); /* version major */
+    write16le(op->packet+10, SKELETON_VERSION_MINOR); /* version minor */
+    write64le(op->packet+12, (ogg_int64_t)0); /* presentationtime numerator */
+    write64le(op->packet+20, (ogg_int64_t)1000); /* presentationtime denominator */
+    write64le(op->packet+28, (ogg_int64_t)0); /* basetime numerator */
+    write64le(op->packet+36, (ogg_int64_t)1000); /* basetime denominator */
+    /* both the numerator are zero hence handled by the memset */
+    write32le(op->packet+44, 0); /* UTC time, set to zero for now */
+
+    op->b_o_s = 1; /* its the first packet of the stream */
+    op->e_o_s = 0; /* its not the last packet of the stream */
+    op->bytes = 64; /* length of the packet in bytes */
+}
+
+void add_fisbone_packet (ogg_packet *op,
+                         ogg_uint32_t serial,
+                         const char *content_type, int headers, int preroll,
+                         int gshift, ogg_int64_t gnum, ogg_int64_t gden)
+{
+    int n;
+    size_t ctlen;
+    size_t plen;
+
+    if (!content_type) return;
+
+    if (params.verbose>=2)
+        printf("adding fisbone for %s\n",content_type);
+
+    ctlen = strlen(content_type);
+    plen = FISBONE_SIZE+16+ctlen; /* 16 is strlen "Content-Type: \r\n" */
+
+    memset (op, 0, sizeof (*op));
+    op->packet = _ogg_calloc (plen, sizeof(unsigned char));
+    if (op->packet == NULL) return;
+
+    memset (op->packet, 0, plen);
+    /* it will be the fisbone packet for the theora video */
+    memcpy (op->packet, FISBONE_IDENTIFIER, 8); /* identifier */
+    write32le(op->packet+8, FISBONE_MESSAGE_HEADER_OFFSET); /* offset of the message header fields */
+    write32le(op->packet+12, serial); /* serialno of the theora stream */
+    write32le(op->packet+16, headers); /* number of header packets */
+    /* granulerate, temporal resolution of the bitstream in samples/microsecond */
+    write64le(op->packet+20, gnum); /* granulrate numerator */
+    write64le(op->packet+28, gden); /* granulrate denominator */
+    write64le(op->packet+36, 0); /* start granule */
+    write32le(op->packet+44, preroll); /* preroll, for theora its 0 */
+    *(op->packet+48) = gshift; /* granule shift */
+    sprintf(op->packet+FISBONE_SIZE, "Content-Type: %s\r\n", content_type); /* message header field, Content-Type */
+
+    op->b_o_s = 0;
+    op->e_o_s = 0;
+    op->bytes = plen; /* size of the packet in bytes */
+}
+
+static void _add_fishtail_packet(ogg_packet *op)
+{
+    if (!op) return;
+
+    /* build and add the e_o_s packet */
+    memset (op, 0, sizeof (*op));
+    op->b_o_s = 0;
+    op->e_o_s = 1; /* its the e_o_s packet */
+    op->granulepos = 0;
+    op->bytes = 0; /* e_o_s packet is an empty packet */
+}
+
+static void _fill_filelist(filelist_t *file)
+{
+    file->fp = NULL;
+    switch (file->type) {
+    case TYPESKELETON:
+            file->state_init = skeleton_state_init;
+            file->data_in = skeleton_data_in;
+            file->page_out = skeleton_page_out;
+            file->fisbone_out = skeleton_fisbone_out;
+            break;
+    case TYPEVORBIS:
+            file->state_init = vorbis_state_init;
+            file->data_in = vorbis_data_in;
+            file->page_out = vorbis_page_out;
+            file->fisbone_out = vorbis_fisbone_out;
+            break;
+    case TYPEKATE:
+            file->state_init = kate_state_init;
+            file->data_in = kate_data_in;
+            file->page_out = kate_page_out;
+            file->fisbone_out = kate_fisbone_out;
+            break;
+    case TYPESPEEX:
+            file->state_init = speex_state_init;
+            file->data_in = speex_data_in;
+            file->page_out = speex_page_out;
+            file->fisbone_out = speex_fisbone_out;
+            break;
+    case TYPETHEORA:
+            file->state_init = theora_state_init;
+            file->data_in = theora_data_in;
+            file->page_out = theora_page_out;
+            file->fisbone_out = theora_fisbone_out;
+            break;
+    case TYPEMIDI:
+            file->state_init = midi_state_init;
+            file->data_in = midi_data_in;
+            file->page_out = midi_page_out;
+            file->fisbone_out = midi_fisbone_out;
+            break;
+    case TYPEMNG:
+            file->state_init = mng_state_init;
+            file->data_in = mng_data_in;
+            file->page_out = mng_page_out;
+            file->fisbone_out = mng_fisbone_out;
+            break;
+    }
+
+    file->serialno = 0;
+    file->status = EMOREDATA;
+    file->page = NULL;
+    file->next = NULL;
+    file->fisbone_done = 0;
+}
+
 #define BUFSIZE 1024
 
 int main(int argc, char **argv)
@@ -162,6 +382,7 @@
 	int serialno;
 	int first_pages = 1;
 	ogg_page *page;
+	int fishtail_done = 0;
 	char buf[BUFSIZE];
 
 	srand(time(NULL));
@@ -192,33 +413,32 @@
 			fprintf(stderr, "Error: File %s has unknown type.\n", file->name);
 			return 1;
 		}
+                else {
+                  if (params.verbose>=1) printf("%s: %s\n",file->name,_get_file_type_name(file->type));
+                }
 
-		file->fp = NULL;
-		switch (file->type) {
-		case TYPEVORBIS:
-			file->state_init = vorbis_state_init;
-			file->data_in = vorbis_data_in;
-			file->page_out = vorbis_page_out;
-			break;
-		case TYPEMIDI:
-			file->state_init = midi_state_init;
-			file->data_in = midi_data_in;
-			file->page_out = midi_page_out;
-			break;
-		case TYPEMNG:
-			file->state_init = mng_state_init;
-			file->data_in = mng_data_in;
-			file->page_out = mng_page_out;
-		}
 
-		file->serialno = 0;
-		file->status = EMOREDATA;
-		file->page = NULL;
-		file->next = NULL;
+		_fill_filelist(file);
 
 		_add_file(file);
 	}
 
+        /* If skeleton is requested, add a new stream for it at the start */
+        if (params.skeleton) {
+		file = (filelist_t *)malloc(sizeof(filelist_t));
+		if (file == NULL) {
+			fprintf(stderr, "Error: Memory error.\n");
+			return 1;
+		}
+
+		file->name = NULL;
+		file->type = TYPESKELETON;
+
+                _fill_filelist(file);
+
+                _prepend_file(file);
+        }
+
 	/* open output file */
 	if (params.outfile == NULL) {
 		params.outfile = (char *)strdup("stdout");
@@ -234,11 +454,13 @@
 	/* open all files and prepare for processing */
 	file = params.input;
 	while (file) {
-		fprintf(stderr, "Opening %s for reading...\n", file->name);
-		file->fp = fopen(file->name, "r");
-		if (file->fp == NULL) {
-			fprintf(stderr, "Error: Couldn't open input file %s.\n", file->name);
-			return 1;
+                if (file->type != TYPESKELETON) {
+                    if (params.verbose>=2) printf("Opening %s for reading...\n", file->name);
+                    file->fp = fopen(file->name, "r");
+                    if (file->fp == NULL) {
+                          fprintf(stderr, "Error: Couldn't open input file %s.\n", file->name);
+                         return 1;
+                    }
 		}
 		
 		do {
@@ -246,11 +468,19 @@
 		} while (!_unique_serialno(serialno));
 		file->serialno = serialno;
 
-		file->state_init(&file->state, file->serialno);
+		file->state_init(&file->state, file->serialno, params.old_style);
 
 		file = file->next;
 	}
 
+        /* a fishead first if skeleton is required */
+        if (params.skeleton) {
+            ogg_packet op;
+            _add_fishead_packet(&op);
+            skeleton_packetin(&params.input->state, &op, FISHEAD);
+            _ogg_free (op.packet);
+        }
+
 	/* let her rip! */
 	while (1) {
 		/* Step 1: make sure an ogg page is available for each input 
@@ -260,22 +490,45 @@
 		while (file) {
 			if (file->page == NULL) {
 				while ((file->page = file->page_out(&file->state)) == NULL && file->status == EMOREDATA) {
-					if (feof(file->fp)) {
-						file->status = 0;
-						break;
-					}
-					bytes = fread(buf, 1, BUFSIZE, file->fp);
-					file->status = file->data_in(&file->state, buf, bytes);
-					if (file->status < 0 && file->status != EMOREDATA) {
-						fprintf(stderr, "Error: Packetizer error on file %s.\n", file->name);
-						return 1;
-					}
+                                    bytes = 0;
+                                    if (file->type != TYPESKELETON) {
+					    if (feof(file->fp)) {
+						    file->status = 0;
+						    break;
+					    }
+					    bytes = fread(buf, 1, BUFSIZE, file->fp);
+                                    }
+				    file->status = file->data_in(&file->state, buf, bytes);
+                                    if (file->type != TYPESKELETON) {
+				        if (file->status < 0 && file->status != EMOREDATA) {
+					    fprintf(stderr, "Error: Packetizer error on file %s.\n", file->name);
+					    return 1;
+                                        }
+				    }
 				}
+                                /* now that we have at least the BOS packet, we can get a skeleton packet is required */
+                                if (params.skeleton && !file->fisbone_done) {
+                                        ogg_packet op;
+                                        if (!file->fisbone_out(&file->state, &op)) {
+                                              skeleton_packetin(&params.input->state, &op, FISBONE);
+                                              _ogg_free (op.packet);
+                                              file->fisbone_done = 1;
+                                        }
+                                }
 			}
 
 			file = file->next;
 		}
 
+                /* a fishtail last, but before any data packet, if skeleton is required */
+                if (params.skeleton && !fishtail_done) {
+                    ogg_packet op;
+                    _add_fishtail_packet(&op);
+                    skeleton_packetin(&params.input->state, &op, FISHTAIL);
+                    _ogg_free (op.packet);
+                    fishtail_done = 1;
+                }
+
 		/* Step 1.5: Write out the first page of each stream
 		** because headers must come together before any
 		** non-header pages.
@@ -288,6 +541,7 @@
 					fprintf(stderr, "Error: File %s didn't produce a header page.\n", file->name);
 					return 1;
 				}
+                                if (params.verbose>=2) printf("writing header page for %08x: %s\n",file->serialno,file->name);
 
 				page = file->page->og;
 
@@ -335,6 +589,8 @@
 		if (winner->page == NULL) break;
 
 		/* Step 3: Write out the winning page */
+                if (params.verbose>=2)
+                  printf("writing winning page (%s, timestamp %lld)\n",winner->name,winner->page->timestamp);
 		page = winner->page->og;
 
 		bytes = fwrite(page->header, 1, page->header_len, params.out);
@@ -358,13 +614,11 @@
 
 	file = params.input;
 	while (file) {
-		fclose(file->fp);
+                if (file->fp)
+                    fclose(file->fp);
 		file = file->next;
 	}
 	fclose(params.out);
 
 	return 0;
 }
-
-
-

Modified: trunk/ogg-tools/oggmerge/oggmerge.h
===================================================================
--- trunk/ogg-tools/oggmerge/oggmerge.h	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/oggmerge.h	2008-04-08 18:27:55 UTC (rev 14677)
@@ -26,10 +26,12 @@
 	oggmerge_page_t *page;
 	int status;
 	unsigned serialno;
+	int fisbone_done;
 
-	int (*state_init)(oggmerge_state_t *state, int serialno);
+	int (*state_init)(oggmerge_state_t *state, int serialno, int old_style);
 	int (*data_in)(oggmerge_state_t *state, char *buffer, unsigned long size);
 	oggmerge_page_t *(*page_out)(oggmerge_state_t *state);
+	int (*fisbone_out)(oggmerge_state_t *state, ogg_packet *op);
 
 	struct filelist_tag *next;
 } filelist_t;
@@ -39,6 +41,8 @@
 	FILE *out;
 	filelist_t *input;
 	int verbose;
+        int old_style;
+        int skeleton;
 } param_t;
 
 /* errors */
@@ -49,10 +53,18 @@
 #define EOTHER -5
 
 /* types */
+#define TYPESKELETON (-1)
 #define TYPEUNKNOWN 0
 #define TYPEVORBIS 1
 #define TYPEMIDI 2
 #define TYPEMNG 3
+#define TYPEKATE 4
+#define TYPETHEORA 5
+#define TYPESPEEX 6
 
+extern void add_fisbone_packet (ogg_packet *op,
+                                ogg_uint32_t serial,
+                                const char *content_type, int headers, int preroll,
+                                int gshift, ogg_int64_t gnum, ogg_int64_t gden);
+
 #endif  /* __OGGMERGE_H__ */
-

Added: trunk/ogg-tools/oggmerge/skeleton.c
===================================================================
--- trunk/ogg-tools/oggmerge/skeleton.c	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/skeleton.c	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,150 @@
+/* skeleton.c
+**
+** skeleton dummy packetizing module
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+
+#include "oggmerge.h"
+#include "skeleton.h"
+
+typedef struct skeleton_page_tag {
+	oggmerge_page_t *page;
+	struct skeleton_page_tag *next;
+} skeleton_page_t;
+
+typedef struct {
+	int serialno;
+	ogg_stream_state os;
+	skeleton_page_t *pages;
+} skeleton_state_t;
+
+static void _add_skeleton_page(skeleton_state_t *sstate, ogg_page *og);
+
+int skeleton_state_init(oggmerge_state_t *state, int serialno, int old_style)
+{
+	skeleton_state_t *sstate;
+
+	if (state == NULL) return 0;
+
+	sstate = (skeleton_state_t *)malloc(sizeof(skeleton_state_t));
+	if (sstate == NULL) return 0;
+
+	ogg_stream_init(&sstate->os, sstate->serialno=serialno);
+
+        sstate->pages = NULL;
+
+	// NOTE: we just ignore serialno for now
+	// until a future libogg supports ogg_page_set_serialno
+
+	state->private = (void *)sstate;
+
+	return 1;
+}
+
+int skeleton_data_in(oggmerge_state_t *state, char *buffer, unsigned long size)
+{
+	return EOTHER;
+}
+
+oggmerge_page_t *skeleton_page_out(oggmerge_state_t *state)
+{
+	skeleton_state_t *sstate;
+	oggmerge_page_t *page;
+
+	sstate = (skeleton_state_t *)state->private;
+
+	if (sstate->pages == NULL) return NULL;
+
+	page = sstate->pages->page;
+	sstate->pages = sstate->pages->next;
+
+	return page;
+}
+
+static ogg_page *_copy_ogg_page(ogg_page *og)
+{
+	ogg_page *page;
+
+	page = (ogg_page *)malloc(sizeof(ogg_page));
+	if (page == NULL) return NULL;
+
+	page->header_len = og->header_len;
+	page->body_len = og->body_len;
+	page->header = (unsigned char *)malloc(page->header_len);
+	if (page->header == NULL) {
+		free(page);
+		return NULL;
+	}
+	memcpy(page->header, og->header, page->header_len);
+	page->body = (unsigned char *)malloc(page->body_len);
+	if (page->body == NULL) {
+		free(page->header);
+		free(page);
+		return NULL;
+	}
+	memcpy(page->body, og->body, page->body_len);
+
+	return page;
+}
+
+static void _add_skeleton_page(skeleton_state_t *sstate, ogg_page *og)
+{
+	oggmerge_page_t *page;
+	skeleton_page_t *spage, *temp;
+
+	// build oggmerge page
+	page = (oggmerge_page_t *)malloc(sizeof(oggmerge_page_t));
+	page->og = _copy_ogg_page(og);
+        page->timestamp = 0;
+	
+	// build skeleton page
+	spage = (skeleton_page_t *)malloc(sizeof(skeleton_page_t));
+	if (spage == NULL) {
+		free(page->og->header);
+		free(page->og->body);
+		free(page->og);
+		free(page);
+		return;
+	}
+
+	spage->page = page;
+	spage->next = NULL;
+
+	// add page to state
+	temp = sstate->pages;
+	if (temp == NULL) {
+		sstate->pages = spage;
+	} else {
+		while (temp->next) temp = temp->next;
+		temp->next = spage;
+	}
+}
+
+int skeleton_packetin(oggmerge_state_t *state, ogg_packet *op, int type)
+{
+	skeleton_state_t *sstate;
+        ogg_page og;
+        int (*pager)(ogg_stream_state*,ogg_page*);
+
+	sstate = (skeleton_state_t *)state->private;
+
+        ogg_stream_packetin(&sstate->os, op);
+
+        /* flush on head and tail */
+        pager = type==FISBONE?&ogg_stream_pageout:&ogg_stream_flush;
+        while ((*pager)(&sstate->os, &og)) {
+            _add_skeleton_page(sstate, &og);
+        }
+}
+
+int skeleton_fisbone_out(oggmerge_state_t *state, ogg_packet *op)
+{
+    /* skeleton never has a fisbone, alas poor skeleton */
+    return 1;
+}

Added: trunk/ogg-tools/oggmerge/skeleton.h
===================================================================
--- trunk/ogg-tools/oggmerge/skeleton.h	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/skeleton.h	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,23 @@
+/* skeleton.h
+**
+** oggmerge skeleton file module
+**
+*/
+
+#ifndef __SKELETON_H__
+#define __KELETON_H__
+
+#include "config.h"
+
+#include "oggmerge.h"
+
+int skeleton_state_init(oggmerge_state_t *state, int serialno, int old_style);
+int skeleton_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
+oggmerge_page_t *skeleton_page_out(oggmerge_state_t *state);
+int skeleton_fisbone_out(oggmerge_state_t *state, ogg_packet *op);
+
+enum { FISHEAD, FISBONE, FISHTAIL };
+
+int skeleton_packetin(oggmerge_state_t *state, ogg_packet *op, int type);
+
+#endif  /* __SKELETON_H__*/

Added: trunk/ogg-tools/oggmerge/speex.c
===================================================================
--- trunk/ogg-tools/oggmerge/speex.c	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/speex.c	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,249 @@
+/* speex.c
+**
+** speex dummy packetizing module
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+
+#ifdef HAVE_SPEEX
+#include <speex/speex.h>
+#include <speex/speex_header.h>
+#endif
+
+#include "oggmerge.h"
+#include "speex.h"
+
+typedef struct speex_page_tag {
+	oggmerge_page_t *page;
+	struct speex_page_tag *next;
+} speex_page_t;
+
+typedef struct {
+	ogg_sync_state oy;
+	int serialno;
+	ogg_int64_t old_granulepos;
+	int first_page;
+        int rate;
+	speex_page_t *pages;
+        int old_style;
+} speex_state_t;
+
+static void _add_speex_page(speex_state_t *sstate, ogg_page *og);
+
+static unsigned long read32le(const unsigned char *bytes)
+{
+  unsigned long v=0;
+  v|=*bytes++;
+  v|=((*bytes++)<<8);
+  v|=((*bytes++)<<16);
+  v|=((*bytes++)<<24);
+  return v;
+}
+
+
+int speex_state_init(oggmerge_state_t *state, int serialno, int old_style)
+{
+	speex_state_t *sstate;
+
+	if (state == NULL) return 0;
+
+	sstate = (speex_state_t *)malloc(sizeof(speex_state_t));
+	if (sstate == NULL) return 0;
+
+	ogg_sync_init(&sstate->oy);
+	sstate->first_page = 1;
+        sstate->rate = 0;
+	sstate->old_granulepos = 0;
+        sstate->pages = NULL;
+        sstate->old_style = old_style;
+
+	// NOTE: we just ignore serialno for now
+	// until a future libogg supports ogg_page_set_serialno
+
+	state->private = (void *)sstate;
+
+	return 1;
+}
+
+int speex_data_in(oggmerge_state_t *state, char *buffer, unsigned long size)
+{
+	int ret;
+	char *buf;
+	speex_state_t *sstate;
+	ogg_page og;
+	ogg_packet op;
+	ogg_stream_state os;
+#ifdef HAVE_SPEEX
+        SpeexHeader *header;
+#endif
+
+	sstate = (speex_state_t *)state->private;
+
+	// stuff data in
+	buf = ogg_sync_buffer(&sstate->oy, size);
+	memcpy(buf, buffer, size);
+	ogg_sync_wrote(&sstate->oy, size);
+
+	// pull pages out
+	while ((ret = ogg_sync_pageout(&sstate->oy, &og)) == 1) {
+		if (sstate->first_page) {
+			sstate->first_page = 0;
+			ogg_stream_init(&os, sstate->serialno=ogg_page_serialno(&og));
+			if (ogg_stream_pagein(&os, &og) < 0) {
+				ogg_stream_clear(&os);
+				return EBADHEADER;
+			}
+			if (ogg_stream_packetout(&os, &op) != 1) {
+				ogg_stream_clear(&os);
+				return EBADHEADER;
+			}
+#ifdef HAVE_SPEEX
+                        header = speex_packet_to_header((char*)op.packet, op.bytes);
+			if (!header) {
+				ogg_stream_clear(&os);
+				return EBADHEADER;
+			}
+                        sstate->rate = header->rate;
+#else
+                        sstate->rate = read32le(op.packet+36);
+#endif
+			ogg_stream_clear(&os);
+			
+		}
+		_add_speex_page(sstate, &og);
+	}
+
+	if (ret == 0) return EMOREDATA;
+	return EOTHER;
+}
+
+oggmerge_page_t *speex_page_out(oggmerge_state_t *state)
+{
+	speex_state_t *sstate;
+	oggmerge_page_t *page;
+
+	sstate = (speex_state_t *)state->private;
+
+	if (sstate->pages == NULL) return NULL;
+
+        /* here, we don't want to make available pages with -1 granulepos,
+           instead we wait till we have the next page with an set granulepos.
+           returning NULL will force a further read */
+        if (sstate->pages->page->timestamp<0) {
+          return NULL;
+        }
+
+	page = sstate->pages->page;
+	sstate->pages = sstate->pages->next;
+
+	return page;
+}
+
+static ogg_page *_copy_ogg_page(ogg_page *og)
+{
+	ogg_page *page;
+
+	page = (ogg_page *)malloc(sizeof(ogg_page));
+	if (page == NULL) return NULL;
+
+	page->header_len = og->header_len;
+	page->body_len = og->body_len;
+	page->header = (unsigned char *)malloc(page->header_len);
+	if (page->header == NULL) {
+		free(page);
+		return NULL;
+	}
+	memcpy(page->header, og->header, page->header_len);
+	page->body = (unsigned char *)malloc(page->body_len);
+	if (page->body == NULL) {
+		free(page->header);
+		free(page);
+		return NULL;
+	}
+	memcpy(page->body, og->body, page->body_len);
+
+	return page;
+}
+
+static u_int64_t _make_timestamp(speex_state_t *sstate, ogg_int64_t granulepos)
+{
+	u_int64_t stamp;
+        ogg_int64_t gp=sstate->old_style?sstate->old_granulepos:granulepos;
+	
+	if (sstate->rate == 0) return 0;
+	
+	stamp = (double)gp * (double)1000000 / (double)sstate->rate;
+
+        if (granulepos>0) {
+          sstate->old_granulepos = granulepos;
+        }
+
+	return stamp;
+}
+
+static void _add_speex_page(speex_state_t *sstate, ogg_page *og)
+{
+	oggmerge_page_t *page;
+	speex_page_t *spage, *temp;
+
+        if (ogg_page_serialno(og)!=sstate->serialno) {
+          fprintf(stderr,"Error: oggmerge does not support merging multiplexed streams\n");
+          return;
+        }
+
+	// build oggmerge page
+	page = (oggmerge_page_t *)malloc(sizeof(oggmerge_page_t));
+	page->og = _copy_ogg_page(og);
+	page->timestamp = -1;
+        if (ogg_page_granulepos(og)>=0) {
+            page->timestamp = _make_timestamp(sstate, ogg_page_granulepos(og));
+            /* if we had queued pages with -1 timestamp (eg, we didn't know yet),
+               we want to update them now to the newly known timestamp so they'll
+               automatically be selected when appropriate */
+            temp = sstate->pages;
+            while (temp && temp->page->timestamp<0) {
+                temp->page->timestamp = page->timestamp;
+                temp=temp->next;
+            }
+        }
+	
+	// build speex page
+	spage = (speex_page_t *)malloc(sizeof(speex_page_t));
+	if (spage == NULL) {
+		free(page->og->header);
+		free(page->og->body);
+		free(page->og);
+		free(page);
+		return;
+	}
+
+	spage->page = page;
+	spage->next = NULL;
+
+	// add page to state
+	temp = sstate->pages;
+	if (temp == NULL) {
+		sstate->pages = spage;
+	} else {
+		while (temp->next) temp = temp->next;
+		temp->next = spage;
+	}
+}
+
+int speex_fisbone_out(oggmerge_state_t *state, ogg_packet *op)
+{
+    speex_state_t *sstate = (speex_state_t *)state->private;
+
+    int gshift = 0;
+    ogg_int64_t gnum = sstate->rate;
+    ogg_int64_t gden = 1;
+#warning this seems plausible enough, but I'm not sure - ask jm
+    add_fisbone_packet(op, sstate->serialno, "audio/speex", 2, 1, gshift, gnum, gden);
+
+    return 0;
+}

Added: trunk/ogg-tools/oggmerge/speex.h
===================================================================
--- trunk/ogg-tools/oggmerge/speex.h	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/speex.h	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,19 @@
+/* speex.h
+**
+** oggmerge speex file module
+**
+*/
+
+#ifndef __SPEEX_H__
+#define __SPEEX_H__
+
+#include "config.h"
+
+#include "oggmerge.h"
+
+int speex_state_init(oggmerge_state_t *state, int serialno, int old_style);
+int speex_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
+oggmerge_page_t *speex_page_out(oggmerge_state_t *state);
+int speex_fisbone_out(oggmerge_state_t *state, ogg_packet *op);
+
+#endif  /* __SPEEX_H__*/

Added: trunk/ogg-tools/oggmerge/theora.c
===================================================================
--- trunk/ogg-tools/oggmerge/theora.c	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/theora.c	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,230 @@
+/* theora.c
+**
+** theora dummy packetizing module
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+#include <theora/theora.h>
+
+#include "oggmerge.h"
+#include "theora.h"
+
+typedef struct theora_page_tag {
+	oggmerge_page_t *page;
+	struct theora_page_tag *next;
+} theora_page_t;
+
+typedef struct {
+	ogg_sync_state oy;
+	int serialno;
+	ogg_int64_t old_granulepos;
+	int first_page;
+        unsigned long granule_shift;
+	unsigned long fps_numerator;
+	unsigned long fps_denominator;
+	theora_page_t *pages;
+        int old_style;
+} theora_state_t;
+
+static void _add_theora_page(theora_state_t *tstate, ogg_page *og);
+
+
+int theora_state_init(oggmerge_state_t *state, int serialno, int old_style)
+{
+	theora_state_t *tstate;
+
+	if (state == NULL) return 0;
+
+	tstate = (theora_state_t *)malloc(sizeof(theora_state_t));
+	if (tstate == NULL) return 0;
+
+	ogg_sync_init(&tstate->oy);
+	tstate->first_page = 1;
+        tstate->granule_shift = 0;
+	tstate->fps_numerator = 0;
+	tstate->fps_denominator = 0;
+	tstate->old_granulepos = 0;
+        tstate->pages = NULL;
+        tstate->old_style = old_style;
+
+	// NOTE: we just ignore serialno for now
+	// until a future libogg supports ogg_page_set_serialno
+
+	state->private = (void *)tstate;
+
+	return 1;
+}
+
+int theora_data_in(oggmerge_state_t *state, char *buffer, unsigned long size)
+{
+	int ret;
+	char *buf;
+	theora_state_t *tstate;
+	ogg_page og;
+	ogg_packet op;
+	ogg_stream_state os;
+	theora_info ti;
+	theora_comment tc;
+
+	tstate = (theora_state_t *)state->private;
+
+	// stuff data in
+	buf = ogg_sync_buffer(&tstate->oy, size);
+	memcpy(buf, buffer, size);
+	ogg_sync_wrote(&tstate->oy, size);
+
+	// pull pages out
+	while ((ret = ogg_sync_pageout(&tstate->oy, &og)) == 1) {
+		if (tstate->first_page) {
+			tstate->first_page = 0;
+			ogg_stream_init(&os, tstate->serialno=ogg_page_serialno(&og));
+			theora_info_init(&ti);
+			theora_comment_init(&tc);
+			if (ogg_stream_pagein(&os, &og) < 0) {
+				theora_comment_clear(&tc);
+				theora_info_clear(&ti);
+				ogg_stream_clear(&os);
+				return EBADHEADER;
+			}
+			if (ogg_stream_packetout(&os, &op) != 1) {
+				theora_comment_clear(&tc);
+				theora_info_clear(&ti);
+				ogg_stream_clear(&os);
+				return EBADHEADER;
+			}
+			if (theora_decode_header(&ti, &tc, &op) < 0) {
+				theora_comment_clear(&tc);
+				theora_info_clear(&ti);
+				ogg_stream_clear(&os);
+				return EBADHEADER;
+			}
+			tstate->granule_shift = theora_granule_shift(&ti);
+			tstate->fps_numerator = ti.fps_numerator;
+			tstate->fps_denominator = ti.fps_denominator;
+			theora_comment_clear(&tc);
+			theora_info_clear(&ti);
+			ogg_stream_clear(&os);
+			
+		}
+		_add_theora_page(tstate, &og);
+	}
+
+	if (ret == 0) return EMOREDATA;
+	return EOTHER;
+}
+
+oggmerge_page_t *theora_page_out(oggmerge_state_t *state)
+{
+	theora_state_t *tstate;
+	oggmerge_page_t *page;
+
+	tstate = (theora_state_t *)state->private;
+
+	if (tstate->pages == NULL) return NULL;
+
+	page = tstate->pages->page;
+	tstate->pages = tstate->pages->next;
+
+	return page;
+}
+
+static ogg_page *_copy_ogg_page(ogg_page *og)
+{
+	ogg_page *page;
+
+	page = (ogg_page *)malloc(sizeof(ogg_page));
+	if (page == NULL) return NULL;
+
+	page->header_len = og->header_len;
+	page->body_len = og->body_len;
+	page->header = (unsigned char *)malloc(page->header_len);
+	if (page->header == NULL) {
+		free(page);
+		return NULL;
+	}
+	memcpy(page->header, og->header, page->header_len);
+	page->body = (unsigned char *)malloc(page->body_len);
+	if (page->body == NULL) {
+		free(page->header);
+		free(page);
+		return NULL;
+	}
+	memcpy(page->body, og->body, page->body_len);
+
+	return page;
+}
+
+static u_int64_t _make_timestamp(theora_state_t *tstate, ogg_int64_t granulepos)
+{
+	u_int64_t stamp = 0;
+        ogg_int64_t iframe,pframe;
+        ogg_int64_t gp = tstate->old_style?tstate->old_granulepos:granulepos;
+	
+	if (tstate->fps_denominator == 0) return 0;
+	
+        iframe=gp>>tstate->granule_shift;
+        pframe=gp-(iframe<<tstate->granule_shift);
+
+        stamp = (double)(iframe+pframe) * (double)1000000 * tstate->fps_denominator / (double)tstate->fps_numerator;
+
+        if (granulepos>=0) {
+          tstate->old_granulepos = granulepos;
+        }
+
+	return stamp;
+}
+
+static void _add_theora_page(theora_state_t *tstate, ogg_page *og)
+{
+	oggmerge_page_t *page;
+	theora_page_t *tpage, *temp;
+
+        if (ogg_page_serialno(og)!=tstate->serialno) {
+          fprintf(stderr,"Error: oggmerge does not support merging multiplexed streams\n");
+          return;
+        }
+
+	// build oggmerge page
+	page = (oggmerge_page_t *)malloc(sizeof(oggmerge_page_t));
+	page->og = _copy_ogg_page(og);
+	page->timestamp = -1;
+        if (ogg_page_granulepos(og)>=0)
+            page->timestamp = _make_timestamp(tstate, ogg_page_granulepos(og));
+	
+	// build theora page
+	tpage = (theora_page_t *)malloc(sizeof(theora_page_t));
+	if (tpage == NULL) {
+		free(page->og->header);
+		free(page->og->body);
+		free(page->og);
+		free(page);
+		return;
+	}
+
+	tpage->page = page;
+	tpage->next = NULL;
+
+	// add page to state
+	temp = tstate->pages;
+	if (temp == NULL) {
+		tstate->pages = tpage;
+	} else {
+		while (temp->next) temp = temp->next;
+		temp->next = tpage;
+	}
+}
+
+int theora_fisbone_out(oggmerge_state_t *state, ogg_packet *op)
+{
+    theora_state_t *tstate = (theora_state_t *)state->private;
+
+    int gshift = tstate->granule_shift;
+    ogg_int64_t gnum = tstate->fps_numerator;
+    ogg_int64_t gden = tstate->fps_denominator;
+    add_fisbone_packet(op, tstate->serialno, "video/theora", 3, 0, gshift, gnum, gden);
+
+    return 0;
+}

Added: trunk/ogg-tools/oggmerge/theora.h
===================================================================
--- trunk/ogg-tools/oggmerge/theora.h	                        (rev 0)
+++ trunk/ogg-tools/oggmerge/theora.h	2008-04-08 18:27:55 UTC (rev 14677)
@@ -0,0 +1,17 @@
+/* theora.h
+**
+** oggmerge theora file module
+**
+*/
+
+#ifndef __THEORA_H__
+#define __THEORA_H__
+
+#include "oggmerge.h"
+
+int theora_state_init(oggmerge_state_t *state, int serialno, int old_style);
+int theora_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
+oggmerge_page_t *theora_page_out(oggmerge_state_t *state);
+int theora_fisbone_out(oggmerge_state_t *state, ogg_packet *op);
+
+#endif  /* __THEORA_H__*/

Modified: trunk/ogg-tools/oggmerge/vorbis.c
===================================================================
--- trunk/ogg-tools/oggmerge/vorbis.c	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/vorbis.c	2008-04-08 18:27:55 UTC (rev 14677)
@@ -4,6 +4,8 @@
 */
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <ogg/ogg.h>
 #include <vorbis/codec.h>
 
@@ -22,12 +24,13 @@
 	int first_page;
 	unsigned long samplerate;
 	vorbis_page_t *pages;
+	int old_style;
 } vorbis_state_t;
 
 static void _add_vorbis_page(vorbis_state_t *vstate, ogg_page *og);
 
 
-int vorbis_state_init(oggmerge_state_t *state, int serialno)
+int vorbis_state_init(oggmerge_state_t *state, int serialno, int old_style)
 {
 	vorbis_state_t *vstate;
 
@@ -40,6 +43,8 @@
 	vstate->first_page = 1;
 	vstate->samplerate = 0;
 	vstate->old_granulepos = 0;
+        vstate->pages = NULL;
+        vstate->old_style = old_style;
 
 	// NOTE: we just ignore serialno for now
 	// until a future libogg supports ogg_page_set_serialno
@@ -71,7 +76,7 @@
 	while ((ret = ogg_sync_pageout(&vstate->oy, &og)) == 1) {
 		if (vstate->first_page) {
 			vstate->first_page = 0;
-			ogg_stream_init(&os, ogg_page_serialno(&og));
+			ogg_stream_init(&os, vstate->serialno=ogg_page_serialno(&og));
 			vorbis_info_init(&vi);
 			vorbis_comment_init(&vc);
 			if (ogg_stream_pagein(&os, &og) < 0) {
@@ -149,12 +154,15 @@
 static u_int64_t _make_timestamp(vorbis_state_t *vstate, ogg_int64_t granulepos)
 {
 	u_int64_t stamp;
+	ogg_int64_t gp=vstate->old_style?vstate->old_granulepos:granulepos;
 	
 	if (vstate->samplerate == 0) return 0;
 	
-	stamp = (double)vstate->old_granulepos * (double)1000000 / (double)vstate->samplerate;
+	stamp = (double)gp * (double)1000000 / (double)vstate->samplerate;
 
-	vstate->old_granulepos = granulepos;
+        if (granulepos>=0) {
+          vstate->old_granulepos = granulepos;
+        }
 
 	return stamp;
 }
@@ -164,10 +172,17 @@
 	oggmerge_page_t *page;
 	vorbis_page_t *vpage, *temp;
 
+        if (ogg_page_serialno(og)!=vstate->serialno) {
+          fprintf(stderr,"Error: oggmerge does not support merging multiplexed streams\n");
+          return;
+        }
+
 	// build oggmerge page
 	page = (oggmerge_page_t *)malloc(sizeof(oggmerge_page_t));
 	page->og = _copy_ogg_page(og);
-	page->timestamp = _make_timestamp(vstate, ogg_page_granulepos(og));
+	page->timestamp = -1;
+        if (ogg_page_granulepos(og)>=0)
+            page->timestamp = _make_timestamp(vstate, ogg_page_granulepos(og));
 	
 	// build vorbis page
 	vpage = (vorbis_page_t *)malloc(sizeof(vorbis_page_t));
@@ -192,16 +207,14 @@
 	}
 }
 
+int vorbis_fisbone_out(oggmerge_state_t *state, ogg_packet *op)
+{
+    vorbis_state_t *vstate = (vorbis_state_t *)state->private;
 
+    int gshift = 0;
+    ogg_int64_t gnum = vstate->samplerate;
+    ogg_int64_t gden = 1;
+    add_fisbone_packet(op, vstate->serialno, "audio/vorbis", 3, 2, gshift, gnum, gden);
 
-
-
-
-
-
-
-
-
-
-
-
+    return 0;
+}

Modified: trunk/ogg-tools/oggmerge/vorbis.h
===================================================================
--- trunk/ogg-tools/oggmerge/vorbis.h	2008-04-08 15:39:55 UTC (rev 14676)
+++ trunk/ogg-tools/oggmerge/vorbis.h	2008-04-08 18:27:55 UTC (rev 14677)
@@ -9,8 +9,9 @@
 
 #include "oggmerge.h"
 
-int vorbis_state_init(oggmerge_state_t *state, int serialno);
+int vorbis_state_init(oggmerge_state_t *state, int serialno, int old_style);
 int vorbis_data_in(oggmerge_state_t *state, char *buffer, unsigned long size);
 oggmerge_page_t *vorbis_page_out(oggmerge_state_t *state);
+int vorbis_fisbone_out(oggmerge_state_t *state, ogg_packet *op);
 
 #endif  /* __VORBIS_H__*/



More information about the commits mailing list