[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(¶ms.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(¶ms.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(¶ms.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