diff --git a/acinclude.m4 b/acinclude.m4 index 5e8354ce4..e060bcce5 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -74,9 +74,11 @@ int main () if test "x$no_ogg" = "x" ; then AC_MSG_RESULT(yes) + echo '#define HAVE_OGG 1' > config.h ifelse([$1], , :, [$1]) else AC_MSG_RESULT(no) + echo '/*#define HAVE_OGG 1*/' > config.h if test -f conf.oggtest ; then : else @@ -171,9 +173,17 @@ int main () if test "x$no_vorbis" = "x" ; then AC_MSG_RESULT(yes) + echo '#define HAVE_VORBIS 1' >> config.h + if test "x$no_vorbis" = "x" ; then + echo '#define HAVE_OGGVORBIS 1' >> config.h + else + echo '/*#define HAVE_OGGVORBIS 1*/' >> config.h + fi ifelse([$1], , :, [$1]) else AC_MSG_RESULT(no) + echo '/*#define HAVE_VORBIS 1*/' >> config.h + echo '/*#define HAVE_OGGVORBIS 1*/' >> config.h if test -f conf.vorbistest ; then : else @@ -232,9 +242,11 @@ AC_ARG_ENABLE([debug], [ --enable-debug compile with debug information]) if test x"$enable_debug" = x"yes"; then dnl debug information - DEBUG_CFLAGS="-g -DDEBUG" + DEBUG_CFLAGS="-g" + echo '#define DEBUG' >> config.h else DEBUG_CFLAGS="" + echo '/*#define DEBUG*/' >> config.h fi AC_SUBST(DEBUG_CFLAGS) ]) diff --git a/common.h b/common.h index 1ae2718b9..908cf722c 100644 --- a/common.h +++ b/common.h @@ -13,7 +13,7 @@ /*! \file - \version \$Id: common.h,v 1.4 2003/02/16 17:04:38 mosu Exp $ + \version \$Id: common.h,v 1.5 2003/03/03 13:47:50 mosu Exp $ \brief definitions used in all programs, helper functions \author Moritz Bunkus */ @@ -21,7 +21,8 @@ #ifndef __COMMON_H #define __COMMON_H -#define VERSION "0.0.1" +#include "config.h" + #define VERSIONINFO "mkvmerge v" VERSION #define DISPLAYPRIORITY_HIGH 10 diff --git a/mkvmerge.cpp b/mkvmerge.cpp index f4f3988ac..4a61978d8 100644 --- a/mkvmerge.cpp +++ b/mkvmerge.cpp @@ -13,7 +13,7 @@ /*! \file - \version \$Id: mkvmerge.cpp,v 1.18 2003/02/27 19:51:53 mosu Exp $ + \version \$Id: mkvmerge.cpp,v 1.19 2003/03/03 13:47:50 mosu Exp $ \brief command line parameter parsing, looping, output handling \author Moritz Bunkus */ @@ -54,6 +54,9 @@ #include "r_avi.h" #include "r_mp3.h" #include "r_wav.h" +#ifdef HAVE_OGGVORBIS +#include "r_ogm.h" +#endif #ifdef DMALLOC #include @@ -100,7 +103,9 @@ StdIOCallback *out; file_type_t file_types[] = {{"---", TYPEUNKNOWN, ""}, {"demultiplexers:", -1, ""}, -// {"ogg", TYPEOGM, "general OGG media stream, Vorbis audio embedded in OGG"}, +#ifdef HAVE_OGGVORBIS + {"ogg", TYPEOGM, "general OGG media stream, audio/video embedded in OGG"}, +#endif // HAVE_OGGVORBIS {"avi", TYPEAVI, "AVI (Audio/Video Interleaved)"}, {"wav", TYPEWAV, "WAVE (uncompressed PCM)"}, // {"srt", TYPESRT, "SRT text subtitles"}, @@ -109,7 +114,9 @@ file_type_t file_types[] = {"mp3", TYPEMP3, "MPEG1 layer III audio (CBR and VBR/ABR)"}, {"ac3", TYPEAC3, "A/52 (aka AC3)"}, {"output modules:", -1, ""}, -// {" ", -1, "Vorbis audio"}, +#ifdef HAVE_OGGVORBIS + {" ", -1, "Vorbis audio"}, +#endif // HAVE_OGGVORBIS {" ", -1, "Video (not MPEG1/2)"}, // {" ", -1, "uncompressed PCM audio"}, // {" ", -1, "text subtitles"}, @@ -186,8 +193,10 @@ static int get_type(char *filename) { return TYPEAVI; else if (wav_reader_c::probe_file(f, size)) return TYPEWAV; -// else if (ogm_reader_c::probe_file(f, size)) -// return TYPEOGM; +#ifdef HAVE_OGGVORBIS + else if (ogm_reader_c::probe_file(f, size)) + return TYPEOGM; +#endif // HAVE_OGGVORBIS // else if (srt_reader_c::probe_file(f, size)) // return TYPESRT; else if (mp3_reader_c::probe_file(f, size)) @@ -610,11 +619,12 @@ static void parse_args(int argc, char **argv) { file->fp = NULL; try { switch (file->type) { -/* case TYPEOGM: +#ifdef HAVE_OGGVORBIS + case TYPEOGM: file->reader = new ogm_reader_c(file->name, astreams, vstreams, - tstreams, &async, &range, - comments, fourcc); - break;*/ + tstreams, &async, &range, fourcc); + break; +#endif // HAVE_OGGVORBIS case TYPEAVI: if (tstreams != NULL) fprintf(stderr, "Warning: -t/-T are ignored for AVI files.\n"); diff --git a/r_ogm.cpp b/r_ogm.cpp index 691b66d4c..4220baff5 100644 --- a/r_ogm.cpp +++ b/r_ogm.cpp @@ -1,20 +1,27 @@ /* - ogmmerge -- utility for splicing together ogg bitstreams + mkvmerge -- utility for splicing together matroska files from component media subtypes r_ogm.cpp - OGG demultiplexer module (supports both the 'flat' Vorbis-only OGG - files and the OGG files produced by ogmmerge itself) Written by Moritz Bunkus - Based on Xiph.org's 'oggmerge' found in their CVS repository - See http://www.xiph.org Distributed under the GPL see the file COPYING for details or visit http://www.gnu.org/copyleft/gpl.html */ +/*! + \file + \version \$Id: r_ogm.cpp,v 1.3 2003/03/03 13:47:50 mosu Exp $ + \brief OGG media stream reader + \author Moritz Bunkus +*/ + +#include "config.h" + +#ifdef HAVE_OGGVORBIS + #include #include #include @@ -22,17 +29,17 @@ #include #include -#include "ogmmerge.h" +#include "common.h" +#include "pr_generic.h" #include "ogmstreams.h" #include "queue.h" #include "r_ogm.h" -#include "p_vorbis.h" +//#include "p_vorbis.h" #include "p_video.h" #include "p_pcm.h" -#include "p_textsubs.h" +//#include "p_textsubs.h" #include "p_mp3.h" #include "p_ac3.h" -#include "vorbis_header_utils.h" #ifdef DMALLOC #include @@ -65,7 +72,7 @@ int ogm_reader_c::probe_file(FILE *file, u_int64_t size) { ogm_reader_c::ogm_reader_c(char *fname, unsigned char *astreams, unsigned char *vstreams, unsigned char *tstreams, audio_sync_t *nasync, range_t *nrange, - char **ncomments, char *nfourcc) throw (error_c) { + char *nfourcc) throw (error_c) { u_int64_t size; if ((file = fopen(fname, "r")) == NULL) @@ -85,41 +92,51 @@ ogm_reader_c::ogm_reader_c(char *fname, unsigned char *astreams, nvstreams = 0; ntstreams = 0; numstreams = 0; + sdemuxers = NULL; + num_sdemuxers = 0; if (astreams != NULL) this->astreams = (unsigned char *)strdup((char *)astreams); else this->astreams = NULL; + if (vstreams != NULL) this->vstreams = (unsigned char *)strdup((char *)vstreams); else this->vstreams = NULL; + if (tstreams != NULL) this->tstreams = (unsigned char *)strdup((char *)tstreams); else this->tstreams = NULL; + if (nfourcc != NULL) { fourcc = strdup(nfourcc); if (fourcc == NULL) die("malloc"); } else fourcc = NULL; + if (verbose) fprintf(stdout, "Using OGG/OGM demultiplexer for %s.\n", fname); + filename = strdup(fname); if (filename == NULL) die("malloc"); + memcpy(&async, nasync, sizeof(audio_sync_t)); memcpy(&range, nrange, sizeof(range_t)); + o_eos = 0; - if (ncomments == NULL) - comments = NULL; - else - comments = dup_comments(ncomments); + + if (read_headers() <= 0) + throw error_c("ogm_reader: Could not read all header packets."); + create_packetizers(); } ogm_reader_c::~ogm_reader_c() { - ogm_demuxer_t *dmx, *tmp; + int i; + ogm_demuxer_t *dmx; if (astreams != NULL) free(astreams); @@ -127,19 +144,21 @@ ogm_reader_c::~ogm_reader_c() { free(vstreams); if (tstreams != NULL) free(tstreams); + ogg_sync_clear(&oy); - dmx = sdemuxers; - while (dmx != NULL) { + + for (i = 0; i < num_sdemuxers; i++) { + dmx = sdemuxers[i]; ogg_stream_clear(&dmx->os); delete dmx->packetizer; - tmp = dmx->next; free(dmx); - dmx = tmp; } + if (sdemuxers != NULL) + free(sdemuxers); + if (filename != NULL) free(filename); - if (comments != NULL) - free_comments(comments); + if (fourcc != NULL) free(fourcc); } @@ -161,34 +180,33 @@ int ogm_reader_c::demuxing_requested(unsigned char *streams, int streamno) { } ogm_demuxer_t *ogm_reader_c::find_demuxer(int serialno) { - ogm_demuxer_t *demuxer = sdemuxers; - - while (demuxer != NULL) { - if (demuxer->serial == serialno) - return demuxer; - demuxer = demuxer->next; - } - + int i; + + for (i = 0; i < num_sdemuxers; i++) + if (sdemuxers[i]->serial == serialno) + return sdemuxers[i]; + return NULL; } -void ogm_reader_c::flush_packetizers() { - ogm_demuxer_t *demuxer; - - demuxer = sdemuxers; - while (demuxer != NULL) { - // Is the input stream at its end and has not yet flagged EOS? - if (!demuxer->eos) - demuxer->packetizer->produce_eos_packet(); - demuxer->packetizer->flush_pages(); - demuxer = demuxer->next; - } - - return; -} - #define BUFFER_SIZE 4096 +void ogm_reader_c::free_demuxer(int idx) { + int i; + ogm_demuxer_t *dmx; + + if (idx < num_sdemuxers) + return; + dmx = sdemuxers[idx]; + for (i = 0; i < 3; i++) + if (dmx->packet_data[i] != NULL) + free(dmx->packet_data[i]); + free(dmx); + + memmove(&sdemuxers[idx], &sdemuxers[idx + 1], num_sdemuxers - idx - 1); + num_sdemuxers--; +} + /* * Reads an OGG page from the stream. Returns 0 if there are no more pages * left, EMOREDATA otherwise. @@ -205,6 +223,7 @@ int ogm_reader_c::read_page(ogg_page *og) { fprintf(stderr, "FATAL: ogm_reader: ogg_sync_pageseek failed\n"); exit(1); } + // np == 0 means that there is not enough data for a complete page. if (np == 0) { buf = ogg_sync_buffer(&oy, BUFFER_SIZE); @@ -212,10 +231,10 @@ int ogm_reader_c::read_page(ogg_page *og) { fprintf(stderr, "FATAL: ogm_reader: ogg_sync_buffer failed\n"); exit(1); } - if ((nread = fread(buf, 1, BUFFER_SIZE, file)) <= 0) { - flush_packetizers(); + + if ((nread = fread(buf, 1, BUFFER_SIZE, file)) <= 0) return 0; - } + ogg_sync_wrote(&oy, nread); } else // Alright, we have a page. @@ -227,41 +246,139 @@ int ogm_reader_c::read_page(ogg_page *og) { } void ogm_reader_c::add_new_demuxer(ogm_demuxer_t *dmx) { - ogm_demuxer_t *append_to; - - if (sdemuxers == NULL) { - sdemuxers = dmx; - return; + sdemuxers = (ogm_demuxer_t **)realloc(sdemuxers, sizeof(ogm_demuxer_t *) * + (num_sdemuxers + 1)); + if (sdemuxers == NULL) + die ("realloc"); + sdemuxers[num_sdemuxers] = dmx; + num_sdemuxers++; +} + +void ogm_reader_c::create_packetizers() { + char *codec; + stream_header *sth; + int i; + ogm_demuxer_t *dmx; + + i = 0; + while (i < num_sdemuxers) { + dmx = sdemuxers[i]; + sth = (stream_header *)&((char *)dmx->packet_data[0])[1]; + + switch (dmx->stype) { + case OGM_STREAM_TYPE_VIDEO: + if (fourcc == NULL) + codec = sth->subtype; + else + codec = fourcc; + try { + dmx->packetizer = + new video_packetizer_c(NULL, 0, codec, + (double)10000000 / (double)sth->time_unit, + sth->sh.video.width, sth->sh.video.height, + sth->bits_per_sample, sth->buffersize, NULL, + &range, 1); + } catch (error_c error) { + fprintf(stderr, "Fatal: ogm_reader: could not initialize video " \ + "packetizer for stream id %d. Will try to continue and " \ + "ignore this stream.\n", numstreams); + free(dmx); + continue; + } + + if (verbose) + fprintf(stdout, "OGG/OGM demultiplexer (%s): using video output " \ + "module for stream %d.\n", filename, numstreams); + + break; + + case OGM_STREAM_TYPE_PCM: + try { + dmx->packetizer = + new pcm_packetizer_c(NULL, 0, sth->samples_per_unit, + sth->sh.audio.channels, + sth->bits_per_sample, &async, &range); + } catch (error_c error) { + fprintf(stderr, "Fatal: ogm_reader: could not initialize PCM " \ + "packetizer for stream id %d. Will try to continue and " \ + "ignore this stream.\n", numstreams); + free_demuxer(i); + continue; + } + + if (verbose) + fprintf(stdout, "OGG/OGM demultiplexer (%s): using PCM output " \ + "module for stream %d.\n", filename, numstreams); + break; + + case OGM_STREAM_TYPE_MP3: + try { + dmx->packetizer = + new mp3_packetizer_c(NULL, 0, sth->samples_per_unit, + sth->sh.audio.channels, + sth->sh.audio.avgbytespersec * 8 / 1000, + &async, &range); + } catch (error_c error) { + fprintf(stderr, "Fatal: ogm_reader: could not initialize MP3 " \ + "packetizer for stream id %d. Will try to continue and " \ + "ignore this stream.\n", numstreams); + free_demuxer(i); + continue; + } + + if (verbose) + fprintf(stdout, "OGG/OGM demultiplexer (%s): using MP3 output " \ + "module for stream %d.\n", filename, numstreams); + break; + + case OGM_STREAM_TYPE_AC3: + try { + dmx->packetizer = + new ac3_packetizer_c(NULL, 0, sth->samples_per_unit, + sth->sh.audio.channels, + sth->sh.audio.avgbytespersec * 8 / 1000, + &async, &range); + } catch (error_c error) { + fprintf(stderr, "FATAL: ogm_reader: could not initialize AC3 " \ + "packetizer for stream id %d. Will try to continue and " \ + "ignore this stream.\n", numstreams); + free_demuxer(i); + continue; + } + + if (verbose) + fprintf(stdout, "OGG/OGM demultiplexer (%s): using AC3 output " \ + "module for stream %d.\n", filename, numstreams); + + break; + + case OGM_STREAM_TYPE_VORBIS: + break; + } + i++; } - - append_to = sdemuxers; - while (append_to->next != NULL) - append_to = append_to->next; - append_to->next = dmx; } /* * Checks every demuxer if it has a page available. */ -int ogm_reader_c::pages_available() { - ogm_demuxer_t *dmx; - - if (sdemuxers == NULL) +int ogm_reader_c::packet_available() { + int i; + + if (num_sdemuxers == 0) return 0; - dmx = sdemuxers; - while (dmx != NULL) { - if (!dmx->packetizer->page_available()) + + for (i = 0; i < num_sdemuxers; i++) + if (!sdemuxers[i]->packetizer->packet_available()) return 0; - dmx = dmx->next; - } - + return 1; } /* * The page is the beginning of a new stream. Check the contents for known * stream headers. If it is a known stream and the user has requested that - * it should be extracted than allocate a new packetizer based on the + * it should be extracted then allocate a new packetizer based on the * stream type and store the needed data in a new ogm_demuxer_t. */ void ogm_reader_c::handle_new_stream(ogg_page *og) { @@ -269,10 +386,11 @@ void ogm_reader_c::handle_new_stream(ogg_page *og) { ogg_packet op; ogm_demuxer_t *dmx; stream_header *sth; - char *codec; + char buf[5]; + u_int32_t codec_id; if (ogg_stream_init(&new_oss, ogg_page_serialno(og))) { - fprintf(stderr, "FATAL: ogm_reader: ogg_stream_init for stream number " \ + fprintf(stderr, "Fatal: ogm_reader: ogg_stream_init for stream number " \ "%d failed. Will try to continue and ignore this stream.", numstreams + 1); return; @@ -282,6 +400,17 @@ void ogm_reader_c::handle_new_stream(ogg_page *og) { ogg_stream_pagein(&new_oss, og); ogg_stream_packetout(&new_oss, &op); + dmx = (ogm_demuxer_t *)malloc(sizeof(ogm_demuxer_t)); + if (dmx == NULL) + die("malloc"); + memset(dmx, 0, sizeof(ogm_demuxer_t)); + dmx->num_packets = 1; + dmx->packet_data[0] = malloc(op.bytes); + if (dmx->packet_data[0] == NULL) + die("malloc"); + memcpy(dmx->packet_data[0], op.packet, op.bytes); + dmx->packet_sizes[0] = op.bytes; + /* * Check the contents for known stream headers. This one is the * standard Vorbis header. @@ -293,37 +422,35 @@ void ogm_reader_c::handle_new_stream(ogg_page *og) { ogg_stream_clear(&new_oss); return; } - dmx = (ogm_demuxer_t *)malloc(sizeof(ogm_demuxer_t)); - if (dmx == NULL) - die("malloc"); - memset(dmx, 0, sizeof(ogm_demuxer_t)); - try { - dmx->packetizer = new vorbis_packetizer_c(&async, &range, comments); - } catch (error_c error) { - fprintf(stderr, "FATAL: ogm_reader: could not initialize Vorbis " \ - "packetizer for stream id %d. Will try to continue and " \ - "ignore this stream.\n", numstreams); - free(dmx); - ogg_stream_clear(&new_oss); - return; - } - dmx->stype = OGM_STREAM_TYPE_VORBIS; - dmx->serial = ogg_page_serialno(og); - memcpy(&dmx->os, &new_oss, sizeof(ogg_stream_state)); - dmx->sid = nastreams; - add_new_demuxer(dmx); - if (verbose) - fprintf(stdout, "OGG/OGM demultiplexer (%s): using Vorbis audio " \ - "output module for stream %d.\n", filename, numstreams); - do { - ((vorbis_packetizer_c *)dmx->packetizer)-> - process(&op, ogg_page_granulepos(og)); - if (op.e_o_s) { - dmx->packetizer->flush_pages(); - dmx->eos = 1; - return; - } - } while (ogg_stream_packetout(&dmx->os, &op)); + fprintf(stderr, "Warning: ogm_reader: No Vorbis support at the moment. " + "Ignoring stream id %d.\n", numstreams); +// try { +// dmx->packetizer = new vorbis_packetizer_c(&async, &range, comments); +// } catch (error_c error) { +// fprintf(stderr, "Fatal: ogm_reader: could not initialize Vorbis " \ +// "packetizer for stream id %d. Will try to continue and " \ +// "ignore this stream.\n", numstreams); +// free(dmx); +// ogg_stream_clear(&new_oss); +// return; +// } +// dmx->stype = OGM_STREAM_TYPE_VORBIS; +// dmx->serial = ogg_page_serialno(og); +// memcpy(&dmx->os, &new_oss, sizeof(ogg_stream_state)); +// dmx->sid = nastreams; +// add_new_demuxer(dmx); +// if (verbose) +// fprintf(stdout, "OGG/OGM demultiplexer (%s): using Vorbis audio " \ +// "output module for stream %d.\n", filename, numstreams); +// do { +// ((vorbis_packetizer_c *)dmx->packetizer)-> +// process(&op, ogg_page_granulepos(og)); +// if (op.e_o_s) { +// dmx->packetizer->flush_pages(); +// dmx->eos = 1; +// return; +// } +// } while (ogg_stream_packetout(&dmx->os, &op)); return; } @@ -337,33 +464,11 @@ void ogm_reader_c::handle_new_stream(ogg_page *og) { numstreams++; if (!demuxing_requested(vstreams, nvstreams)) { ogg_stream_clear(&new_oss); + free(dmx->packet_data[0]); + free(dmx); return; } - dmx = (ogm_demuxer_t *)malloc(sizeof(ogm_demuxer_t)); - if (dmx == NULL) - die("malloc"); - memset(dmx, 0, sizeof(ogm_demuxer_t)); - if (fourcc == NULL) - codec = sth->subtype; - else - codec = fourcc; - try { - dmx->packetizer = - new video_packetizer_c(codec, - (double)10000000 / (double)sth->time_unit, - sth->sh.video.width, sth->sh.video.height, - sth->bits_per_sample, sth->buffersize, NULL, - &range, comments); - } catch (error_c error) { - fprintf(stderr, "FATAL: ogm_reader: could not initialize video " \ - "packetizer for stream id %d. Will try to continue and " \ - "ignore this stream.\n", numstreams); - free(dmx); - ogg_stream_clear(&new_oss); - return; - } - ((video_packetizer_c *)dmx->packetizer)->set_chapter_info(chapter_info); dmx->stype = OGM_STREAM_TYPE_VIDEO; dmx->serial = ogg_page_serialno(og); memcpy(&dmx->os, &new_oss, sizeof(ogg_stream_state)); @@ -371,17 +476,11 @@ void ogm_reader_c::handle_new_stream(ogg_page *og) { add_new_demuxer(dmx); if (video_fps < 0) video_fps = 10000000.0 / (float)sth->time_unit; - if (verbose) - fprintf(stdout, "OGG/OGM demultiplexer (%s): using video output " \ - "module for stream %d.\n", filename, numstreams); return; } if (!strncmp(sth->streamtype, "audio", 5)) { - char buf[5]; - long codec_id; - nastreams++; numstreams++; if (!demuxing_requested(astreams, nastreams)) { @@ -389,84 +488,25 @@ void ogm_reader_c::handle_new_stream(ogg_page *og) { return; } + sth = (stream_header *)&op.packet[1]; memcpy(buf, (char *)sth->subtype, 4); buf[4] = 0; codec_id = strtol(buf, (char **)NULL, 16); - - dmx = (ogm_demuxer_t *)malloc(sizeof(ogm_demuxer_t)); - if (dmx == NULL) - die("malloc"); - memset(dmx, 0, sizeof(ogm_demuxer_t)); - switch (codec_id) { - case 0x0001: // raw PCM - try { - dmx->packetizer = - new pcm_packetizer_c(sth->samples_per_unit, - sth->sh.audio.channels, - sth->bits_per_sample, &async, &range, - comments); - } catch (error_c error) { - fprintf(stderr, "FATAL: ogm_reader: could not initialize PCM " \ - "packetizer for stream id %d. Will try to continue and " \ - "ignore this stream.\n", numstreams); - free(dmx); - ogg_stream_clear(&new_oss); - return; - } - dmx->stype = OGM_STREAM_TYPE_PCM; - if (verbose) - fprintf(stdout, "OGG/OGM demultiplexer (%s): using PCM output " \ - "module for stream %d.\n", filename, numstreams); - break; - case 0x0055: // MP3 - try { - dmx->packetizer = - new mp3_packetizer_c(sth->samples_per_unit, - sth->sh.audio.channels, - sth->sh.audio.avgbytespersec * 8 / 1000, - &async, &range, comments); - } catch (error_c error) { - fprintf(stderr, "FATAL: ogm_reader: could not initialize MP3 " \ - "packetizer for stream id %d. Will try to continue and " \ - "ignore this stream.\n", numstreams); - free(dmx); - ogg_stream_clear(&new_oss); - return; - } - dmx->stype = OGM_STREAM_TYPE_MP3; - if (verbose) - fprintf(stdout, "OGG/OGM demultiplexer (%s): using MP3 output " \ - "module for stream %d.\n", filename, numstreams); - break; - case 0x2000: // AC3 - try { - dmx->packetizer = - new ac3_packetizer_c(sth->samples_per_unit, - sth->sh.audio.channels, - sth->sh.audio.avgbytespersec * 8 / 1000, - &async, &range, comments); - } catch (error_c error) { - fprintf(stderr, "FATAL: ogm_reader: could not initialize AC3 " \ - "packetizer for stream id %d. Will try to continue and " \ - "ignore this stream.\n", numstreams ); - free(dmx); - ogg_stream_clear(&new_oss); - return; - } - dmx->stype = OGM_STREAM_TYPE_AC3; - if (verbose) - fprintf(stdout, "OGG/OGM demultiplexer (%s): using AC3 output " \ - "module for stream %d.\n", filename, numstreams); - break; - default: - fprintf(stderr, "FATAL: ogm_reader: Audio stream %d has a unknown " \ - "type. Will try to continue and ignore this stream.\n", - numstreams); - free(dmx); - ogg_stream_clear(&new_oss); - return; - break; + + if (codec_id == 0x0001) + dmx->stype = OGM_STREAM_TYPE_PCM; + else if (codec_id == 0x0055) + dmx->stype = OGM_STREAM_TYPE_MP3; + else if (codec_id == 0x2000) + dmx->stype = OGM_STREAM_TYPE_AC3; + else { + fprintf(stderr, "Error: ogm_reader: Unknown audio stream type %u. " + "Ignoring stream id %d.\n", codec_id, numstreams); + free(dmx->packet_data[0]); + free(dmx); + return; } + dmx->serial = ogg_page_serialno(og); memcpy(&dmx->os, &new_oss, sizeof(ogg_stream_state)); dmx->sid = nastreams; @@ -479,36 +519,39 @@ void ogm_reader_c::handle_new_stream(ogg_page *og) { ntstreams++; numstreams++; - if (!demuxing_requested(tstreams, ntstreams)) { - ogg_stream_clear(&new_oss); - return; - } + fprintf(stderr, "Warning: ogm_reader: No support for subtitles at " + "the moment. Ignoring stream id %d.\n", numstreams); + +// if (!demuxing_requested(tstreams, ntstreams)) { +// ogg_stream_clear(&new_oss); +// return; +// } - dmx = (ogm_demuxer_t *)malloc(sizeof(ogm_demuxer_t)); - if (dmx == NULL) - die("malloc"); - memset(dmx, 0, sizeof(ogm_demuxer_t)); +// dmx = (ogm_demuxer_t *)malloc(sizeof(ogm_demuxer_t)); +// if (dmx == NULL) +// die("malloc"); +// memset(dmx, 0, sizeof(ogm_demuxer_t)); - try { - dmx->packetizer = - new textsubs_packetizer_c(&async, &range, comments); - } catch (error_c error) { - fprintf(stderr, "FATAL: ogm_reader: could not initialize text " \ - "subtitle packetizer for stream id %d. Will try to " \ - "continue and ignore this stream.\n", numstreams); - free(dmx); - ogg_stream_clear(&new_oss); - return; - } - dmx->stype = OGM_STREAM_TYPE_TEXT; +// try { +// dmx->packetizer = +// new textsubs_packetizer_c(&async, &range, comments); +// } catch (error_c error) { +// fprintf(stderr, "FATAL: ogm_reader: could not initialize text " \ +// "subtitle packetizer for stream id %d. Will try to " \ +// "continue and ignore this stream.\n", numstreams); +// free(dmx); +// ogg_stream_clear(&new_oss); +// return; +// } +// dmx->stype = OGM_STREAM_TYPE_TEXT; - dmx->serial = ogg_page_serialno(og); - memcpy(&dmx->os, &new_oss, sizeof(ogg_stream_state)); - dmx->sid = ntstreams; - add_new_demuxer(dmx); - if (verbose) - fprintf(stdout, "OGG/OGM demultiplexer (%s): using text subtitle " \ - "output module for stream %d.\n", filename, numstreams); +// dmx->serial = ogg_page_serialno(og); +// memcpy(&dmx->os, &new_oss, sizeof(ogg_stream_state)); +// dmx->sid = ntstreams; +// add_new_demuxer(dmx); +// if (verbose) +// fprintf(stdout, "OGG/OGM demultiplexer (%s): using text subtitle " \ +// "output module for stream %d.\n", filename, numstreams); return; } @@ -519,8 +562,11 @@ void ogm_reader_c::handle_new_stream(ogg_page *og) { * are not supported. */ -// Failed to detect a supported header. + // Failed to detect a supported header. ogg_stream_clear(&new_oss); + free(dmx->packet_data[0]); + free(dmx); + return; } @@ -551,26 +597,20 @@ void ogm_reader_c::process_page(ogg_page *og) { lenbytes = lenbytes << 8; lenbytes += *((unsigned char *)op.packet + hdrlen - i); } + eos = op.e_o_s; if (o_eos) op.e_o_s = 0; + switch (dmx->stype) { - case OGM_STREAM_TYPE_VORBIS: - ((vorbis_packetizer_c *)dmx->packetizer)-> - process(&op, ogg_page_granulepos(og)); - break; +// case OGM_STREAM_TYPE_VORBIS: +// ((vorbis_packetizer_c *)dmx->packetizer)-> +// process(&op, ogg_page_granulepos(og)); +// break; + case OGM_STREAM_TYPE_VIDEO: - if ((*op.packet & 3) == PACKET_TYPE_COMMENT) { - /* - * Give the video packetizer the old comments if the user did not - * provide comments via -c. - */ - if ((comments == NULL) || (comments[0] == NULL)) { - vorbis_comment vc; - if (vorbis_unpack_comment(&vc, (char *)op.packet, op.bytes) >= 0) - dmx->packetizer->set_comments(&vc); - } - } else if ((*op.packet & 3) != PACKET_TYPE_HEADER) { + if (((*op.packet & 3) != PACKET_TYPE_HEADER) && + ((*op.packet & 3) != PACKET_TYPE_COMMENT)) { ((video_packetizer_c *)dmx->packetizer)-> process((char *)&op.packet[hdrlen + 1], op.bytes - 1 - hdrlen, hdrlen > 0 ? lenbytes : 1, @@ -578,101 +618,140 @@ void ogm_reader_c::process_page(ogg_page *og) { dmx->units_processed += (hdrlen > 0 ? lenbytes : 1); } break; + case OGM_STREAM_TYPE_PCM: - if ((*op.packet & 3) == PACKET_TYPE_COMMENT) { - /* - * Give the video packetizer the old comments if the user did not - * provide comments via -c. - */ - if ((comments == NULL) || (comments[0] == NULL)) { - vorbis_comment vc; - if (vorbis_unpack_comment(&vc, (char *)op.packet, op.bytes) >= 0) - dmx->packetizer->set_comments(&vc); - } - } else if ((*op.packet & 3) != PACKET_TYPE_HEADER) { + if (((*op.packet & 3) != PACKET_TYPE_HEADER) && + ((*op.packet & 3) != PACKET_TYPE_COMMENT)) { ((pcm_packetizer_c *)dmx->packetizer)-> process((char *)&op.packet[hdrlen + 1], op.bytes - 1 - hdrlen, op.e_o_s); dmx->units_processed += op.bytes - 1; } break; + case OGM_STREAM_TYPE_MP3: // MP3 - if ((*op.packet & 3) == PACKET_TYPE_COMMENT) { - /* - * Give the video packetizer the old comments if the user did not - * provide comments via -c. - */ - if ((comments == NULL) || (comments[0] == NULL)) { - vorbis_comment vc; - if (vorbis_unpack_comment(&vc, (char *)op.packet, op.bytes) >= 0) - dmx->packetizer->set_comments(&vc); - } - } else if ((*op.packet & 3) != PACKET_TYPE_HEADER) { + if (((*op.packet & 3) != PACKET_TYPE_HEADER) && + ((*op.packet & 3) != PACKET_TYPE_COMMENT)) { ((mp3_packetizer_c *)dmx->packetizer)-> process((char *)&op.packet[hdrlen + 1], op.bytes - 1 - hdrlen, op.e_o_s); dmx->units_processed += op.bytes - 1; } break; + case OGM_STREAM_TYPE_AC3: // AC3 - if ((*op.packet & 3) == PACKET_TYPE_COMMENT) { - /* - * Give the video packetizer the old comments if the user did not - * provide comments via -c. - */ - if ((comments == NULL) || (comments[0] == NULL)) { - vorbis_comment vc; - if (vorbis_unpack_comment(&vc, (char *)op.packet, op.bytes) >= 0) - dmx->packetizer->set_comments(&vc); - } - } else if ((*op.packet & 3) != PACKET_TYPE_HEADER) { + if (((*op.packet & 3) != PACKET_TYPE_HEADER) && + ((*op.packet & 3) != PACKET_TYPE_COMMENT)) { ((ac3_packetizer_c *)dmx->packetizer)-> process((char *)&op.packet[hdrlen + 1], op.bytes - 1 - hdrlen, op.e_o_s); dmx->units_processed += op.bytes - 1; } break; - case OGM_STREAM_TYPE_TEXT: // text subtitles - if ((*op.packet & 3) == PACKET_TYPE_COMMENT) { - /* - * Give the text packetizer the old comments if the user did not - * provide comments via -c. - */ - if ((comments == NULL) || (comments[0] == NULL)) { - vorbis_comment vc; - if (vorbis_unpack_comment(&vc, (char *)op.packet, op.bytes) >= 0) - dmx->packetizer->set_comments(&vc); - } - } else if ((*op.packet & 3) != PACKET_TYPE_HEADER) { - char *subs = (char *)&op.packet[1 + hdrlen]; - if ((*subs != 0) && (*subs != '\n') && (*subs != '\r') && - strcmp(subs, " ")) { - ((textsubs_packetizer_c *)dmx->packetizer)-> - process(op.granulepos, op.granulepos + lenbytes, subs, - op.e_o_s); - dmx->units_processed++; - } - } - break; + +// case OGM_STREAM_TYPE_TEXT: // text subtitles +// if (((*op.packet & 3) != PACKET_TYPE_HEADER) && +// ((*op.packet & 3) != PACKET_TYPE_COMMENT)) { +// char *subs = (char *)&op.packet[1 + hdrlen]; +// if ((*subs != 0) && (*subs != '\n') && (*subs != '\r') && +// strcmp(subs, " ")) { +// ((textsubs_packetizer_c *)dmx->packetizer)-> +// process(op.granulepos, op.granulepos + lenbytes, subs, +// op.e_o_s); +// dmx->units_processed++; +// } +// } +// break; } + if (eos) { - dmx->packetizer->flush_pages(); dmx->eos = 1; return; } } } +/* + * Search and store additional headers for the Vorbis streams. + */ +void ogm_reader_c::process_header_page(ogg_page *og) { + ogm_demuxer_t *dmx; + ogg_packet op; + + dmx = find_demuxer(ogg_page_serialno(og)); + if (dmx == NULL) + return; + + ogg_stream_pagein(&dmx->os, og); + while (ogg_stream_packetout(&dmx->os, &op) == 1) { + if ((*op.packet & 3) == PACKET_TYPE_HEADER) { + dmx->num_packets++; + dmx->packet_data[2] = malloc(op.bytes); + if (dmx->packet_data[2] == NULL) + die("malloc"); + memcpy(dmx->packet_data[2], op.packet, op.bytes); + dmx->packet_sizes[2] = op.bytes; + } else if ((*op.packet & 3) == PACKET_TYPE_COMMENT) { + dmx->num_packets++; + dmx->packet_data[1] = malloc(op.bytes); + if (dmx->packet_data[1] == NULL) + die("malloc"); + memcpy(dmx->packet_data[1], op.packet, op.bytes); + dmx->packet_sizes[1] = op.bytes; + } + } +} + +/* + * Read all header packets and - for Vorbis streams - the comment and + * codec data packets. + */ +int ogm_reader_c::read_headers() { + int done, i; + ogm_demuxer_t *dmx; + ogg_page og; + + done = 0; + while (!done) { + // Make sure we have a page that we can work with. + if (read_page(&og) == 0) + return 0; + + // Is this the first page of a new stream? + if (ogg_page_bos(&og)) + handle_new_stream(&og); + else { // No, so check if it's still a header page. + bos_pages_read = 1; + process_header_page(&og); + + done = 1; + + for (i = 0; i < num_sdemuxers; i++) { + dmx = sdemuxers[i]; + if ((dmx->stype == OGM_STREAM_TYPE_VORBIS) && (dmx->num_packets < 3)) { + // Not all three headers have been found for this Vorbis stream. + done = 0; + break; + } + } + } + } + + fseek(file, 0, SEEK_SET); + + return 1; +} + /* * General reader. Before returning it MUST guarantee that each demuxer has * a page available OR that the corresponding stream is finished. */ int ogm_reader_c::read() { - int done; + int done, i; ogm_demuxer_t *dmx; ogg_page og; - if (pages_available() && !o_eos) + if (packet_available() && !o_eos) return EMOREDATA; done = 0; @@ -680,90 +759,59 @@ int ogm_reader_c::read() { // Make sure we have a page that we can work with. if (read_page(&og) == 0) return 0; + // Is this the first page of a new stream? if (ogg_page_bos(&og)) - handle_new_stream(&og); + continue; else // No, so process it normally. process_page(&og); + done = 1; - dmx = sdemuxers; - if (dmx == NULL) + if (num_sdemuxers == 0) // We haven't encountered a stream yet that we want to extract. done = 0; else - do { - if (!dmx->eos && !dmx->packetizer->page_available()) - // This stream is not finished but has not yet produced a new page. + for (i = 0; i < num_sdemuxers; i++) { + dmx = sdemuxers[i]; + if (!dmx->eos && !dmx->packetizer->packet_available()) + // This stream is not finished but has not produced a new page yet. done = 0; - dmx = dmx->next; - } while ((dmx != NULL) && done); + } } // Each known stream has now produced a new page or is at its end. - dmx = sdemuxers; - if (dmx == NULL) + if (num_sdemuxers == 0) return EMOREDATA; // Are there streams that have not finished yet? - do { - if (!dmx->eos) + for (i = 0; i < num_sdemuxers; i++) + if (!sdemuxers[i]->eos) return EMOREDATA; - dmx = dmx->next; - } while (dmx != NULL); // No, we're done with this file. return 0; } -int ogm_reader_c::serial_in_use(int serial) { - ogm_demuxer_t *demuxer; - - demuxer = sdemuxers; - while (demuxer != NULL) { - if (demuxer->packetizer->serial_in_use(serial)) - return 1; - demuxer = demuxer->next; - } - return 0; -} - -ogmmerge_page_t *ogm_reader_c::get_header_page(int header_type) { - ogmmerge_page_t *ompage = NULL; - ogm_demuxer_t *demuxer; - - demuxer = sdemuxers; - while (demuxer != NULL) { - if (demuxer->packetizer != NULL) { - ompage = demuxer->packetizer->get_header_page(header_type); - if (ompage != NULL) - return ompage; - } - demuxer = demuxer->next; - } - - return NULL; -} - -ogmmerge_page_t *ogm_reader_c::get_page() { +packet_t *ogm_reader_c::get_packet() { generic_packetizer_c *winner; ogm_demuxer_t *demuxer; + int i; winner = NULL; - demuxer = sdemuxers; - while (demuxer != NULL) { + for (i = 0; i < num_sdemuxers; i++) { + demuxer = sdemuxers[i]; if (winner == NULL) { - if (demuxer->packetizer->page_available()) + if (demuxer->packetizer->packet_available()) winner = demuxer->packetizer; - } else if (winner->page_available() && + } else if (winner->packet_available() && (winner->get_smallest_timestamp() > demuxer->packetizer->get_smallest_timestamp())) winner = demuxer->packetizer; - demuxer = demuxer->next; } if (winner != NULL) - return winner->get_page(); + return winner->get_packet(); else return NULL; } @@ -773,31 +821,26 @@ void ogm_reader_c::overwrite_eos(int no_eos) { } int ogm_reader_c::display_priority() { - ogm_demuxer_t *dmx; + int i; - dmx = sdemuxers; - while (dmx != NULL) { - if (dmx->stype == OGM_STREAM_TYPE_VIDEO) + for (i = 0; i < num_sdemuxers; i++) + if (sdemuxers[i]->stype == OGM_STREAM_TYPE_VIDEO) return DISPLAYPRIORITY_MEDIUM; - dmx = dmx->next; - } + return DISPLAYPRIORITY_LOW; } static char wchar[] = "-\\|/-\\|/-"; void ogm_reader_c::display_progress() { - ogm_demuxer_t *dmx; - - dmx = sdemuxers; - while (dmx != NULL) { - if (dmx->stype == OGM_STREAM_TYPE_VIDEO) { - fprintf(stdout, "progress: %d frames\r", dmx->units_processed); + int i; + + for (i = 0; i < num_sdemuxers; i++) + if (sdemuxers[i]->stype == OGM_STREAM_TYPE_VIDEO) { + fprintf(stdout, "progress: %d frames\r", sdemuxers[i]->units_processed); fflush(stdout); return; } - dmx = dmx->next; - } fprintf(stdout, "working... %c\r", wchar[act_wchar]); act_wchar++; @@ -811,17 +854,11 @@ generic_packetizer_c *ogm_reader_c::set_packetizer(generic_packetizer_c *np) { if (sdemuxers == NULL) return NULL; - old = sdemuxers->packetizer; - sdemuxers->packetizer = np; + + old = sdemuxers[0]->packetizer; + sdemuxers[0]->packetizer = np; + return old; } -void ogm_reader_c::reset() { - ogm_demuxer_t *dmx; - - dmx = sdemuxers; - while (dmx != NULL) { - dmx->packetizer->reset(); - dmx = dmx->next; - } -} +#endif // HAVE_OGGVORBIS diff --git a/r_ogm.h b/r_ogm.h index c85df52ef..7090296bf 100644 --- a/r_ogm.h +++ b/r_ogm.h @@ -1,19 +1,23 @@ /* - ogmmerge -- utility for splicing together ogg bitstreams + mkvmerge -- utility for splicing together matroska files from component media subtypes r_ogm.h - class definitions for the OGG demultiplexer module Written by Moritz Bunkus - Based on Xiph.org's 'oggmerge' found in their CVS repository - See http://www.xiph.org Distributed under the GPL see the file COPYING for details or visit http://www.gnu.org/copyleft/gpl.html */ +/*! + \file + \version \$Id: r_ogm.h,v 1.5 2003/03/03 13:47:50 mosu Exp $ + \brief class definitions for the OGG media stream reader + \author Moritz Bunkus +*/ + #ifndef __R_OGM_H #define __R_OGM_H @@ -21,8 +25,8 @@ #include -#include "ogmmerge.h" -#include "queue.h" +#include "common.h" +#include "pr_generic.h" #define OGM_STREAM_TYPE_UNKNOWN 0 #define OGM_STREAM_TYPE_VORBIS 1 @@ -32,7 +36,7 @@ #define OGM_STREAM_TYPE_AC3 5 #define OGM_STREAM_TYPE_TEXT 6 -typedef struct ogm_demuxer_t { +typedef struct { ogg_stream_state os; generic_packetizer_c *packetizer; int sid; @@ -40,7 +44,9 @@ typedef struct ogm_demuxer_t { int eos; int serial; int units_processed; - ogm_demuxer_t *next; + int num_packets; + int packet_sizes[3]; + void *packet_data[3]; } ogm_demuxer_t; class ogm_reader_c: public generic_reader_c { @@ -50,28 +56,25 @@ private: FILE *file; char *filename; int act_wchar; - ogm_demuxer_t *sdemuxers; + ogm_demuxer_t **sdemuxers; + int num_sdemuxers; int nastreams, nvstreams, ntstreams, numstreams; audio_sync_t async; range_t range; char **comments; char *fourcc; - int o_eos; + int o_eos, bos_pages_read; public: ogm_reader_c(char *fname, unsigned char *astreams, unsigned char *vstreams, unsigned char *tstreams, - audio_sync_t *nasync, range_t *nrange, char **ncomments, - char *nfourcc) throw (error_c); + audio_sync_t *nasync, range_t *nrange, char *nfourcc) + throw (error_c); virtual ~ogm_reader_c(); virtual int read(); - virtual int serial_in_use(int); - virtual ogmmerge_page_t *get_page(); - virtual ogmmerge_page_t *get_header_page(int header_type = - PACKET_TYPE_HEADER); + virtual packet_t *get_packet(); - virtual void reset(); virtual int display_priority(); virtual void display_progress(); @@ -83,12 +86,15 @@ public: private: virtual ogm_demuxer_t *find_demuxer(int serialno); virtual int demuxing_requested(unsigned char *, int); - virtual void flush_packetizers(); virtual int read_page(ogg_page *); virtual void add_new_demuxer(ogm_demuxer_t *); - virtual int pages_available(); virtual void handle_new_stream(ogg_page *); virtual void process_page(ogg_page *); + virtual int packet_available(); + virtual int read_headers(); + virtual void process_header_page(ogg_page *); + virtual void create_packetizers(); + virtual void free_demuxer(int); };