Added support for Ogg/Theora (not specs compliant because we haven't decided about the specs yet.

This commit is contained in:
Moritz Bunkus 2005-10-23 18:21:13 +00:00
parent 0591a72ca1
commit ccdcebaddf
11 changed files with 425 additions and 127 deletions

View File

@ -1,3 +1,7 @@
2005-10-23 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: new feature: Added support for Ogg/Theora.
2005-10-22 Moritz Bunkus <moritz@bunkus.org> 2005-10-22 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: Changed the CodecID for AAC audio tracks to "A_AAC" by * mkvmerge: Changed the CodecID for AAC audio tracks to "A_AAC" by

View File

@ -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 <moritz@bunkus.org>.
*/
#include "os.h"
#include <vector>
#include "common_memory.h"
#include "error.h"
using namespace std;
memory_cptr
lace_memory_xiph(const vector<memory_cptr> &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<memory_cptr>
unlace_memory_xiph(memory_cptr &buffer) {
int i, num_blocks, size, last_size;
unsigned char *ptr, *end;
vector<memory_cptr> blocks;
vector<int> 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;
}

View File

@ -126,4 +126,7 @@ struct buffer_t {
~buffer_t(); ~buffer_t();
}; };
memory_cptr MTX_DLL_API lace_memory_xiph(const vector<memory_cptr> &blocks);
vector<memory_cptr> MTX_DLL_API unlace_memory_xiph(memory_cptr &buffer);
#endif // __COMMON_MEMORY_H #endif // __COMMON_MEMORY_H

View File

@ -59,6 +59,7 @@
#define MKV_V_REALV3 "V_REAL/RV30" #define MKV_V_REALV3 "V_REAL/RV30"
#define MKV_V_REALV4 "V_REAL/RV40" #define MKV_V_REALV4 "V_REAL/RV40"
#define MKV_V_QUICKTIME "V_QUICKTIME" #define MKV_V_QUICKTIME "V_QUICKTIME"
#define MKV_V_THEORA "V_THEORA"
#define MKV_S_TEXTUTF8 "S_TEXT/UTF8" #define MKV_S_TEXTUTF8 "S_TEXT/UTF8"
#define MKV_S_TEXTSSA "S_TEXT/SSA" #define MKV_S_TEXTSSA "S_TEXT/SSA"

View File

@ -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 <moritz@bunkus.org>.
*/
#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");
}

View File

@ -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 <moritz@bunkus.org>.
*/
#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

View File

@ -228,9 +228,7 @@ kax_reader_c::find_track_by_uid(uint64_t uid,
void void
kax_reader_c::verify_tracks() { kax_reader_c::verify_tracks() {
int tnum, i; int tnum, u;
unsigned char *c;
uint32_t u, offset, length;
kax_track_t *t; kax_track_t *t;
alBITMAPINFOHEADER *bih; alBITMAPINFOHEADER *bih;
alWAVEFORMATEX *wfe; alWAVEFORMATEX *wfe;
@ -294,6 +292,13 @@ kax_reader_c::verify_tracks() {
memcpy(t->v_fourcc, &bih->bi_compression, 4); 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) { if (t->v_width == 0) {
@ -381,38 +386,26 @@ kax_reader_c::verify_tracks() {
continue; continue;
} }
c = (unsigned char *)t->private_data; try {
if (c[0] != 2) { memory_cptr temp(new memory_c((unsigned char *)t->private_data,
t->private_size, false));
vector<memory_cptr> 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) if (verbose)
mxwarn(PFX "Vorbis track does not contain valid headers.\n"); mxwarn(PFX "Vorbis track does not contain valid headers.\n");
continue; 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; t->a_formattag = 0xFFFE;
// mkvmerge around 0.6.x had a bug writing a default duration // 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) || starts_with(t->codec_id, "V_REAL", 6) ||
(t->codec_id == MKV_V_QUICKTIME) || (t->codec_id == MKV_V_QUICKTIME) ||
(t->codec_id == MKV_V_MPEG1) || (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; const char *fourcc;
if ((t->codec_id == MKV_V_MSCOMP) && if ((t->codec_id == MKV_V_MSCOMP) &&

View File

@ -68,9 +68,8 @@ free_string_array(char **comments) {
} }
static char ** static char **
extract_vorbis_comments(unsigned char *buffer, extract_vorbis_comments(const memory_cptr &mem) {
int size) { mm_mem_io_c in(mem->get(), mem->get_size());
mm_mem_io_c in(buffer, size);
char **comments; char **comments;
uint32_t i, n, len; uint32_t i, n, len;
@ -397,6 +396,7 @@ ogm_reader_c::create_packetizer(int64_t tid) {
generic_packetizer_c *ptzr; generic_packetizer_c *ptzr;
double fps; double fps;
int width, height; int width, height;
memory_cptr codecprivate;
if ((tid < 0) || (tid >= sdemuxers.size())) if ((tid < 0) || (tid >= sdemuxers.size()))
return; return;
@ -404,7 +404,7 @@ ogm_reader_c::create_packetizer(int64_t tid) {
if ((dmx->ptzr == -1) && dmx->in_use) { if ((dmx->ptzr == -1) && dmx->in_use) {
memset(&bih, 0, sizeof(alBITMAPINFOHEADER)); 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_data = NULL;
ti.private_size = 0; ti.private_size = 0;
ti.id = tid; ti.id = tid;
@ -476,9 +476,11 @@ ogm_reader_c::create_packetizer(int64_t tid) {
case OGM_STREAM_TYPE_AAC: case OGM_STREAM_TYPE_AAC:
aac_info_extracted = false; aac_info_extracted = false;
if (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][sizeof(stream_header) + 5], if (parse_aac_data(dmx->packet_data[0]->get() +
dmx->packet_sizes[0] - sizeof(stream_header) - 5, sizeof(stream_header) + 5,
dmx->packet_data[0]->get_size() -
sizeof(stream_header) - 5,
profile, channels, sample_rate, profile, channels, sample_rate,
output_sample_rate, sbr)) { output_sample_rate, sbr)) {
aac_info_extracted = true; aac_info_extracted = true;
@ -511,8 +513,8 @@ ogm_reader_c::create_packetizer(int64_t tid) {
vorbis_info_init(&vi); vorbis_info_init(&vi);
vorbis_comment_init(&vc); vorbis_comment_init(&vc);
memset(&op, 0, sizeof(ogg_packet)); memset(&op, 0, sizeof(ogg_packet));
op.packet = dmx->packet_data[0]; op.packet = dmx->packet_data[0]->get();
op.bytes = dmx->packet_sizes[0]; op.bytes = dmx->packet_data[0]->get_size();
op.b_o_s = 1; op.b_o_s = 1;
vorbis_synthesis_headerin(&vi, &vc, &op); vorbis_synthesis_headerin(&vi, &vc, &op);
dmx->vorbis_rate = vi.rate; dmx->vorbis_rate = vi.rate;
@ -520,9 +522,12 @@ ogm_reader_c::create_packetizer(int64_t tid) {
vorbis_comment_clear(&vc); vorbis_comment_clear(&vc);
ptzr = ptzr =
new vorbis_packetizer_c(this, new vorbis_packetizer_c(this,
dmx->packet_data[0], dmx->packet_sizes[0], dmx->packet_data[0]->get(),
dmx->packet_data[1], dmx->packet_sizes[1], dmx->packet_data[0]->get_size(),
dmx->packet_data[2], dmx->packet_sizes[2], dmx->packet_data[1]->get(),
dmx->packet_data[1]->get_size(),
dmx->packet_data[2]->get(),
dmx->packet_data[2]->get_size(),
ti); ti);
mxinfo(FMT_TID "Using the Vorbis output module.\n", ti.fname.c_str(), 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; int size, i;
size = 0; size = 0;
for (i = 1; i < (int)dmx->packet_sizes.size(); i++) for (i = 1; i < (int)dmx->packet_data.size(); i++)
size += dmx->packet_sizes[i]; size += dmx->packet_data[i]->get_size();
buf = (unsigned char *)safemalloc(size); buf = (unsigned char *)safemalloc(size);
size = 0; size = 0;
for (i = 1; i < (int)dmx->packet_sizes.size(); i++) { for (i = 1; i < (int)dmx->packet_data.size(); i++) {
memcpy(&buf[size], dmx->packet_data[i], dmx->packet_sizes[i]); memcpy(&buf[size], dmx->packet_data[i]->get(),
size += dmx->packet_sizes[i]; dmx->packet_data[i]->get_size());
size += dmx->packet_data[i]->get_size();
} }
ptzr = new flac_packetizer_c(this, buf, size, ti); ptzr = new flac_packetizer_c(this, buf, size, ti);
safefree(buf); safefree(buf);
@ -560,6 +566,21 @@ ogm_reader_c::create_packetizer(int64_t tid) {
break; break;
#endif #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: default:
die("ogm_reader: Don't know how to create a packetizer for stream " die("ogm_reader: Don't know how to create a packetizer for stream "
"type %d.\n", (int)dmx->stype); "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_pagein(&dmx->os, og);
ogg_stream_packetout(&dmx->os, &op); ogg_stream_packetout(&dmx->os, &op);
dmx->packet_data.push_back((unsigned char *)safememdup(op.packet, op.bytes)); dmx->packet_data.push_back(memory_cptr(new memory_c((unsigned char *)
dmx->packet_sizes.push_back(op.bytes); safememdup(op.packet,
op.bytes),
op.bytes, true)));
dmx->serialno = ogg_page_serialno(og); dmx->serialno = ogg_page_serialno(og);
/* /*
@ -653,6 +676,24 @@ ogm_reader_c::handle_new_stream(ogg_page *og) {
return; 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 // FLAC
if ((op.bytes >= 4) && !strncmp((char *)op.packet, "fLaC", 4)) { if ((op.bytes >= 4) && !strncmp((char *)op.packet, "fLaC", 4)) {
if (!demuxing_requested('a', sdemuxers.size() - 1)) 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) if (dmx->units_processed <= dmx->flac_header_packets)
continue; continue;
for (i = 0; i < (int)dmx->nh_packet_data.size(); i++) { for (i = 0; i < (int)dmx->nh_packet_data.size(); i++) {
memory_c *mem = new memory_c(dmx->nh_packet_data[i], memory_c *mem = dmx->nh_packet_data[i]->clone();
dmx->nh_packet_sizes[i], true);
PTZR(dmx->ptzr)->process(new packet_t(mem, 0)); PTZR(dmx->ptzr)->process(new packet_t(mem, 0));
} }
dmx->nh_packet_data.clear(); dmx->nh_packet_data.clear();
@ -804,6 +844,42 @@ ogm_reader_c::process_page(ogg_page *og) {
} }
#endif #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_BITS01) >> 6;
duration_len |= (*op.packet & PACKET_LEN_BITS2) << 1; duration_len |= (*op.packet & PACKET_LEN_BITS2) << 1;
if ((duration_len > 0) && (op.bytes >= (duration_len + 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 #if defined HAVE_FLAC_FORMAT_H
while ((dmx->packet_data.size() < dmx->flac_header_packets) && while ((dmx->packet_data.size() < dmx->flac_header_packets) &&
(ogg_stream_packetout(&dmx->os, &op) == 1)) { (ogg_stream_packetout(&dmx->os, &op) == 1)) {
dmx->packet_data.push_back((unsigned char *) memory_c *mem = new memory_c((unsigned char *)
safememdup(op.packet, op.bytes)); safememdup(op.packet, op.bytes),
dmx->packet_sizes.push_back(op.bytes); op.bytes, true);
dmx->packet_data.push_back(memory_cptr(mem));
} }
if (dmx->packet_data.size() >= dmx->flac_header_packets) if (dmx->packet_data.size() >= dmx->flac_header_packets)
dmx->headers_read = true; dmx->headers_read = true;
while (ogg_stream_packetout(&dmx->os, &op) == 1) { while (ogg_stream_packetout(&dmx->os, &op) == 1) {
dmx->nh_packet_data.push_back((unsigned char *) memory_c *mem = new memory_c((unsigned char *)
safememdup(op.packet, op.bytes)); safememdup(op.packet, op.bytes),
dmx->nh_packet_sizes.push_back(op.bytes); op.bytes, true);
dmx->nh_packet_data.push_back(memory_cptr(mem));
} }
#else #else
dmx->headers_read = true; dmx->headers_read = true;
@ -914,9 +992,10 @@ ogm_reader_c::process_header_packets(ogm_demuxer_t *dmx) {
ogg_stream_reset(&dmx->os); ogg_stream_reset(&dmx->os);
return; return;
} }
dmx->packet_data.push_back((unsigned char *) memory_c *mem = new memory_c((unsigned char *)
safememdup(op.packet, op.bytes)); safememdup(op.packet, op.bytes),
dmx->packet_sizes.push_back(op.bytes); op.bytes, true);
dmx->packet_data.push_back(memory_cptr(mem));
} }
if (dmx->stype == OGM_STREAM_TYPE_VORBIS) { 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()); mxinfo("File '%s': container: Ogg/OGM%s\n", ti.fname.c_str(), info.c_str());
for (i = 0; i < sdemuxers.size(); i++) { for (i = 0; i < sdemuxers.size(); i++) {
if (sdemuxers[i]->stype == OGM_STREAM_TYPE_VIDEO) { 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); memcpy(fourcc, sth->subtype, 4);
fourcc[4] = 0; fourcc[4] = 0;
} }
@ -1091,8 +1170,7 @@ ogm_reader_c::handle_stream_comments() {
if ((dmx->stype == OGM_STREAM_TYPE_FLAC) || if ((dmx->stype == OGM_STREAM_TYPE_FLAC) ||
(dmx->packet_data.size() < 2)) (dmx->packet_data.size() < 2))
continue; continue;
comments = extract_vorbis_comments(dmx->packet_data[1], comments = extract_vorbis_comments(dmx->packet_data[1]);
dmx->packet_sizes[1]);
if (comments == NULL) if (comments == NULL)
continue; continue;
chapters.clear(); chapters.clear();

View File

@ -30,6 +30,7 @@
#include "mm_io.h" #include "mm_io.h"
#include "common.h" #include "common.h"
#include "pr_generic.h" #include "pr_generic.h"
#include "theora_common.h"
#define OGM_STREAM_TYPE_UNKNOWN 0 #define OGM_STREAM_TYPE_UNKNOWN 0
#define OGM_STREAM_TYPE_VORBIS 1 #define OGM_STREAM_TYPE_VORBIS 1
@ -40,6 +41,7 @@
#define OGM_STREAM_TYPE_TEXT 6 #define OGM_STREAM_TYPE_TEXT 6
#define OGM_STREAM_TYPE_FLAC 7 #define OGM_STREAM_TYPE_FLAC 7
#define OGM_STREAM_TYPE_AAC 8 #define OGM_STREAM_TYPE_AAC 8
#define OGM_STREAM_TYPE_THEORA 9
#if defined(HAVE_FLAC_FORMAT_H) #if defined(HAVE_FLAC_FORMAT_H)
class flac_header_extractor_c { class flac_header_extractor_c {
@ -69,26 +71,23 @@ struct ogm_demuxer_t {
int units_processed, vorbis_rate; int units_processed, vorbis_rate;
bool headers_read, native_mode; bool headers_read, native_mode;
string language, title; string language, title;
vector<unsigned char *> packet_data, nh_packet_data; vector<memory_cptr> packet_data, nh_packet_data;
vector<int> packet_sizes, nh_packet_sizes;
#if defined(HAVE_FLAC_FORMAT_H) #if defined(HAVE_FLAC_FORMAT_H)
flac_header_extractor_c *fhe; flac_header_extractor_c *fhe;
int flac_header_packets, channels, bits_per_sample; int flac_header_packets, channels, bits_per_sample;
int64_t last_granulepos;
#endif #endif
int64_t last_granulepos, last_keyframe_number;
bool in_use; bool in_use;
theora_identification_header_t theora;
ogm_demuxer_t(): ogm_demuxer_t():
ptzr(-1), stype(0), serialno(0), eos(0), units_processed(0), ptzr(-1), stype(0), serialno(0), eos(0), units_processed(0),
vorbis_rate(0), headers_read(false), native_mode(true), vorbis_rate(0), headers_read(false), native_mode(true),
last_granulepos(0), last_keyframe_number(-1),
in_use(false) { in_use(false) {
memset(&os, 0, sizeof(ogg_stream_state)); 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 { class ogm_reader_c: public generic_reader_c {

View File

@ -46,21 +46,34 @@ vorbis_packetizer_c::vorbis_packetizer_c(generic_reader_c *_reader,
timecode_offset(0) { timecode_offset(0) {
int i; int i;
ogg_packet ogg_headers[3];
memset(headers, 0, 3 * sizeof(ogg_packet)); headers.push_back(memory_cptr(new memory_c((unsigned char *)
headers[0].packet = (unsigned char *)safememdup(d_header, l_header); safememdup(d_header, l_header),
headers[1].packet = (unsigned char *)safememdup(d_comments, l_comments); l_header)));
headers[2].packet = (unsigned char *)safememdup(d_codecsetup, l_codecsetup); headers.push_back(memory_cptr(new memory_c((unsigned char *)
headers[0].bytes = l_header; safememdup(d_comments,
headers[1].bytes = l_comments; l_comments),
headers[2].bytes = l_codecsetup; l_comments)));
headers[0].b_o_s = 1; headers.push_back(memory_cptr(new memory_c((unsigned char *)
headers[1].packetno = 1; safememdup(d_codecsetup,
headers[2].packetno = 2; 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_info_init(&vi);
vorbis_comment_init(&vc); vorbis_comment_init(&vc);
for (i = 0; i < 3; i++) 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 " throw error_c("Error: vorbis_packetizer: Could not extract the "
"stream's parameters from the first packets.\n"); "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() { 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_info_clear(&vi);
vorbis_comment_clear(&vc); vorbis_comment_clear(&vc);
} }
void void
vorbis_packetizer_c::set_headers() { vorbis_packetizer_c::set_headers() {
unsigned char *buffer; memory_cptr codecprivate;
int n, offset, i, lsize;
set_codec_id(MKV_A_VORBIS); set_codec_id(MKV_A_VORBIS);
// We use lacing for the blocks. The first bytes is the number of codecprivate = lace_memory_xiph(headers);
// packets being laced which is one less than the number of blocks that set_codec_private(codecprivate->get(), codecprivate->get_size());
// 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);
set_audio_sampling_freq((float)vi.rate); set_audio_sampling_freq((float)vi.rate);
set_audio_channels(vi.channels); set_audio_channels(vi.channels);
@ -240,10 +217,9 @@ vorbis_packetizer_c::can_connect_to(generic_packetizer_c *src,
return CAN_CONNECT_NO_FORMAT; return CAN_CONNECT_NO_FORMAT;
connect_check_a_samplerate(vi.rate, vsrc->vi.rate); connect_check_a_samplerate(vi.rate, vsrc->vi.rate);
connect_check_a_channels(vi.channels, vsrc->vi.channels); connect_check_a_channels(vi.channels, vsrc->vi.channels);
if ((headers[2].bytes != vsrc->headers[2].bytes) || if ((headers[2]->get_size() != vsrc->headers[2]->get_size()) ||
(headers[2].packet == NULL) || memcmp(headers[2]->get(), vsrc->headers[2]->get(),
(vsrc->headers[2].packet == NULL) || headers[2]->get_size())) {
memcmp(headers[2].packet, vsrc->headers[2].packet, headers[2].bytes)) {
error_message = "The Vorbis codebooks are different; such tracks cannot " error_message = "The Vorbis codebooks are different; such tracks cannot "
"be concatenated without reencoding"; "be concatenated without reencoding";
return CAN_CONNECT_NO_PARAMETERS; return CAN_CONNECT_NO_PARAMETERS;

View File

@ -30,9 +30,9 @@
class vorbis_packetizer_c: public generic_packetizer_c { class vorbis_packetizer_c: public generic_packetizer_c {
private: private:
int64_t last_bs, samples, last_samples_sum, last_timecode, timecode_offset; int64_t last_bs, samples, last_samples_sum, last_timecode, timecode_offset;
vector<memory_cptr> headers;
vorbis_info vi; vorbis_info vi;
vorbis_comment vc; vorbis_comment vc;
ogg_packet headers[3];
public: public:
vorbis_packetizer_c(generic_reader_c *_reader, vorbis_packetizer_c(generic_reader_c *_reader,