mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-25 20:32:33 +00:00
mkvextract: implement support for Opus
This commit is contained in:
parent
b914d14431
commit
79e2bda369
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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/"))
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
7
tests/test-404opus_extraction.rb
Executable file
7
tests/test-404opus_extraction.rb
Executable 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
|
Loading…
Reference in New Issue
Block a user