Added support for extracting Theora video tracks into Ogg files. Fix for bug 298.

This commit is contained in:
Moritz Bunkus 2008-09-12 17:04:11 +00:00
parent 8a4cd7fc44
commit 2e02c96599
4 changed files with 130 additions and 5 deletions

View File

@ -1,5 +1,8 @@
2008-09-12 Moritz Bunkus <moritz@bunkus.org>
* mkvextract: new feature: Added support for extracting Theora
video tracks into Ogg files.
* mkvmerge: bug fix: Fixed the frame type (key or non-key frame)
detection for Theora tracks.

View File

@ -169,6 +169,8 @@ xtr_base_c::create_extractor(const string &new_codec_id,
else if ((new_codec_id == MKV_V_MPEG1) ||
(new_codec_id == MKV_V_MPEG2))
return new xtr_mpeg1_2_video_c(new_codec_id, new_tid, tspec);
else if (new_codec_id == MKV_V_THEORA)
return new xtr_oggtheora_c(new_codec_id, new_tid, tspec);
// Subtitle formats
else if ((new_codec_id == MKV_S_TEXTUTF8) ||

View File

@ -99,6 +99,10 @@ xtr_oggbase_c::handle_frame(memory_cptr &frame,
previous_end = timecode + duration;
}
xtr_oggbase_c::~xtr_oggbase_c() {
ogg_stream_clear(&os);
}
void
xtr_oggbase_c::finish_file() {
ogg_packet op;
@ -117,7 +121,6 @@ xtr_oggbase_c::finish_file() {
op.granulepos = previous_end * sfreq / 1000000000;
ogg_stream_packetin(&os, &op);
flush_pages();
ogg_stream_clear(&os);
}
void
@ -392,3 +395,102 @@ xtr_oggkate_c::handle_frame(memory_cptr &frame,
packetno++;
}
// ------------------------------------------------------------------------
xtr_oggtheora_c::xtr_oggtheora_c(const string &_codec_id,
int64_t _tid,
track_spec_t &tspec)
: xtr_oggbase_c(_codec_id, _tid, tspec)
, keyframe_number(0)
, non_keyframe_number(-1)
{
}
void
xtr_oggtheora_c::create_file(xtr_base_c *_master,
KaxTrackEntry &track) {
KaxCodecPrivate *priv = FINDFIRST(&track, KaxCodecPrivate);
if (NULL == priv)
mxerror("Track " LLD " with the CodecID '%s' is missing the \"codec private\" element and cannot be extracted.\n", tid, codec_id.c_str());
init_content_decoder(track);
memory_cptr mpriv = decode_codec_private(priv);
vector<memory_cptr> header_packets;
try {
header_packets = unlace_memory_xiph(mpriv);
if (header_packets.empty())
throw false;
theora_parse_identification_header(header_packets[0]->get(), header_packets[0]->get_size(), theora);
} catch (...) {
mxerror("Track " LLD " with the CodecID '%s' does not contain valid headers.\n", tid, codec_id.c_str());
}
xtr_oggbase_c::create_file(_master, track);
ogg_packet op;
for (packetno = 0; packetno < header_packets.size(); packetno++) {
// Handle all the header packets: ID header, comments, etc
op.b_o_s = (0 == packetno ? 1 : 0);
op.e_o_s = 0;
op.packetno = packetno;
op.packet = header_packets[packetno]->get();
op.bytes = header_packets[packetno]->get_size();
op.granulepos = 0;
ogg_stream_packetin(&os, &op);
if (0 == packetno) /* ID header must be alone on a separate page */
flush_pages();
}
/* flush at last header, data must start on a new page */
flush_pages();
}
void
xtr_oggtheora_c::handle_frame(memory_cptr &frame,
KaxBlockAdditions *additions,
int64_t timecode,
int64_t duration,
int64_t bref,
int64_t fref,
bool keyframe,
bool discardable,
bool references_valid) {
content_decoder.reverse(frame, CONTENT_ENCODING_SCOPE_BLOCK);
if (frame->get_size() && (0x00 == (frame->get()[0] & 0x40))) {
keyframe = true;
keyframe_number += non_keyframe_number + 1;
non_keyframe_number = 0;
} else
non_keyframe_number += 1;
ogg_packet op;
op.b_o_s = 0;
op.e_o_s = 0;
op.packetno = packetno;
op.packet = frame->get();
op.bytes = frame->get_size();
op.granulepos = (keyframe_number << theora.kfgshift) | (non_keyframe_number & ((1 << theora.kfgshift) - 1));
ogg_stream_packetin(&os, &op);
write_pages();
packetno++;
// mxinfo("Theora kfgshift %d granulepos 0x%08x %08x keyframe_number " LLD " non_keyframe_number " LLD "%s size %d\n",
// theora.kfgshift, (uint32_t)(op.granulepos >> 32), (uint32_t)(op.granulepos & 0xffffffff), keyframe_number, non_keyframe_number, keyframe ? " key" : "",
// frame->get_size());
}
void
xtr_oggtheora_c::finish_file() {
flush_pages();
}

View File

@ -20,6 +20,7 @@
#include <ogg/ogg.h>
#include "kate_common.h"
#include "theora_common.h"
#include "xtr_base.h"
class xtr_flac_c: public xtr_base_c {
@ -39,12 +40,11 @@ public:
public:
xtr_oggbase_c(const string &_codec_id, int64_t _tid, track_spec_t &tspec);
virtual ~xtr_oggbase_c();
virtual void create_file(xtr_base_c *_master, KaxTrackEntry &track);
virtual void handle_frame(memory_cptr &frame, KaxBlockAdditions *additions,
int64_t timecode, int64_t duration, int64_t bref,
int64_t fref, bool keyframe, bool discardable,
bool references_valid);
virtual void handle_frame(memory_cptr &frame, KaxBlockAdditions *additions, int64_t timecode, int64_t duration, int64_t bref,
int64_t fref, bool keyframe, bool discardable, bool references_valid);
virtual void finish_file();
virtual void write_pages();
@ -89,4 +89,22 @@ private:
kate_identification_header_t kate_id_header;
};
class xtr_oggtheora_c: public xtr_oggbase_c {
public:
xtr_oggtheora_c(const string &_codec_id, int64_t _tid, track_spec_t &tspec);
virtual void create_file(xtr_base_c *_master, KaxTrackEntry &track);
virtual void handle_frame(memory_cptr &frame, KaxBlockAdditions *additions, int64_t timecode, int64_t duration, int64_t bref,
int64_t fref, bool keyframe, bool discardable, bool references_valid);
virtual void finish_file();
virtual const char *get_container_name() {
return "Ogg (Theora in Ogg)";
};
private:
theora_identification_header_t theora;
int64_t keyframe_number, non_keyframe_number;
};
#endif