mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-23 19:31:44 +00:00
Added support for Ogg/Theora (not specs compliant because we haven't decided about the specs yet.
This commit is contained in:
parent
0591a72ca1
commit
ccdcebaddf
@ -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>
|
||||
|
||||
* mkvmerge: Changed the CodecID for AAC audio tracks to "A_AAC" by
|
||||
|
100
src/common/common_memory.cpp
Normal file
100
src/common/common_memory.cpp
Normal 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;
|
||||
}
|
@ -126,4 +126,7 @@ struct 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
|
||||
|
@ -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"
|
||||
|
78
src/common/theora_common.cpp
Normal file
78
src/common/theora_common.cpp
Normal 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");
|
||||
}
|
65
src/common/theora_common.h
Normal file
65
src/common/theora_common.h
Normal 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
|
@ -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<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)
|
||||
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) &&
|
||||
|
@ -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();
|
||||
|
@ -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<unsigned char *> packet_data, nh_packet_data;
|
||||
vector<int> packet_sizes, nh_packet_sizes;
|
||||
vector<memory_cptr> 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 {
|
||||
|
@ -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;
|
||||
|
@ -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<memory_cptr> headers;
|
||||
vorbis_info vi;
|
||||
vorbis_comment vc;
|
||||
ogg_packet headers[3];
|
||||
|
||||
public:
|
||||
vorbis_packetizer_c(generic_reader_c *_reader,
|
||||
|
Loading…
Reference in New Issue
Block a user