mkvextract: implement support for Opus

This commit is contained in:
Moritz Bunkus 2013-09-15 20:20:14 +02:00
parent b914d14431
commit 79e2bda369
8 changed files with 115 additions and 12 deletions

View File

@ -1,3 +1,8 @@
2013-09-15 Moritz Bunkus <moritz@bunkus.org>
* mkvextract: new feature: Implemented extraction of Opus tracks
into OggOpus files.
2013-09-14 Monty Montgomery <xiphmont@gmail.com>
* mkvinfo: bug fix: The track information summary enabled with

View File

@ -184,9 +184,14 @@ handle_blockgroup(KaxBlockGroup &blockgroup,
this_duration = duration / block->NumberFrames();
}
auto discard_padding = timecode_c::ns(0);
auto kdiscard_padding = FindChild<KaxDiscardPadding>(blockgroup);
if (kdiscard_padding)
discard_padding = timecode_c::ns(kdiscard_padding->GetValue());
auto &data = block->GetBuffer(i);
auto frame = std::make_shared<memory_c>(data.Buffer(), data.Size(), false);
auto f = xtr_frame_t{frame, kadditions, this_timecode, this_duration, bref, fref, false, false, true};
auto f = xtr_frame_t{frame, kadditions, this_timecode, this_duration, bref, fref, false, false, true, discard_padding};
extractor->decode_and_handle_frame(f);
max_timecode = std::max(max_timecode, this_timecode);
@ -231,7 +236,7 @@ handle_simpleblock(KaxSimpleBlock &simpleblock,
auto &data = simpleblock.GetBuffer(i);
auto frame = std::make_shared<memory_c>(data.Buffer(), data.Size(), false);
auto f = xtr_frame_t{frame, nullptr, this_timecode, this_duration, -1, -1, simpleblock.IsKeyframe(), simpleblock.IsDiscardable(), false};
auto f = xtr_frame_t{frame, nullptr, this_timecode, this_duration, -1, -1, simpleblock.IsKeyframe(), simpleblock.IsDiscardable(), false, timecode_c::ns(0)};
extractor->decode_and_handle_frame(f);
max_timecode = std::max(max_timecode, this_timecode);

View File

@ -147,6 +147,8 @@ xtr_base_c::create_extractor(const std::string &new_codec_id,
return new xtr_alac_c(new_codec_id, new_tid, tspec);
else if (new_codec_id == MKV_A_VORBIS)
return new xtr_oggvorbis_c(new_codec_id, new_tid, tspec);
else if (new_codec_id == MKV_A_OPUS)
return new xtr_oggopus_c(new_codec_id, new_tid, tspec);
else if (balg::istarts_with(new_codec_id, "A_AAC"))
return new xtr_aac_c(new_codec_id, new_tid, tspec);
else if (balg::istarts_with(new_codec_id, "A_REAL/"))

View File

@ -19,6 +19,7 @@
#include <matroska/KaxTracks.h>
#include "common/content_decoder.h"
#include "common/timecode.h"
#include "extract/mkvextract.h"
using namespace libmatroska;
@ -28,6 +29,7 @@ struct xtr_frame_t {
KaxBlockAdditions *additions;
int64_t timecode, duration, bref, fref;
bool keyframe, discardable, references_valid;
timecode_c discard_duration;
};
class xtr_base_c {

View File

@ -17,8 +17,10 @@
#include "common/ebml.h"
#include "common/endian.h"
#include "common/hacks.h"
#include "common/random.h"
#include "common/kate.h"
#include "common/opus.h"
#include "common/random.h"
#include "common/version.h"
#include "extract/xtr_ogg.h"
// ------------------------------------------------------------------------
@ -67,7 +69,8 @@ xtr_oggbase_c::create_file(xtr_base_c *master,
void
xtr_oggbase_c::create_standard_file(xtr_base_c *master,
KaxTrackEntry &track) {
KaxTrackEntry &track,
LacingType lacing) {
KaxCodecPrivate *priv = FindChild<KaxCodecPrivate>(&track);
if (!priv)
mxerror(boost::format(Y("Track %1% with the CodecID '%2%' is missing the \"codec private\" element and cannot be extracted.\n")) % m_tid % m_codec_id);
@ -76,11 +79,17 @@ xtr_oggbase_c::create_standard_file(xtr_base_c *master,
memory_cptr mpriv = decode_codec_private(priv);
std::vector<memory_cptr> header_packets;
try {
header_packets = unlace_memory_xiph(mpriv);
if (header_packets.empty())
throw false;
try {
if (lacing == LACING_NONE)
header_packets.push_back(mpriv);
else {
header_packets = unlace_memory_xiph(mpriv);
if (header_packets.empty())
throw false;
}
header_packets_unlaced(header_packets);
@ -214,7 +223,7 @@ xtr_oggvorbis_c::~xtr_oggvorbis_c() {
void
xtr_oggvorbis_c::create_file(xtr_base_c *master,
KaxTrackEntry &track) {
create_standard_file(master, track);
create_standard_file(master, track, LACING_AUTO);
}
void
@ -263,7 +272,7 @@ xtr_oggkate_c::xtr_oggkate_c(const std::string &codec_id,
void
xtr_oggkate_c::create_file(xtr_base_c *master,
KaxTrackEntry &track) {
create_standard_file(master, track);
create_standard_file(master, track, LACING_AUTO);
}
void
@ -316,7 +325,7 @@ xtr_oggtheora_c::xtr_oggtheora_c(const std::string &codec_id,
void
xtr_oggtheora_c::create_file(xtr_base_c *master,
KaxTrackEntry &track) {
create_standard_file(master, track);
create_standard_file(master, track, LACING_AUTO);
}
void
@ -341,3 +350,56 @@ xtr_oggtheora_c::finish_file() {
write_queued_frame(true);
flush_pages();
}
// ------------------------------------------------------------------------
xtr_oggopus_c::xtr_oggopus_c(const std::string &codec_id,
int64_t tid,
track_spec_t &tspec)
: xtr_oggbase_c{codec_id, tid, tspec}
, m_position{timecode_c::ns(0)}
{
m_debug = debugging_requested("opus|opus_extrator");
}
void
xtr_oggopus_c::create_file(xtr_base_c *master,
KaxTrackEntry &track) {
create_standard_file(master, track, LACING_NONE);
}
void
xtr_oggopus_c::header_packets_unlaced(std::vector<memory_cptr> &header_packets) {
auto signature = std::string{"OpusTags"};
auto version = std::string{"unknown encoder; extracted from Matroska with "} + get_version_info("mkvextract");
auto ver_len = version.length();
auto mem = memory_c::alloc(8 + 4 + ver_len + 4);
auto buffer = reinterpret_cast<char *>(mem->get_buffer());
signature.copy(buffer, 8);
put_uint32_le(buffer + 8, ver_len);
version.copy(buffer + 8 + 4, ver_len);
put_uint32_le(buffer + mem->get_size() - 4, 0);
header_packets.push_back(mem);
}
void
xtr_oggopus_c::handle_frame(xtr_frame_t &f) {
try {
auto toc = mtx::opus::toc_t::decode(f.frame);
mxdebug_if(m_debug, boost::format("Position: %1% discard_duration: %2% TOC: %3%\n") % m_position % f.discard_duration % toc);
m_position = m_position + toc.packet_duration - f.discard_duration;
queue_frame(f.frame, m_position.to_samples(48000));
} catch (mtx::opus::exception &ex) {
mxdebug_if(m_debug, boost::format("Exception: %1%\n") % ex.what());
}
}
void
xtr_oggopus_c::finish_file() {
write_queued_frame(true);
flush_pages();
}

View File

@ -50,7 +50,7 @@ public:
virtual void flush_pages();
protected:
virtual void create_standard_file(xtr_base_c *master, KaxTrackEntry &track);
virtual void create_standard_file(xtr_base_c *master, KaxTrackEntry &track, LacingType lacing);
virtual void header_packets_unlaced(std::vector<memory_cptr> &header_packets);
virtual void queue_frame(memory_cptr &frame, int64_t granulepos);
@ -114,4 +114,23 @@ protected:
virtual void header_packets_unlaced(std::vector<memory_cptr> &header_packets);
};
class xtr_oggopus_c: public xtr_oggbase_c {
private:
timecode_c m_position;
public:
xtr_oggopus_c(const std::string &codec_id, int64_t tid, track_spec_t &tspec);
virtual void create_file(xtr_base_c *master, KaxTrackEntry &track);
virtual void handle_frame(xtr_frame_t &f);
virtual void finish_file();
virtual const char *get_container_name() {
return "Ogg (Opus in Ogg)";
};
protected:
virtual void header_packets_unlaced(std::vector<memory_cptr> &header_packets);
};
#endif

View File

@ -249,3 +249,4 @@ T_400opus_experimental:ce4f0bd0c52a9d410ce34b5751416c63:passed:20130703-213929:0
T_401opus_experimental_remux:a910a17383083f2fa75001efd175c352:passed:20130703-213932:0.06857043
T_402opus_output_order:e7d89f40e06bd81db8fa20b2ea6218b8:passed:20130705-115856:0.252820697
T_403opus_remux_final:da99246c61326c47a530f95948b088be:passed:20130705-135811:0.068533558
T_404opus_extraction:58498a76a35cd151b1c75ad913887f00:passed:20130915-201931:0.050758351

View File

@ -0,0 +1,7 @@
#!/usr/bin/ruby -w
describe "mkvmerge / extract Opus from MKA in final mode"
test "extraction" do
extract "data/opus/v-opus.mka", 0 => tmp
hash_tmp
end