From ccdcebaddff54d96f54c67b9cb1b76a839576032 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Sun, 23 Oct 2005 18:21:13 +0000 Subject: [PATCH] Added support for Ogg/Theora (not specs compliant because we haven't decided about the specs yet. --- ChangeLog | 4 + src/common/common_memory.cpp | 100 ++++++++++++++++++++++++ src/common/common_memory.h | 3 + src/common/matroska.h | 1 + src/common/theora_common.cpp | 78 +++++++++++++++++++ src/common/theora_common.h | 65 ++++++++++++++++ src/input/r_matroska.cpp | 56 ++++++-------- src/input/r_ogm.cpp | 144 +++++++++++++++++++++++++++-------- src/input/r_ogm.h | 15 ++-- src/output/p_vorbis.cpp | 84 ++++++++------------ src/output/p_vorbis.h | 2 +- 11 files changed, 425 insertions(+), 127 deletions(-) create mode 100644 src/common/common_memory.cpp create mode 100644 src/common/theora_common.cpp create mode 100644 src/common/theora_common.h diff --git a/ChangeLog b/ChangeLog index d41904268..70c39a308 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2005-10-23 Moritz Bunkus + + * mkvmerge: new feature: Added support for Ogg/Theora. + 2005-10-22 Moritz Bunkus * mkvmerge: Changed the CodecID for AAC audio tracks to "A_AAC" by diff --git a/src/common/common_memory.cpp b/src/common/common_memory.cpp new file mode 100644 index 000000000..a49328e6c --- /dev/null +++ b/src/common/common_memory.cpp @@ -0,0 +1,100 @@ +/* + mkvmerge -- utility for splicing together matroska files + from component media subtypes + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html + + $Id$ + + class definition for memory handling classes + + Written by Moritz Bunkus . +*/ + +#include "os.h" + +#include + +#include "common_memory.h" +#include "error.h" + +using namespace std; + +memory_cptr +lace_memory_xiph(const vector &blocks) { + unsigned char *buffer; + int size, i, offset, n; + + size = 1; + for (i = 0; (blocks.size() - 1) > i; ++i) + size += blocks[i]->get_size() / 255 + 1 + blocks[i]->get_size(); + size += blocks.back()->get_size(); + + buffer = (unsigned char *)safemalloc(size); + + buffer[0] = blocks.size() - 1; + offset = 1; + for (i = 0; (blocks.size() - 1) > i; ++i) { + for (n = blocks[i]->get_size(); n >= 255; n -= 255) { + buffer[offset] = 255; + ++offset; + } + buffer[offset] = n; + ++offset; + } + for (i = 0; blocks.size() > i; ++i) { + memcpy(&buffer[offset], blocks[i]->get(), blocks[i]->get_size()); + offset += blocks[i]->get_size(); + } + + return memory_cptr(new memory_c(buffer, size, true)); +} + +vector +unlace_memory_xiph(memory_cptr &buffer) { + int i, num_blocks, size, last_size; + unsigned char *ptr, *end; + vector blocks; + vector sizes; + + if (1 > buffer->get_size()) + throw error_c("Lacing error: Buffer too small"); + + ptr = buffer->get(); + end = buffer->get() + buffer->get_size(); + num_blocks = ptr[0] + 1; + ++ptr; + + last_size = buffer->get_size(); + for (i = 0; (num_blocks - 1) > i; ++i) { + size = 0; + while ((ptr < end) && (*ptr == 255)) { + size += 255; + ++ptr; + } + + if (ptr >= end) + throw error_c("Lacing error: End-of-buffer while reading the block " + "sizes"); + + size += *ptr; + ++ptr; + + sizes.push_back(size); + last_size -= size; + } + + sizes.push_back(last_size - (ptr - buffer->get())); + + for (i = 0; sizes.size() > i; ++i) { + if ((ptr + sizes[i]) > end) + throw error_c("Lacing error: End-of-buffer while assigning the blocks"); + + blocks.push_back(memory_cptr(new memory_c(ptr, sizes[i], false))); + ptr += sizes[i]; + } + + return blocks; +} diff --git a/src/common/common_memory.h b/src/common/common_memory.h index 90186fa70..ea91fa031 100644 --- a/src/common/common_memory.h +++ b/src/common/common_memory.h @@ -126,4 +126,7 @@ struct buffer_t { ~buffer_t(); }; +memory_cptr MTX_DLL_API lace_memory_xiph(const vector &blocks); +vector MTX_DLL_API unlace_memory_xiph(memory_cptr &buffer); + #endif // __COMMON_MEMORY_H diff --git a/src/common/matroska.h b/src/common/matroska.h index 0f11d06ae..b854c185c 100644 --- a/src/common/matroska.h +++ b/src/common/matroska.h @@ -59,6 +59,7 @@ #define MKV_V_REALV3 "V_REAL/RV30" #define MKV_V_REALV4 "V_REAL/RV40" #define MKV_V_QUICKTIME "V_QUICKTIME" +#define MKV_V_THEORA "V_THEORA" #define MKV_S_TEXTUTF8 "S_TEXT/UTF8" #define MKV_S_TEXTSSA "S_TEXT/SSA" diff --git a/src/common/theora_common.cpp b/src/common/theora_common.cpp new file mode 100644 index 000000000..46532a428 --- /dev/null +++ b/src/common/theora_common.cpp @@ -0,0 +1,78 @@ +/* + mkvmerge -- utility for splicing together matroska files + from component media subtypes + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html + + $Id$ + + Ogg Theora helper functions + + Written by Moritz Bunkus . +*/ + +#include "os.h" + +#include "bit_cursor.h" +#include "common.h" +#include "theora_common.h" + +theora_identification_header_t::theora_identification_header_t() { + memset(this, 0, sizeof(theora_identification_header_t)); +} + +void +theora_parse_identification_header(unsigned char *buffer, + int size, + theora_identification_header_t &header) + throw(error_c) { + + bit_cursor_c bc(buffer, size); + int i; + + header.headertype = bc.get_bits(8); + if (THEORA_HEADERTYPE_IDENTIFICATION != header.headertype) + throw error_c(mxsprintf("Wrong header type: 0x%02x != 0x%02x", + header.headertype, + THEORA_HEADERTYPE_IDENTIFICATION)); + + for (i = 0; 6 > i; ++i) + header.theora_string[i] = bc.get_bits(8); + if (strncmp(header.theora_string, "theora", 6)) + throw error_c(mxsprintf("Wrong identifaction string: '%6s' != 'theora'", + header.theora_string)); + + header.vmaj = bc.get_bits(8); + header.vmin = bc.get_bits(8); + header.vrev = bc.get_bits(8); + + if ((3 != header.vmaj) || (2 != header.vmin) || (0 != header.vrev)) + throw error_c(mxsprintf("Wrong Theora version: %d.%d.%d != 3.2.0", + header.vmaj, header.vmin, header.vrev)); + + header.fmbw = bc.get_bits(16) * 16; + header.fmbh = bc.get_bits(16) * 16; + header.picw = bc.get_bits(24); + header.pich = bc.get_bits(24); + header.picx = bc.get_bits(8); + header.picy = bc.get_bits(8); + + header.frn = bc.get_bits(32); + header.frd = bc.get_bits(32); + + header.parn = bc.get_bits(24); + header.pard = bc.get_bits(24); + + header.cs = bc.get_bits(8); + header.nombr = bc.get_bits(24); + header.qual = bc.get_bits(6); + + header.kfgshift = bc.get_bits(5); + + header.pf = bc.get_bits(2); + + if (0 != bc.get_bits(3)) + throw error_c("Reserved bits are not 0"); +} diff --git a/src/common/theora_common.h b/src/common/theora_common.h new file mode 100644 index 000000000..efd7cfd69 --- /dev/null +++ b/src/common/theora_common.h @@ -0,0 +1,65 @@ +/* + mkvmerge -- utility for splicing together matroska files + from component media subtypes + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html + + $Id$ + + Ogg Theora helper functions + + Written by Moritz Bunkus . +*/ + +#ifndef __THEORA_COMMON_H +#define __THEORA_COMMON_H + +#include "os.h" + +#include "error.h" + +#define THEORA_HEADERTYPE_IDENTIFICATION 0x80 +#define THEORA_HEADERTYPE_COMMENT 0x81 +#define THEORA_HEADERTYPE_SETUP 0x82 + +struct MTX_DLL_API theora_identification_header_t { + uint8_t headertype; + char theora_string[6]; + + uint8_t vmaj; + uint8_t vmin; + uint8_t vrev; + + uint16_t fmbw; + uint16_t fmbh; + + uint32_t picw; + uint32_t pich; + uint8_t picx; + uint8_t picy; + + uint32_t frn; + uint32_t frd; + + uint32_t parn; + uint32_t pard; + + uint8_t cs; + uint8_t pf; + + uint32_t nombr; + uint8_t qual; + + uint8_t kfgshift; + + theora_identification_header_t(); +}; + +void MTX_DLL_API +theora_parse_identification_header(unsigned char *buffer, int size, + theora_identification_header_t &header) + throw(error_c); + +#endif // __THEORA_COMMON_H diff --git a/src/input/r_matroska.cpp b/src/input/r_matroska.cpp index dfa542506..2e7a9eabe 100644 --- a/src/input/r_matroska.cpp +++ b/src/input/r_matroska.cpp @@ -228,9 +228,7 @@ kax_reader_c::find_track_by_uid(uint64_t uid, void kax_reader_c::verify_tracks() { - int tnum, i; - unsigned char *c; - uint32_t u, offset, length; + int tnum, u; kax_track_t *t; alBITMAPINFOHEADER *bih; alWAVEFORMATEX *wfe; @@ -294,6 +292,13 @@ kax_reader_c::verify_tracks() { memcpy(t->v_fourcc, &bih->bi_compression, 4); } + } else if (t->codec_id == MKV_V_THEORA) { + if (NULL == t->private_data) { + if (verbose) + mxwarn(PFX "CodecID for track " LLU " is '" MKV_V_THEORA + "', but there was no codec private headers.\n", t->tnum); + continue; + } } if (t->v_width == 0) { @@ -381,38 +386,26 @@ kax_reader_c::verify_tracks() { continue; } - c = (unsigned char *)t->private_data; - if (c[0] != 2) { + try { + memory_cptr temp(new memory_c((unsigned char *)t->private_data, + t->private_size, false)); + vector blocks = unlace_memory_xiph(temp); + if (blocks.size() != 3) + throw false; + + t->headers[0] = blocks[0]->get(); + t->headers[1] = blocks[1]->get(); + t->headers[2] = blocks[2]->get(); + t->header_sizes[0] = blocks[0]->get_size(); + t->header_sizes[1] = blocks[1]->get_size(); + t->header_sizes[2] = blocks[2]->get_size(); + + } catch (...) { if (verbose) mxwarn(PFX "Vorbis track does not contain valid headers.\n"); continue; } - offset = 1; - for (i = 0; i < 2; i++) { - length = 0; - while ((c[offset] == (unsigned char )255) && - (length < t->private_size)) { - length += 255; - offset++; - } - if (offset >= (t->private_size - 1)) { - if (verbose) - mxwarn(PFX "Vorbis track does not contain valid headers.\n"); - continue; - } - length += c[offset]; - offset++; - t->header_sizes[i] = length; - } - - t->headers[0] = &c[offset]; - t->headers[1] = &c[offset + t->header_sizes[0]]; - t->headers[2] = &c[offset + t->header_sizes[0] + - t->header_sizes[1]]; - t->header_sizes[2] = t->private_size - offset - - t->header_sizes[0] - t->header_sizes[1]; - t->a_formattag = 0xFFFE; // mkvmerge around 0.6.x had a bug writing a default duration @@ -1417,7 +1410,8 @@ kax_reader_c::create_packetizer(int64_t tid) { starts_with(t->codec_id, "V_REAL", 6) || (t->codec_id == MKV_V_QUICKTIME) || (t->codec_id == MKV_V_MPEG1) || - (t->codec_id == MKV_V_MPEG2)) { + (t->codec_id == MKV_V_MPEG2) || + (t->codec_id == MKV_V_THEORA)) { const char *fourcc; if ((t->codec_id == MKV_V_MSCOMP) && diff --git a/src/input/r_ogm.cpp b/src/input/r_ogm.cpp index d7e829b7c..88e830799 100644 --- a/src/input/r_ogm.cpp +++ b/src/input/r_ogm.cpp @@ -68,9 +68,8 @@ free_string_array(char **comments) { } static char ** -extract_vorbis_comments(unsigned char *buffer, - int size) { - mm_mem_io_c in(buffer, size); +extract_vorbis_comments(const memory_cptr &mem) { + mm_mem_io_c in(mem->get(), mem->get_size()); char **comments; uint32_t i, n, len; @@ -397,6 +396,7 @@ ogm_reader_c::create_packetizer(int64_t tid) { generic_packetizer_c *ptzr; double fps; int width, height; + memory_cptr codecprivate; if ((tid < 0) || (tid >= sdemuxers.size())) return; @@ -404,7 +404,7 @@ ogm_reader_c::create_packetizer(int64_t tid) { if ((dmx->ptzr == -1) && dmx->in_use) { memset(&bih, 0, sizeof(alBITMAPINFOHEADER)); - sth = (stream_header *)&dmx->packet_data[0][1]; + sth = (stream_header *)&dmx->packet_data[0]->get()[1]; ti.private_data = NULL; ti.private_size = 0; ti.id = tid; @@ -476,9 +476,11 @@ ogm_reader_c::create_packetizer(int64_t tid) { case OGM_STREAM_TYPE_AAC: aac_info_extracted = false; - if (dmx->packet_sizes[0] >= (sizeof(stream_header) + 5)) { - if (parse_aac_data(&dmx->packet_data[0][sizeof(stream_header) + 5], - dmx->packet_sizes[0] - sizeof(stream_header) - 5, + if (dmx->packet_data[0]->get_size() >= (sizeof(stream_header) + 5)) { + if (parse_aac_data(dmx->packet_data[0]->get() + + sizeof(stream_header) + 5, + dmx->packet_data[0]->get_size() - + sizeof(stream_header) - 5, profile, channels, sample_rate, output_sample_rate, sbr)) { aac_info_extracted = true; @@ -511,8 +513,8 @@ ogm_reader_c::create_packetizer(int64_t tid) { vorbis_info_init(&vi); vorbis_comment_init(&vc); memset(&op, 0, sizeof(ogg_packet)); - op.packet = dmx->packet_data[0]; - op.bytes = dmx->packet_sizes[0]; + op.packet = dmx->packet_data[0]->get(); + op.bytes = dmx->packet_data[0]->get_size(); op.b_o_s = 1; vorbis_synthesis_headerin(&vi, &vc, &op); dmx->vorbis_rate = vi.rate; @@ -520,9 +522,12 @@ ogm_reader_c::create_packetizer(int64_t tid) { vorbis_comment_clear(&vc); ptzr = new vorbis_packetizer_c(this, - dmx->packet_data[0], dmx->packet_sizes[0], - dmx->packet_data[1], dmx->packet_sizes[1], - dmx->packet_data[2], dmx->packet_sizes[2], + dmx->packet_data[0]->get(), + dmx->packet_data[0]->get_size(), + dmx->packet_data[1]->get(), + dmx->packet_data[1]->get_size(), + dmx->packet_data[2]->get(), + dmx->packet_data[2]->get_size(), ti); mxinfo(FMT_TID "Using the Vorbis output module.\n", ti.fname.c_str(), @@ -544,13 +549,14 @@ ogm_reader_c::create_packetizer(int64_t tid) { int size, i; size = 0; - for (i = 1; i < (int)dmx->packet_sizes.size(); i++) - size += dmx->packet_sizes[i]; + for (i = 1; i < (int)dmx->packet_data.size(); i++) + size += dmx->packet_data[i]->get_size(); buf = (unsigned char *)safemalloc(size); size = 0; - for (i = 1; i < (int)dmx->packet_sizes.size(); i++) { - memcpy(&buf[size], dmx->packet_data[i], dmx->packet_sizes[i]); - size += dmx->packet_sizes[i]; + for (i = 1; i < (int)dmx->packet_data.size(); i++) { + memcpy(&buf[size], dmx->packet_data[i]->get(), + dmx->packet_data[i]->get_size()); + size += dmx->packet_data[i]->get_size(); } ptzr = new flac_packetizer_c(this, buf, size, ti); safefree(buf); @@ -560,6 +566,21 @@ ogm_reader_c::create_packetizer(int64_t tid) { break; #endif + case OGM_STREAM_TYPE_THEORA: + codecprivate = lace_memory_xiph(dmx->packet_data); + ti.private_data = codecprivate->get(); + ti.private_size = codecprivate->get_size(); + + fps = (double)dmx->theora.frn / (double)dmx->theora.frd; + ptzr = new video_packetizer_c(this, MKV_V_THEORA, fps, dmx->theora.fmbw, + dmx->theora.fmbh, ti); + mxinfo(FMT_TID "Using the Theora video output module.\n", ti.fname.c_str(), + (int64_t)tid); + + ti.private_data = NULL; + + break; + default: die("ogm_reader: Don't know how to create a packetizer for stream " "type %d.\n", (int)dmx->stype); @@ -635,8 +656,10 @@ ogm_reader_c::handle_new_stream(ogg_page *og) { ogg_stream_pagein(&dmx->os, og); ogg_stream_packetout(&dmx->os, &op); - dmx->packet_data.push_back((unsigned char *)safememdup(op.packet, op.bytes)); - dmx->packet_sizes.push_back(op.bytes); + dmx->packet_data.push_back(memory_cptr(new memory_c((unsigned char *) + safememdup(op.packet, + op.bytes), + op.bytes, true))); dmx->serialno = ogg_page_serialno(og); /* @@ -653,6 +676,24 @@ ogm_reader_c::handle_new_stream(ogg_page *og) { return; } + if ((op.bytes >= 7) && !strncmp((char *)&op.packet[1], "theora", 6)) { + if (!demuxing_requested('v', sdemuxers.size() - 1)) + return; + + dmx->stype = OGM_STREAM_TYPE_THEORA; + dmx->in_use = true; + + try { + theora_parse_identification_header(op.packet, op.bytes, dmx->theora); + } catch (error_c &e) { + mxerror(FMT_TID "The Theora identifaction header could not be parsed " + "(%s).\n", ti.fname.c_str(), (int64_t)sdemuxers.size() - 1, + e.get_error().c_str()); + } + + return; + } + // FLAC if ((op.bytes >= 4) && !strncmp((char *)op.packet, "fLaC", 4)) { if (!demuxing_requested('a', sdemuxers.size() - 1)) @@ -778,8 +819,7 @@ ogm_reader_c::process_page(ogg_page *og) { if (dmx->units_processed <= dmx->flac_header_packets) continue; for (i = 0; i < (int)dmx->nh_packet_data.size(); i++) { - memory_c *mem = new memory_c(dmx->nh_packet_data[i], - dmx->nh_packet_sizes[i], true); + memory_c *mem = dmx->nh_packet_data[i]->clone(); PTZR(dmx->ptzr)->process(new packet_t(mem, 0)); } dmx->nh_packet_data.clear(); @@ -804,6 +844,42 @@ ogm_reader_c::process_page(ogg_page *og) { } #endif + if (dmx->stype == OGM_STREAM_TYPE_THEORA) { + int frame_number, keyframe_number, non_keyframe_number; + int64_t timecode, duration, bref; + + if ((0 == op.bytes) || (0 != (op.packet[0] & 0x80))) + continue; + + keyframe_number = ogg_page_granulepos(og) >> dmx->theora.kfgshift; + non_keyframe_number = ogg_page_granulepos(og) & dmx->theora.kfgshift; + frame_number = keyframe_number + non_keyframe_number; + + timecode = (int64_t)(1000000000.0 * dmx->last_granulepos * + dmx->theora.frd / dmx->theora.frn); + duration = (int64_t)(1000000000.0 * dmx->theora.frd / dmx->theora.frn); + bref = (keyframe_number != dmx->last_keyframe_number) && + (0 == non_keyframe_number) ? VFT_IFRAME : VFT_PFRAMEAUTOMATIC; + PTZR(dmx->ptzr)->process(new packet_t(new memory_c(op.packet, op.bytes, + false), timecode, + duration, bref, VFT_NOBFRAME)); + + if (frame_number > dmx->last_granulepos) + dmx->last_granulepos = frame_number; + else + ++dmx->last_granulepos; + + dmx->last_keyframe_number = keyframe_number; + + if (eos) { + dmx->eos = 1; + debug_leave("ogm_reader_c::process_page"); + return; + } + + continue; + } + duration_len = (*op.packet & PACKET_LEN_BITS01) >> 6; duration_len |= (*op.packet & PACKET_LEN_BITS2) << 1; if ((duration_len > 0) && (op.bytes >= (duration_len + 1))) @@ -886,16 +962,18 @@ ogm_reader_c::process_header_packets(ogm_demuxer_t *dmx) { #if defined HAVE_FLAC_FORMAT_H while ((dmx->packet_data.size() < dmx->flac_header_packets) && (ogg_stream_packetout(&dmx->os, &op) == 1)) { - dmx->packet_data.push_back((unsigned char *) - safememdup(op.packet, op.bytes)); - dmx->packet_sizes.push_back(op.bytes); + memory_c *mem = new memory_c((unsigned char *) + safememdup(op.packet, op.bytes), + op.bytes, true); + dmx->packet_data.push_back(memory_cptr(mem)); } if (dmx->packet_data.size() >= dmx->flac_header_packets) dmx->headers_read = true; while (ogg_stream_packetout(&dmx->os, &op) == 1) { - dmx->nh_packet_data.push_back((unsigned char *) - safememdup(op.packet, op.bytes)); - dmx->nh_packet_sizes.push_back(op.bytes); + memory_c *mem = new memory_c((unsigned char *) + safememdup(op.packet, op.bytes), + op.bytes, true); + dmx->nh_packet_data.push_back(memory_cptr(mem)); } #else dmx->headers_read = true; @@ -914,9 +992,10 @@ ogm_reader_c::process_header_packets(ogm_demuxer_t *dmx) { ogg_stream_reset(&dmx->os); return; } - dmx->packet_data.push_back((unsigned char *) - safememdup(op.packet, op.bytes)); - dmx->packet_sizes.push_back(op.bytes); + memory_c *mem = new memory_c((unsigned char *) + safememdup(op.packet, op.bytes), + op.bytes, true); + dmx->packet_data.push_back(memory_cptr(mem)); } if (dmx->stype == OGM_STREAM_TYPE_VORBIS) { @@ -1030,7 +1109,7 @@ ogm_reader_c::identify() { mxinfo("File '%s': container: Ogg/OGM%s\n", ti.fname.c_str(), info.c_str()); for (i = 0; i < sdemuxers.size(); i++) { if (sdemuxers[i]->stype == OGM_STREAM_TYPE_VIDEO) { - sth = (stream_header *)&sdemuxers[i]->packet_data[0][1]; + sth = (stream_header *)(sdemuxers[i]->packet_data[0]->get() + 1); memcpy(fourcc, sth->subtype, 4); fourcc[4] = 0; } @@ -1091,8 +1170,7 @@ ogm_reader_c::handle_stream_comments() { if ((dmx->stype == OGM_STREAM_TYPE_FLAC) || (dmx->packet_data.size() < 2)) continue; - comments = extract_vorbis_comments(dmx->packet_data[1], - dmx->packet_sizes[1]); + comments = extract_vorbis_comments(dmx->packet_data[1]); if (comments == NULL) continue; chapters.clear(); diff --git a/src/input/r_ogm.h b/src/input/r_ogm.h index 4998ebdce..8a73350b5 100644 --- a/src/input/r_ogm.h +++ b/src/input/r_ogm.h @@ -30,6 +30,7 @@ #include "mm_io.h" #include "common.h" #include "pr_generic.h" +#include "theora_common.h" #define OGM_STREAM_TYPE_UNKNOWN 0 #define OGM_STREAM_TYPE_VORBIS 1 @@ -40,6 +41,7 @@ #define OGM_STREAM_TYPE_TEXT 6 #define OGM_STREAM_TYPE_FLAC 7 #define OGM_STREAM_TYPE_AAC 8 +#define OGM_STREAM_TYPE_THEORA 9 #if defined(HAVE_FLAC_FORMAT_H) class flac_header_extractor_c { @@ -69,26 +71,23 @@ struct ogm_demuxer_t { int units_processed, vorbis_rate; bool headers_read, native_mode; string language, title; - vector packet_data, nh_packet_data; - vector packet_sizes, nh_packet_sizes; + vector packet_data, nh_packet_data; #if defined(HAVE_FLAC_FORMAT_H) flac_header_extractor_c *fhe; int flac_header_packets, channels, bits_per_sample; - int64_t last_granulepos; #endif + int64_t last_granulepos, last_keyframe_number; bool in_use; + theora_identification_header_t theora; + ogm_demuxer_t(): ptzr(-1), stype(0), serialno(0), eos(0), units_processed(0), vorbis_rate(0), headers_read(false), native_mode(true), + last_granulepos(0), last_keyframe_number(-1), in_use(false) { memset(&os, 0, sizeof(ogg_stream_state)); } - ~ogm_demuxer_t() { - uint32_t i; - for (i = 0; i < packet_data.size(); i++) - safefree(packet_data[i]); - } }; class ogm_reader_c: public generic_reader_c { diff --git a/src/output/p_vorbis.cpp b/src/output/p_vorbis.cpp index 98d7565d1..c6564f99d 100644 --- a/src/output/p_vorbis.cpp +++ b/src/output/p_vorbis.cpp @@ -46,21 +46,34 @@ vorbis_packetizer_c::vorbis_packetizer_c(generic_reader_c *_reader, timecode_offset(0) { int i; + ogg_packet ogg_headers[3]; - memset(headers, 0, 3 * sizeof(ogg_packet)); - headers[0].packet = (unsigned char *)safememdup(d_header, l_header); - headers[1].packet = (unsigned char *)safememdup(d_comments, l_comments); - headers[2].packet = (unsigned char *)safememdup(d_codecsetup, l_codecsetup); - headers[0].bytes = l_header; - headers[1].bytes = l_comments; - headers[2].bytes = l_codecsetup; - headers[0].b_o_s = 1; - headers[1].packetno = 1; - headers[2].packetno = 2; + headers.push_back(memory_cptr(new memory_c((unsigned char *) + safememdup(d_header, l_header), + l_header))); + headers.push_back(memory_cptr(new memory_c((unsigned char *) + safememdup(d_comments, + l_comments), + l_comments))); + headers.push_back(memory_cptr(new memory_c((unsigned char *) + safememdup(d_codecsetup, + l_codecsetup), + l_codecsetup))); + + memset(ogg_headers, 0, 3 * sizeof(ogg_packet)); + ogg_headers[0].packet = headers[0]->get(); + ogg_headers[1].packet = headers[1]->get(); + ogg_headers[2].packet = headers[2]->get(); + ogg_headers[0].bytes = l_header; + ogg_headers[1].bytes = l_comments; + ogg_headers[2].bytes = l_codecsetup; + ogg_headers[0].b_o_s = 1; + ogg_headers[1].packetno = 1; + ogg_headers[2].packetno = 2; vorbis_info_init(&vi); vorbis_comment_init(&vc); for (i = 0; i < 3; i++) - if (vorbis_synthesis_headerin(&vi, &vc, &headers[i]) < 0) + if (vorbis_synthesis_headerin(&vi, &vc, &ogg_headers[i]) < 0) throw error_c("Error: vorbis_packetizer: Could not extract the " "stream's parameters from the first packets.\n"); @@ -72,54 +85,18 @@ vorbis_packetizer_c::vorbis_packetizer_c(generic_reader_c *_reader, } vorbis_packetizer_c::~vorbis_packetizer_c() { - int i; - - for (i = 0; i < 3; i++) - if (headers[i].packet != NULL) - safefree(headers[i].packet); vorbis_info_clear(&vi); vorbis_comment_clear(&vc); } void vorbis_packetizer_c::set_headers() { - unsigned char *buffer; - int n, offset, i, lsize; + memory_cptr codecprivate; set_codec_id(MKV_A_VORBIS); - // We use lacing for the blocks. The first bytes is the number of - // packets being laced which is one less than the number of blocks that - // are actually stored. For each packet in the lace there's the length - // coded like this: - // length = 0 - // while (next byte == 255) { length += 255 } - // length += this byte which is < 255 - // The last packet's length can be calculated by the length of - // the KaxCodecPrivate and all prior packets, so there's no length for it - - // and that's why the first byte is (num_packets - 1). - lsize = 1 + (headers[0].bytes / 255) + 1 + (headers[1].bytes / 255) + 1 + - headers[0].bytes + headers[1].bytes + headers[2].bytes; - buffer = (unsigned char *)safemalloc(lsize); - - buffer[0] = 2; // The number of packets less one. - offset = 1; - for (i = 0; i < 2; i++) { - for (n = headers[i].bytes; n >= 255; n -= 255) { - buffer[offset] = 255; - offset++; - } - buffer[offset] = n; - offset++; - } - for (i = 0; i <= 2; i++) { - memcpy(&buffer[offset], headers[i].packet, headers[i].bytes); - offset += headers[i].bytes; - } - - set_codec_private(buffer, lsize); - - safefree(buffer); + codecprivate = lace_memory_xiph(headers); + set_codec_private(codecprivate->get(), codecprivate->get_size()); set_audio_sampling_freq((float)vi.rate); set_audio_channels(vi.channels); @@ -240,10 +217,9 @@ vorbis_packetizer_c::can_connect_to(generic_packetizer_c *src, return CAN_CONNECT_NO_FORMAT; connect_check_a_samplerate(vi.rate, vsrc->vi.rate); connect_check_a_channels(vi.channels, vsrc->vi.channels); - if ((headers[2].bytes != vsrc->headers[2].bytes) || - (headers[2].packet == NULL) || - (vsrc->headers[2].packet == NULL) || - memcmp(headers[2].packet, vsrc->headers[2].packet, headers[2].bytes)) { + if ((headers[2]->get_size() != vsrc->headers[2]->get_size()) || + memcmp(headers[2]->get(), vsrc->headers[2]->get(), + headers[2]->get_size())) { error_message = "The Vorbis codebooks are different; such tracks cannot " "be concatenated without reencoding"; return CAN_CONNECT_NO_PARAMETERS; diff --git a/src/output/p_vorbis.h b/src/output/p_vorbis.h index 92b6b0802..548450141 100644 --- a/src/output/p_vorbis.h +++ b/src/output/p_vorbis.h @@ -30,9 +30,9 @@ class vorbis_packetizer_c: public generic_packetizer_c { private: int64_t last_bs, samples, last_samples_sum, last_timecode, timecode_offset; + vector headers; vorbis_info vi; vorbis_comment vc; - ogg_packet headers[3]; public: vorbis_packetizer_c(generic_reader_c *_reader,