Support additional MP4 codecs (#1293, #1338)

Adds MP4::Properties::Codec enum values AC3, EAC3, FLAC, DTS, Opus and a
MP4::Properties::codecId() method.
Parse high-res FLAC bitrate from MP4 dfLa box.
Parse E-AC-3 bitrate from MP4 dec3 box.

The test files were generated using ffmpeg:

ffmpeg -hide_banner -y -f lavfi \
  -i "sine=frequency=440:sample_rate=48000:duration=1" \
  -c:a flac -ac 2 -flags +bitexact -fflags +bitexact \
  -metadata:s:a:0 encoder= -metadata encoder= \
  -f mp4 tests/data/flac.m4a

ffmpeg -hide_banner -y -f lavfi \
  -i "sine=frequency=440:sample_rate=48000:duration=1" \
  -c:a libopus -ac 2 -flags +bitexact -fflags +bitexact \
  -metadata:s:a:0 encoder= -metadata encoder= \
  -f mp4 tests/data/opus.m4a

ffmpeg -hide_banner -y -f lavfi \
  -i "sine=frequency=440:sample_rate=48000:duration=1" \
  -c:a ac3 -b:a 128k -ac 2 -strict experimental \
  -flags +bitexact -fflags +bitexact \
  -metadata:s:a:0 encoder= -metadata encoder= \
  -f mp4 tests/data/ac3.m4a

ffmpeg -hide_banner -y -f lavfi \
  -i "sine=frequency=440:sample_rate=48000:duration=1" \
  -c:a eac3 -b:a 128k -ac 2 -strict experimental \
  -flags +bitexact -fflags +bitexact \
  -metadata:s:a:0 encoder= -metadata encoder= \
  -f mp4 tests/data/eac3.m4a

ffmpeg -hide_banner -y -f lavfi \
  -i "sine=frequency=440:sample_rate=96000:duration=1" \
  -c:a flac -sample_fmt s32 -ac 2 -flags +bitexact -fflags +bitexact \
  -metadata:s:a:0 encoder= -metadata encoder= \
  -f mp4 tests/data/flac96.m4a
This commit is contained in:
Urs Fleisch
2026-08-07 14:54:50 +02:00
committed by GitHub
parent ce3b45f186
commit 2a5fc12b68
8 changed files with 149 additions and 1 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+74
View File
@@ -130,6 +130,11 @@ class TestMP4 : public CppUnit::TestFixture
CPPUNIT_TEST(testPropertiesALAC);
CPPUNIT_TEST(testPropertiesALACWithoutBitrate);
CPPUNIT_TEST(testPropertiesAACWithoutLength);
CPPUNIT_TEST(testPropertiesAC3);
CPPUNIT_TEST(testPropertiesEAC3);
CPPUNIT_TEST(testPropertiesFLAC);
CPPUNIT_TEST(testPropertiesFLACHighResolution);
CPPUNIT_TEST(testPropertiesOpus);
CPPUNIT_TEST(testPropertiesM4V);
CPPUNIT_TEST(testFreeForm);
CPPUNIT_TEST(testCheckValid);
@@ -271,6 +276,75 @@ public:
CPPUNIT_ASSERT_EQUAL(MP4::Properties::AAC, f.audioProperties()->codec());
}
void testPropertiesAC3()
{
MP4::File f(TEST_FILE_PATH_C("ac3.m4a"));
CPPUNIT_ASSERT(f.audioProperties());
CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->lengthInSeconds());
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate());
CPPUNIT_ASSERT(f.audioProperties()->bitrate() > 0);
CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
CPPUNIT_ASSERT_EQUAL(MP4::Properties::AC3, f.audioProperties()->codec());
CPPUNIT_ASSERT_EQUAL(String("ac-3"), f.audioProperties()->codecId());
}
void testPropertiesEAC3()
{
MP4::File f(TEST_FILE_PATH_C("eac3.m4a"));
CPPUNIT_ASSERT(f.audioProperties());
CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->lengthInSeconds());
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate());
CPPUNIT_ASSERT_EQUAL(128, f.audioProperties()->bitrate());
CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
CPPUNIT_ASSERT_EQUAL(MP4::Properties::EAC3, f.audioProperties()->codec());
CPPUNIT_ASSERT_EQUAL(String("ec-3"), f.audioProperties()->codecId());
}
void testPropertiesFLAC()
{
MP4::File f(TEST_FILE_PATH_C("flac.m4a"));
CPPUNIT_ASSERT(f.audioProperties());
CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->lengthInSeconds());
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate());
CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample());
CPPUNIT_ASSERT(f.audioProperties()->bitrate() > 0);
CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
CPPUNIT_ASSERT_EQUAL(MP4::Properties::FLAC, f.audioProperties()->codec());
CPPUNIT_ASSERT_EQUAL(String("fLaC"), f.audioProperties()->codecId());
}
void testPropertiesFLACHighResolution()
{
// The AudioSampleEntry sample rate field cannot hold rates above 65535 Hz,
// so the exact values must come from the FLAC STREAMINFO in the 'dfLa' box.
MP4::File f(TEST_FILE_PATH_C("flac96.m4a"));
CPPUNIT_ASSERT(f.audioProperties());
CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->lengthInSeconds());
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
CPPUNIT_ASSERT_EQUAL(96000, f.audioProperties()->sampleRate());
CPPUNIT_ASSERT_EQUAL(24, f.audioProperties()->bitsPerSample());
CPPUNIT_ASSERT(f.audioProperties()->bitrate() > 0);
CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
CPPUNIT_ASSERT_EQUAL(MP4::Properties::FLAC, f.audioProperties()->codec());
CPPUNIT_ASSERT_EQUAL(String("fLaC"), f.audioProperties()->codecId());
}
void testPropertiesOpus()
{
MP4::File f(TEST_FILE_PATH_C("opus.m4a"));
CPPUNIT_ASSERT(f.audioProperties());
CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->lengthInSeconds());
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate());
CPPUNIT_ASSERT(f.audioProperties()->bitrate() > 0);
CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted());
CPPUNIT_ASSERT_EQUAL(MP4::Properties::Opus, f.audioProperties()->codec());
CPPUNIT_ASSERT_EQUAL(String("Opus"), f.audioProperties()->codecId());
}
void testPropertiesM4V()
{
MP4::File f(TEST_FILE_PATH_C("blank_video.m4v"));