Skip to content

Commit

Permalink
support opus in isobmff
Browse files Browse the repository at this point in the history
  • Loading branch information
nu774 committed Jan 21, 2024
1 parent c6d97e8 commit 64996aa
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 4 deletions.
21 changes: 21 additions & 0 deletions input/MP4Source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "ALACPacketDecoder.h"
#endif
#include "FLACPacketDecoder.h"
#include "OpusPacketDecoder.h"

unsigned
MP4Edits::editForPosition(int64_t position, int64_t *offset_in_edit) const
Expand Down Expand Up @@ -57,6 +58,7 @@ MP4Source::MP4Source(const std::shared_ptr<FILE> &fp)
#ifdef QAAC
case 'mp4a': setupMPEG4Audio(); break;
#endif
case 'Opus': setupOpus(); break;
default: throw std::runtime_error("Not supported input codec");
}

Expand Down Expand Up @@ -392,6 +394,23 @@ void MP4Source::setupMPEG4Audio()
}
#endif

void MP4Source::setupOpus()
{
const char *dOpsprop = "mdia.minf.stbl.stsd.Opus.dOps.data";
std::vector<uint8_t> dOps;
{
uint8_t *value;
uint32_t size;
m_file.GetTrackBytesProperty(m_track_id, dOpsprop, &value, &size);
std::copy(value, value + size, std::back_inserter(dOps));
MP4Free(value);
}
m_decoder = std::make_shared<OpusPacketDecoder>(this);
m_decoder->setMagicCookie(dOps);
m_iasbd = ((FLACPacketDecoder*)m_decoder.get())->getInputFormat();
m_oasbd = m_decoder->getSampleFormat();
}

unsigned MP4Source::getMaxFrameDependency()
{
switch (m_iasbd.mFormatID) {
Expand All @@ -402,6 +421,8 @@ unsigned MP4Source::getMaxFrameDependency()
case 'aach':
case 'aacp':
return m_iasbd.mSampleRate / 2 / m_iasbd.mFramesPerPacket;
case 'opus':
return 4;
}
return 1;
}
Expand Down
1 change: 1 addition & 0 deletions input/MP4Source.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class MP4Source: public ISeekableSource, public ITagParser,
void setupALAC();
void setupFLAC();
void setupMPEG4Audio();
void setupOpus();
unsigned getMaxFrameDependency();
unsigned getDecoderDelay();
};
122 changes: 122 additions & 0 deletions input/OpusPacketDecoder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include "OpusPacketDecoder.h"
#include "cautil.h"

#define CHECK(expr) do { if (!(expr)) throw std::runtime_error("!?"); } \
while (0)

// cf. https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810004.3.9
const uint8_t vorbis_channel_layout[8][8] = {
{ 0 },
{ 0, 1 },
{ 0, 2, 1 },
{ 0, 1, 2, 3 },
{ 0, 2, 1, 3, 4 },
{ 0, 2, 1, 5, 3, 4 },
{ 0, 2, 1, 6, 5, 3, 4 },
{ 0, 2, 1, 7, 5, 6, 3, 4 },
};

bool LibOpusModule::load(const std::wstring &path)
{
if (!m_dl.load(path))
return false;
try {
CHECK(get_version_string = m_dl.fetch("opus_get_version_string"));
CHECK(strerror = m_dl.fetch("opus_strerror"));
CHECK(packet_get_nb_samples = m_dl.fetch("opus_packet_get_nb_samples"));
CHECK(multistream_decoder_create = m_dl.fetch("opus_multistream_decoder_create"));
CHECK(multistream_decode_float = m_dl.fetch("opus_multistream_decode_float"));
CHECK(multistream_decoder_ctl = m_dl.fetch("opus_multistream_decoder_ctl"));
CHECK(multistream_decoder_destroy = m_dl.fetch("opus_multistream_decoder_destroy"));
return true;
} catch (...) {
m_dl.reset();
return false;
}
}

OpusPacketDecoder::OpusPacketDecoder(IPacketFeeder *feeder)
: m_module(LibOpusModule::instance())
, m_feeder(feeder)
{
if (!m_module.loaded()) throw std::runtime_error("libopus not loaded");
memset(&m_iasbd, 0, sizeof(m_iasbd));
memset(&m_oasbd, 0, sizeof(m_oasbd));
}

OpusPacketDecoder::~OpusPacketDecoder()
{
}

void OpusPacketDecoder::reset()
{
if (m_decoder) {
m_module.multistream_decoder_ctl(m_decoder.get(), OPUS_RESET_STATE);
m_packet_buffer.clear();
m_decode_buffer.reset();
}
}

void OpusPacketDecoder::setMagicCookie(const std::vector<uint8_t> &cookie)
{
int channel_count = 0;
unsigned pre_skip = 0;
opus_uint32 input_sample_rate = 0;
int output_gain = 0;
int mapping_family = 0;
int stream_count = 0;
int coupled_count = 0;
unsigned char mapping[256] = { 0 };
if (cookie.size() >= 11) {
channel_count = cookie[1];
pre_skip = (cookie[2]<<8|cookie[3]);
input_sample_rate = (cookie[4]<<24|cookie[5]<<16|cookie[6]<<8|cookie[7]);
output_gain = (cookie[8]<<8|cookie[9]);
mapping_family = cookie[10];
if (mapping_family > 0 && cookie.size() >= 13 + channel_count) {
stream_count = cookie[11];
coupled_count = cookie[12];
if (mapping_family == 1 && channel_count <= 8) {
for (int i = 0; i < channel_count; ++i) {
mapping[i] = cookie[13 + vorbis_channel_layout[channel_count-1][i]];
}
} else {
memcpy(mapping, &cookie[13], channel_count);
}
} else {
stream_count = 1;
coupled_count = channel_count - 1;
mapping[0] = 0;
mapping[1] = 1;
}
}
if (channel_count && input_sample_rate) {
int err;
OpusMSDecoder *decoder = m_module.multistream_decoder_create(48000,
channel_count, stream_count, coupled_count, mapping, &err);
if (err) throw std::runtime_error(m_module.strerror(err));
m_decoder = std::shared_ptr<OpusMSDecoder>(decoder, m_module.multistream_decoder_destroy);
m_iasbd.mFormatID = util::fourcc("opus");
m_iasbd.mSampleRate = 48000;
m_iasbd.mChannelsPerFrame = channel_count;
m_oasbd = cautil::buildASBDForPCM2(48000, channel_count, 32, 32, kAudioFormatFlagIsFloat);
}
}

size_t OpusPacketDecoder::decode(void *data, size_t nsamples)
{
if (m_decoder && m_feeder->feed(&m_packet_buffer)) {
int fpp = m_module.packet_get_nb_samples(m_packet_buffer.data(), m_packet_buffer.size(), 48000);
m_decode_buffer.reserve(fpp * m_iasbd.mChannelsPerFrame);
int nc = m_module.multistream_decode_float(m_decoder.get(),
m_packet_buffer.data(),
m_packet_buffer.size(),
m_decode_buffer.write_ptr(), fpp * m_iasbd.mChannelsPerFrame, 0);
m_decode_buffer.commit(nc * m_iasbd.mChannelsPerFrame);
nsamples = std::min(nsamples, (size_t)(m_decode_buffer.count() / m_iasbd.mChannelsPerFrame));
std::memcpy(data, m_decode_buffer.read_ptr(), nsamples * m_iasbd.mChannelsPerFrame * sizeof(float));
m_decode_buffer.advance(nsamples * m_iasbd.mChannelsPerFrame);
return nsamples;
}
return 0;
}
54 changes: 54 additions & 0 deletions input/OpusPacketDecoder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#ifndef OPUSPACKETDECODER_H
#define OPUSPACKETDECODER_H

#include <memory>
#include "PacketDecoder.h"
#include "dl.h"
#include <opus_multistream.h>

struct HINSTANCE__;

class LibOpusModule {
DL m_dl;
private:
LibOpusModule() {
load(L"opus.dll");
if (!loaded()) load(L"libopus-0.dll");
}
LibOpusModule(const LibOpusModule&);
LibOpusModule& operator=(const LibOpusModule&);
public:
static LibOpusModule &instance() {
static LibOpusModule self;
return self;
}
bool load(const std::wstring &path);
bool loaded() const { return m_dl.loaded(); }

const char *(*get_version_string)();
const char *(*strerror)(int);
int (*packet_get_nb_samples)(const unsigned char *, opus_int32, opus_int32);
OpusMSDecoder *(*multistream_decoder_create)(opus_int32, int, int, int, const unsigned char *, int *);
int (*multistream_decode_float)(OpusMSDecoder *, const unsigned char *, opus_int32, float *, int, int);
int (*multistream_decoder_ctl)(OpusMSDecoder *, int, ...);
void (*multistream_decoder_destroy)(OpusMSDecoder *);
};

class OpusPacketDecoder: public IPacketDecoder {
LibOpusModule &m_module;
IPacketFeeder *m_feeder;
std::shared_ptr<OpusMSDecoder> m_decoder;
AudioStreamBasicDescription m_iasbd, m_oasbd;
std::vector<uint8_t> m_packet_buffer;
util::FIFO<float> m_decode_buffer;
public:
OpusPacketDecoder(IPacketFeeder *feeder);
~OpusPacketDecoder();
void reset();
const AudioStreamBasicDescription &getInputFormat() { return m_iasbd; }
const AudioStreamBasicDescription &getSampleFormat() { return m_oasbd; }
void setMagicCookie(const std::vector<uint8_t> &cookie);
size_t decode(void *data, size_t nsamples);
};

#endif
3 changes: 3 additions & 0 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "TakSource.h"
#include "WavpackSource.h"
#include "AvisynthSource.h"
#include "OpusPacketDecoder.h"
#ifdef REFALAC
#include "ALACEncoderX.h"
#endif
Expand Down Expand Up @@ -1313,6 +1314,8 @@ int wmain1(int argc, wchar_t **argv)
TakModule::instance().compatible() ? "compatible"
: "incompatible");
}
if (LibOpusModule::instance().loaded())
LOG(L"%hs\n", LibOpusModule::instance().get_version_string());
return 0;
}
#ifdef QAAC
Expand Down
3 changes: 2 additions & 1 deletion vcproject/common/common.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<WarningLevel>Level3</WarningLevel>
<DisableSpecificWarnings>4018;4244;4267;4838;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
<AdditionalIncludeDirectories>..\..;..\..\input;..\..\output;..\..\filters;..\..\include;..\..\CoreAudio;..\..\alac;..\..\uchardet;$(mp4v2Includes);$(taglibIncludes);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..;..\..\input;..\..\output;..\..\filters;..\..\include;..\..\CoreAudio;..\..\alac;..\..\uchardet;..\..\include\opus;$(mp4v2Includes);$(taglibIncludes);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;MP4V2_USE_STATIC_LIB;MP4V2_NO_STDINT_DEFS;TAGLIB_STATIC;WIN32;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
Expand Down Expand Up @@ -73,6 +73,7 @@
<ClCompile Include="..\..\input\FLACPacketDecoder.cpp" />
<ClCompile Include="..\..\input\FLACSource.cpp" />
<ClCompile Include="..\..\input\LibSndfileSource.cpp" />
<ClCompile Include="..\..\input\OpusPacketDecoder.cpp" />
<ClCompile Include="..\..\input\RawSource.cpp" />
<ClCompile Include="..\..\input\TakSource.cpp" />
<ClCompile Include="..\..\input\WaveSource.cpp" />
Expand Down
5 changes: 4 additions & 1 deletion vcproject/common/common.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,8 @@
<ClCompile Include="..\..\metadata.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\input\OpusPacketDecoder.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
</Project>
2 changes: 1 addition & 1 deletion vcproject/qaac/qaac.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
<WarningLevel>Level3</WarningLevel>
<DisableSpecificWarnings>4018;4091;4244;4267;4838;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
<AdditionalIncludeDirectories>..\..;..\..\input;..\..\output;..\..\filters;..\..\include;..\..\CoreAudio;..\..\alac;$(mp4v2Includes);$(taglibIncludes)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..;..\..\input;..\..\output;..\..\filters;..\..\include;..\..\CoreAudio;..\..\alac;..\..\include\opus;$(mp4v2Includes);$(taglibIncludes)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;MP4V2_USE_STATIC_LIB;MP4V2_NO_STDINT_DEFS;TAGLIB_STATIC;QAAC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
Expand Down
2 changes: 1 addition & 1 deletion vcproject/refalac/refalac.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
<DisableSpecificWarnings>4018;4091;4244;4267;4838;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;MP4V2_USE_STATIC_LIB;MP4V2_NO_STDINT_DEFS;TAGLIB_STATIC;REFALAC;NO_COREAUDIO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..;..\..\input;..\..\output;..\..\filters;..\..\include;..\..\CoreAudio;..\..\alac;$(mp4v2Includes);$(taglibIncludes)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..;..\..\input;..\..\output;..\..\filters;..\..\include;..\..\CoreAudio;..\..\alac;..\..\include\opus;$(mp4v2Includes);$(taglibIncludes)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
Expand Down

0 comments on commit 64996aa

Please sign in to comment.